Implementing WebStorage for PhoneGap + Windows Phone Mango ( Part 1 )

Windows Phone 7 with the Mango updates includes the new IE9 which is a very capable browser, and has support for many new standard HTML5 APIs. Some of the feature highlights (IMHO) include:

  • addEventListener : no more conditional event code
  • querySelectorAll : easily grab DOM elements by id/class/type etc.
  • WebStorage APIs : localStorage + sessionStorage
  • ES5 : Function.bind, Array.forEach, ...

The reader: But wait, I thought this post was going to be about how the WebStorage APIs were added to PhoneGap for WP7, but you just said IE9 supports it, what gives?

The writer: I'm getting there just wait ...

So, IE9 implements the complete spec for WebStorage as defined here: Unfortunately, this implementation is only available to web pages that are loaded from a domain because of the way the data stores are sandboxed per domain. In the context of a PhoneGap application, our page is loaded from IsolatedStorage ( aka: file:// ) so we are not assigned a domain by IE9, so we do not have access to localStorage.

Okay, so having seen this I realized that I needed to polyfill here, and provide an implementation. However, upon attempting to set window.localStorage to my own object, I received an error stating that window.localStorage was read-only? oh crap! After a little more exploration, I remembered my ECMA. ECMAScript 5th Edition to be precise, and IE9 supports it. So after a quick test, I discovered that I could duck-punch/polyfill window.localStorage using Object.defineProperty(obj, prop, descriptor); Sweet!

You can look ahead to the JavaScript implementation here : DomStorage.js

The JavaScript implementation supplies the following interface to window.localStorage and window.sessionStorage :

interface Storage { readonly attribute unsigned long length; DOMString? key(unsigned long index); getter DOMString getItem(DOMString key); setter creator void setItem(DOMString key, DOMString value); deleter void removeItem(DOMString key); void clear(); };

Implementing this on the native side was a little different than a typical plugin. Many of the calls are synchronous, so I needed to have some data up front, and a method to call native code synchronously. Note: I could have implemented the entire interface using the FileAPI, but this would mean storing the entire key/value set in JavaScript so that it could be called synchronously. After some experimentation, I discovered that even though C# code cannot return a value to a JavaScript call to ScriptNotify, it can set a value from C# before execution is passed back to JavaScript, so we can mimic the behavior of a return value.

In this example, the value of this._result is set before window.external.Notify returns, so it is available as a return value from the JavaScript function.

var retVal = null; if(this.keys.indexOf(key) > -1) { window.external.Notify("DOMStorage/" + this._type + "/get/" + key); retVal = this._result; this._result = null; } return retVal;

Note: While this works, perfectly, we do have to be careful that we don't again call ScriptNotify in the callback to set the _result value, otherwise we could overflow the stack with multiple nested calls.

More to come soon, done a lot of blogging and not enough coding for today. Stay tuned.