HTML5 introduced the ‘Web Storage‘ specification, which aims to provide a method for web developers to store data persistently on users’ local file system. By using the ‘localStorage’ object, key-value pairs can be stored (as strings) and persist through browser restarts and system power cycles. The process itself is transparent to both the web developer and the user.
This has been a much desired feature for quite a long time and is now officially supported by all major desktop as well as mobile browser implementations. But as is so often the case, all browsers do not behave the same way regarding one commonly-supported feature.
Both Google’s Webkit-based Chromium/Chrome and Mozilla’s Gecko-based Firefox/SeaMonkey/etc. support ‘localStorage’ and they both support extensions. However, due to differences in implementation details and possibly also security policies, Chromium/Chrome provides ‘localStorage’ for both (remote) web content and (local) extensions, whereas Gecko applications only allow webpages served from valid domain names to access ‘localStorage’ and denies extensions and locally-save html files this useful feature. This issue has been raised several times in various contexts on Mozilla’s bugzilla (e.g. 495747, 507361) since 2009, but it seems no forthcoming solution could be expected for now.
Although Gecko implemented a preference system and its own SQLite-based local storage system, which could be harnessed to server a similar purpose, writing code based on ‘localStorage’ that is portable to other browsers is certainly preferred. Fortunately, 495747 points to the following workaround (source) offered by Mozilla’s Mochitest automated browser testing framework:
var url = "http://example.com"; var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] .getService(Components.interfaces.nsIScriptSecurityManager); var dsm = Components.classes["@mozilla.org/dom/storagemanager;1"] .getService(Components.interfaces.nsIDOMStorageManager); var uri = ios.newURI(url, "", null); var principal = ssm.getCodebasePrincipal(uri); var storage = dsm.getLocalStorageForPrincipal(principal, ""); storage.setItem("chromekey", "chromevalue");
What the above code does is basically requiring access to ‘localStorage’ of ‘http://example.com’ and store the reference to it in the ‘storage’ variable.
Because ‘localStorage’ is ‘scoped to an HTML5 origin (scheme + hostname + non-standard port)’ and follows the ‘same origin’ rule, care must be taken to prevent data corruption or loss caused by hostname conflicts. For instance, if two different extensions both use ‘http://example.com’ to access persistent local storage, they may overwrite or delete each other’s data. Actually existing domain names should also be avoided, so that the extension’s data, which may contain privacy-related information, will not be exposed accidentally to remote servers. As the ‘origin’ is not really accessed or even resolved when the code executes, pretty much anything can be used, for instance ‘http://foo.bar’.