Jeff Prosise's Blog

Using HTML5 Web Storage for Interprocess Communication

One of the exciting new features coming in HTML5 – and one that works in most HTML5 browsers today – is Web storage. The latest draft of the specification defines two types of Web storage: local storage and session storage. Local storage, which is analogous to isolated storage in Silverlight, persists data across browser sessions, and does it purely on the client side. It provides an easy-to-use mechanism for storing arbitrary data on the client and is a cost-efficient alternative to cookies. Session storage, by contrast, persists data only for the length of the browser session. Session storage is useful for sharing data among pages hosted in the same browser window (it can’t span browser windows as local storage can), but I believe developers will generally find local storage more useful. One reason they’ll find it useful is that it offers a means for passing messages and data between browser instances – a freedom we’ve never really enjoyed without help from browser plug-ins such as Silverlight and Flash.

Local storage is accessed through the browser’s window.localStorage property. The following statements detect the presence of local storage support in the host browser:

 

if ("localStorage" in window && window["localStorage"] != null) {

    // Local storage supported

}

 

Not surprisingly, browsers are somewhat inconsistent in how they respond to this query. For example, if you run this code in IE9 in a page loaded from the file system, IE9 will report that it doesn’t support local storage. But load the exact same page from an HTTP URI and IE9 will provide local storage support just fine. Firefox 4 Beta 12 indicates that it supports local storage no matter where the page is loaded from, but it doesn’t persist local storage if the page is loaded locally. Chrome behaves in yet another way, happily supporting local storage (and persisting it) no matter where the page is loaded from. All three browsers support local storage and mostly do a fine job of conforming to spec, but as you’re testing, be aware that your local storage code might not work properly if you’re testing against FILE URIs.

Once you’ve ascertained that local support support is there, you can write to it using window.localStorage.setItem and read from it using window.localStorage.getItem. Local storage isn’t a repository for virtual files and directories as Silverlight isolated storage is; it can only hold key-value pairs. Furthermore, data is stringified when it’s written to isolated storage, so if you write an integer and read it back, you have to coerce it back to an integer. Simple types such as strings and numbers (and even arrays) can be written to local storage just fine, but for more complex types, you’ll need to serialize them with JSON.stringify and deserialize them with JSON.parse, or use a serialization library to do the same.

Here’s a simple example demonstrating how to write a string to local storage and read it back:

 

// Write to local storage

localStorage.setItem("language", "en-us");

 

// Read from local storage

var lang = localStorage.getItem("language");

 

Instead of calling setItem and getItem directly, you can use square brackets instead:

 

// Write to local storage

localStorage["language"] = "en-us";

 

// Read from local storage

var lang = localStorage["language"];

 

Here’s an example that persists an integer instead of a string. Note the call to parseInt to convert the string retrieved from local storage back to an integer:

 

// Write to local storage

localStorage["index"] = 0;

 

// Read from local storage

var index = parseInt(localStorage["index"]);

 

If you write a value to local storage and local storage contains an item with the same name, the new value overwrites the old. Similarly, if you try to read a nonexistent value from local storage, getItem will simply return null. You can also use removeItem to remove an item from local storage, or call the clear method to remove them all. Calling removeItem with a nonexistent key does nothing. Perhaps more importantly, it does not thrown an exception.

Data written to local storage is scoped per origin, where origin is the combination of scheme, host, and port. The page at http://www.wintellect.com/html5/Mandelbrot.html can access local storage written by http://www.wintellect.com/Scribble.html, but it can’t access local storage written by http://www.microsoft.com/html5/sample.html. Nor can it access data written to local storage by https://www.wintellect.com/html5/secure.html (notice the HTTPS at the beginning of the URI).

Browsers limit the amount of data that can be written to local storage by apps at a given origin, but the limits vary by browser. The HTML5 spec suggests a limit of 5 MB, but browsers aren’t compelled to comply. The spec doesn’t define an API for inquiring about remaining space, but if a write exceeds the quota, the browser throws a QUOTA_EXCEEDED_ERR error. Therefore, anytime you write to local storage, you should structure your code like this:

 

try {

    localStorage["language"] = "en-us";

}

catch (e) {

    if (e == QUOTA_EXCEEDED_ERR)

        window.alert("Storage quota exceeded");

}

 

Remember that this isn’t a per-app quota, but a per-origin quota. Your app might not have written anything to local storage, but if other apps from the same origin have already consumed the quota, your app won’t be able to write anything to local storage. And unlike Silverlight, HTML5 does not define any means for requesting a higher quota from the user.

One aspect of local storage that may surprise you is that it doesn’t work cross-browser. In other words, data written to local storage by a page running in Firefox can’t be read by the same page running in Chrome, and vice versa. Perhaps this shouldn’t be surprising, because every browser has its own unique way of implementing local storage. Still, Silverlight isolated storage works great cross-browser, and I had hoped that HTML5 local storage might offer the same capability. But no such luck.

