Bug with Chrome's Localstorage Implementation

Bug with Chrome's localStorage implementation?

Update and final conlusion

It turns out that the spec actually says that this is the desired behavior, therefore IE9's implementation is broken.

4.2 The sessionStorage attribute

When the setItem(), removeItem(), and clear() methods are called on a Storage object x ... if the methods did something, then in every HTMLDocument ... [that is] associated with the same storage area, other than x, a storage event must be fired....

So as we can see, the spec does do a really bad job at making it clear, that this is the specified behavior. That was the reason why Opera 10's implementation was broken and it's most likely also the reason why IE9's implementation is broken.

What do we learn from that? Always read every, single, word of the specification (especially if you're implementing the stuff...).

Old answer

As you said the basic behavior here is "Invoke on all but the current page".

There's an old Chrome Bug Report from last July.

As one can read there, Firefox has the same "issue". I've tested this with the latest nightly, still behaves the same way like in Chrome.

Another test in Opera 11 shows that this must be some kind spec'ed behavior, since Opera 11 does the exact same thing but Opera 10 did fire events on all windows / tabs. Sadly the official changelogs for Opera 11 do not state any change for this behavior.

Reading through the specification, nothing there states this behavior. The only thing I could find is:

The storage event is fired when a storage area changes, as described in the previous two sections (for session storage, for local storage).

When this happens, the user agent must queue a task to fire an event with the name storage, which does not bubble and is not cancelable, and which uses the StorageEvent interface, at each Window object whose Document object has a Storage object that is affected.

Note: This includes Document objects that are not fully active, but events fired on those are ignored by the event loop until the Document becomes fully active again.

Well, what does the Note mean?

From another specification:

A Document is said to be fully active when it is the active document of its browsing context, and either its browsing context is a top-level browsing context, or the Document through which that browsing context is nested is itself fully active.

Doesn't make sense? Yes. Does it help us in any way? No.

So we (in the JavaScript Chatroom) poked on #whatwg to see what all of this is about, so far there has been no response. I'll update my answer as soon as we get any response.

To conclude for now

Firefox, Chrome, Safari and Opera have the exact same behavior. That is, they do not fire on the tab / window that made the chance to localStorage.

But IE9 Beta behaves like Opera 10, so it fires on all tabs / windows.

Since the author of the localStorage spec works at Google in R&D, I hardly doubt Chrome would get this wrong. And since there are no Bugs on this over at Bugzilla and Opera changed the behavior in 11, it seems that this is the way it should work. Still no answer why it works this way and why IE9 behaves differently, but we're still waiting for a response from #whatwg.

IE localStorage event misfired

Changing your script to the following prevents the handling of any storage events in the focused window.

This isn't precisely what you asked, as I believe that would require a patch to the browser, but it causes IE 9/10 to conform to the spec while having no adverse effects on other browsers (other than the global and listeners).

<script type="text/javascript" >
var focused;

window.addEventListener('focus', function(){focused=1;}, false);
window.addEventListener('blur', function(){focused=0;}, false);

var handle_storage = function (e) {
if(!focused)
alert("Storage",focused);
};

window.addEventListener("storage", handle_storage, false);

</script>

See this fiddle for the updated, conforming behavior.

Edit: The following also works and avoids the listeners at the cost of a runtime check of window focus:

<script type="text/javascript" >

var handle_storage = function (e) {
if(!document.hasFocus())
alert("Storage");
};

window.addEventListener("storage", handle_storage, false);

</script>

chrome.storage.local strange behaviour: confused by a duplicate object

  1. No it is not intentional and should be reported as a bug: https://crbug.com/606955 (and now it is fixed as of Chrome 52!).
  2. As I explained in the bug report, the cause of the bug is that the objects are identical. If your object dup only contains simple values (i.e. no nested arrays or objects, only primitive values such as strings, numbers, booleans, null, ...), then a shallow clone of the object is sufficient:

    dup = {a: [1]}
    dup2 = Object.assign({}, dup);
    chrome.storage.local.set({x: [dup, dup2]});

    If you need support for nested objects, then you have to make a deep clone. There are many existing libraries or code snippets for that, so I won't repeat it here. A simple way to prepare values for chrome.storage is by serializing it to JSON and then parsing it again (then all objects are unique).

    dup = {a: [1]}
    var valueToSave = JSON.parse(JSON.stringify([dup, dup]));
    chrome.storage.local.set({x: valueToSave});

    // Or:
    var valueToSave = [ dup, JSON.parse(JSON.stringify(dup)) ];
    chrome.storage.local.set({x: valueToSave});

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.

Chome Extension - QuotaExceededError localStorage

localStorage is not, and will not be, unlimited

The fact that the unlimitedStorage permission does not apply to localStorage is stated in the documentation. The referenced bug, issue 58985, was marked as "WontFix" in December of 2010. Thus, there is not, and will not be, a solution for you to store unlimited data in localStorage. You will need to select some other method of storing your data.

Unlimited storage options

storage.local

Your options include chrome.storage which is explicitly intended for extensions to store data. You can store data that is local to the machine, using storage.local (can be unlimited with the unlimitedStorage permission), or data that is synchronized across the user's Google account, with storage.sync (quota is not set to unlimited by unlimitedStorage).

Web SQL Database

There are other options for storing data. For instance, the Web SQL Database, which is specifically granted unlimited storage by the unlimitedStorage permission.

HTML5 local File API (MDN)

The amount of data you can store with the File API becomes unlimited with the unlimitedStorage permission. You can also separately request a specific quota size with a call to webkitStorageInfo.requestQuota(), without using the unlimitedStorage permission. When you do, the user will be asked to approve the storage request. If you do use the unlimitedStorage permission, you do not need to separately request a quota.

What to use

What is best to use will depend on exactly what you are using storage for. You have provided no information as to your actual use, so there is no way for us to gauge what might be a good fit in your case.

Application cache

As to your issue with the application cache growing to a large size with the unlimitedStorage permission: Yes, the documenation explicitly states that declaring the unlimitedStorage permission will result in the application cache becoming unlimited. If this is an issue, you will need to not declare the unlimitedStorage permission.

Chrome: Solving limited number of WebSockets (possibly with localStorage)

Number 2 won't work, because LocalStorage is limited to string types.

I would recommend looking at ServiceWorkers.

A single service worker can control many pages. Each time a page within your scope is loaded, the service worker is installed against that page and operates on it.

This sounds pretty close to what you want. Register a ServiceWorker that simply accepts messages and rebroadcasts them to clients - any page from your domain. So you can have one main page that creates the WebSocket connection, and every time it gets a push it will broadcast a message through the ServiceWorker messaging system. Other tabs can pick up on it as needed.

Alternatively, you could use a shared WebWorker on the same principle. Just install it as a messaging system that broadcasts the messages from your WebSocket.

These aren't exactly the intended uses for these technologies... but if it works it works.



Related Topics



Leave a reply



Submit