I am currently developing a digital wallet application as a chrome extension and trying to figure out what I should use as my persistent storage layer: chrome.storage.local or indexedDb. I have looked into other similar open-source projects, and it seems like most use the former instead of the latter.
I would like to understand if there is any advantage in using one over the other. Currently, the reasons I lean towards using chrome.storage.local are:
I understand that my use-cases and data shape is likely a big factor: As far as my app is concerned
Based on the above, is there any reason that one would serve my application better than the other? Is there anything else I should take into consideration? Thanks in advance!
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:
chrome.storage.local
and .sync
(string, number, boolean, null, and arrays/objects that recursively consist of these trivial types).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.