Window.Localstorage VS Chrome.Storage.Local

window.localStorage vs chrome.storage.local

It depends entirely on what your Chrome Extension will be doing. window.localStorage is HTML5 storage. Unless you're running it in the background page, it can only allow you to get and set data into storage for a specific domain. This is also true for code injected into the DOM, since it would use the localStorage on the web page.

In other words, you won't be able to share data across different web pages unless you use localStorage in the background page, which operates independently of web pages, since it has a chrome:// URI as its domain.

chrome.storage.local, on the other hand, is designed for Chrome Extensions and Chrome Apps to store data in a more central location. Since this isn't accessible to normal web pages, each Extension gets its own storage. One possibility is for your background page to handle dealing with the setting and getting of the data, while your content scripts deal with modifying and interacting with the web page.

However, these API's work in content scripts as well, and both of the extensions I've written use chrome.storage.local called from the content scripts.

As an example, I built a Stack App that preserves inbox items in Stack Exchange until you've actually read them, called StackInbox. Since Stack Exchange sites span across hundreds of domains, I chose chrome.storage.local because I could save the user's accountId and reuse it across all the sites, ensuring that the inbox data is synchronized, while also using this directly in the content script.

As a simple test, put some data in localStorage on one domain, in a content script, and try to pull it from another, and you'll see that the data won't be there. With chrome.storage.local, this isn't a problem.

Lastly, Chrome Extensions and Chrome Apps are whitelisted, since the user chose to install it, so they typically can do more things than a normal website. For instance, by specifying the "unlimitedStorage" permission in your manifest, you can store data well beyond the 5MB limit placed upon HTML5 localStorage.

For more information, see Google's documentation on Chrome Storage.

Chrome window.localStorage vs. chrome.storage.local in dependencies

First problem is that chrome.storage APIs are asynchronous and localStorage is sync. It is theoretically possible to create localStorage mock backed by the chrome.storage, but it will break in many cases.

Second problem is that chrome.storage operates on objects, when localStorage only allows strings. So if you'll have some kind of code which will rely on localStorage mode, you will have only store strings in chrome.storage.local, otherwise you will have very weird bugs.

Last problem is that you can't reassign window.localStorage variable in chrome apps, so the only way is to wrap the code into self executing closure, and provide window and localStorage mocks as closure variables, e.g:

(function(window,localStorage){
//some code relying on localStorage here
})(windowObjectMock,windowObjectMock.localStorage);

It is easier and more error-prone to rewrite the external code to use chrome.storage.local rather than trying to provide localStorage implementation backed by chrome.storage.local.

Chrome extension development: chrome.storage.local vs Indexeddb

In Chrome and in Firefox, IndexedDB used by an extension is persistent. In Chrome it's unlimited even without adding "unlimitedStorage" to "permissions" so it uses global quota management, see crbug.com/1209236. I've imported a dummy 40MB JSON (15MB compressed) and it worked. Safari WebExtensions specification is probably much less generous.

The reasons to use IndexedDB:

  1. Non-string keys and values supported by the structured clone algorithm (Map, Set, Date, RegExp, Blob, File, FileList, ArrayBuffer, ImageBitmap, ImageData, and recently BigInt, Error) in addition to the basic JSON-compatible types offered by chrome.storage.local and .sync (string, number, boolean, null, and arrays/objects that recursively consist of these trivial types).
  2. Storing and reading big or deeply nested objects may be easily 10 times faster or even more.
  3. Checking for an existence of a key without reading its value.
  4. Reading just the names of the keys without reading their values.
  5. Indexing by several keys.
  6. Dedicating an entire object store (or lots of them) per each array that may grow or shrink unpredictably. This will be many times faster (maybe even 1000 if the array is big) than reading the entire array, modifying it, and writing back to a single key in chrome.storage.

IndexedDB of the extension can't be used in a content script directly, only via workarounds:

  • Messaging. In Chrome ManifestV2 it's limited to JSON-compatible types so to transfer complex types you'll have to stringify them (slow) or convert the data via new Response into a Blob, feed it to URL.createObjectURL, send the resultant URL, revoke it after confirming the receipt. Chrome's ManifestV3 will support the structured clone algo for messages soon but their implementation still stringifies the cloned data internally so it's bound to be slow with big amounts of data until they fix it.

    Firefox doesn't suffer from this restriction, AFAIK.

  • iframe in the web page with src pointing to an html page of the extension exposed via web_accessible_resources in its manifest.json. This iframe (in Chrome) runs in the extension process so it'll have direct access to its storage, and it can use parent.postMessage, which supports structured clone algo, to send the result. To avoid interception by the page you should run your content script at document_start to attach a listener in capturing mode before the page does and prevent its listeners from seeing the event:

    // this script must be declared with "run-at": "document_start"
    const extensionOrigin = chrome.runtime.getURL('').slice(0, -1);
    window.addEventListener('message', e => {
    if (e.origin === extensionOrigin) {
    e.stopImmediatePropagation(); // hide from the page
    console.log(e); // process the event
    }
    }, true); // register in the first phase, "capture"

    It's not enough though! Sites can install a listener before the extension does because of a foundational security hole in the implementation of WebExtensions (observed in Chrome and Firefox) that allows pages to modify the environment of a newly created same-origin iframe or tab/window, see crbug.com/1261964, so for sensitive extensions you'll have to implement very complicated workarounds until this hole is patched. Hopefully, ManifestV3 will provide a secure postMessage limited to the "isolated world" of content scripts.

chrome extension: switching from localstorage to chrome.storage.local

chrome.storage.local.get is asynchronous, it is mean, Chrome returns value to callback function:

chrome.storage.local.get("ts_id_js", function(obj) {
let value = obj["ts_id_js"];
});


Related Topics



Leave a reply



Submit