Remember state chrome extension

前端 未结 1 663
暖寄归人
暖寄归人 2021-01-15 18:47

I use a chrome extension to fire two content scripts to inject css. If the user opens the page the contentscript-on.js loads (defined in my manifest.json):

m

相关标签:
1条回答
  • 2021-01-15 19:50

    You have two methods (at least), one is "old" and one is "new".

    1. Old: localStorage

      Your extension pages share a common localStorage object you can read/write, and it is persistent through browser restarts.

      Working with it is synchronous:

      var toggle;
      if(localStorage.toggle === undefined){
        localStorage.toggle = true;
      }
      toggle = localStorage.toggle;
      
      chrome.browserAction.onClicked.addListener( function(tab) {
        var toggle = !toggle;
        localStorage.toggle = toggle;
        /* The rest of your code; at this point toggle is saved */
      });
      

      It's simple to work with, but there are downsides: localStorage context is different for content scripts, so they need to communicate via Messaging to get the values from the background script; also, complications arise if the extension is used in Incognito mode.

    2. New: chrome.storage API

      To work with the new method, you need permission "storage" in the manifest (does not generate a warning).

      Also, unlike localStorage, working with it is asynchronous, i.e. you will need to use callbacks:

      function getToggle(callback) { // expects function(value){...}
        chrome.storage.local.get('toggle', function(data){
          if(data.toggle === undefined) {
            callback(true); // default value
          } else {
            callback(data.toggle);
          }
        });
      }
      
      function setToggle(value, callback){ // expects function(){...}
        chrome.storage.local.set({toggle : value}, function(){
          if(chrome.runtime.lastError) {
            throw Error(chrome.runtime.lastError);
          } else {
            callback();
          }
        });
      }
      
      chrome.browserAction.onClicked.addListener( function(tab) {
        getToggle(function(toggle){
          toggle = !toggle;
          setToggle(toggle, function(){
            /* The rest of your code; at this point toggle is saved */
          });
        });
      });
      

      Asynchronous code is a bit harder to work with, but you get some advantages. Namely, content scripts can use chrome.storage directly instead of communicating with the parent, you can watch for changes with onChanged, and you can use chrome.storage.sync instead of (or together with) chrome.storage.local to propagate changes to all browsers a user is logged into.

    EDIT

    I'm including a full solution, since the OP made a mistake of mixing per-tab state and global state.

    contentscript.js

    $(document).ready(function() {
      chrome.storage.local.get('toggle', function(data) {
        if (data.toggle === false) {
          return;
        } else {
          /* do some css inject */
        }
      });
    
      chrome.storage.onChanged.addListener(function(changes, areaName){
        if(areaName == "local" && changes.toggle) { 
          if(changes.toggle.newValue) {
            /* do some css inject */
          } else {
            /* set css to original */
          }
        }
      });
    });
    

    background.js:

        /* getToggle, setToggle as above */
    
        function setIcon(value){
          var path = (value)?"icon-on.png":"icon-off.png";
          chrome.browserAction.setIcon({path: path});
        }
    
        getToggle(setIcon); // Initial state
    
        chrome.browserAction.onClicked.addListener( function(tab) {
          getToggle(function(toggle){
            setToggle(!toggle, function(){
              setIcon(!toggle);
            });
          });
        });
    

    This way, you only need one content script.

    0 讨论(0)
提交回复
热议问题