The WebExtension Storage component can be used to power an implementation of the
chrome.storage.sync
WebExtension API,
which gives each WebExtensions its own private key-value store that will sync between a user's
devices. This particular implementation sits atop Firefox Sync.
With a small amount of work, this component would also be capable of powering an implementation
of chrome.storage.local
, but this is not an explicit goal at this stage.
The WebExtension Storage component offers:
- Local storage of key-value data indexed by WebExtension ID.
- Basic Create, Read, Update and Delete (CRUD) operations for items in the database.
- Syncing of stored data between applications, via Firefox Sync.
The component does not offer, but may offer in the future:
- Separate storage for key-value data that does not sync, per the
chrome.storage.local
WebExtension API. - Import functionality from previous WebExtension storage implementations backed by Kinto.
The component does not offer, and we have no concrete plans to offer:
- Any facilities for loading or running WebExtensions, or exposing this data to them.
- Any helpers to secure data access between different WebExtensions.
As a consuming application, you will need to implement code that plumbs this component in to your WebExtensions infrastructure, so that each WebExtension gets access to its own data (and only its own data) stored in this component.
To use this component for local storage of WebExtension data, you will need to know how to integrate appservices components into an application on your target platform:
- Firefox Desktop: There's some custom bridging code in mozilla-central.
- Android: Bindings not yet available; please reach out on slack to discuss!
- iOS: Bindings not yet available; please reach out on slack to discuss!
- Other Platforms: We don't know yet; please reach out on slack to discuss!
- We assume each WebExtension is uniquely identified by an immutable extension id.
- A WebExtenstion Store is a database that maps extension ids to key-value JSON maps, one per extension.
It exposes methods that mirror those of the
chrome.storage
spec (e.g.get
,set
, anddelete
) and which take an extension id as their first argument.
To effectively work on the WebExtension Storage component, you will need to be familiar with:
- Our general guidelines for contributors.
- The core concepts for users of the component, outlined above.
- The way we generate ffi bindings and expose them to Kotlin and Swift.
- The key ideas behind how Firefox Sync works and the sync15 crate.
This component stores WebExtension data in a SQLite database, one row per extension id.
The key-value map data for each extension is stored as serialized JSON in a TEXT
field;
this is nice and simple and helps ensure that the stored data has the semantics we want,
which are pretty much just the semantics of JSON.
For syncing, we maintain a "mirror" table which contains one item per record known to exist on the server. These items are identified by a randomly-generated GUID, in order to hide the raw extension ids from the sync server.
When uploading records to the server, we write one encrypted BSO per extension. Its server-visible id is the randomly-generated GUID, and its encrypted payload contains the plaintext extension id and corresponding key-value map data.
The end result is something like this (highly simplified!) diagram:
The details of the encryption are handled by the sync15 crate, following the formats defied in sync storage format v5.