Bug 660237 - implement nsIDOMStorage with a proxy, r=bzbarsky, r=mayhemer
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 23 Jul 2014 01:07:12 -0400
changeset 217281 326c91338df021db68e55ac9549c6dc7e5eb906b
parent 217280 588ea38a32fcb241002042759fb09f563e0909af
child 217282 dc7ae0a5d9b812b933dc6fa3cfafdf032da9a7a8
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky, mayhemer
bugs660237
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 660237 - implement nsIDOMStorage with a proxy, r=bzbarsky, r=mayhemer
browser/components/sessionstore/src/SessionStorage.jsm
docshell/base/nsDocShell.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsDOMClassInfoClasses.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/bindings/Bindings.conf
dom/bindings/test/test_proxies_via_xray.html
dom/events/StorageEvent.cpp
dom/events/StorageEvent.h
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/mochitest.ini
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html.json
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html.json
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html.json
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html.json
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html.json
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html.json
dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html.json
dom/imptests/moz.build
dom/interfaces/base/nsIDOMWindow.idl
dom/interfaces/storage/moz.build
dom/interfaces/storage/nsIDOMStorage.idl
dom/interfaces/storage/nsIDOMStorageManager.idl
dom/interfaces/storage/nsPIDOMStorage.h
dom/src/storage/DOMStorage.cpp
dom/src/storage/DOMStorage.h
dom/src/storage/DOMStorageCache.cpp
dom/src/storage/DOMStorageCache.h
dom/src/storage/DOMStorageManager.cpp
dom/src/storage/DOMStorageManager.h
dom/src/storage/moz.build
dom/tests/mochitest/localstorage/frame_clear_browser_data.html
dom/tests/mochitest/localstorage/test_clear_browser_data.html
dom/tests/mochitest/localstorage/test_localStorageBase.html
dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
dom/tests/mochitest/localstorage/test_localStorageBaseSessionOnly.html
dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html
dom/webidl/Storage.webidl
dom/webidl/StorageEvent.webidl
dom/webidl/moz.build
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
js/xpconnect/src/dom_quickstubs.qsconf
toolkit/components/social/FrameWorkerContent.js
toolkit/devtools/server/tests/browser/browser_storage_dynamic_windows.js
toolkit/devtools/server/tests/browser/browser_storage_listings.js
toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
--- a/browser/components/sessionstore/src/SessionStorage.jsm
+++ b/browser/components/sessionstore/src/SessionStorage.jsm
@@ -102,21 +102,22 @@ let SessionStorageInternal = {
    *        {"example.com": {"key": "value", "my_number": 123}}
    */
   restore: function (aDocShell, aStorageData) {
     for (let host of Object.keys(aStorageData)) {
       let data = aStorageData[host];
       let uri = Services.io.newURI(host, null, null);
       let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
+      let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 
       // There is no need to pass documentURI, it's only used to fill documentURI property of
       // domstorage event, which in this case has no consumer. Prevention of events in case
       // of missing documentURI will be solved in a followup bug to bug 600307.
-      let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing);
+      let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);
 
       for (let key of Object.keys(data)) {
         try {
           storage.setItem(key, data[key]);
         } catch (e) {
           // throws e.g. for URIs that can't have sessionStorage
           console.error(e);
         }
@@ -130,19 +131,21 @@ let SessionStorageInternal = {
    *        That history entry uri
    * @param aDocShell
    *        A tab's docshell (containing the sessionStorage)
    */
   _readEntry: function (aPrincipal, aDocShell) {
     let hostData = {};
     let storage;
 
+    let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+
     try {
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
-      storage = storageManager.getStorage(aPrincipal);
+      storage = storageManager.getStorage(window, aPrincipal);
     } catch (e) {
       // sessionStorage might throw if it's turned off, see bug 458954
     }
 
     if (storage && storage.length) {
        for (let i = 0; i < storage.length; i++) {
         try {
           let key = storage.key(i);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -30,17 +30,16 @@
 #endif
 
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMStorage.h"
-#include "nsPIDOMStorage.h"
 #include "nsIContentViewer.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsCURILoader.h"
 #include "nsDocShellCID.h"
 #include "nsDOMCID.h"
 #include "nsNetUtil.h"
 #include "nsRect.h"
 #include "prenv.h"
@@ -2785,30 +2784,37 @@ nsDocShell::GetSessionStorageForPrincipa
                                           bool aCreate,
                                           nsIDOMStorage** aStorage)
 {
     nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
     if (!manager) {
         return NS_ERROR_UNEXPECTED;
     }
 
+    nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(GetAsSupports(this));
+
     if (aCreate) {
-        return manager->CreateStorage(aPrincipal, aDocumentURI,
+        return manager->CreateStorage(domWin, aPrincipal, aDocumentURI,
                                       mInPrivateBrowsing, aStorage);
     }
 
-    return manager->GetStorage(aPrincipal, mInPrivateBrowsing, aStorage);
+    return manager->GetStorage(domWin, aPrincipal, mInPrivateBrowsing,
+                               aStorage);
 }
 
 nsresult
 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
                               nsIDOMStorage* aStorage)
 {
-    nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
-    nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
+    nsRefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
+    if (!storage) {
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    nsIPrincipal* storagePrincipal = storage->GetPrincipal();
     if (storagePrincipal != aPrincipal) {
         NS_ERROR("Wanting to add a sessionStorage for different principal");
         return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
     if (!manager) {
         return NS_ERROR_UNEXPECTED;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -108,20 +108,16 @@
 #include "nsITreeSelection.h"
 #include "nsITreeContentView.h"
 #include "nsITreeView.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsITreeColumns.h"
 #endif
 #include "nsIDOMXPathNSResolver.h"
 
-// Storage includes
-#include "nsIDOMStorage.h"
-#include "nsPIDOMStorage.h"
-
 // Drag and drop
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h" // nsDOMMultipartFile
 
 #include "nsIEventListenerService.h"
 #include "nsIMessageManager.h"
 
 #include "mozilla/dom/TouchEvent.h"
@@ -340,31 +336,16 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XSLTProcessor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XPathNSResolver, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  // WhatWG Storage
-
-  // mrbkap says we don't need WANT_ADDPROPERTY on Storage objects
-  // since a call to addProperty() is always followed by a call to
-  // setProperty(), except in the case when a getter or setter is set
-  // for a property. But we don't care about getters or setters here.
-  NS_DEFINE_CLASSINFO_DATA(Storage, nsStorage2SH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                           nsIXPCScriptable::WANT_NEWRESOLVE |
-                           nsIXPCScriptable::WANT_GETPROPERTY |
-                           nsIXPCScriptable::WANT_SETPROPERTY |
-                           nsIXPCScriptable::WANT_DELPROPERTY |
-                           nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
-                           nsIXPCScriptable::WANT_NEWENUMERATE)
-
   NS_DEFINE_CLASSINFO_DATA(Blob, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(File, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(ModalContentWindow, nsWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
                            WINDOW_SCRIPTABLE_FLAGS)
@@ -937,20 +918,16 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIXSLTProcessor)
     DOM_CLASSINFO_MAP_ENTRY(nsIXSLTProcessorPrivate)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XPathNSResolver, nsIDOMXPathNSResolver)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathNSResolver)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(Blob, nsIDOMBlob)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlob)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(File, nsIDOMFile)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlob)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMFile)
   DOM_CLASSINFO_MAP_END
@@ -3384,227 +3361,16 @@ nsEventTargetSH::AddProperty(nsIXPConnec
 
 void
 nsEventTargetSH::PreserveWrapper(nsISupports *aNative)
 {
   DOMEventTargetHelper* target = DOMEventTargetHelper::FromSupports(aNative);
   target->PreserveWrapper(aNative);
 }
 
-// Storage2SH
-
-// One reason we need a newResolve hook is that in order for
-// enumeration of storage object keys to work the keys we're
-// enumerating need to exist on the storage object for the JS engine
-// to find them.
-
-NS_IMETHODIMP
-nsStorage2SH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, jsid aId, JSObject **objp,
-                         bool *_retval)
-{
-  JS::Rooted<jsid> id(cx, aId);
-  if (ObjectIsNativeWrapper(cx, obj)) {
-    return NS_OK;
-  }
-
-  JS::Rooted<JSObject*> realObj(cx, wrapper->GetJSObject());
-
-  JSAutoCompartment ac(cx, realObj);
-
-  // First check to see if the property is defined on our prototype,
-  // after converting id to a string if it's an integer.
-
-  JS::Rooted<JSString*> jsstr(cx, IdToString(cx, id));
-  if (!jsstr) {
-    return NS_OK;
-  }
-
-  JS::Rooted<JSObject*> proto(cx);
-  if (!::JS_GetPrototype(cx, realObj, &proto)) {
-    return NS_ERROR_FAILURE;
-  }
-  bool hasProp;
-
-  if (proto &&
-      (::JS_HasPropertyById(cx, proto, id, &hasProp) &&
-       hasProp)) {
-    // We found the property we're resolving on the prototype,
-    // nothing left to do here then.
-
-    return NS_OK;
-  }
-
-  // We're resolving property that doesn't exist on the prototype,
-  // check if the key exists in the storage object.
-
-  nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
-
-  nsAutoJSString autoStr;
-  NS_ENSURE_TRUE(autoStr.init(cx, jsstr), NS_ERROR_UNEXPECTED);
-
-  // GetItem() will return null if the caller can't access the session
-  // storage item.
-  nsAutoString data;
-  nsresult rv = storage->GetItem(autoStr, data);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!DOMStringIsNull(data)) {
-    if (!::JS_DefinePropertyById(cx, realObj, id, JS::UndefinedHandleValue,
-                                 JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    *objp = realObj;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsStorage2SH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                          JSObject *aObj, jsid aId, jsval *vp, bool *_retval)
-{
-  JS::Rooted<JSObject*> obj(cx, aObj);
-  JS::Rooted<jsid> id(cx, aId);
-  nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
-  NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
-
-  JSString* key = IdToString(cx, id);
-  NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
-
-  nsAutoJSString keyStr;
-  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
-
-  // For native wrappers, do not get random names on storage objects.
-  if (ObjectIsNativeWrapper(cx, obj)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsAutoString val;
-  nsresult rv = storage->GetItem(keyStr, val);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (DOMStringIsNull(val)) {
-    // No such key.
-    *vp = JSVAL_VOID;
-  } else {
-    JSString* str =
-      JS_NewUCStringCopyN(cx, static_cast<const jschar *>(val.get()),
-                          val.Length());
-    NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
-
-    *vp = STRING_TO_JSVAL(str);
-  }
-
-  return NS_SUCCESS_I_DID_SOMETHING;
-}
-
-NS_IMETHODIMP
-nsStorage2SH::SetProperty(nsIXPConnectWrappedNative *wrapper,
-                          JSContext *cx, JSObject *obj, jsid aId,
-                          jsval *vp, bool *_retval)
-{
-  JS::Rooted<jsid> id(cx, aId);
-  nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
-  NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
-
-  JSString *key = IdToString(cx, id);
-  NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
-
-  nsAutoJSString keyStr;
-  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
-
-  JS::Rooted<JS::Value> val(cx, *vp);
-  JSString *value = JS::ToString(cx, val);
-  NS_ENSURE_TRUE(value, NS_ERROR_UNEXPECTED);
-
-  nsAutoJSString valueStr;
-  NS_ENSURE_TRUE(valueStr.init(cx, value), NS_ERROR_UNEXPECTED);
-
-  nsresult rv = storage->SetItem(keyStr, valueStr);
-  if (NS_SUCCEEDED(rv)) {
-    rv = NS_SUCCESS_I_DID_SOMETHING;
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsStorage2SH::DelProperty(nsIXPConnectWrappedNative *wrapper,
-                          JSContext *cx, JSObject *obj, jsid aId,
-                          bool *_retval)
-{
-  JS::Rooted<jsid> id(cx, aId);
-  nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
-  NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
-
-  JSString *key = IdToString(cx, id);
-  NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
-
-  nsAutoJSString keyStr;
-  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
-
-  nsresult rv = storage->RemoveItem(keyStr);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *_retval = true;
-  return NS_SUCCESS_I_DID_SOMETHING;
-}
-
-
-NS_IMETHODIMP
-nsStorage2SH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                           JSObject *obj, uint32_t enum_op, jsval *statep,
-                           jsid *idp, bool *_retval)
-{
-  if (enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) {
-    nsCOMPtr<nsPIDOMStorage> storage(do_QueryWrappedNative(wrapper));
-
-    // XXXndeakin need to free the keys afterwards
-    nsTArray<nsString> *keys = storage->GetKeys();
-    NS_ENSURE_TRUE(keys, NS_ERROR_OUT_OF_MEMORY);
-
-    *statep = PRIVATE_TO_JSVAL(keys);
-
-    if (idp) {
-      *idp = INT_TO_JSID(keys->Length());
-    }
-    return NS_OK;
-  }
-
-  nsTArray<nsString> *keys = (nsTArray<nsString> *)statep->toPrivate();
-
-  if (enum_op == JSENUMERATE_NEXT && keys->Length() != 0) {
-    nsString& key = keys->ElementAt(0);
-    JS::Rooted<JSString*> str(cx, JS_NewUCStringCopyN(cx, key.get(), key.Length()));
-    NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
-
-    JS::Rooted<jsid> id(cx);
-    JS_StringToId(cx, str, &id);
-    *idp = id;
-
-    keys->RemoveElementAt(0);
-
-    return NS_OK;
-  }
-
-  // destroy the keys array if we have no keys or if we're done
-  NS_ABORT_IF_FALSE(enum_op == JSENUMERATE_DESTROY ||
-                    (enum_op == JSENUMERATE_NEXT && keys->Length() == 0),
-                    "Bad call from the JS engine");
-  delete keys;
-
-  *statep = JSVAL_NULL;
-
-  return NS_OK;
-}
-
 // nsIDOMEventListener::HandleEvent() 'this' converter helper
 
 NS_INTERFACE_MAP_BEGIN(nsEventListenerThisTranslator)
   NS_INTERFACE_MAP_ENTRY(nsIXPCFunctionThisTranslator)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -298,49 +298,16 @@ public:
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsLocationSH(aData);
   }
 };
 
 
-// WebApps Storage helpers
-
-class nsStorage2SH : public nsDOMGenericSH
-{
-protected:
-  nsStorage2SH(nsDOMClassInfoData* aData) : nsDOMGenericSH(aData)
-  {
-  }
-
-  virtual ~nsStorage2SH()
-  {
-  }
-
-  NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                        JSObject *obj, jsid id, JSObject **objp,
-                        bool *_retval) MOZ_OVERRIDE;
-  NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE;
-  NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE;
-  NS_IMETHOD DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, jsid id, bool *_retval) MOZ_OVERRIDE;
-  NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                          JSObject *obj, uint32_t enum_op, JS::Value *statep,
-                          jsid *idp, bool *_retval) MOZ_OVERRIDE;
-
-public:
-  static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
-  {
-    return new nsStorage2SH(aData);
-  }
-};
-
 // Event handler 'this' translator class, this is called by XPConnect
 // when a "function interface" (nsIDOMEventListener) is called, this
 // class extracts 'this' fomr the first argument to the called
 // function (nsIDOMEventListener::HandleEvent(in nsIDOMEvent)), this
 // class will pass back nsIDOMEvent::currentTarget to be used as
 // 'this'.
 
 class nsEventListenerThisTranslator : public nsIXPCFunctionThisTranslator
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -43,19 +43,16 @@ DOMCI_CLASS(CSSMozDocumentRule)
 DOMCI_CLASS(CSSSupportsRule)
 
 // XSLTProcessor
 DOMCI_CLASS(XSLTProcessor)
 
 // DOM Level 3 XPath objects
 DOMCI_CLASS(XPathNSResolver)
 
-// WhatWG WebApps Objects
-DOMCI_CLASS(Storage)
-
 DOMCI_CLASS(Blob)
 DOMCI_CLASS(File)
 
 // DOM modal content window class, almost identical to Window
 DOMCI_CLASS(ModalContentWindow)
 
 DOMCI_CLASS(MozSmsMessage)
 DOMCI_CLASS(MozMmsMessage)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11,19 +11,20 @@
 #include "mozilla/MemoryReporting.h"
 
 // Local Includes
 #include "Navigator.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsPerformance.h"
 #include "nsDOMNavigationTiming.h"
-#include "nsIDOMStorage.h"
 #include "nsIDOMStorageManager.h"
-#include "DOMStorage.h"
+#include "mozilla/dom/DOMStorage.h"
+#include "mozilla/dom/StorageEvent.h"
+#include "mozilla/dom/StorageEventBinding.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsISizeOfEventTarget.h"
 #include "nsDOMJSUtils.h"
 #include "nsArrayUtils.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsDOMWindowList.h"
@@ -84,17 +85,16 @@
 #include "Crypto.h"
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
 #include "nsIDOMCryptoLegacy.h"
 #endif
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMOfflineResourceList.h"
-#include "nsPIDOMStorage.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow.h"
 #include "nsThreadUtils.h"
 #include "nsILoadContext.h"
 #include "nsIPresShell.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsView.h"
@@ -10344,17 +10344,17 @@ nsGlobalWindow::GetComputedStyleHelper(E
   nsRefPtr<nsComputedDOMStyle> compStyle =
     NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell,
                            aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
                                                 nsComputedDOMStyle::eAll);
 
   return compStyle.forget();
 }
 
-nsIDOMStorage*
+DOMStorage*
 nsGlobalWindow::GetSessionStorage(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetSessionStorage, (aError), aError, nullptr);
 
   nsIPrincipal *principal = GetPrincipal();
   nsIDocShell* docShell = GetDocShell();
 
   if (!principal || !docShell || !Preferences::GetBool(kStorageEnabled)) {
@@ -10362,25 +10362,22 @@ nsGlobalWindow::GetSessionStorage(ErrorR
   }
 
   if (mSessionStorage) {
 #ifdef PR_LOGGING
     if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
       PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get());
     }
 #endif
-    nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(mSessionStorage);
-    if (piStorage) {
-      bool canAccess = piStorage->CanAccess(principal);
-      NS_ASSERTION(canAccess,
-                   "window %x owned sessionStorage "
-                   "that could not be accessed!");
-      if (!canAccess) {
-        mSessionStorage = nullptr;
-      }
+    bool canAccess = mSessionStorage->CanAccess(principal);
+    NS_ASSERTION(canAccess,
+                 "This window owned sessionStorage "
+                 "that could not be accessed!");
+    if (!canAccess) {
+      mSessionStorage = nullptr;
     }
   }
 
   if (!mSessionStorage) {
     nsString documentURI;
     if (mDoc) {
       mDoc->GetDocumentURI(documentURI);
     }
@@ -10402,24 +10399,27 @@ nsGlobalWindow::GetSessionStorage(ErrorR
     nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
     if (NS_FAILED(rv)) {
       aError.Throw(rv);
       return nullptr;
     }
 
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
 
-    aError = storageManager->CreateStorage(principal,
-                                           documentURI,
+    nsCOMPtr<nsIDOMStorage> storage;
+    aError = storageManager->CreateStorage(this, principal, documentURI,
                                            loadContext && loadContext->UsePrivateBrowsing(),
-                                           getter_AddRefs(mSessionStorage));
+                                           getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
+    mSessionStorage = static_cast<DOMStorage*>(storage.get());
+    MOZ_ASSERT(mSessionStorage);
+
 #ifdef PR_LOGGING
     if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
       PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get());
     }
 #endif
 
     if (!mSessionStorage) {
       aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
@@ -10432,26 +10432,26 @@ nsGlobalWindow::GetSessionStorage(ErrorR
     PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
   }
 #endif
 
   return mSessionStorage;
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
+nsGlobalWindow::GetSessionStorage(nsISupports** aSessionStorage)
 {
   ErrorResult rv;
   nsCOMPtr<nsIDOMStorage> storage = GetSessionStorage(rv);
   storage.forget(aSessionStorage);
 
   return rv.ErrorCode();
 }
 
-nsIDOMStorage*
+DOMStorage*
 nsGlobalWindow::GetLocalStorage(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetLocalStorage, (aError), aError, nullptr);
 
   if (!Preferences::GetBool(kStorageEnabled)) {
     return nullptr;
   }
 
@@ -10484,27 +10484,33 @@ nsGlobalWindow::GetLocalStorage(ErrorRes
     nsString documentURI;
     if (mDoc) {
       mDoc->GetDocumentURI(documentURI);
     }
 
     nsIDocShell* docShell = GetDocShell();
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
 
-    aError = storageManager->CreateStorage(principal,
-                                           documentURI,
+    nsCOMPtr<nsIDOMStorage> storage;
+    aError = storageManager->CreateStorage(this, principal, documentURI,
                                            loadContext && loadContext->UsePrivateBrowsing(),
-                                           getter_AddRefs(mLocalStorage));
+                                           getter_AddRefs(storage));
+    if (aError.Failed()) {
+      return nullptr;
+    }
+
+    mLocalStorage = static_cast<DOMStorage*>(storage.get());
+    MOZ_ASSERT(mLocalStorage);
   }
 
   return mLocalStorage;
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
+nsGlobalWindow::GetLocalStorage(nsISupports** aLocalStorage)
 {
   NS_ENSURE_ARG(aLocalStorage);
 
   ErrorResult rv;
   nsCOMPtr<nsIDOMStorage> storage = GetLocalStorage(rv);
   storage.forget(aLocalStorage);
 
   return rv.ErrorCode();
@@ -11281,79 +11287,80 @@ nsGlobalWindow::Observe(nsISupports* aSu
     nsIPrincipal *principal;
     nsresult rv;
 
     nsRefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
     if (!event) {
       return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsIDOMStorage> changingStorage = event->GetStorageArea();
+    nsRefPtr<DOMStorage> changingStorage = event->GetStorageArea();
     if (!changingStorage) {
       return NS_ERROR_FAILURE;
     }
 
+    nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
+
     bool fireMozStorageChanged = false;
     principal = GetPrincipal();
     if (!principal) {
       return NS_OK;
     }
 
-    nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
-
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
     bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
-    if (pistorage->IsPrivate() != isPrivate) {
+    if (changingStorage->IsPrivate() != isPrivate) {
       return NS_OK;
     }
 
-    switch (pistorage->GetType())
+    switch (changingStorage->GetType())
     {
-    case nsPIDOMStorage::SessionStorage:
+    case DOMStorage::SessionStorage:
     {
       bool check = false;
 
       nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
       if (storageManager) {
-        rv = storageManager->CheckStorage(principal, changingStorage, &check);
+        rv = storageManager->CheckStorage(principal, istorage, &check);
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
 
       if (!check) {
         // This storage event is not coming from our storage or is coming
         // from a different docshell, i.e. it is a clone, ignore this event.
         return NS_OK;
       }
 
 #ifdef PR_LOGGING
       if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
-        PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p", this, mSessionStorage.get(), pistorage.get());
-      }
-#endif
-
-      fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
+        PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p",
+                    this, mSessionStorage.get(), changingStorage.get());
+      }
+#endif
+
+      fireMozStorageChanged = mSessionStorage == changingStorage;
       break;
     }
 
-    case nsPIDOMStorage::LocalStorage:
+    case DOMStorage::LocalStorage:
     {
       // Allow event fire only for the same principal storages
       // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
-      nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
+      nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
 
       bool equals = false;
       rv = storagePrincipal->Equals(principal, &equals);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (!equals)
         return NS_OK;
 
-      fireMozStorageChanged = SameCOMIdentity(mLocalStorage, changingStorage);
+      fireMozStorageChanged = mLocalStorage == changingStorage;
       break;
     }
     default:
       return NS_OK;
     }
 
     // Clone the storage event included in the observer notification. We want
     // to dispatch clones rather than the original event.
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -910,18 +910,18 @@ public:
                       const mozilla::dom::Optional<int32_t>& aTimeout,
                       const mozilla::dom::Sequence<JS::Value>& /* unused */,
                       mozilla::ErrorResult& aError);
   void ClearInterval(int32_t aHandle, mozilla::ErrorResult& aError);
   void Atob(const nsAString& aAsciiBase64String, nsAString& aBinaryData,
             mozilla::ErrorResult& aError);
   void Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String,
             mozilla::ErrorResult& aError);
-  nsIDOMStorage* GetSessionStorage(mozilla::ErrorResult& aError);
-  nsIDOMStorage* GetLocalStorage(mozilla::ErrorResult& aError);
+  mozilla::dom::DOMStorage* GetSessionStorage(mozilla::ErrorResult& aError);
+  mozilla::dom::DOMStorage* GetLocalStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
   mozilla::dom::indexedDB::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
   already_AddRefed<nsICSSDeclaration>
     GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
                      mozilla::ErrorResult& aError);
   already_AddRefed<mozilla::dom::MediaQueryList> MatchMedia(const nsAString& aQuery,
                                                             mozilla::ErrorResult& aError);
   nsScreen* GetScreen(mozilla::ErrorResult& aError);
@@ -1540,18 +1540,18 @@ protected:
   nsCOMPtr<nsIDOMCrypto>        mCrypto;
   nsRefPtr<mozilla::dom::Console> mConsole;
   // We need to store an nsISupports pointer to this object because the
   // mozilla::dom::External class doesn't exist on b2g and using the type
   // forward declared here means that ~nsGlobalWindow wouldn't compile because
   // it wouldn't see the ~External function's declaration.
   nsCOMPtr<nsISupports>         mExternal;
 
-  nsCOMPtr<nsIDOMStorage>      mLocalStorage;
-  nsCOMPtr<nsIDOMStorage>      mSessionStorage;
+  nsRefPtr<mozilla::dom::DOMStorage> mLocalStorage;
+  nsRefPtr<mozilla::dom::DOMStorage> mSessionStorage;
 
   // These member variable are used only on inner windows.
   nsRefPtr<mozilla::EventListenerManager> mListenerManager;
   // mTimeouts is generally sorted by mWhen, unless mTimeoutInsertionPoint is
   // non-null.  In that case, the dummy timeout pointed to by
   // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
   // that come after it.
   mozilla::LinkedList<nsTimeout> mTimeouts;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1078,16 +1078,20 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'workers': True,
 },
 
 'SourceBufferList': {
     'resultNotAddRefed': [ '__indexedGetter' ],
 },
 
+'Storage': {
+    'nativeType': 'mozilla::dom::DOMStorage',
+},
+
 'StyleSheet': {
     'nativeType': 'mozilla::CSSStyleSheet',
 },
 
 'SVGAnimatedLengthList': {
     'nativeType': 'mozilla::DOMSVGAnimatedLengthList',
     'headerFile': 'DOMSVGAnimatedLengthList.h',
 },
--- a/dom/bindings/test/test_proxies_via_xray.html
+++ b/dom/bindings/test/test_proxies_via_xray.html
@@ -71,17 +71,26 @@ function test()
      "Attribute setter should have been called");
   d.foo = "baz";
   // Check that this actually got passed on to the underlying object.
   is(d.wrappedJSObject.foo, "baz",
      "Set should get forwarded to the underlying object again");
   is(doc.documentElement.getAttribute("data-foo"), "baz",
      "Attribute setter should have been called again");
 
-  // XXXbz no way yet to test non-overridebuiltins stuff with named setter
+  // Test non-overridebuiltins binding with named setter
+  var s = doc.defaultView.localStorage;
+  s["test_proxies_via_xray"] = "bar";
+  // Check that this actually got passed on to the underlying object.
+  is(s.wrappedJSObject["test_proxies_via_xray"], "bar",
+     "Set should get forwarded to the underlying object without overridebuiltins");
+  s["test_proxies_via_xray"] = "baz";
+  // Check that this actually got passed on to the underlying object.
+  is(s.wrappedJSObject["test_proxies_via_xray"], "baz",
+     "Set should get forwarded to the underlying object again without overridebuiltins");
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(test);
 
 </script>
--- a/dom/events/StorageEvent.cpp
+++ b/dom/events/StorageEvent.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/StorageEvent.h"
-#include "nsIDOMStorage.h"
+#include "mozilla/dom/DOMStorage.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(StorageEvent)
 
 NS_IMPL_ADDREF_INHERITED(StorageEvent, Event)
 NS_IMPL_RELEASE_INHERITED(StorageEvent, Event)
@@ -80,17 +80,17 @@ StorageEvent::Constructor(const GlobalOb
 }
 
 void
 StorageEvent::InitStorageEvent(const nsAString& aType, bool aCanBubble,
                                bool aCancelable, const nsAString& aKey,
                                const nsAString& aOldValue,
                                const nsAString& aNewValue,
                                const nsAString& aURL,
-                               nsIDOMStorage* aStorageArea,
+                               DOMStorage* aStorageArea,
                                ErrorResult& aRv)
 {
   aRv = InitEvent(aType, aCanBubble, aCancelable);
   if (aRv.Failed()) {
     return;
   }
 
   mKey = aKey;
--- a/dom/events/StorageEvent.h
+++ b/dom/events/StorageEvent.h
@@ -8,41 +8,41 @@
 #define mozilla_dom_StorageEvent_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/StorageEventBinding.h"
 
-class nsIDOMStorage;
-
 // Helper for EventDispatcher.
 nsresult NS_NewDOMStorageEvent(nsIDOMEvent** aDOMEvent,
                                mozilla::dom::EventTarget* aOwner);
 
 namespace mozilla {
 namespace dom {
 
+class DOMStorage;
+
 class StorageEvent : public Event
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(StorageEvent, Event)
 
   StorageEvent(EventTarget* aOwner);
 
 protected:
   virtual ~StorageEvent();
 
   nsString mKey;
   nsString mOldValue;
   nsString mNewValue;
   nsString mUrl;
-  nsCOMPtr<nsIDOMStorage> mStorageArea;
+  nsRefPtr<DOMStorage> mStorageArea;
 
 public:
   virtual StorageEvent* AsStorageEvent();
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   static already_AddRefed<StorageEvent>
   Constructor(EventTarget* aOwner, const nsAString& aType,
@@ -52,17 +52,17 @@ public:
   Constructor(const GlobalObject& aGlobal, const nsAString& aType,
               const StorageEventInit& aEventInitDict, ErrorResult& aRv);
 
   void InitStorageEvent(const nsAString& aType, bool aCanBubble,
                         bool aCancelable, const nsAString& aKey,
                         const nsAString& aOldValue,
                         const nsAString& aNewValue,
                         const nsAString& aURL,
-                        nsIDOMStorage* aStorageArea,
+                        DOMStorage* aStorageArea,
                         ErrorResult& aRv);
 
   void GetKey(nsString& aRetVal) const
   {
     aRetVal = mKey;
   }
 
   void GetOldValue(nsString& aRetVal) const
@@ -75,17 +75,17 @@ public:
     aRetVal = mNewValue;
   }
 
   void GetUrl(nsString& aRetVal) const
   {
     aRetVal = mUrl;
   }
 
-  nsIDOMStorage* GetStorageArea() const
+  DOMStorage* GetStorageArea() const
   {
     return mStorageArea;
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/mochitest.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
-[DEFAULT]
-support-files =
-
-
-[test_missing_arguments.html.json]
-[test_storage_local_builtins.html.json]
-[test_storage_local_in_js.html.json]
-[test_storage_local_removeitem_js.html.json]
-[test_storage_session_builtins.html.json]
-[test_storage_session_in_js.html.json]
-[test_storage_session_removeitem_js.html.json]
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "Should throw TypeError for function \"function () { localStorage.key(); }\".": true,
-  "Should throw TypeError for function \"function () { localStorage.getItem(); }\".": true,
-  "Should throw TypeError for function \"function () { localStorage.setItem(); }\".": true,
-  "Should throw TypeError for function \"function () { localStorage.setItem(\"a\"); }\".": true,
-  "Should throw TypeError for function \"function () { localStorage.removeItem(); }\".": true,
-  "Should throw TypeError for function \"function () { sessionStorage.key(); }\".": true,
-  "Should throw TypeError for function \"function () { sessionStorage.getItem(); }\".": true,
-  "Should throw TypeError for function \"function () { sessionStorage.setItem(); }\".": true,
-  "Should throw TypeError for function \"function () { sessionStorage.setItem(\"a\"); }\".": true,
-  "Should throw TypeError for function \"function () { sessionStorage.removeItem(); }\".": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "Web Storage": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "Web Storage 1": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "Web Storage 2": true,
-  "Web Storage 3": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "Web Storage": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "Web Storage 1": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "Web Storage 2": true,
-  "Web Storage 3": true
-}
--- a/dom/imptests/moz.build
+++ b/dom/imptests/moz.build
@@ -27,11 +27,10 @@ MOCHITEST_MANIFESTS += [
     'failures/html/html/semantics/forms/the-select-element/mochitest.ini',
     'failures/html/html/semantics/scripting-1/the-script-element/mochitest.ini',
     'failures/html/html/semantics/tabular-data/the-table-element/mochitest.ini',
     'failures/html/html/webappapis/atob/mochitest.ini',
     'failures/html/js/builtins/mochitest.ini',
     'failures/html/microdata/microdata-dom-api/mochitest.ini',
     'failures/html/typedarrays/mochitest.ini',
     'failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini',
-    'failures/webapps/WebStorage/tests/submissions/Ms2ger/mochitest.ini',
     'failures/webapps/XMLHttpRequest/tests/submissions/Ms2ger/mochitest.ini',
 ]
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -5,31 +5,30 @@
 
 #include "domstubs.idl"
 
 interface nsIFrameRequestCallback;
 interface nsIControllers;
 interface nsIDOMBlob;
 interface nsIDOMLocation;
 interface nsIDOMOfflineResourceList;
-interface nsIDOMStorage;
 interface nsIPrompt;
 interface nsISelection;
 interface nsIVariant;
 
 /**
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(d4316591-d16e-405c-8093-b441cbef3230)]
+[scriptable, uuid(c3ff0328-6c47-4e64-a22f-ac221959e258)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -236,25 +235,27 @@ interface nsIDOMWindow : nsISupports
   // Ascii base64 data to binary data and vice versa...
   DOMString                 atob(in DOMString aAsciiString);
   DOMString                 btoa(in DOMString aBase64Data);
 
 
   // WindowSessionStorage
   /**
    * Session storage for the current browsing context.
+   * This attribute is a DOMStorage
    */
-  readonly attribute nsIDOMStorage sessionStorage;
+  readonly attribute nsISupports sessionStorage;
 
 
   // WindowLocalStorage
   /**
    * Local storage for the current browsing context.
+   * This attribute is a DOMStorage
    */
-  readonly attribute nsIDOMStorage localStorage;
+  readonly attribute nsISupports localStorage;
 
 
   // IndexedDB
   // https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#requests
   // IDBEnvironment
   readonly attribute nsISupports indexedDB;
   readonly attribute nsISupports mozIndexedDB;
 
--- a/dom/interfaces/storage/moz.build
+++ b/dom/interfaces/storage/moz.build
@@ -5,13 +5,8 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsIDOMStorage.idl',
     'nsIDOMStorageManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_storage'
-
-EXPORTS += [
-    'nsPIDOMStorage.h',
-]
-
--- a/dom/interfaces/storage/nsIDOMStorage.idl
+++ b/dom/interfaces/storage/nsIDOMStorage.idl
@@ -1,68 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 /**
- * Interface for client side storage. See
- * http://www.whatwg.org/specs/web-apps/current-work/multipage/structured.html#storage0
- * for more information.
- *
- * A storage object stores an arbitrary set of key-value pairs, which
- * may be retrieved, modified and removed as needed. A key may only
- * exist once within a storage object, and only one value may be
- * associated with a particular key. Keys are stored in a particular
- * order with the condition that this order not change by merely changing
- * the value associated with a key, but the order may change when a
- * key is added or removed.
+ * Empty interface for client side storage. DOMStorage is now ported to WebIDL
+ * but we still need an XPConnect interface for casting.
  */
 
-[scriptable, uuid(43E5EDAD-1E02-42c4-9D99-C3D9DEE22A20)]
+[scriptable, uuid(425a33f0-e0e9-45e7-a95f-9908bd6ae988)]
 interface nsIDOMStorage : nsISupports
 {
-  /**
-   * The number of keys stored.
-   */
-  readonly attribute unsigned long length;
-
-  /**
-   * Retrieve the name of the key at a particular index.
-   *
-   * @param index index of the item to retrieve
-   * @returns the key at index, null if there is no key at that index
-   */
-  DOMString key(in unsigned long index);
-
-  /**
-   * Retrieve an item with a given key
-   *
-   * @param key key to retrieve
-   * @returns found data or empty string if the key was not found
-   */
-  DOMString getItem([Null(Stringify)] in DOMString key);
-
-  /**
-   * Assign a value with a key. If the key does not exist already, a new
-   * key is added associated with that value. If the key already exists,
-   * then the existing value is replaced with a new value.
-   *
-   * @param key key to set
-   * @param data data to associate with the key
-   */
-  void setItem([Null(Stringify)] in DOMString key, [Null(Stringify)] in DOMString data);
-
-  /**
-   * Remove a key and its corresponding value.
-   *
-   * @param key key to remove
-   */
-  void removeItem([Null(Stringify)] in DOMString key);
-
-  /**
-   * Clear the content of this storage bound to a domain
-   * or an origin.
-   */
-  void clear();
 };
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -2,58 +2,65 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMStorage;
 interface nsIPrincipal;
+interface nsIDOMWindow;
 
 /**
  * General purpose interface that has two implementations, for localStorage
  * resp. sessionStorage with "@mozilla.org/dom/localStorage-manager;1" resp.
  * "@mozilla.org/dom/sessionStorage-manager;1" contract IDs.
  */
-[scriptable, uuid(8096f9ea-fa61-4960-b5d7-fb30ac42c8d8)]
+[scriptable, uuid(a15f7ebd-4f35-4e73-a2d8-255d27fd14ee)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
    * This starts async preloading of a storage cache for scope
    * defined by the principal.
    */
   void precacheStorage(in nsIPrincipal aPrincipal);
 
   /**
    * Returns instance of DOM storage object for given principal.
    * A new object is always returned and it is ensured there is
    * a storage for the scope created.
    *
+   * @param aWindow
+   *    The parent window.
    * @param aPrincipal
    *    Principal to bound storage to.
    * @param aDocumentURI
    *    URL of the demanding document, used for DOM storage event only.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
-  nsIDOMStorage createStorage(in nsIPrincipal aPrincipal,
+  nsIDOMStorage createStorage(in nsIDOMWindow aWindow,
+                              in nsIPrincipal aPrincipal,
                               in DOMString aDocumentURI,
                               [optional] in bool aPrivate);
   /**
    * Returns instance of DOM storage object for given principal.
    * If there is no storage managed for the scope, then null is returned and
    * no object is created.  Otherwise, an object (new) for the existing storage
    * scope is returned.
    *
+   * @param aWindow
+   *    The parent window.
    * @param aPrincipal
    *    Principal to bound storage to.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
-  nsIDOMStorage getStorage(in nsIPrincipal aPrincipal,
+  nsIDOMStorage getStorage(in nsIDOMWindow aWindow,
+                           in nsIPrincipal aPrincipal,
                            [optional] in bool aPrivate);
 
   /**
    * Clones given storage into this storage manager.
    *
    * @param aStorageToCloneFrom
    *    The storage to copy all items from into this manager.  Manager will then
    *    return a new and independent object that contains snapshot of data from
deleted file mode 100644
--- a/dom/interfaces/storage/nsPIDOMStorage.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set sw=2 ts=2 et tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef __nsPIDOMStorage_h_
-#define __nsPIDOMStorage_h_
-
-#include "nsISupports.h"
-#include "nsString.h"
-#include "nsTArray.h"
-
-class nsIPrincipal;
-
-namespace mozilla {
-namespace dom {
-
-class DOMStorageCache;
-class DOMStorageManager;
-
-} // ::dom
-} // ::mozilla
-
-// {09198A51-5D27-4992-97E4-38A9CEA2A65D}
-#define NS_PIDOMSTORAGE_IID \
-  { 0x9198a51, 0x5d27, 0x4992, \
-    { 0x97, 0xe4, 0x38, 0xa9, 0xce, 0xa2, 0xa6, 0x5d } }
-
-class nsPIDOMStorage : public nsISupports
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
-
-  enum StorageType {
-    LocalStorage = 1,
-    SessionStorage = 2
-  };
-
-  virtual StorageType GetType() const = 0;
-  virtual mozilla::dom::DOMStorageManager* GetManager() const = 0;
-  virtual const mozilla::dom::DOMStorageCache* GetCache() const = 0;
-
-  virtual nsTArray<nsString>* GetKeys() = 0;
-
-  virtual nsIPrincipal* GetPrincipal() = 0;
-  virtual bool PrincipalEquals(nsIPrincipal* principal) = 0;
-  virtual bool CanAccess(nsIPrincipal *aPrincipal) = 0;
-  virtual bool IsPrivate() = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMStorage, NS_PIDOMSTORAGE_IID)
-
-#endif // __nsPIDOMStorage_h_
--- a/dom/src/storage/DOMStorage.cpp
+++ b/dom/src/storage/DOMStorage.cpp
@@ -2,166 +2,179 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMStorage.h"
 #include "DOMStorageCache.h"
 #include "DOMStorageManager.h"
 
-#include "mozilla/dom/StorageEvent.h"
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsICookiePermission.h"
 
-#include "nsDOMClassInfoID.h"
+#include "mozilla/dom/StorageBinding.h"
+#include "mozilla/dom/StorageEvent.h"
+#include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 
-DOMCI_DATA(Storage, mozilla::dom::DOMStorage)
-
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_ADDREF(DOMStorage)
-NS_IMPL_RELEASE(DOMStorage)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMStorage, mManager, mPrincipal, mWindow)
 
-NS_INTERFACE_MAP_BEGIN(DOMStorage)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMStorage)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMStorage)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStorage)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
-  NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
 NS_INTERFACE_MAP_END
 
-DOMStorage::DOMStorage(DOMStorageManager* aManager,
+DOMStorage::DOMStorage(nsIDOMWindow* aWindow,
+                       DOMStorageManager* aManager,
                        DOMStorageCache* aCache,
                        const nsAString& aDocumentURI,
                        nsIPrincipal* aPrincipal,
                        bool aIsPrivate)
-: mManager(aManager)
+: mWindow(aWindow)
+, mManager(aManager)
 , mCache(aCache)
 , mDocumentURI(aDocumentURI)
 , mPrincipal(aPrincipal)
 , mIsPrivate(aIsPrivate)
 , mIsSessionOnly(false)
 {
   mCache->Preload();
+  SetIsDOMBinding();
 }
 
 DOMStorage::~DOMStorage()
 {
   mCache->KeepAlive();
 }
 
-// nsIDOMStorage (web content public API implementation)
+/* virtual */ JSObject*
+DOMStorage::WrapObject(JSContext* aCx)
+{
+  return StorageBinding::Wrap(aCx, this);
+}
 
-NS_IMETHODIMP
-DOMStorage::GetLength(uint32_t* aLength)
+uint32_t
+DOMStorage::GetLength(ErrorResult& aRv)
 {
   if (!CanUseStorage(this)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  return mCache->GetLength(this, aLength);
-}
-
-NS_IMETHODIMP
-DOMStorage::Key(uint32_t aIndex, nsAString& aRetval)
-{
-  if (!CanUseStorage(this)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return 0;
   }
 
-  return mCache->GetKey(this, aIndex, aRetval);
+  uint32_t length;
+  aRv = mCache->GetLength(this, &length);
+  return length;
 }
 
-NS_IMETHODIMP
-DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
+void
+DOMStorage::Key(uint32_t aIndex, nsAString& aResult, ErrorResult& aRv)
 {
   if (!CanUseStorage(this)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
   }
 
-  return mCache->GetItem(this, aKey, aRetval);
+  aRv = mCache->GetKey(this, aIndex, aResult);
 }
 
-NS_IMETHODIMP
-DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
+void
+DOMStorage::GetItem(const nsAString& aKey, nsAString& aResult, ErrorResult& aRv)
 {
   if (!CanUseStorage(this)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  aRv = mCache->GetItem(this, aKey, aResult);
+}
+
+void
+DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData,
+                    ErrorResult& aRv)
+{
+  if (!CanUseStorage(this)) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
   }
 
   Telemetry::Accumulate(GetType() == LocalStorage
       ? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES
       : Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length());
   Telemetry::Accumulate(GetType() == LocalStorage
       ? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES
       : Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length());
 
   nsString data;
   bool ok = data.Assign(aData, fallible_t());
-  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+  if (!ok) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
 
   nsString old;
-  nsresult rv = mCache->SetItem(this, aKey, data, old);
-  if (NS_FAILED(rv)) {
-    return rv;
+  aRv = mCache->SetItem(this, aKey, data, old);
+  if (aRv.Failed()) {
+    return;
   }
 
-  if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
+  if (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION) {
     BroadcastChangeNotification(aKey, old, aData);
   }
-
-  return NS_OK;
 }
 
-NS_IMETHODIMP
-DOMStorage::RemoveItem(const nsAString& aKey)
+void
+DOMStorage::RemoveItem(const nsAString& aKey, ErrorResult& aRv)
 {
   if (!CanUseStorage(this)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
   }
 
   nsAutoString old;
-  nsresult rv = mCache->RemoveItem(this, aKey, old);
-  if (NS_FAILED(rv)) {
-    return rv;
+  aRv = mCache->RemoveItem(this, aKey, old);
+  if (aRv.Failed()) {
+    return;
   }
 
-  if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
+  if (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION) {
     BroadcastChangeNotification(aKey, old, NullString());
   }
-
-  return NS_OK;
 }
 
-NS_IMETHODIMP
-DOMStorage::Clear()
+void
+DOMStorage::Clear(ErrorResult& aRv)
 {
   if (!CanUseStorage(this)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
   }
 
-  nsresult rv = mCache->Clear(this);
-  if (NS_FAILED(rv)) {
-    return rv;
+  aRv = mCache->Clear(this);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
   }
 
-  if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
+  if (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION) {
     BroadcastChangeNotification(NullString(), NullString(), NullString());
   }
-
-  return NS_OK;
 }
 
 namespace {
 
 class StorageNotifierRunnable : public nsRunnable
 {
 public:
   StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType)
@@ -194,17 +207,17 @@ DOMStorage::BroadcastChangeNotification(
                                         const nsSubstring& aNewValue)
 {
   StorageEventInit dict;
   dict.mBubbles = false;
   dict.mCancelable = false;
   dict.mKey = aKey;
   dict.mNewValue = aNewValue;
   dict.mOldValue = aOldValue;
-  dict.mStorageArea = static_cast<nsIDOMStorage*>(this);
+  dict.mStorageArea = this;
   dict.mUrl = mDocumentURI;
 
   // Note, this DOM event should never reach JS. It is cloned later in
   // nsGlobalWindow.
   nsRefPtr<StorageEvent> event =
     StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
 
   nsRefPtr<StorageNotifierRunnable> r =
@@ -280,19 +293,17 @@ DOMStorage::CanUseStorage(DOMStorage* aS
 
   if (aStorage) {
     return aStorage->CanAccess(subjectPrincipal);
   }
 
   return true;
 }
 
-// nsPIDOMStorage
-
-nsPIDOMStorage::StorageType
+DOMStorage::StorageType
 DOMStorage::GetType() const
 {
   return mManager->Type();
 }
 
 nsIPrincipal*
 DOMStorage::GetPrincipal()
 {
@@ -310,20 +321,22 @@ DOMStorage::PrincipalEquals(nsIPrincipal
 }
 
 bool
 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
 {
   return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
 }
 
-nsTArray<nsString>*
-DOMStorage::GetKeys()
+void
+DOMStorage::GetSupportedNames(unsigned, nsTArray<nsString>& aKeys)
 {
   if (!CanUseStorage(this)) {
-    return new nsTArray<nsString>(); // return just an empty array
+    // return just an empty array
+    aKeys.Clear();
+    return;
   }
 
-  return mCache->GetKeys(this);
+  mCache->GetKeys(this, aKeys);
 }
 
 } // ::dom
 } // ::mozilla
--- a/dom/src/storage/DOMStorage.h
+++ b/dom/src/storage/DOMStorage.h
@@ -2,51 +2,123 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDOMStorage_h___
 #define nsDOMStorage_h___
 
 #include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
 #include "nsIDOMStorage.h"
-#include "nsPIDOMStorage.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsWeakReference.h"
-#include "nsAutoPtr.h"
+#include "nsWrapperCache.h"
+#include "nsISupports.h"
+
+class nsIPrincipal;
+class nsIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class DOMStorageManager;
 class DOMStorageCache;
 
-class DOMStorage MOZ_FINAL : public nsIDOMStorage
-                           , public nsPIDOMStorage
-                           , public nsSupportsWeakReference
+class DOMStorage MOZ_FINAL
+  : public nsIDOMStorage
+  , public nsSupportsWeakReference
+  , public nsWrapperCache
 {
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMSTORAGE
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(DOMStorage,
+                                                         nsIDOMStorage)
+
+  enum StorageType {
+    LocalStorage = 1,
+    SessionStorage = 2
+  };
+
+  StorageType GetType() const;
 
-  // nsPIDOMStorage
-  virtual StorageType GetType() const MOZ_OVERRIDE;
-  virtual DOMStorageManager* GetManager() const MOZ_OVERRIDE { return mManager; }
-  virtual const DOMStorageCache* GetCache() const MOZ_OVERRIDE { return mCache; }
+  DOMStorageManager* GetManager() const
+  {
+    return mManager;
+  }
+
+  DOMStorageCache const* GetCache() const
+  {
+    return mCache;
+  }
 
-  virtual nsTArray<nsString>* GetKeys() MOZ_OVERRIDE;
-  virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE;
-  virtual bool PrincipalEquals(nsIPrincipal* aPrincipal) MOZ_OVERRIDE;
-  virtual bool CanAccess(nsIPrincipal* aPrincipal) MOZ_OVERRIDE;
-  virtual bool IsPrivate() MOZ_OVERRIDE { return mIsPrivate; }
+  nsIPrincipal* GetPrincipal();
+  bool PrincipalEquals(nsIPrincipal* aPrincipal);
+  bool CanAccess(nsIPrincipal* aPrincipal);
+  bool IsPrivate()
+  {
+    return mIsPrivate;
+  }
 
-  DOMStorage(DOMStorageManager* aManager,
+  DOMStorage(nsIDOMWindow* aWindow,
+             DOMStorageManager* aManager,
              DOMStorageCache* aCache,
              const nsAString& aDocumentURI,
              nsIPrincipal* aPrincipal,
              bool aIsPrivate);
 
+  // WebIDL
+  JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  nsIDOMWindow* GetParentObject() const
+  {
+    return mWindow;
+  }
+
+  uint32_t GetLength(ErrorResult& aRv);
+
+  void Key(uint32_t aIndex, nsAString& aResult, ErrorResult& aRv);
+
+  void GetItem(const nsAString& aKey, nsAString& aResult, ErrorResult& aRv);
+
+  bool NameIsEnumerable(const nsAString& aName) const
+  {
+    return true;
+  }
+
+  void GetSupportedNames(unsigned, nsTArray<nsString>& aKeys);
+
+  void NamedGetter(const nsAString& aKey, bool& aFound, nsAString& aResult,
+	           ErrorResult& aRv)
+  {
+    GetItem(aKey, aResult, aRv);
+    aFound = !aResult.IsVoid();
+  }
+
+  void SetItem(const nsAString& aKey, const nsAString& aValue,
+               ErrorResult& aRv);
+
+  void NamedSetter(const nsAString& aKey, const nsAString& aValue,
+                   ErrorResult& aRv)
+  {
+    SetItem(aKey, aValue, aRv);
+  }
+
+  void RemoveItem(const nsAString& aKey, ErrorResult& aRv);
+
+  void NamedDeleter(const nsAString& aKey, bool& aFound, ErrorResult& aRv)
+  {
+    RemoveItem(aKey, aRv);
+
+    aFound = (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION);
+  }
+
+  void Clear(ErrorResult& aRv);
+
   // The method checks whether the caller can use a storage.
   // CanUseStorage is called before any DOM initiated operation
   // on a storage is about to happen and ensures that the storage's
   // session-only flag is properly set according the current settings.
   // It is an optimization since the privileges check and session only
   // state determination are complex and share the code (comes hand in
   // hand together).
   static bool CanUseStorage(DOMStorage* aStorage = nullptr);
@@ -55,16 +127,17 @@ class DOMStorage MOZ_FINAL : public nsID
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
 private:
   ~DOMStorage();
 
   friend class DOMStorageManager;
   friend class DOMStorageCache;
 
+  nsCOMPtr<nsIDOMWindow> mWindow;
   nsRefPtr<DOMStorageManager> mManager;
   nsRefPtr<DOMStorageCache> mCache;
   nsString mDocumentURI;
 
   // Principal this DOMStorage (i.e. localStorage or sessionStorage) has
   // been created for
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
--- a/dom/src/storage/DOMStorageCache.cpp
+++ b/dom/src/storage/DOMStorageCache.cpp
@@ -423,31 +423,30 @@ KeysArrayBuilder(const nsAString& aKey, 
   nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
 
   keys->AppendElement(aKey);
   return PL_DHASH_NEXT;
 }
 
 } // anon
 
-nsTArray<nsString>*
-DOMStorageCache::GetKeys(const DOMStorage* aStorage)
+void
+DOMStorageCache::GetKeys(const DOMStorage* aStorage, nsTArray<nsString>& aKeys)
 {
   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer;
 
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
   }
 
-  nsTArray<nsString>* result = new nsTArray<nsString>();
-  if (NS_SUCCEEDED(mLoadResult)) {
-    DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result);
+  if (NS_FAILED(mLoadResult)) {
+    return;
   }
 
-  return result;
+  DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, &aKeys);
 }
 
 nsresult
 DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
                          nsAString& aRetval)
 {
   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
 
--- a/dom/src/storage/DOMStorageCache.h
+++ b/dom/src/storage/DOMStorageCache.h
@@ -93,17 +93,17 @@ public:
   // Get* methods return error when load from the database has failed.
   nsresult GetLength(const DOMStorage* aStorage, uint32_t* aRetval);
   nsresult GetKey(const DOMStorage* aStorage, uint32_t index, nsAString& aRetval);
   nsresult GetItem(const DOMStorage* aStorage, const nsAString& aKey, nsAString& aRetval);
   nsresult SetItem(const DOMStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld);
   nsresult RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld);
   nsresult Clear(const DOMStorage* aStorage);
 
-  nsTArray<nsString>* GetKeys(const DOMStorage* aStorage);
+  void GetKeys(const DOMStorage* aStorage, nsTArray<nsString>& aKeys);
 
   // Whether the principal equals principal the cache was created for
   bool CheckPrincipal(nsIPrincipal* aPrincipal) const;
   nsIPrincipal* Principal() const { return mPrincipal; }
 
   // Starts the database engine thread or the IPC bridge
   static DOMStorageDBBridge* StartDatabase();
   static DOMStorageDBBridge* GetDatabase();
--- a/dom/src/storage/DOMStorageManager.cpp
+++ b/dom/src/storage/DOMStorageManager.cpp
@@ -92,17 +92,17 @@ PrincipalsEqual(nsIPrincipal* aObjectPri
   }
 
   return aSubjectPrincipal->Equals(aObjectPrincipal);
 }
 
 NS_IMPL_ISUPPORTS(DOMStorageManager,
                   nsIDOMStorageManager)
 
-DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
+DOMStorageManager::DOMStorageManager(DOMStorage::StorageType aType)
   : mCaches(10)
   , mType(aType)
   , mLowDiskSpace(false)
 {
   DOMStorageObserver* observer = DOMStorageObserver::Self();
   NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
 
   if (observer) {
@@ -320,16 +320,17 @@ DOMStorageManager::DropCache(DOMStorageC
     NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
   }
 
   mCaches.RemoveEntry(aCache->Scope());
 }
 
 nsresult
 DOMStorageManager::GetStorageInternal(bool aCreate,
+                                      nsIDOMWindow* aWindow,
                                       nsIPrincipal* aPrincipal,
                                       const nsAString& aDocumentURI,
                                       bool aPrivate,
                                       nsIDOMStorage** aRetval)
 {
   nsresult rv;
 
   nsAutoCString scope;
@@ -367,60 +368,66 @@ DOMStorageManager::GetStorageInternal(bo
     cache = PutCache(scope, aPrincipal);
   } else if (mType == SessionStorage) {
     if (!cache->CheckPrincipal(aPrincipal)) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
   }
 
   if (aRetval) {
-    *aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate);
-    NS_ADDREF(*aRetval);
+    nsCOMPtr<nsIDOMStorage> storage = new DOMStorage(
+      aWindow, this, cache, aDocumentURI, aPrincipal, aPrivate);
+    storage.forget(aRetval);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
 {
-  return GetStorageInternal(true, aPrincipal, EmptyString(), false, nullptr);
+  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
+                            nullptr);
 }
 
 NS_IMETHODIMP
-DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
+DOMStorageManager::CreateStorage(nsIDOMWindow* aWindow,
+                                 nsIPrincipal* aPrincipal,
                                  const nsAString& aDocumentURI,
                                  bool aPrivate,
                                  nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(true, aPrincipal, aDocumentURI, aPrivate, aRetval);
+  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
+                            aRetval);
 }
 
 NS_IMETHODIMP
-DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
+DOMStorageManager::GetStorage(nsIDOMWindow* aWindow,
+                              nsIPrincipal* aPrincipal,
                               bool aPrivate,
                               nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(false, aPrincipal, EmptyString(), aPrivate, aRetval);
+  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
+                            aRetval);
 }
 
 NS_IMETHODIMP
 DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
 {
   if (mType != SessionStorage) {
     // Cloning is supported only for sessionStorage
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
-  if (!pstorage) {
+  nsRefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
+  if (!storage) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  const DOMStorageCache* origCache = pstorage->GetCache();
+  const DOMStorageCache* origCache = storage->GetCache();
 
   DOMStorageCache* existingCache = GetCache(origCache->Scope());
   if (existingCache) {
     // Do not replace an existing sessionStorage.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Since this manager is sessionStorage manager, PutCache hard references
@@ -432,39 +439,39 @@ DOMStorageManager::CloneStorage(nsIDOMSt
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
                                 nsIDOMStorage* aStorage,
                                 bool* aRetval)
 {
-  nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
-  if (!pstorage) {
+  nsRefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
+  if (!storage) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *aRetval = false;
 
   if (!aPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoCString scope;
   nsresult rv = CreateScopeKey(aPrincipal, scope);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   DOMStorageCache* cache = GetCache(scope);
-  if (cache != pstorage->GetCache()) {
+  if (cache != storage->GetCache()) {
     return NS_OK;
   }
 
-  if (!pstorage->PrincipalEquals(aPrincipal)) {
+  if (!storage->PrincipalEquals(aPrincipal)) {
     return NS_OK;
   }
 
   *aRetval = true;
   return NS_OK;
 }
 
 // Obsolete nsIDOMStorageManager methods
@@ -474,17 +481,17 @@ DOMStorageManager::GetLocalStorageForPri
                                                const nsAString& aDocumentURI,
                                                bool aPrivate,
                                                nsIDOMStorage** aRetval)
 {
   if (mType != LocalStorage) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
+  return CreateStorage(nullptr, aPrincipal, aDocumentURI, aPrivate, aRetval);
 }
 
 namespace { // anon
 
 class ClearCacheEnumeratorData
 {
 public:
   ClearCacheEnumeratorData(uint32_t aFlags)
--- a/dom/src/storage/DOMStorageManager.h
+++ b/dom/src/storage/DOMStorageManager.h
@@ -4,49 +4,49 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDOMStorageManager_h__
 #define nsDOMStorageManager_h__
 
 #include "nsIDOMStorageManager.h"
 #include "DOMStorageObserver.h"
 
-#include "nsPIDOMStorage.h"
 #include "DOMStorageCache.h"
+#include "mozilla/dom/DOMStorage.h"
 
 #include "nsTHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 
+class nsIDOMWindow;
+
 namespace mozilla {
 namespace dom {
 
-const nsPIDOMStorage::StorageType SessionStorage = nsPIDOMStorage::SessionStorage;
-const nsPIDOMStorage::StorageType LocalStorage = nsPIDOMStorage::LocalStorage;
-
-class DOMStorage;
+const DOMStorage::StorageType SessionStorage = DOMStorage::SessionStorage;
+const DOMStorage::StorageType LocalStorage = DOMStorage::LocalStorage;
 
 class DOMStorageManager : public nsIDOMStorageManager
                         , public DOMStorageObserverSink
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMSTORAGEMANAGER
 
 public:
-  virtual nsPIDOMStorage::StorageType Type() { return mType; }
+  virtual DOMStorage::StorageType Type() { return mType; }
 
   // Reads the preference for DOM storage quota
   static uint32_t GetQuota();
   // Gets (but not ensures) cache for the given scope
   DOMStorageCache* GetCache(const nsACString& aScope) const;
   // Returns object keeping usage cache for the scope.
   already_AddRefed<DOMStorageUsage> GetScopeUsage(const nsACString& aScope);
 
 protected:
-  DOMStorageManager(nsPIDOMStorage::StorageType aType);
+  DOMStorageManager(DOMStorage::StorageType aType);
   virtual ~DOMStorageManager();
 
 private:
   // DOMStorageObserverSink, handler to various chrome clearing notification
   virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix);
 
   // Since nsTHashtable doesn't like multiple inheritance, we have to aggregate
   // DOMStorageCache into the entry.
@@ -77,24 +77,25 @@ private:
 
   // Ensures cache for a scope, when it doesn't exist it is created and initalized,
   // this also starts preload of persistent data.
   already_AddRefed<DOMStorageCache> PutCache(const nsACString& aScope,
                                              nsIPrincipal* aPrincipal);
 
   // Helper for creation of DOM storage objects
   nsresult GetStorageInternal(bool aCreate,
+                              nsIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
                               const nsAString& aDocumentURI,
                               bool aPrivate,
                               nsIDOMStorage** aRetval);
 
   // Scope->cache map
   nsTHashtable<DOMStorageCacheHashKey> mCaches;
-  const nsPIDOMStorage::StorageType mType;
+  const DOMStorage::StorageType mType;
 
   // If mLowDiskSpace is true it indicates a low device storage situation and
   // so no localStorage writes are allowed. sessionStorage writes are still
   // allowed.
   bool mLowDiskSpace;
   bool IsLowDiskSpace() const { return mLowDiskSpace; };
 
   static PLDHashOperator ClearCacheEnumerator(DOMStorageCacheHashKey* aCache,
--- a/dom/src/storage/moz.build
+++ b/dom/src/storage/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
+    'DOMStorage.h',
     'DOMStorageIPC.h',
 ]
 
 UNIFIED_SOURCES += [
     'DOMStorage.cpp',
     'DOMStorageCache.cpp',
     'DOMStorageDBThread.cpp',
     'DOMStorageIPC.cpp',
--- a/dom/tests/mochitest/localstorage/frame_clear_browser_data.html
+++ b/dom/tests/mochitest/localstorage/frame_clear_browser_data.html
@@ -1,15 +1,16 @@
 <!DOCTYPE html>
 <html>
   <body>
+    <div id="status"></div>
   </body>
   <script>
     addEventListener('message', function(e) {
       if (e.data == 'clear') {
         navigator.mozApps.getSelf().onsuccess = function() {
           this.result.clearBrowserData();
-          document.body.innerHTML = "<done></done>";
+          document.getElementById('status').textContent = 'done';
         };
       }
     });
   </script>
 </html>
--- a/dom/tests/mochitest/localstorage/test_clear_browser_data.html
+++ b/dom/tests/mochitest/localstorage/test_clear_browser_data.html
@@ -159,23 +159,24 @@ function browserLoadEvent() {
 
   frames[1].postMessage("clear", "*");
 
   waitForClearBrowserData();
 };
 
 function waitForClearBrowserData() {
   SimpleTest.executeSoon(function() {
-    if (frames[1].document.getElementsByTagName('done').length == 0) {
+    if (frames[1].document.getElementById('status').textContent != 'done') {
       waitForClearBrowserData();
     } else {
       checks();
     }
   });
 }
+
 function checks() {
   navigator.mozApps.mgmt.getAll().onsuccess = function() {
     for (i in this.result) {
       var app = this.result[i];
       if (app.manifestURL == gManifestURL) {
         is(gBrowserStorage.localStorage.getItem("foo"), null, "localstorage data have been deleted");
         is(gBrowserStorage.sessionStorage.getItem("foo"), "bar", "sessionstorage data have not been deleted");
 
--- a/dom/tests/mochitest/localstorage/test_localStorageBase.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageBase.html
@@ -199,24 +199,22 @@ function startTest()
   is(localStorage.testC, "null");
 
   localStorage.setItem(null, "test");
   is("null" in localStorage, true);
   is(localStorage.getItem("null"), "test");
   is(localStorage.getItem(null), "test");
   is(localStorage["null"], "test");
   localStorage.removeItem(null, "test");
-  // bug 350023
-  todo_is("null" in localStorage, false);
+  is("null" in localStorage, false);
 
   localStorage.setItem(null, "test");
   is("null" in localStorage, true);
   localStorage.removeItem("null", "test");
-  // bug 350023
-  todo_is("null" in localStorage, false);
+  is("null" in localStorage, false);
 
   // Clear the storage
   localStorage.clear();
   is("testB" in localStorage, false, "Keys are not in the JS scope of the storage");
   is("testC" in localStorage, false, "Keys are not in the JS scope of the storage");
   is(localStorage.length, 0, "The storage is empty [3]");
   is(localStorage.key(0), null, "key() should return null for out-of-bounds access");
   is(localStorage.key(-1), null, "key() should return null for out-of-bounds access");
--- a/dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
@@ -202,24 +202,22 @@ function doTest() {
       is(privateWin.content.localStorage.testC, "null");
 
       privateWin.content.localStorage.setItem(null, "test");
       is("null" in privateWin.content.localStorage, true);
       is(privateWin.content.localStorage.getItem("null"), "test");
       is(privateWin.content.localStorage.getItem(null), "test");
       is(privateWin.content.localStorage["null"], "test");
       privateWin.content.localStorage.removeItem(null, "test");
-      // bug 350023
-      todo_is("null" in privateWin.content.localStorage, false);
+      is("null" in privateWin.content.localStorage, false);
 
       privateWin.content.localStorage.setItem(null, "test");
       is("null" in privateWin.content.localStorage, true);
       privateWin.content.localStorage.removeItem("null", "test");
-      // bug 350023
-      todo_is("null" in privateWin.content.localStorage, false);
+      is("null" in privateWin.content.localStorage, false);
 
       // Clear the storage
       privateWin.content.localStorage.clear();
       is(privateWin.content.localStorage.length, 0, "The storage is empty [3]");
       is(privateWin.content.localStorage.key(0), null, "key() should return null for out-of-bounds access");
       is(privateWin.content.localStorage.key(-1), null, "key() should return null for out-of-bounds access");
       is(privateWin.content.localStorage.key(1), null, "key() should return null for out-of-bounds access");
       is(privateWin.content.localStorage.getItem("nonexisting"), null, "Nonexisting item is null");
--- a/dom/tests/mochitest/localstorage/test_localStorageBaseSessionOnly.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageBaseSessionOnly.html
@@ -164,24 +164,22 @@ function test1() {
   is(localStorage.testC, "null");
 
   localStorage.setItem(null, "test");
   is("null" in localStorage, true);
   is(localStorage.getItem("null"), "test");
   is(localStorage.getItem(null), "test");
   is(localStorage["null"], "test");
   localStorage.removeItem(null, "test");
-  // bug 350023
-  todo_is("null" in localStorage, false);
+  is("null" in localStorage, false);
 
   localStorage.setItem(null, "test");
   is("null" in localStorage, true);
   localStorage.removeItem("null", "test");
-  // bug 350023
-  todo_is("null" in localStorage, false);
+  is("null" in localStorage, false);
 
   // Clear the storage
   localStorage.clear();
   is(localStorage.length, 0, "The storage is empty [3]");
   is(localStorage.key(0), null, "key() should return null for out-of-bounds access");
   is(localStorage.key(-1), null, "key() should return null for out-of-bounds access");
   is(localStorage.key(1), null, "key() should return null for out-of-bounds access");
   is(localStorage.getItem("nonexisting"), null, "Nonexisting item is null");
--- a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
+++ b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
@@ -14,17 +14,17 @@ function startTest()
     .getService(Components.interfaces.nsIIOService);
   var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
     .getService(Components.interfaces.nsIScriptSecurityManager);
   var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
     .getService(Components.interfaces.nsIDOMStorageManager);
 
   var uri = ios.newURI(url, "", null);
   var principal = ssm.getNoAppCodebasePrincipal(uri);
-  var storage = dsm.createStorage(principal, "");
+  var storage = dsm.createStorage(window, principal, "");
   
   storage.setItem("chromekey", "chromevalue");
   
   var aframe = document.getElementById("aframe");
   aframe.onload = function()
   {
     is(storage.getItem("chromekey"), "chromevalue");
     is(aframe.contentDocument.getElementById("data").innerHTML, "chromevalue");
--- a/dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html
+++ b/dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html
@@ -186,24 +186,22 @@ function test1() {
   is(sessionStorage.testC, "null");
 
   sessionStorage.setItem(null, "test");
   is("null" in sessionStorage, true);
   is(sessionStorage.getItem("null"), "test");
   is(sessionStorage.getItem(null), "test");
   is(sessionStorage["null"], "test");
   sessionStorage.removeItem(null, "test");
-  // bug 350023
-  todo_is("null" in sessionStorage, false);
+  is("null" in sessionStorage, false);
 
   sessionStorage.setItem(null, "test");
   is("null" in sessionStorage, true);
   sessionStorage.removeItem("null", "test");
-  // bug 350023
-  todo_is("null" in sessionStorage, false);
+  is("null" in sessionStorage, false);
 
   // Clear the storage
   sessionStorage.clear();
   is(sessionStorage.length, 0, "The storage is empty [3]");
   is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
   is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
   is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
   is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null");
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Storage.webidl
@@ -0,0 +1,32 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/.
+*
+* The origin of this IDL file is
+* http://www.whatwg.org/html/#the-storage-interface
+*
+* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
+* Opera Software ASA. You are granted a license to use, reproduce
+* and create derivative works of this document.
+*/
+
+interface Storage {
+  [Throws]
+  readonly attribute unsigned long length;
+
+  [Throws]
+  DOMString? key(unsigned long index);
+
+  [Throws]
+  getter DOMString? getItem(DOMString key);
+
+  [Throws]
+  setter creator void setItem(DOMString key, DOMString value);
+
+  [Throws]
+  deleter void removeItem(DOMString key);
+
+  [Throws]
+  void clear();
+};
--- a/dom/webidl/StorageEvent.webidl
+++ b/dom/webidl/StorageEvent.webidl
@@ -4,17 +4,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * Interface for a client side storage. See
  * http://dev.w3.org/html5/webstorage/#the-storage-event
  * for more information.
  *
  * Event sent to a window when a storage area changes.
  */
-interface Storage;
 
 [Constructor(DOMString type, optional StorageEventInit eventInitDict)]
 interface StorageEvent : Event
 {
   readonly attribute DOMString? key;
   readonly attribute DOMString? oldValue;
   readonly attribute DOMString? newValue;
   readonly attribute DOMString? url;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -320,16 +320,17 @@ WEBIDL_FILES = [
     'ServiceWorkerGlobalScope.webidl',
     'SettingsManager.webidl',
     'ShadowRoot.webidl',
     'SharedWorker.webidl',
     'SharedWorkerGlobalScope.webidl',
     'SimpleGestureEvent.webidl',
     'SourceBuffer.webidl',
     'SourceBufferList.webidl',
+    'Storage.webidl',
     'StorageEvent.webidl',
     'StorageType.webidl',
     'StyleSheet.webidl',
     'StyleSheetList.webidl',
     'SubtleCrypto.webidl',
     'SVGAElement.webidl',
     'SVGAltGlyphElement.webidl',
     'SVGAngle.webidl',
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -43,28 +43,28 @@
 #include "nsIWindowCreator2.h"
 #include "nsIXPConnect.h"
 #include "nsIXULRuntime.h"
 #include "nsPIDOMWindow.h"
 #include "nsIContentViewer.h"
 #include "nsIWindowProvider.h"
 #include "nsIMutableArray.h"
 #include "nsISupportsArray.h"
-#include "nsIDOMStorage.h"
 #include "nsIDOMStorageManager.h"
 #include "nsIWidget.h"
 #include "nsFocusManager.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/DOMStorage.h"
 
 #ifdef USEWEAKREFS
 #include "nsIWeakReference.h"
 #endif
 
 using namespace mozilla;
 
 /****************************************************************
@@ -946,19 +946,27 @@ nsWindowWatcher::OpenWindowInternal(nsID
 
   // Copy the current session storage for the current domain.
   if (subjectPrincipal && parentDocShell) {
     nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
     nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
 
     if (parentStorageManager && newStorageManager) {
       nsCOMPtr<nsIDOMStorage> storage;
-      parentStorageManager->GetStorage(subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage));
-      if (storage)
+      nsCOMPtr<nsPIDOMWindow> pWin = do_QueryInterface(aParent);
+      nsCOMPtr<nsPIDOMWindow> pInnerWin = pWin->IsInnerWindow()
+                                            ? pWin.get()
+                                            : pWin->GetCurrentInnerWindow();
+
+      parentStorageManager->GetStorage(pInnerWin, subjectPrincipal,
+                                       isPrivateBrowsingWindow,
+                                       getter_AddRefs(storage));
+      if (storage) {
         newStorageManager->CloneStorage(storage);
+      }
     }
   }
 
   if (isNewToplevelWindow)
     SizeOpenedDocShellItem(newDocShellItem, aParent, isCallerChrome, sizeSpec);
 
   // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
   if (windowIsModal || windowIsModalContentDialog) {
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -27,24 +27,16 @@ members = [
     #
     # (And nsIDOMModalContentWindow.returnValue is an attribute of type
     # nsIVariant, which qsgen.py can't handle.)
     #
     # nsLocationSH has ~ALLOW_PROP_MODS_TO_PROTOTYPE, so don't try.
     #'nsIDOMLocation.hostname',
     #'nsIDOMLocation.href',
 
-    # dom/interfaces/storage
-    'nsIDOMStorage.setItem',
-    'nsIDOMStorage.length',
-    'nsIDOMStorage.getItem',
-    'nsIDOMStorage.key',
-    'nsIDOMStorage.removeItem',
-    'nsIDOMStorage.clear',
-
     # dom/interfaces/xpath
     'nsIDOMXPathNSResolver.lookupNamespaceURI',
 
     # layout/xul/base/public
     'nsIBoxObject.x',
     'nsIBoxObject.y',
     'nsIBoxObject.screenX',
     'nsIBoxObject.screenY',
@@ -65,26 +57,14 @@ irregularFilenames = {
     }
 
 customIncludes = [
     'mozilla/dom/BindingUtils.h',
     'mozilla/dom/EventTargetBinding.h',
     'mozilla/dom/WindowBinding.h',
     ]
 
-nsIDOMStorage_Clear_customMethodCallCode = """
-    rv = self->Clear();
-    if (NS_SUCCEEDED(rv))
-        JS_ClearNonGlobalObject(cx, obj);
-"""
-
-customMethodCalls = {
-    'nsIDOMStorage_Clear': {
-        'code': nsIDOMStorage_Clear_customMethodCallCode
-        },
-    }
-
 newBindingProperties = {
     # Once the last entry here goes away, we can make the sNativePropertyHooks
     # of bindings static.
     'nsIDOMEventTarget': '&mozilla::dom::EventTargetBinding::sNativePropertyHooks->mNativeProperties',
     'nsIDOMWindow': '&mozilla::dom::WindowBinding::sNativePropertyHooks->mNativeProperties',
     }
--- a/toolkit/components/social/FrameWorkerContent.js
+++ b/toolkit/components/social/FrameWorkerContent.js
@@ -92,17 +92,17 @@ FrameWorker.prototype = {
 
     // Only expose localStorage if the caller opted-in
     if (this.exposeLocalStorage) {
       workerAPI.push('localStorage');
     }
 
     // Bug 798660 - XHR, WebSocket and Worker have issues in a sandbox and need
     // to be unwrapped to work
-    let needsWaive = ['XMLHttpRequest', 'WebSocket', 'Worker'];
+    let needsWaive = ['XMLHttpRequest', 'WebSocket', 'Worker' ];
     // Methods need to be bound with the proper |this|.
     let needsBind = ['atob', 'btoa', 'dump', 'setInterval', 'clearInterval',
                      'setTimeout', 'clearTimeout'];
     workerAPI.forEach(function(fn) {
       try {
         if (needsWaive.indexOf(fn) != -1)
           sandbox[fn] = XPCNativeWrapper.unwrap(workerWindow)[fn];
         else if (needsBind.indexOf(fn) != -1)
--- a/toolkit/devtools/server/tests/browser/browser_storage_dynamic_windows.js
+++ b/toolkit/devtools/server/tests/browser/browser_storage_dynamic_windows.js
@@ -101,16 +101,20 @@ function markOutMatched(toBeEmptied, dat
     if (!data[storageType]) {
       continue;
     }
     info("Testing for " + storageType);
     for (let host in data[storageType]) {
       ok(toBeEmptied[storageType][host], "Host " + host + " found");
       if (!deleted) {
         for (let item of data[storageType][host]) {
+          if ([ 'length', 'key', 'getItem', 'setItem',
+                'removeItem', 'clear'].indexOf(item) != -1) {
+            continue;
+          }
           let index = toBeEmptied[storageType][host].indexOf(item);
           ok(index > -1, "Item found - " + item);
           if (index > -1) {
             toBeEmptied[storageType][host].splice(index, 1);
           }
         }
         if (!toBeEmptied[storageType][host].length) {
           delete toBeEmptied[storageType][host];
--- a/toolkit/devtools/server/tests/browser/browser_storage_listings.js
+++ b/toolkit/devtools/server/tests/browser/browser_storage_listings.js
@@ -5,16 +5,18 @@ Cu.import("resource://gre/modules/devtoo
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm", tempScope);
 let {DebuggerServer, DebuggerClient} = tempScope;
 tempScope = null;
 
 const {StorageFront} = require("devtools/server/actors/storage");
 let {Task} = require("resource://gre/modules/Task.jsm");
 let gWindow = null;
 
+const domStorageProperties = ['length', 'key', 'getItem','setItem', 'removeItem', 'clear'];
+
 const storeMap = {
   cookies: {
     "test1.example.org": [
       {
         name: "c1",
         value: "foobar",
         expires: 2000000000000,
         path: "/browser",
@@ -414,19 +416,23 @@ function testLocalStorage(localStorageAc
   is(Object.keys(localStorageActor.hosts).length, 3,
      "Correct number of host entries for local storage");
   return testLocalStorageObjects(0, localStorageActor.hosts, localStorageActor);
 }
 
 let testLocalStorageObjects = Task.async(function*(index, hosts, localStorageActor) {
   let host = Object.keys(hosts)[index];
   let matchItems = data => {
-    is(data.total, storeMap.localStorage[host].length,
+    is(data.total - domStorageProperties.length, storeMap.localStorage[host].length,
        "Number of local storage items in host " + host + " matches");
     for (let item of data.data) {
+      if (domStorageProperties.indexOf(item.name) != -1) {
+        continue;
+      }
+
       let found = false;
       for (let toMatch of storeMap.localStorage[host]) {
         if (item.name == toMatch.name) {
           found = true;
           ok(true, "Found local storage item " + item.name + " in response");
           is(item.value.str, toMatch.value, "The value matches.");
           break;
         }
@@ -451,19 +457,23 @@ function testSessionStorage(sessionStora
      "Correct number of host entries for session storage");
   return testSessionStorageObjects(0, sessionStorageActor.hosts,
                                    sessionStorageActor);
 }
 
 let testSessionStorageObjects = Task.async(function*(index, hosts, sessionStorageActor) {
   let host = Object.keys(hosts)[index];
   let matchItems = data => {
-    is(data.total, storeMap.sessionStorage[host].length,
+    is(data.total - domStorageProperties.length, storeMap.sessionStorage[host].length,
        "Number of session storage items in host " + host + " matches");
     for (let item of data.data) {
+      if (domStorageProperties.indexOf(item.name) != -1) {
+        continue;
+      }
+
       let found = false;
       for (let toMatch of storeMap.sessionStorage[host]) {
         if (item.name == toMatch.name) {
           found = true;
           ok(true, "Found session storage item " + item.name + " in response");
           is(item.value.str, toMatch.value, "The value matches.");
           break;
         }
--- a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
+++ b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
@@ -668,17 +668,17 @@ function test_storage_cleared()
 {
   function getStorageForURI(aURI)
   {
     let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
                     getService(Ci.nsIScriptSecurityManager).
                     getNoAppCodebasePrincipal(aURI);
     let dsm = Cc["@mozilla.org/dom/localStorage-manager;1"].
               getService(Ci.nsIDOMStorageManager);
-    return dsm.createStorage(principal, "");
+    return dsm.createStorage(null, principal, "");
   }
 
   let s = [
     getStorageForURI(uri("http://mozilla.org")),
     getStorageForURI(uri("http://my.mozilla.org")),
     getStorageForURI(uri("http://ilovemozilla.org")),
   ];