Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome extension: store data on background

I want to be able to store data on background (on my extension) so I can access this data between multiple domains.

Where's what I'm doing:

content-script.js

function setItem(name, data) {
    chrome.extension.sendMessage({ command: 'setItem', name: name, data: data });
}

function getItem(name) {
    chrome.extension.sendMessage({ command: 'getItem', name: name }, function(response) {
        return response;
    });
}

background-script.js

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    var value = this.getItem(key);
    return value && JSON.parse(value);
}

chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
    switch (request.command) {
        case 'setItem':
            localStorage.setObject(request.name, request.data);
            return;
        case 'getItem':
            sendResponse(localStorage.getObject(request.name));
            return;
    }
});

But without sucess, since I cant return from inside the callback on getItem.

I do get the data inside the function(response) { } callback, I just can't return it as the return of getItem.

How should I do this?

like image 503
Thiago Belem Avatar asked Jun 28 '12 06:06

Thiago Belem


3 Answers

This 2012 question was brought up for an up-to-date answer. Well, then..

Right now the proper answer would be to use chrome.storage API. It's an API accessible for both extension pages and content scripts, and it provides asynchronous storage. It requires "storage" permission, but this permission generates no warnings.

// Setting
chrome.storage.local.set({key: data}, function() {
  if(chrome.runtime.lastError) {
    console.error(
      "Error setting " + key + " to " + JSON.stringify(data) +
      ": " + chrome.runtime.lastError.message
    );
  }
});

// Getting
chrome.storage.local.get("key", function(data) {
  // Do something with data.key
});

See also the Examples part of the documentation.

Note that in either case (this approach, or messaging-the-background approach) you can't make a function getData that returns a result, since the call is asynchronous.

Some tips and tricks:

  1. You can set or get multiple values at once by passing an object or an array as a query. You can read all values by passing the null query.

  2. You can provide a default value for the get() operation in case there's no value stored for the key, by passing a query like {key: defaultValue}

  3. You can be notified of all changes to the storage with chrome.storage.onChanged event.

  4. chrome.storage.local obeys "unlimitedStorage" permission.

  5. chrome.storage.sync will propagate the value to all profiles signed in to the same Google Account, if Chrome Sync is enabled for extensions. However, keep quotas in mind.

  6. If you absolutely need synchronous access, you can fake it with a local cache backed by chrome.storage. There are, however, limitations: while in a synchronous code block, your cache won't be updated with changes from other pages, and you need to read the values once asynchronously to populate the cache.

like image 135
Xan Avatar answered Oct 31 '22 00:10

Xan


Content.js

var someVar = "hey hey!";

chrome.extension.sendRequest({method: "fromContentScript",greeting: someVar}, function(response) {

    console.log(response.data); // response back from BG

    if(response.who == 'bg'){ // checks if the response is from BG
            //Something happened ...
    }

    var responseFromBg = response.data; // the response incase we need to use the message sent back... in this case should be 'hey yourself'


});

Background.js

chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
  // From content script.
  if (sender.tab) {
    if (request.method == "fromContentScript"){

        localStorage.setItem("welcome-message",request.greeting); // in this case there will now be a localStorage variable called 'welcome-message' set with the value of 'hey hey!'. This will be viewable in the chrome:extensions page, click on the 'background.html / generated background.html' then view the 'Development Tools' or in Windows hit 'CTRL + SHIFT + I' and look at the 'LocalStorage' tab...

      sendResponse({who: "bg",data: "hey yourself"}); // this is the response sent back, 'who' tells the content page where is responding, so it can do something with the response if needed.
        }else{
      sendResponse({}); // snub them.
        }
  }
});

Manifest.json // just incase it is a manifest issue you are having... here is most of mine..

{
  "name": "Name here",
  "version": "1",
  "manifest_version": 2,
  "description": "Enter desc here.",  
    "browser_action": {
    "default_icon": "img/icon16.png",
    "default_popup": "popup.html"
  },    
    "background": {
    "scripts": ["background.js"]
  },
  "permissions": [
    "tabs", "*://*/*"
  ],
    "icons": { "16": "img/icon16.png",
           "48": "img/icon48.png",
          "128": "img/icon128.png" },
  "content_scripts": [
    {
      "matches": ["*://*/*"],
      "js": ["js/jquery-1.7.2.min.js","content_script.js"],
      "run_at": "document_end"
    }
  ]
}

Would have used your example, but I am in a hurry this morn. I have tried to explain all of the vars as carefully as possible - sorry :(

like image 25
Chris Avatar answered Oct 31 '22 00:10

Chris


background-script.js:

chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
    switch (request.command) {
        case 'setItem':
            localStorage[request.name] = JSON.stringify(request.data));
            return;
        case 'getItem':
            var retValue;
            if (typeof(localStorage[request.name]) === 'string') {
                retValue = JSON.parse(localStorage[request.name]);
            }
            sendResponse(retValue);
            return;
        case 'deleteItem':
            if (typeof localStorage[request.name] !== 'undefined') {
                delete localStorage[request.name];
            }
            return;
    }
});

If the key is not in the localStorage, getItem will return undefined. Instead of defining the function getItem the way you did, you should send a message to the background with a callback and then do something with the value when the callback is called. You can't return the value from the function getItem, but you can use the value in the callback when it is called:

function getItem(name, callback) {
    chrome.extension.sendMessage({command: 'getItem', name: name}, function(response) {
        if (typeof(callback) === "function") {
            callback(response);
        }
    });
}
like image 24
Uri Avatar answered Oct 31 '22 00:10

Uri