If you’d like to see local storage in action, point your favorite HTML5 browser to http://www.wintellect.com/html5/storagescribble.html. Draw a few doodles on the chalkboard, and then close the browser. Restart the browser (the same browser, not a different one, since local storage doesn’t comport across browsers), and if your browser supports local storage, your doodles will reappear. As was true of the other Scribble tutorial I published recently, you can double-click the chalkboard to erase everything you’ve drawn.

Screen

Now, here’s the really interesting part – something that you can do with local storage that really has nothing to do with persisting data. Can you spell…interprocess communication?

In my Silverlight classes, I often demonstrate local connections by running two scribble apps side by side in different browser windows. As I draw in one window, my strokes appear in the other. Behind the scenes, I’m using a LocalMessageSender and a LocalMessageReceiver to fire messages from one browser to the other.

You can do something similar with local storage in HTML5. When a change is made to local storage – when an item is added, removed, or modified, or all the items are cleared – an HTML5 browser fires a storage event. You can register a handler for that event as follows:

 

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

  .

  .

  .

function onStorageChanged(e) {

    // Something changed

}

 

The sole parameter passed to the event handler is a StorageEvent object, which has properties named key, oldValue, newValue, and url, among others. These tell you what item changed and what its value now is. newValue is supposed to be null if the item was removed from local storage, and if all items were removed with a call to clear, key is supposed to be null. But the RC release of IE9 passes empty strings instead. This is important, because if your code only checks for null to detect removed items, you’ll get false positives in IE. Hopefully this will be fixed before IE9 ships.

More importantly, if there are two browser instances running and they’re hosting pages from a common origin, and one page modifies local storage in any way, the other page receives a storage event notifying it of the change! Consequently, we can use local storage events as a communications channel between browser instances. (Once more, IE9 behaves in a subtly different manner than other browsers. In Firefox and Chrome, if browser A modifies local storage, only browser B receives a storage event. But in IE9, both A and B receive storage events. This could break an app that writes to local storage and processes storage events, so be careful. You could argue that this is a feature and not a bug, because it could be used to couple two otherwise independent components running in the same application. The HTML5 spec isn’t clear about which behavior is correct.)

To demonstrate how to connect browser processes this way, I wrote a companion app for StorageScribble called ScribbleReceiver. Start StorageScribble from http://www.wintellect.com/html5/storagescribble.html, and ScribbleReceiver  from http://www.wintellect.com/html5/scribblereceiver.html. Be sure to run them in two browser windows. As you scribble in the first browser instance, your scribbles should be replicated in the second one. It works really well in Firefox and Chrome, and pretty well in IE9 except for the latency, which can be up to several seconds. Hopefully this, too, will change before IE9 is released.

image

I think developers are going to love local storage. It beats cookies hands-down when it comes to persisting state, and the fact that it can span browser instances opens up all sorts of possibilities, including a mechanism for detecting at startup when the app is already running in another browser. Think about it!

On Mar 10 2011 12:35 PMBy jprosise With 8 Comments

Comments (8)

  1. Rasmus Fløe

    About square bracket access on localStorage - this might interest you:

    http://groups.google.com/group/jsmentors/browse_thread/thread/af4ec5330e3167f/e8e71695cb6651eb?lnk=gst&q=localstorage#e8e71695cb6651eb

  2. I thought I read that web storage is being dropped from the html5 spec...could be confusing it with the local SQL browser db. There are benefits over cookies, but the current inconsistency you point out in browser implementations scares me to use this until 2012 or so when html5 hopefully hits mass adoption.

  3. WebSQL is going away (probably to be replaced by Indexed DB), but local storage is here to stay.

  4. Hi Jeff.

    Really good post on HTML5 storage and IPC.
    But what about security of the data in the storage? Is the data encrypted in any way? and if not, how can I go about doing it?

    Thanks

  5. Ashraf Thoppukadavil

    FYI, IE 8 and IE9 does support local storage. In fact we just converted a silverlight app to HTML 5 utilizing local storage capabilities.

  6. "The HTML5 spec isn’t clear about which behavior is correct"

    The spec says this, which seems unambiguous and clear (and means IE is wrong) — note the "other than x":

    "When the setItem(), removeItem(), and clear() methods are called on a Storage object x that is associated with a local storage area, [...] then for every Document object whose Window object's localStorage attribute's Storage object is associated with the same storage area, other than x, send a storage notification."

  7. Question -- Does this work with an instance of System.Windows.Forms.WebBrowser? In other words, if I have an application running with a System.Windows.Forms.WebBrowser control, and the I also has a browser running, will the events fire between the two processes?

Leave a Comment