Bug 600307 - localStorage and sessionStorage implementation overhaul, r=mak77+smaug, sr=smaug
authorHonza Bambas <honzab.moz@firemni.cz>
Mon, 15 Apr 2013 14:38:48 +0200
changeset 128794 8480e7da11d17bf6925932cd60548c8cd3e9f399
parent 128793 644f16c3f87cdf4f3ad23a22e2932b6ca405cd30
child 128795 626f981f90e66032b4f2bb56f43fa2ddb92322b8
push id24543
push userryanvm@gmail.com
push dateTue, 16 Apr 2013 01:49:32 +0000
treeherdermozilla-central@1d9c510b3742 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak77, smaug
bugs600307
milestone23.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 600307 - localStorage and sessionStorage implementation overhaul, r=mak77+smaug, sr=smaug
browser/base/content/test/browser_aboutHome.js
browser/components/sessionstore/src/SessionStorage.jsm
content/events/src/nsEventDispatcher.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/indexedDB/CheckPermissionsHelper.cpp
dom/interfaces/storage/moz.build
dom/interfaces/storage/nsIDOMStorageItem.idl
dom/interfaces/storage/nsIDOMStorageManager.idl
dom/interfaces/storage/nsIDOMStorageObsolete.idl
dom/interfaces/storage/nsPIDOMStorage.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/src/storage/DOMStorage.cpp
dom/src/storage/DOMStorage.h
dom/src/storage/DOMStorageCache.cpp
dom/src/storage/DOMStorageCache.h
dom/src/storage/DOMStorageDBThread.cpp
dom/src/storage/DOMStorageDBThread.h
dom/src/storage/DOMStorageIPC.cpp
dom/src/storage/DOMStorageIPC.h
dom/src/storage/DOMStorageManager.cpp
dom/src/storage/DOMStorageManager.h
dom/src/storage/DOMStorageObserver.cpp
dom/src/storage/DOMStorageObserver.h
dom/src/storage/Makefile.in
dom/src/storage/PStorage.ipdl
dom/src/storage/StorageChild.cpp
dom/src/storage/StorageChild.h
dom/src/storage/StorageParent.cpp
dom/src/storage/StorageParent.h
dom/src/storage/nsDOMStorage.cpp
dom/src/storage/nsDOMStorage.h
dom/src/storage/nsDOMStorageBaseDB.cpp
dom/src/storage/nsDOMStorageBaseDB.h
dom/src/storage/nsDOMStorageDBWrapper.cpp
dom/src/storage/nsDOMStorageDBWrapper.h
dom/src/storage/nsDOMStorageMemoryDB.cpp
dom/src/storage/nsDOMStorageMemoryDB.h
dom/src/storage/nsDOMStoragePersistentDB.cpp
dom/src/storage/nsDOMStoragePersistentDB.h
dom/src/storage/nsLocalStorageCache.cpp
dom/src/storage/nsLocalStorageCache.h
dom/tests/mochitest/localstorage/Makefile.in
dom/tests/mochitest/localstorage/frameBug624047.html
dom/tests/mochitest/localstorage/localStorageCommon.js
dom/tests/mochitest/localstorage/test_bug600307-DBOps.html
dom/tests/mochitest/localstorage/test_bug624047.html
dom/tests/mochitest/localstorage/test_localStorageBase.html
dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
dom/tests/mochitest/sessionstorage/Makefile.in
dom/tests/mochitest/sessionstorage/test_cookieSession.html
dom/tests/mochitest/sessionstorage/test_sessionStorageBase.html
dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
js/xpconnect/src/dom_quickstubs.qsconf
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
layout/build/nsLayoutStatics.cpp
testing/mochitest/b2g.json
toolkit/components/telemetry/Histograms.json
toolkit/content/Services.jsm
toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
xpcom/base/ErrorList.h
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -20,19 +20,19 @@ registerCleanupFunction(function() {
 });
 
 let gTests = [
 
 {
   desc: "Check that clearing cookies does not clear storage",
   setup: function ()
   {
-    Cc["@mozilla.org/dom/storagemanager;1"]
-      .getService(Ci.nsIObserver)
-      .observe(null, "cookie-changed", "cleared");
+    Cc["@mozilla.org/observer-service;1"]
+      .getService(Ci.nsIObserverService)
+      .notifyObservers(null, "cookie-changed", "cleared");
   },
   run: function (aSnippetsMap)
   {
     isnot(aSnippetsMap.get("snippets-last-update"), null,
           "snippets-last-update should have a value");
   }
 },
 
--- a/browser/components/sessionstore/src/SessionStorage.jsm
+++ b/browser/components/sessionstore/src/SessionStorage.jsm
@@ -80,17 +80,22 @@ let DomStorage = {
    *        A tab's docshell (containing the sessionStorage)
    * @param aStorageData
    *        Storage data to be restored
    */
   write: function DomStorage_write(aDocShell, aStorageData) {
     for (let [host, data] in Iterator(aStorageData)) {
       let uri = Services.io.newURI(host, null, null);
       let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
-      let storage = aDocShell.getSessionStorageForPrincipal(principal, "", true);
+      let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
+
+      // 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);
 
       for (let [key, value] in Iterator(data)) {
         try {
           storage.setItem(key, value);
         } catch (e) {
           // throws e.g. for URIs that can't have sessionStorage
           Cu.reportError(e);
         }
@@ -105,22 +110,18 @@ let DomStorage = {
    * @param aDocShell
    *        A tab's docshell (containing the sessionStorage)
    */
   _readEntry: function DomStorage_readEntry(aPrincipal, aDocShell) {
     let hostData = {};
     let storage;
 
     try {
-      // Using getSessionStorageForPrincipal instead of
-      // getSessionStorageForURI just to be able to pass aCreate = false,
-      // that avoids creation of the sessionStorage object for the page
-      // earlier than the page really requires it. It was causing problems
-      // while accessing a storage when a page later changed its domain.
-      storage = aDocShell.getSessionStorageForPrincipal(aPrincipal, "", false);
+      let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
+      storage = storageManager.getStorage(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/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -11,17 +11,16 @@
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsMutationEvent.h"
 #include NEW_H
 #include "nsINode.h"
 #include "nsPIDOMWindow.h"
 #include "nsFrameLoader.h"
 #include "nsDOMTouchEvent.h"
-#include "nsDOMStorage.h"
 #include "GeckoProfiler.h"
 #include "GeneratedEvents.h"
 #include "mozilla/dom/EventTarget.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH  (1 << 0)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -844,18 +844,16 @@ nsDocShell::Init()
     NS_ASSERTION(mLoadGroup, "Something went wrong!");
 
     mContentListener = new nsDSURIContentListener(this);
     NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
 
     rv = mContentListener->Init();
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mStorages.Init();
-
     // We want to hold a strong ref to the loadgroup, so it better hold a weak
     // ref to us...  use an InterfaceRequestorProxy to do this.
     nsCOMPtr<InterfaceRequestorProxy> proxy =
         new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
                                                (this));
     NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
     mLoadGroup->SetNotificationCallbacks(proxy);
 
@@ -912,16 +910,17 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
     NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
     NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
     NS_INTERFACE_MAP_ENTRY(nsIObserver)
     NS_INTERFACE_MAP_ENTRY(nsILoadContext)
     NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
     NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
     NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
+    NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
 
 ///*****************************************************************************
 // nsDocShell::nsIInterfaceRequestor
 //*****************************************************************************   
 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
 {
     NS_PRECONDITION(aSink, "null out param");
@@ -2591,209 +2590,80 @@ nsDocShell::HistoryTransactionRemoved(in
             static_cast<nsDocShell*>(shell.get())->
                 HistoryTransactionRemoved(aIndex);
         }
     }
 
     return NS_OK;
 }
 
+nsIDOMStorageManager*
+nsDocShell::TopSessionStorageManager()
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIDocShellTreeItem> topItem;
+    rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
+    if (NS_FAILED(rv)) {
+        return nullptr;
+    }
+
+    if (!topItem) {
+        return nullptr;
+    }
+
+    nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
+    if (topDocShell != this) {
+        return topDocShell->TopSessionStorageManager();
+    }
+
+    if (!mSessionStorageManager) {
+        mSessionStorageManager =
+            do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
+    }
+
+    return mSessionStorageManager;
+}
+
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
                                           const nsAString& aDocumentURI,
                                           bool aCreate,
                                           nsIDOMStorage** aStorage)
 {
-    NS_ENSURE_ARG_POINTER(aStorage);
-    *aStorage = nullptr;
-
-    if (!aPrincipal)
-        return NS_OK;
-
-    nsresult rv;
-
-    nsCOMPtr<nsIDocShellTreeItem> topItem;
-    rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
-    if (NS_FAILED(rv))
-        return rv;
-
-    if (!topItem)
-        return NS_ERROR_FAILURE;
-
-    nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
-    if (topDocShell != this)
-        return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
-                                                          aDocumentURI,
-                                                          aCreate,
-                                                          aStorage);
-
-    nsXPIDLCString origin;
-    rv = aPrincipal->GetOrigin(getter_Copies(origin));
-    if (NS_FAILED(rv))
-        return rv;
-
-    if (origin.IsEmpty())
-        return NS_OK;
-
-    if (!mStorages.Get(origin, aStorage) && aCreate) {
-        nsCOMPtr<nsIDOMStorage> newstorage =
-            do_CreateInstance("@mozilla.org/dom/storage;2");
-        if (!newstorage)
-            return NS_ERROR_OUT_OF_MEMORY;
-
-        nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
-        if (!pistorage)
-            return NS_ERROR_FAILURE;
-
-        rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI, mInPrivateBrowsing);
-        if (NS_FAILED(rv))
-            return rv;
-
-        mStorages.Put(origin, newstorage);
-
-        newstorage.swap(*aStorage);
-#if defined(PR_LOGGING) && defined(DEBUG)
-        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
-               ("nsDocShell[%p]: created a new sessionStorage %p",
-                this, *aStorage));
-#endif
-    }
-    else if (*aStorage) {
-        nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
-        if (piStorage) {
-            nsCOMPtr<nsIPrincipal> storagePrincipal = piStorage->Principal();
-
-            // The origin string used to map items in the hash table is 
-            // an implicit security check. That check is double-confirmed 
-            // by checking the principal a storage was demanded for 
-            // really is the principal for which that storage was originally 
-            // created. Originally, the check was hidden in the CanAccess 
-            // method but it's implementation has changed.
-            bool equals;
-            nsresult rv = aPrincipal->EqualsIgnoringDomain(storagePrincipal, &equals);
-            NS_ASSERTION(NS_SUCCEEDED(rv) && equals,
-                         "GetSessionStorageForPrincipal got a storage "
-                         "that could not be accessed!");
-
-            if (NS_FAILED(rv) || !equals) {
-                NS_RELEASE(*aStorage);
-                return NS_ERROR_DOM_SECURITY_ERR;
-            }
-        }
-
-#if defined(PR_LOGGING) && defined(DEBUG)
-        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
-               ("nsDocShell[%p]: returns existing sessionStorage %p",
-                this, *aStorage));
-#endif
+    nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
+    if (!manager) {
+        return NS_ERROR_UNEXPECTED;
     }
 
     if (aCreate) {
-        // We are asked to create a new storage object. This indicates
-        // that a new windows wants it. At this moment we "fork" the existing
-        // storage object (what it means is described in the paragraph bellow).
-        // We must create a single object per a single window to distinguish
-        // a window originating oparations on the storage object to succesfully
-        // prevent dispatch of a storage event to this same window that ivoked
-        // a change in its storage. We also do this to correctly fill
-        // documentURI property in the storage event.
-        //
-        // The difference between clone and fork is that clone creates
-        // a completelly new and independent storage, but fork only creates
-        // a new object wrapping the storage implementation and data and
-        // the forked storage then behaves completelly the same way as
-        // the storage it has been forked of, all such forked storage objects
-        // shares their state and data and change on one such object affects
-        // all others the same way.
-        nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
-        nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
-#if defined(PR_LOGGING) && defined(DEBUG)
-        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
-               ("nsDocShell[%p]: forked sessionStorage %p to %p",
-                this, *aStorage, fork.get()));
-#endif
-        fork.swap(*aStorage);
-    }
-
-    return NS_OK;
+        return manager->CreateStorage(aPrincipal, aDocumentURI,
+                                      mInPrivateBrowsing, aStorage);
+    }
+
+    return manager->GetStorage(aPrincipal, mInPrivateBrowsing, aStorage);
 }
 
 nsresult
 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
                               nsIDOMStorage* aStorage)
 {
-    NS_ENSURE_ARG_POINTER(aStorage);
-
-    if (!aPrincipal)
-        return NS_OK;
-
-    nsCOMPtr<nsIDocShellTreeItem> topItem;
-    nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
-    if (NS_FAILED(rv))
-        return rv;
-
-    if (topItem) {
-        nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
-        if (topDocShell == this) {
-            nsXPIDLCString origin;
-            rv = aPrincipal->GetOrigin(getter_Copies(origin));
-            if (NS_FAILED(rv))
-                return rv;
-
-            if (origin.IsEmpty())
-                return NS_ERROR_FAILURE;
-
-            // Do not replace an existing session storage.
-            if (mStorages.GetWeak(origin))
-                return NS_ERROR_NOT_AVAILABLE;
-
-#if defined(PR_LOGGING) && defined(DEBUG)
-            PR_LOG(gDocShellLog, PR_LOG_DEBUG,
-                   ("nsDocShell[%p]: was added a sessionStorage %p",
-                    this, aStorage));
-#endif
-            mStorages.Put(origin, aStorage);
-        }
-        else {
-            return topDocShell->AddSessionStorage(aPrincipal, aStorage);
-        }
-    }
-
-    return NS_OK;
-}
-
-static PLDHashOperator
-CloneSessionStorages(nsCStringHashKey::KeyType aKey, nsIDOMStorage* aStorage,
-                     void* aUserArg)
-{
-    nsIDocShell *docShell = static_cast<nsIDocShell*>(aUserArg);
     nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
-
-    if (pistorage) {
-        nsCOMPtr<nsIDOMStorage> storage = pistorage->Clone();
-        docShell->AddSessionStorage(pistorage->Principal(), storage);
-    }
-
-    return PL_DHASH_NEXT;
-}
-
-NS_IMETHODIMP
-nsDocShell::CloneSessionStoragesTo(nsIDocShell* aDocShell)
-{
-    aDocShell->ClearSessionStorages();
-    mStorages.EnumerateRead(CloneSessionStorages, aDocShell);
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::ClearSessionStorages()
-{
-    mStorages.Clear();
-    return NS_OK;
+    nsIPrincipal* storagePrincipal = pistorage->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;
+    }
+
+    return manager->CloneStorage(aStorage);
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
 {
     NS_IF_ADDREF(*aResult = GetCurrentDocChannel()); 
     return NS_OK;
 }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -18,16 +18,17 @@
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIBaseWindow.h"
 #include "nsIScrollable.h"
 #include "nsITextScroll.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIContentViewerContainer.h"
+#include "nsIDOMStorageManager.h"
 
 #include "nsDocLoader.h"
 #include "nsIURILoader.h"
 
 #include "nsWeakReference.h"
 
 // Local Includes
 #include "nsDSURIContentListener.h"
@@ -141,17 +142,18 @@ class nsDocShell : public nsDocLoader,
                    public nsIRefreshURI,
                    public nsIWebProgressListener,
                    public nsIWebPageDescriptor,
                    public nsIAuthPromptProvider,
                    public nsIObserver,
                    public nsILoadContext,
                    public nsIWebShellServices,
                    public nsILinkHandler,
-                   public nsIClipboardCommands
+                   public nsIClipboardCommands,
+                   public nsIDOMStorageManager
 {
     friend class nsDSURIContentListener;
 
 public:
     // Object Management
     nsDocShell();
 
     NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
@@ -173,16 +175,17 @@ public:
     NS_DECL_NSIWEBPROGRESSLISTENER
     NS_DECL_NSIREFRESHURI
     NS_DECL_NSICONTENTVIEWERCONTAINER
     NS_DECL_NSIWEBPAGEDESCRIPTOR
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSIOBSERVER
     NS_DECL_NSICLIPBOARDCOMMANDS
     NS_DECL_NSIWEBSHELLSERVICES
+    NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
 
     NS_IMETHOD Stop() {
         // Need this here because otherwise nsIWebNavigation::Stop
         // overrides the docloader's Stop()
         return nsDocLoader::Stop();
     }
 
     // Need to implement (and forward) nsISecurityEventSink, because
@@ -623,20 +626,18 @@ protected:
     
     // Call this when a URI load is handed to us (via OnLinkClick or
     // InternalLoad).  This makes sure that we're not inside unload, or that if
     // we are it's still OK to load this URI.
     bool IsOKToLoadURI(nsIURI* aURI);
     
     void ReattachEditorToWindow(nsISHEntry *aSHEntry);
 
-    nsresult GetSessionStorageForURI(nsIURI* aURI,
-                                     const nsSubstring& aDocumentURI,
-                                     bool create,
-                                     nsIDOMStorage** aStorage);
+    nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
+    nsIDOMStorageManager* TopSessionStorageManager();
 
     // helpers for executing commands
     nsresult GetControllerForCommand(const char *inCommand,
                                      nsIController** outController);
     nsresult IsCommandEnabled(const char * inCommand, bool* outEnabled);
     nsresult DoCommand(const char * inCommand);
     nsresult EnsureCommandHandler();
 
@@ -671,19 +672,16 @@ protected:
         eFrameTypeBrowser,
         eFrameTypeApp
     };
 
     FrameType GetInheritedFrameType();
 
     bool HasUnloadedParent();
 
-    // hash of session storages, keyed by domain
-    nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
-
     // Dimensions of the docshell
     nsIntRect                  mBounds;
     nsString                   mName;
     nsString                   mTitle;
 
     /**
      * Content-Type Hint of the most-recently initiated load. Used for
      * session history entries.
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -35,17 +35,17 @@ interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 
-[scriptable, builtinclass, uuid(4277354d-5069-4278-935a-5d596ce9bfbf)]
+[scriptable, builtinclass, uuid(2b192c9c-dea4-4696-a445-1bef7bc0db6d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -402,50 +402,41 @@ interface nsIDocShell : nsIDocShellTreeI
    * Notification that entries have been removed from the beginning of a
    * nsSHistory which has this as its rootDocShell.
    *
    * @param numEntries - The number of entries removed
    */
   void historyPurged(in long numEntries);
 
   /*
+   * @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
+   *
    * Retrieves the WebApps session storage object for the supplied principal.
    *
    * @param principal returns a storage for this principal
    * @param documentURI new storage will be created with reference to this
    *                    document.documentURI that will appear in storage event
    * @param create If true and a session storage object doesn't
    *               already exist, a new one will be created.
    */
   nsIDOMStorage getSessionStorageForPrincipal(in nsIPrincipal principal,
                                               in DOMString documentURI,
                                               in boolean create);
 
   /*
+   * @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
+   *
    * Add a WebApps session storage object to the docshell.
    *
    * @param principal the principal the storage object is associated with
    * @param storage the storage object to add
    */
   void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
 
   /**
-   * Clones all session storage objects and attaches them to the given docshell.
-   * Useful when duplicating tabs and their states.
-   *
-   * @param docShell the docshell to clone the sessionstorage objects to
-   */
-  void cloneSessionStoragesTo(in nsIDocShell docShell);
-
-  /**
-   * Removes all WebApps session storage objects attached to the docshell.
-   */
-  void clearSessionStorages();
-
-  /**
    * Gets the channel for the currently loaded document, if any. 
    * For a new document load, this will be the channel of the previous document
    * until after OnLocationChange fires.
    */
   readonly attribute nsIChannel currentDocumentChannel;
 
   /**
    * Set the offset of this child in its container.
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -224,17 +224,17 @@
 #include "nsIDOMSVGLength.h"
 #include "nsIDOMSVGNumber.h"
 #include "nsIDOMSVGRect.h"
 #include "nsIDOMSVGZoomEvent.h"
 
 #include "nsIImageDocument.h"
 
 // Storage includes
-#include "nsDOMStorage.h"
+#include "DOMStorage.h"
 
 // Device Storage
 #include "nsIDOMDeviceStorage.h"
 
 // Drag and drop
 #include "nsIDOMDataTransfer.h"
 
 // Geolocation
@@ -753,29 +753,24 @@ static nsDOMClassInfoData sClassInfoData
                            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(StorageObsolete, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   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(StorageItem, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsEventSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CommandEvent, nsEventSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(OfflineResourceList, nsOfflineResourceListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
@@ -2064,29 +2059,20 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(XPathNSResolver, nsIDOMXPathNSResolver)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathNSResolver)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XPathResult, nsIDOMXPathResult)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(StorageObsolete, nsIDOMStorageObsolete)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageObsolete)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(StorageItem, nsIDOMStorageItem)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageItem)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMToString)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(XULCommandEvent, nsIDOMXULCommandEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandEvent)
     DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(CommandEvent, nsIDOMCommandEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCommandEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -131,19 +131,17 @@ DOMCI_CLASS(WindowUtils)
 DOMCI_CLASS(XSLTProcessor)
 
 // DOM Level 3 XPath objects
 DOMCI_CLASS(XPathExpression)
 DOMCI_CLASS(XPathNSResolver)
 DOMCI_CLASS(XPathResult)
 
 // WhatWG WebApps Objects
-DOMCI_CLASS(StorageObsolete)
 DOMCI_CLASS(Storage)
-DOMCI_CLASS(StorageItem)
 
 DOMCI_CLASS(XULCommandEvent)
 DOMCI_CLASS(CommandEvent)
 DOMCI_CLASS(OfflineResourceList)
 
 DOMCI_CLASS(Blob)
 DOMCI_CLASS(File)
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13,22 +13,25 @@
 // Local Includes
 #include "nsGlobalWindow.h"
 #include "Navigator.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsPerformance.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsBarProps.h"
-#include "nsDOMStorage.h"
+#include "nsIDOMStorage.h"
+#include "nsIDOMStorageManager.h"
+#include "DOMStorage.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsIPowerManagerService.h"
 #include "nsISizeOfEventTarget.h"
+#include "nsIPermissionManager.h"
 
 #ifdef XP_WIN
 #ifdef GetClassName
 #undef GetClassName
 #endif // GetClassName
 #endif // XP_WIN
 
 // Helper Classes
@@ -2515,17 +2518,46 @@ nsGlobalWindow::SetNewDocument(nsIDocume
     if (itemType != nsIDocShellTreeItem::typeChrome ||
         nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) {
       newInnerWindow->mHasNotifiedGlobalCreated = true;
       nsContentUtils::AddScriptRunner(
         NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated));
     }
   }
 
-  return NS_OK;
+  PreloadLocalStorage();
+
+  return NS_OK;
+}
+
+void
+nsGlobalWindow::PreloadLocalStorage()
+{
+  if (!Preferences::GetBool(kStorageEnabled)) {
+    return;
+  }
+
+  if (IsChromeWindow()) {
+    return;
+  }
+
+  nsIPrincipal* principal = GetPrincipal();
+  if (!principal) {
+    return;
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIDOMStorageManager> storageManager =
+    do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  storageManager->PrecacheStorage(principal);
 }
 
 void
 nsGlobalWindow::DispatchDOMWindowCreated()
 {
   if (!mDoc || !mDocument) {
     return;
   }
@@ -2703,29 +2735,16 @@ nsGlobalWindow::DetachFromDocShell()
   NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
 
   if (mFrames) {
     mFrames->SetDocShell(nullptr);
   }
 
   MaybeForgiveSpamCount();
   CleanUp(false);
-
-    if (mLocalStorage) {
-      nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
-      if (obs) {
-        mDocShell->AddWeakPrivacyTransitionObserver(obs);
-      }
-    }
-    if (mSessionStorage) {
-      nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
-      if (obs) {
-        mDocShell->AddWeakPrivacyTransitionObserver(obs);
-      }
-    }
 }
 
 void
 nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener,
                                 bool aOriginalOpener)
 {
   FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));
 
@@ -8821,65 +8840,67 @@ nsGlobalWindow::GetSessionStorage(nsIDOM
 #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;
+        mSessionStorage = nullptr;
       }
     }
   }
 
   if (!mSessionStorage) {
     *aSessionStorage = nullptr;
 
     nsString documentURI;
     if (mDocument) {
       mDocument->GetDocumentURI(documentURI);
     }
 
     // If the document has the sandboxed origin flag set
-    // don't allow access to localStorage.
+    // don't allow access to sessionStorage.
     if (!mDoc) {
       return NS_ERROR_FAILURE;
     }
 
     if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
 
-    nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
-                                                          documentURI,
-                                                          true,
-                                                          getter_AddRefs(mSessionStorage));
+    nsresult rv;
+
+    nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+
+    rv = storageManager->CreateStorage(principal,
+                                       documentURI,
+                                       loadContext && loadContext->UsePrivateBrowsing(),
+                                       getter_AddRefs(mSessionStorage));
     NS_ENSURE_SUCCESS(rv, rv);
 
 #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) {
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
-
-    nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
-    if (obs) {
-      docShell->AddWeakPrivacyTransitionObserver(obs);
-    }
   }
 
 #ifdef PR_LOGGING
-    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
-      PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
-    }
+  if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+    PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
+  }
 #endif
 
   NS_ADDREF(*aSessionStorage = mSessionStorage);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
@@ -8893,51 +8914,48 @@ nsGlobalWindow::GetLocalStorage(nsIDOMSt
     return NS_OK;
   }
 
   if (!mLocalStorage) {
     *aLocalStorage = nullptr;
 
     nsresult rv;
 
-    if (!nsDOMStorage::CanUseStorage())
+    if (!DOMStorage::CanUseStorage()) {
       return NS_ERROR_DOM_SECURITY_ERR;
+    }
 
     nsIPrincipal *principal = GetPrincipal();
-    if (!principal)
+    if (!principal) {
       return NS_OK;
+    }
 
     nsCOMPtr<nsIDOMStorageManager> storageManager =
-      do_GetService("@mozilla.org/dom/storagemanager;1", &rv);
+      do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsString documentURI;
-    if (mDocument) {
-      mDocument->GetDocumentURI(documentURI);
-    }
-
     // If the document has the sandboxed origin flag set
     // don't allow access to localStorage.
     if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
 
+    nsString documentURI;
+    if (mDocument) {
+      mDocument->GetDocumentURI(documentURI);
+    }
+
     nsIDocShell* docShell = GetDocShell();
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
 
-    rv = storageManager->GetLocalStorageForPrincipal(principal,
-                                                     documentURI,
-                                                     loadContext && loadContext->UsePrivateBrowsing(),
-                                                     getter_AddRefs(mLocalStorage));
+    rv = storageManager->CreateStorage(principal,
+                                       documentURI,
+                                       loadContext && loadContext->UsePrivateBrowsing(),
+                                       getter_AddRefs(mLocalStorage));
     NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
-    if (obs && docShell) {
-      docShell->AddWeakPrivacyTransitionObserver(obs);
-    }
   }
 
   NS_ADDREF(*aLocalStorage = mLocalStorage);
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIDOMStorageIndexedDB
@@ -9501,66 +9519,65 @@ nsGlobalWindow::Observe(nsISupports* aSu
 
     nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDOMStorage> changingStorage;
     rv = event->GetStorageArea(getter_AddRefs(changingStorage));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    bool fireMozStorageChanged = false;
+    principal = GetPrincipal();
+    if (!principal) {
+      return NS_OK;
+    }
+
     nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
-    nsPIDOMStorage::nsDOMStorageType storageType = pistorage->StorageType();
 
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
     bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
     if (pistorage->IsPrivate() != isPrivate) {
       return NS_OK;
     }
 
-    bool fireMozStorageChanged = false;
-    principal = GetPrincipal();
-    switch (storageType)
+    switch (pistorage->GetType())
     {
     case nsPIDOMStorage::SessionStorage:
     {
-      nsCOMPtr<nsIDOMStorage> storage = mSessionStorage;
-      if (!storage) {
-        nsIDocShell* docShell = GetDocShell();
-        if (principal && docShell) {
-          // No need to pass documentURI here, it's only needed when we want
-          // to create a new storage, the third paramater would be true
-          docShell->GetSessionStorageForPrincipal(principal,
-                                                  EmptyString(),
-                                                  false,
-                                                  getter_AddRefs(storage));
-        }
+      bool check = false;
+
+      nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
+      if (storageManager) {
+        rv = storageManager->CheckStorage(principal, changingStorage, &check);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
 
-      if (!pistorage->IsForkOf(storage)) {
-        // This storage event is coming from a different doc shell,
-        // i.e. it is a clone, ignore this event.
+      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);
       break;
     }
+
     case nsPIDOMStorage::LocalStorage:
     {
       // Allow event fire only for the same principal storages
       // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
-      nsIPrincipal *storagePrincipal = pistorage->Principal();
-      bool equals;
-
+      nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
+
+      bool equals = false;
       rv = storagePrincipal->Equals(principal, &equals);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (!equals)
         return NS_OK;
 
       fireMozStorageChanged = SameCOMIdentity(mLocalStorage, changingStorage);
       break;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -41,17 +41,16 @@
 #include "nsIDOMDocument.h"
 #include "nsIPrincipal.h"
 #include "nsIXPCScriptable.h"
 #include "nsPoint.h"
 #include "nsSize.h"
 #include "nsRect.h"
 #include "mozFlushType.h"
 #include "prclist.h"
-#include "nsIDOMStorageObsolete.h"
 #include "nsIDOMStorageEvent.h"
 #include "nsIDOMStorageIndexedDB.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIArray.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIDOMTouchEvent.h"
@@ -1017,16 +1016,18 @@ protected:
   nsDOMWindowList* GetWindowList();
 
   // Helper for getComputedStyle and getDefaultComputedStyle
   nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
                                   const nsAString& aPseudoElt,
                                   bool aDefaultStylesOnly,
                                   nsIDOMCSSStyleDeclaration** aReturn);
 
+  void PreloadLocalStorage();
+
   // When adding new member variables, be careful not to create cycles
   // through JavaScript.  If there is any chance that a member variable
   // could own objects that are implemented in JavaScript, then those
   // objects will keep the global object (this object) alive.  To prevent
   // these cycles, ownership of such members must be released in
   // |CleanUp| and |DetachFromDocShell|.
 
   // This member is also used on both inner and outer windows, but
--- a/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -12,17 +12,16 @@
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 
 #include "CheckQuotaHelper.h"
 #include "nsContentUtils.h"
-#include "nsDOMStorage.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
 #include "IndexedDatabaseManager.h"
 
--- a/dom/interfaces/storage/moz.build
+++ b/dom/interfaces/storage/moz.build
@@ -3,19 +3,17 @@
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIDOMStorage.idl',
     'nsIDOMStorageEvent.idl',
     'nsIDOMStorageIndexedDB.idl',
-    'nsIDOMStorageItem.idl',
     'nsIDOMStorageManager.idl',
-    'nsIDOMStorageObsolete.idl',
     'nsIDOMToString.idl',
 ]
 
 XPIDL_MODULE = 'dom_storage'
 
 XPIDL_FLAGS += [
     '-I$(topsrcdir)/dom/interfaces/base',
     '-I$(topsrcdir)/dom/interfaces/events',
deleted file mode 100644
--- a/dom/interfaces/storage/nsIDOMStorageItem.idl
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- 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 a client side storage item. See
- * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
- * for more information.
- *
- * A respresentation of a storage object item.
- */
-
-[scriptable, uuid(0CC37C78-4C5F-48E1-ADFC-7480B8FE9DC4)]
-interface nsIDOMStorageItem : nsISupports
-{
-  /**
-   * Indicates whether a key is available only in a secure context.
-   */
-  attribute boolean secure;
-
-  /**
-   * The value associated with the item.
-   */
-  attribute DOMString value;
-};
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -3,30 +3,96 @@
  * 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;
 
-[scriptable, uuid(b16b207c-d883-43f5-a27e-548e7f2f5c20)]
+/**
+ * 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)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
-   * Return the amount of disk space used by a domain.  Usage is checked
-   * against the domain of the page that set the key (the owner domain), not
-   * the domain of the storage object.
-   *
-   * @param aOwnerDomain The domain to check.
-   * @returns the space usage of the domain, in bytes.
+   * This starts async preloading of a storage cache for scope
+   * defined by the principal.
    */
-  long getUsage(in AString aOwnerDomain);
+  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 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,
+                              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 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,
+                           [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
+   *    the moment this method was called.  Modification to this new object will
+   *    not affect the original storage content we cloned from and vice versa.
+   */
+  void cloneStorage(in nsIDOMStorage aStorageToCloneFrom);
+
+  /**
+   * Returns true if the storage belongs to the given principal and is managed
+   * (i.e. has been created and is cached) by this storage manager.
+   *
+   * @param aPrincipal
+   *    Principal to check the storage against.
+   * @param aStorage
+   *    The storage object to examine.
+   *
+   * @result
+   *    true when the storage object is bound with the principal and is managed
+   *         by this storage manager.
+   *    false otherwise
+   */
+  bool checkStorage(in nsIPrincipal aPrincipal,
+                    in nsIDOMStorage aStorage);
+
+  /**
+   * @deprecated
+   *
    * Returns instance of localStorage object for aURI's origin.
    * This method ensures there is always only a single instance
    * for a single origin.
+   *
+   * Currently just forwards to the createStorage method of this
+   * interface.
+   *
+   * Extension developers are strongly encouraged to use getStorage
+   * or createStorage method instead.
    */
   nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
                                             in DOMString aDocumentURI,
                                             [optional] in bool aPrivate);
 };
deleted file mode 100644
--- a/dom/interfaces/storage/nsIDOMStorageObsolete.idl
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- 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/#scs-client-side
- * 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.
- */
-
-interface nsIDOMStorageItem;
-
-[scriptable, uuid(18013CF9-B104-49cf-9484-C2A7A845457E)]
-interface nsIDOMStorageObsolete : 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
-   * @throws INDEX_SIZE_ERR 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 item or null if the key was not found
-   */
-  nsIDOMStorageItem getItem(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(in DOMString key, in DOMString data);
-
-  /**
-   * Remove a key and its corresponding value.
-   *
-   * @param key key to remove
-   */
-  void removeItem(in DOMString key);
-};
--- a/dom/interfaces/storage/nsPIDOMStorage.h
+++ b/dom/interfaces/storage/nsPIDOMStorage.h
@@ -3,51 +3,52 @@
 /* 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 nsIDOMStorageObsolete;
-class nsIURI;
 class nsIPrincipal;
 
+namespace mozilla {
+namespace dom {
+
+class DOMStorageCache;
+class DOMStorageManager;
+
+} // ::dom
+} // ::mozilla
+
+// {09198A51-5D27-4992-97E4-38A9CEA2A65D}
 #define NS_PIDOMSTORAGE_IID \
-{ 0x9c292365, 0x6ae4, 0x461b,                           \
-  { 0x86, 0x98, 0xf5, 0x23, 0x6f, 0xfa, 0xd2, 0x30 } }
+  { 0x9198a51, 0x5d27, 0x4992, \
+    { 0x97, 0xe4, 0x38, 0xa9, 0xce, 0xa2, 0xa6, 0x5d } }
 
 class nsPIDOMStorage : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
 
-  typedef enum {
-    Unknown = 0,
+  enum StorageType {
     LocalStorage = 1,
     SessionStorage = 2
-  } nsDOMStorageType;
+  };
 
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                        bool aPrivate) = 0;
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                      bool aPrivate) = 0;
+  virtual StorageType GetType() const = 0;
+  virtual mozilla::dom::DOMStorageManager* GetManager() const = 0;
+  virtual const mozilla::dom::DOMStorageCache* GetCache() const = 0;
 
-  virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
-  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI) = 0;
-  virtual bool IsForkOf(nsIDOMStorage* aThat) = 0;
-
-  virtual nsTArray<nsString> *GetKeys() = 0;
+  virtual nsTArray<nsString>* GetKeys() = 0;
 
-  virtual nsIPrincipal* Principal() = 0;
+  virtual nsIPrincipal* GetPrincipal() = 0;
+  virtual bool PrincipalEquals(nsIPrincipal* principal) = 0;
   virtual bool CanAccess(nsIPrincipal *aPrincipal) = 0;
-
-  virtual nsDOMStorageType StorageType() = 0;
-
   virtual bool IsPrivate() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMStorage, NS_PIDOMSTORAGE_IID)
 
 #endif // __nsPIDOMStorage_h_
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -14,17 +14,17 @@
 
 #include "ContentChild.h"
 #include "CrashReporterChild.h"
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
-#include "mozilla/dom/StorageChild.h"
+#include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/Hal.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/jsipc/PContextWrapperChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
@@ -834,26 +834,26 @@ ContentChild::AllocPSms()
 bool
 ContentChild::DeallocPSms(PSmsChild* aSms)
 {
     delete aSms;
     return true;
 }
 
 PStorageChild*
-ContentChild::AllocPStorage(const StorageConstructData& aData)
+ContentChild::AllocPStorage()
 {
     NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
     return nullptr;
 }
 
 bool
 ContentChild::DeallocPStorage(PStorageChild* aActor)
 {
-    StorageChild* child = static_cast<StorageChild*>(aActor);
+    DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor);
     child->ReleaseIPDLReference();
     return true;
 }
 
 PBluetoothChild*
 ContentChild::AllocPBluetooth()
 {
 #ifdef MOZ_B2G_BT
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -137,17 +137,17 @@ public:
             const bool& aForceSave,
             const int64_t& aContentLength,
             const OptionalURIParams& aReferrer);
     virtual bool DeallocPExternalHelperApp(PExternalHelperAppChild *aService);
 
     virtual PSmsChild* AllocPSms();
     virtual bool DeallocPSms(PSmsChild*);
 
-    virtual PStorageChild* AllocPStorage(const StorageConstructData& aData);
+    virtual PStorageChild* AllocPStorage();
     virtual bool DeallocPStorage(PStorageChild* aActor);
 
     virtual PBluetoothChild* AllocPBluetooth();
     virtual bool DeallocPBluetooth(PBluetoothChild* aActor);
 
     virtual PSpeechSynthesisChild* AllocPSpeechSynthesis();
     virtual bool DeallocPSpeechSynthesis(PSpeechSynthesisChild* aActor);
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -24,17 +24,17 @@
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "IndexedDatabaseManager.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
-#include "mozilla/dom/StorageParent.h"
+#include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "SmsParent.h"
 #include "mozilla/Hal.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
@@ -1964,25 +1964,26 @@ ContentParent::AllocPSms()
 bool
 ContentParent::DeallocPSms(PSmsParent* aSms)
 {
     static_cast<SmsParent*>(aSms)->Release();
     return true;
 }
 
 PStorageParent*
-ContentParent::AllocPStorage(const StorageConstructData& aData)
+ContentParent::AllocPStorage()
 {
-    return new StorageParent(aData);
+    return new DOMStorageDBParent();
 }
 
 bool
 ContentParent::DeallocPStorage(PStorageParent* aActor)
 {
-    delete aActor;
+    DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor);
+    child->ReleaseIPDLReference();
     return true;
 }
 
 PBluetoothParent*
 ContentParent::AllocPBluetooth()
 {
 #ifdef MOZ_B2G_BT
     if (!AssertAppProcessPermission(this, "bluetooth")) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -280,17 +280,17 @@ private:
             const bool& aForceSave,
             const int64_t& aContentLength,
             const OptionalURIParams& aReferrer);
     virtual bool DeallocPExternalHelperApp(PExternalHelperAppParent* aService);
 
     virtual PSmsParent* AllocPSms();
     virtual bool DeallocPSms(PSmsParent*);
 
-    virtual PStorageParent* AllocPStorage(const StorageConstructData& aData);
+    virtual PStorageParent* AllocPStorage();
     virtual bool DeallocPStorage(PStorageParent* aActor);
 
     virtual PBluetoothParent* AllocPBluetooth();
     virtual bool DeallocPBluetooth(PBluetoothParent* aActor);
     virtual bool RecvPBluetoothConstructor(PBluetoothParent* aActor);
 
     virtual PSpeechSynthesisParent* AllocPSpeechSynthesis();
     virtual bool DeallocPSpeechSynthesis(PSpeechSynthesisParent* aActor);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -45,33 +45,16 @@ using mozilla::void_t;
 using mozilla::dom::AudioChannelType;
 using mozilla::dom::NativeThreadId;
 using mozilla::layout::ScrollingBehavior;
 using gfxIntSize;
 
 namespace mozilla {
 namespace dom {
 
-// Data required to clone an existing DOMStorageImpl in the parent
-struct StorageClone
-{
-    // Existing cross-process storage actor to clone
-    PStorage actor;
-    // Result of calling IsCallerSecure() in the child
-    bool callerSecure;
-};
-
-// When creating a new PStorage protocol, an existing one can be
-// cloned (see nsDOMStorage2::Clone)
-union StorageConstructData
-{
-    null_t;
-    StorageClone;
-};
-
 struct FontListEntry {
     nsString  familyName;
     nsString  faceName;
     nsCString filepath;
     uint16_t  weight;
     int16_t   stretch;
     uint8_t   italic;
     uint8_t   index;
@@ -396,17 +379,17 @@ parent:
     PIndexedDB();
 
     PNecko();
 
     PSms();
 
     PSpeechSynthesis();
 
-    PStorage(StorageConstructData data);
+    PStorage();
 
     PBluetooth();
 
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags);
     async SetURITitle(URIParams uri, nsString title);
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorage.cpp
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; 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 "DOMStorage.h"
+#include "DOMStorageCache.h"
+#include "DOMStorageManager.h"
+
+#include "nsIDOMStorageEvent.h"
+#include "nsIObserverService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrincipal.h"
+#include "nsICookiePermission.h"
+
+#include "nsDOMClassInfoID.h"
+#include "mozilla/Services.h"
+#include "mozilla/Preferences.h"
+#include "GeneratedEvents.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_INTERFACE_MAP_BEGIN(DOMStorage)
+  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,
+                       DOMStorageCache* aCache,
+                       const nsAString& aDocumentURI,
+                       nsIPrincipal* aPrincipal,
+                       bool aIsPrivate)
+: mManager(aManager)
+, mCache(aCache)
+, mDocumentURI(aDocumentURI)
+, mPrincipal(aPrincipal)
+, mIsPrivate(aIsPrivate)
+, mIsSessionOnly(false)
+{
+  mCache->Preload();
+}
+
+DOMStorage::~DOMStorage()
+{
+  mCache->KeepAlive();
+}
+
+// nsIDOMStorage (web content public API implementation)
+
+NS_IMETHODIMP
+DOMStorage::GetLength(uint32_t* aLength)
+{
+  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;
+  }
+
+  return mCache->GetKey(this, aIndex, aRetval);
+}
+
+NS_IMETHODIMP
+DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
+{
+  if (!CanUseStorage(this)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  return mCache->GetItem(this, aKey, aRetval);
+}
+
+NS_IMETHODIMP
+DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
+{
+  if (!CanUseStorage(this)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  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 old;
+  nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
+    BroadcastChangeNotification(aKey, old, aData);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMStorage::RemoveItem(const nsAString& aKey)
+{
+  if (!CanUseStorage(this)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsAutoString old;
+  nsresult rv = mCache->RemoveItem(this, aKey, old);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
+    BroadcastChangeNotification(aKey, old, NullString());
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMStorage::Clear()
+{
+  if (!CanUseStorage(this)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsresult rv = mCache->Clear(this);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
+    BroadcastChangeNotification(NullString(), NullString(), NullString());
+  }
+
+  return NS_OK;
+}
+
+namespace {
+
+class StorageNotifierRunnable : public nsRunnable
+{
+public:
+  StorageNotifierRunnable(nsISupports* aSubject)
+    : mSubject(aSubject)
+  { }
+
+  NS_DECL_NSIRUNNABLE
+
+private:
+  nsCOMPtr<nsISupports> mSubject;
+};
+
+NS_IMETHODIMP
+StorageNotifierRunnable::Run()
+{
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+  if (observerService) {
+    observerService->NotifyObservers(mSubject, "dom-storage2-changed", nullptr);
+  }
+  return NS_OK;
+}
+
+} // anonymous namespace
+
+void
+DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey,
+                                        const nsSubstring& aOldValue,
+                                        const nsSubstring& aNewValue)
+{
+  nsCOMPtr<nsIDOMEvent> domEvent;
+  // Note, this DOM event should never reach JS. It is cloned later in
+  // nsGlobalWindow.
+  NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr);
+
+  nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent);
+  nsresult rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
+                                        false,
+                                        false,
+                                        aKey,
+                                        aOldValue,
+                                        aNewValue,
+                                        mDocumentURI,
+                                        static_cast<nsIDOMStorage*>(this));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsRefPtr<StorageNotifierRunnable> r = new StorageNotifierRunnable(event);
+  NS_DispatchToMainThread(r);
+}
+
+static const uint32_t ASK_BEFORE_ACCEPT = 1;
+static const uint32_t ACCEPT_SESSION = 2;
+static const uint32_t BEHAVIOR_REJECT = 2;
+
+static const char kPermissionType[] = "cookie";
+static const char kStorageEnabled[] = "dom.storage.enabled";
+static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
+static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
+
+// static, public
+bool
+DOMStorage::CanUseStorage(DOMStorage* aStorage)
+{
+  // This method is responsible for correct setting of mIsSessionOnly.
+  // It doesn't work with mIsPrivate flag at all, since it is checked
+  // regardless mIsSessionOnly flag in DOMStorageCache code.
+  if (aStorage) {
+    aStorage->mIsSessionOnly = false;
+  }
+
+  if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
+    return false;
+  }
+
+  // chrome can always use aStorage regardless of permission preferences
+  if (nsContentUtils::IsCallerChrome()) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrincipal> subjectPrincipal;
+  nsresult rv = nsContentUtils::GetSecurityManager()->
+                  GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // if subjectPrincipal were null we'd have returned after
+  // IsCallerChrome().
+
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  if (!permissionManager) {
+    return false;
+  }
+
+  uint32_t perm;
+  permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
+                                                 kPermissionType, &perm);
+
+  if (perm == nsIPermissionManager::DENY_ACTION) {
+    return false;
+  }
+
+  if (perm == nsICookiePermission::ACCESS_SESSION) {
+    if (aStorage) {
+      aStorage->mIsSessionOnly = true;
+    }
+  } else if (perm != nsIPermissionManager::ALLOW_ACTION) {
+    uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior);
+    uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
+
+    // Treat "ask every time" as "reject always".
+    if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) {
+      return false;
+    }
+
+    if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
+      aStorage->mIsSessionOnly = true;
+    }
+  }
+
+  if (aStorage) {
+    return aStorage->CanAccess(subjectPrincipal);
+  }
+
+  return true;
+}
+
+// nsPIDOMStorage
+
+nsPIDOMStorage::StorageType
+DOMStorage::GetType() const
+{
+  return mManager->Type();
+}
+
+nsIPrincipal*
+DOMStorage::GetPrincipal()
+{
+  return mPrincipal;
+}
+
+// Defined in DOMStorageManager.cpp
+extern bool
+PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
+
+bool
+DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
+{
+  return PrincipalsEqual(mPrincipal, aPrincipal);
+}
+
+bool
+DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
+{
+  // Allow C++ callers to access the storage
+  if (!aPrincipal) {
+    return true;
+  }
+
+  // For content, either the code base or domain must be the same.  When code
+  // base is the same, this is enough to say it is safe for a page to access
+  // this storage.
+
+  bool subsumes;
+  nsresult rv = aPrincipal->SubsumesIgnoringDomain(mPrincipal, &subsumes);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  if (!subsumes) {
+    nsresult rv = aPrincipal->Subsumes(mPrincipal, &subsumes);
+    if (NS_FAILED(rv)) {
+      return false;
+    }
+  }
+
+  return subsumes;
+}
+
+nsTArray<nsString>*
+DOMStorage::GetKeys()
+{
+  if (!CanUseStorage(this)) {
+    return new nsTArray<nsString>(); // return just an empty array
+  }
+
+  return mCache->GetKeys(this);
+}
+
+} // ::dom
+} // ::mozilla
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorage.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef nsDOMStorage_h___
+#define nsDOMStorage_h___
+
+#include "nsIDOMStorage.h"
+#include "nsPIDOMStorage.h"
+#include "nsWeakReference.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class DOMStorageManager;
+class DOMStorageCache;
+
+class DOMStorage MOZ_FINAL : public nsIDOMStorage
+                           , public nsPIDOMStorage
+                           , public nsSupportsWeakReference
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSTORAGE
+
+  // nsPIDOMStorage
+  virtual StorageType GetType() const;
+  virtual DOMStorageManager* GetManager() const { return mManager; }
+  virtual const DOMStorageCache* GetCache() const { return mCache; }
+
+  virtual nsTArray<nsString>* GetKeys();
+  virtual nsIPrincipal* GetPrincipal();
+  virtual bool PrincipalEquals(nsIPrincipal* aPrincipal);
+  virtual bool CanAccess(nsIPrincipal* aPrincipal);
+  virtual bool IsPrivate() { return mIsPrivate; }
+
+  DOMStorage(DOMStorageManager* aManager,
+             DOMStorageCache* aCache,
+             const nsAString& aDocumentURI,
+             nsIPrincipal* aPrincipal,
+             bool aIsPrivate);
+  ~DOMStorage();
+
+  // 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);
+
+  bool IsPrivate() const { return mIsPrivate; }
+  bool IsSessionOnly() const { return mIsSessionOnly; }
+
+private:
+  friend class DOMStorageManager;
+  friend class DOMStorageCache;
+
+  nsRefPtr<DOMStorageManager> mManager;
+  nsRefPtr<DOMStorageCache> mCache;
+  nsString mDocumentURI;
+
+  // Principal this DOMStorage (i.e. localStorage or sessionStorage) has
+  // been created for
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
+  // Whether this storage is running in private-browsing window.
+  bool mIsPrivate : 1;
+
+  // Whether storage is set to persist data only per session, may change
+  // dynamically and is set by CanUseStorage function that is called
+  // before any operation on the storage.
+  bool mIsSessionOnly : 1;
+
+  void BroadcastChangeNotification(const nsSubstring& aKey,
+                                   const nsSubstring& aOldValue,
+                                   const nsSubstring& aNewValue);
+};
+
+} // ::dom
+} // ::mozilla
+
+#endif /* nsDOMStorage_h___ */
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageCache.cpp
@@ -0,0 +1,801 @@
+/* -*- Mode: C++; 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 "DOMStorageCache.h"
+
+#include "DOMStorage.h"
+#include "DOMStorageDBThread.h"
+#include "DOMStorageIPC.h"
+#include "DOMStorageManager.h"
+
+#include "nsDOMString.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/unused.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla {
+namespace dom {
+
+#define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
+
+// static
+DOMStorageDBBridge* DOMStorageCache::sDatabase = nullptr;
+
+namespace { // anon
+
+const uint32_t kDefaultSet = 0;
+const uint32_t kPrivateSet = 1;
+const uint32_t kSessionSet = 2;
+
+inline uint32_t
+GetDataSetIndex(bool aPrivate, bool aSessionOnly)
+{
+  if (aPrivate) {
+    return kPrivateSet;
+  }
+
+  if (aSessionOnly) {
+    return kSessionSet;
+  }
+
+  return kDefaultSet;
+}
+
+inline uint32_t
+GetDataSetIndex(const DOMStorage* aStorage)
+{
+  return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
+}
+
+} // anon
+
+// DOMStorageCacheBridge
+
+NS_IMPL_THREADSAFE_ADDREF(DOMStorageCacheBridge)
+
+// Since there is no consumer of return value of Release, we can turn this 
+// method to void to make implementation of asynchronous DOMStorageCache::Release
+// much simpler.
+NS_IMETHODIMP_(void) DOMStorageCacheBridge::Release(void)
+{
+  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+  nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
+  NS_LOG_RELEASE(this, count, "DOMStorageCacheBridge");
+  if (0 == count) {
+    mRefCnt = 1; /* stabilize */
+    /* enable this to find non-threadsafe destructors: */
+    /* NS_ASSERT_OWNINGTHREAD(_class); */
+    delete (this);
+  }
+}
+
+// DOMStorageCache
+
+DOMStorageCache::DOMStorageCache(const nsACString* aScope)
+: mManager(nullptr)
+, mScope(*aScope)
+, mMonitor("DOMStorageCache")
+, mLoaded(false)
+, mLoadResult(NS_OK)
+, mInitialized(false)
+, mSessionOnlyDataSetActive(false)
+, mPreloadTelemetryRecorded(false)
+{
+  MOZ_COUNT_CTOR(DOMStorageCache);
+}
+
+DOMStorageCache::~DOMStorageCache()
+{
+  if (mManager) {
+    mManager->DropCache(this);
+  }
+
+  MOZ_COUNT_DTOR(DOMStorageCache);
+}
+
+NS_IMETHODIMP_(void)
+DOMStorageCache::Release(void)
+{
+  // We must actually release on the main thread since the cache removes it
+  // self from the manager's hash table.  And we don't want to lock access to
+  // that hash table.
+  if (NS_IsMainThread()) {
+    DOMStorageCacheBridge::Release();
+    return;
+  }
+
+  nsRefPtr<nsRunnableMethod<DOMStorageCacheBridge, void, false> > event =
+    NS_NewNonOwningRunnableMethod(static_cast<DOMStorageCacheBridge*>(this),
+                                  &DOMStorageCacheBridge::Release);
+
+  nsresult rv = NS_DispatchToMainThread(event);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("DOMStorageCache::Release() on a non-main thread");
+    DOMStorageCacheBridge::Release();
+  }
+}
+
+void
+DOMStorageCache::Init(DOMStorageManager* aManager,
+                      bool aPersistent,
+                      nsIPrincipal* aPrincipal,
+                      const nsACString& aQuotaScope)
+{
+  if (mInitialized) {
+    return;
+  }
+
+  mManager = aManager;
+  mInitialized = true;
+  mPrincipal = aPrincipal;
+  mPersistent = aPersistent;
+  mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
+
+  if (mPersistent) {
+    Preload();
+  }
+}
+
+inline bool
+DOMStorageCache::Persist(const DOMStorage* aStorage) const
+{
+  return mPersistent &&
+         !aStorage->IsSessionOnly() &&
+         !aStorage->IsPrivate();
+}
+
+namespace { // anon
+
+PLDHashOperator
+CloneSetData(const nsAString& aKey, const nsString aValue, void* aArg)
+{
+  DOMStorageCache::Data* target = static_cast<DOMStorageCache::Data*>(aArg);
+  target->mKeys.Put(aKey, aValue);
+
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+DOMStorageCache::Data&
+DOMStorageCache::DataSet(const DOMStorage* aStorage)
+{
+  uint32_t index = GetDataSetIndex(aStorage);
+
+  if (index == kSessionSet && !mSessionOnlyDataSetActive) {
+    // Session only data set is demanded but not filled with
+    // current data set, copy to session only set now.
+
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
+
+    Data& defaultSet = mData[kDefaultSet];
+    Data& sessionSet = mData[kSessionSet];
+
+    defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet);
+
+    mSessionOnlyDataSetActive = true;
+
+    // This updates sessionSet.mOriginQuotaUsage and also updates global usage
+    // for all session only data
+    ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
+  }
+
+  return mData[index];
+}
+
+bool
+DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta)
+{
+  return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
+}
+
+bool
+DOMStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
+{
+  // Check limit per this origin
+  Data& data = mData[aGetDataSetIndex];
+  uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
+  if (aDelta > 0 && newOriginUsage > DOMStorageManager::GetQuota()) {
+    return false;
+  }
+
+  // Now check eTLD+1 limit
+  GetDatabase();
+  if (sDatabase) {
+    DOMStorageUsage* usage = sDatabase->GetScopeUsage(mQuotaScope);
+    if (!usage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
+      return false;
+    }
+  }
+
+  // Update size in our data set
+  data.mOriginQuotaUsage = newOriginUsage;
+  return true;
+}
+
+void
+DOMStorageCache::Preload()
+{
+  if (mLoaded || !mPersistent) {
+    return;
+  }
+
+  if (!StartDatabase()) {
+    mLoaded = true;
+    mLoadResult = NS_ERROR_FAILURE;
+    return;
+  }
+
+  sDatabase->AsyncPreload(this);
+  sDatabase->GetScopeUsage(mQuotaScope);
+}
+
+namespace { // anon
+
+// This class is passed to timer as a tick observer.  It refers the cache
+// and keeps it alive for a time.
+class DOMStorageCacheHolder : public nsITimerCallback
+{
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHODIMP
+  Notify(nsITimer* aTimer)
+  {
+    mCache = nullptr;
+    return NS_OK;
+  }
+
+  virtual ~DOMStorageCacheHolder() {}
+
+  nsRefPtr<DOMStorageCache> mCache;
+
+public:
+  DOMStorageCacheHolder(DOMStorageCache* aCache) : mCache(aCache) {}
+};
+
+NS_IMPL_ISUPPORTS1(DOMStorageCacheHolder, nsITimerCallback)
+
+} // anon
+
+void
+DOMStorageCache::KeepAlive()
+{
+  // Missing reference back to the manager means the cache is not responsible
+  // for its lifetime.  Used for keeping sessionStorage live forever.
+  if (!mManager) {
+    return;
+  }
+
+  if (!NS_IsMainThread()) {
+    // Timer and the holder must be initialized on the main thread.
+    nsRefPtr<nsRunnableMethod<DOMStorageCache> > event =
+      NS_NewRunnableMethod(this, &DOMStorageCache::KeepAlive);
+
+    NS_DispatchToMainThread(event);
+    return;
+  }
+
+  nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+  if (!timer) {
+    return;
+  }
+
+  nsRefPtr<DOMStorageCacheHolder> holder = new DOMStorageCacheHolder(this);
+  timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
+                          nsITimer::TYPE_ONE_SHOT);
+
+  mKeepAliveTimer.swap(timer);
+}
+
+namespace { // anon
+
+// The AutoTimer provided by telemetry headers is only using static,
+// i.e. compile time known ID, but here we know the ID only at run time.
+// Hence a new class.
+class TelemetryAutoTimer
+{
+public:
+  TelemetryAutoTimer(Telemetry::ID aId)
+    : id(aId), start(TimeStamp::Now()) {}
+  ~TelemetryAutoTimer()
+    { Telemetry::AccumulateDelta_impl<Telemetry::Millisecond>::compute(id, start); }
+private:
+  Telemetry::ID id;
+  const TimeStamp start;
+};
+
+} // anon
+
+void
+DOMStorageCache::WaitForPreload(Telemetry::ID aTelemetryID)
+{
+  if (!mPersistent) {
+    return;
+  }
+
+  bool loaded = mLoaded;
+
+  // Telemetry of rates of pending preloads
+  if (!mPreloadTelemetryRecorded) {
+    mPreloadTelemetryRecorded = true;
+    Telemetry::Accumulate(
+      Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS,
+      !loaded);
+  }
+
+  if (loaded) {
+    return;
+  }
+
+  // Measure which operation blocks and for how long
+  TelemetryAutoTimer timer(aTelemetryID);
+
+  // If preload already started (i.e. we got some first data, but not all)
+  // SyncPreload will just wait for it to finish rather then synchronously
+  // read from the database.  It seems to me more optimal.
+
+  // TODO place for A/B testing (force main thread load vs. let preload finish)
+  sDatabase->SyncPreload(this);
+}
+
+nsresult
+DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval)
+{
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETLENGTH_MS> autoTimer;
+
+  if (Persist(aStorage)) {
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
+    if (NS_FAILED(mLoadResult)) {
+      return mLoadResult;
+    }
+  }
+
+  *aRetval = DataSet(aStorage).mKeys.Count();
+  return NS_OK;
+}
+
+namespace { // anon
+
+class IndexFinderData
+{
+public:
+  IndexFinderData(uint32_t aIndex, nsAString& aRetval)
+    : mIndex(aIndex), mKey(aRetval)
+  {
+    mKey.SetIsVoid(true);
+  }
+
+  uint32_t mIndex;
+  nsAString& mKey;
+};
+
+PLDHashOperator
+FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg)
+{
+  IndexFinderData* data = static_cast<IndexFinderData*>(aArg);
+
+  if (data->mIndex--) {
+    return PL_DHASH_NEXT;
+  }
+
+  data->mKey = aKey;
+  return PL_DHASH_STOP;
+}
+
+} // anon
+
+nsresult
+DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval)
+{
+  // XXX: This does a linear search for the key at index, which would
+  // suck if there's a large numer of indexes. Do we care? If so,
+  // maybe we need to have a lazily populated key array here or
+  // something?
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETKEY_MS> autoTimer;
+
+  if (Persist(aStorage)) {
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
+    if (NS_FAILED(mLoadResult)) {
+      return mLoadResult;
+    }
+  }
+
+  IndexFinderData data(aIndex, aRetval);
+  DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data);
+  return NS_OK;
+}
+
+namespace { // anon
+
+static PLDHashOperator
+KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg)
+{
+  nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
+
+  keys->AppendElement(aKey);
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+nsTArray<nsString>*
+DOMStorageCache::GetKeys(const DOMStorage* aStorage)
+{
+  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);
+  }
+
+  return result;
+}
+
+nsresult
+DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
+                         nsAString& aRetval)
+{
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
+
+  if (Persist(aStorage)) {
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
+    if (NS_FAILED(mLoadResult)) {
+      return mLoadResult;
+    }
+  }
+
+  // not using AutoString since we don't want to copy buffer to result
+  nsString value;
+  if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
+    SetDOMStringToNull(value);
+  }
+
+  aRetval = value;
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
+                         const nsString& aValue, nsString& aOld)
+{
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SETVALUE_MS> autoTimer;
+
+  if (Persist(aStorage)) {
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
+    if (NS_FAILED(mLoadResult)) {
+      return mLoadResult;
+    }
+  }
+
+  Data& data = DataSet(aStorage);
+  if (!data.mKeys.Get(aKey, &aOld)) {
+    SetDOMStringToNull(aOld);
+  }
+
+  // Check the quota first
+  const int64_t delta = static_cast<int64_t>(aValue.Length()) -
+                        static_cast<int64_t>(aOld.Length());
+  if (!ProcessUsageDelta(aStorage, delta)) {
+    return NS_ERROR_DOM_QUOTA_REACHED;
+  }
+
+  if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
+    return NS_SUCCESS_DOM_NO_OPERATION;
+  }
+
+  data.mKeys.Put(aKey, aValue);
+
+  if (Persist(aStorage)) {
+    if (DOMStringIsNull(aOld)) {
+      return sDatabase->AsyncAddItem(this, aKey, aValue);
+    }
+
+    return sDatabase->AsyncUpdateItem(this, aKey, aValue);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
+                            nsString& aOld)
+{
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer;
+
+  if (Persist(aStorage)) {
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
+    if (NS_FAILED(mLoadResult)) {
+      return mLoadResult;
+    }
+  }
+
+  Data& data = DataSet(aStorage);
+  if (!data.mKeys.Get(aKey, &aOld)) {
+    SetDOMStringToNull(aOld);
+    return NS_SUCCESS_DOM_NO_OPERATION;
+  }
+
+  // Recalculate the cached data size
+  const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
+  unused << ProcessUsageDelta(aStorage, delta);
+  data.mKeys.Remove(aKey);
+
+  if (Persist(aStorage)) {
+    return sDatabase->AsyncRemoveItem(this, aKey);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageCache::Clear(const DOMStorage* aStorage)
+{
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer;
+
+  bool refresh = false;
+  if (Persist(aStorage)) {
+    // We need to preload all data (know the size) before we can proceeed
+    // to correctly decrease cached usage number.
+    // XXX as in case of unload, this is not technically needed now, but
+    // after super-scope quota introduction we have to do this.  Get telemetry
+    // right now.
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
+    if (NS_FAILED(mLoadResult)) {
+      // When we failed to load data from the database, force delete of the
+      // scope data and make use of the storage possible again.
+      refresh = true;
+      mLoadResult = NS_OK;
+    }
+  }
+
+  Data& data = DataSet(aStorage);
+  bool hadData = !!data.mKeys.Count();
+
+  if (hadData) {
+    unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
+    data.mKeys.Clear();
+  }
+
+  if (Persist(aStorage) && (refresh || hadData)) {
+    return sDatabase->AsyncClear(this);
+  }
+
+  return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
+}
+
+void
+DOMStorageCache::CloneFrom(const DOMStorageCache* aThat)
+{
+  mLoaded = aThat->mLoaded;
+  mInitialized = aThat->mInitialized;
+  mPersistent = aThat->mPersistent;
+  mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive;
+
+  for (uint32_t i = 0; i < kDataSetCount; ++i) {
+    aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]);
+    ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
+  }
+}
+
+// Defined in DOMStorageManager.cpp
+extern bool
+PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
+
+bool
+DOMStorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
+{
+  return PrincipalsEqual(mPrincipal, aPrincipal);
+}
+
+void
+DOMStorageCache::UnloadItems(uint32_t aUnloadFlags)
+{
+  if (aUnloadFlags & kUnloadDefault) {
+    // Must wait for preload to pass correct usage to ProcessUsageDelta
+    // XXX this is not technically needed right now since there is just
+    // per-origin isolated quota handling, but when we introduce super-
+    // -scope quotas, we have to do this.  Better to start getting
+    // telemetry right now.
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
+
+    mData[kDefaultSet].mKeys.Clear();
+    ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
+  }
+
+  if (aUnloadFlags & kUnloadPrivate) {
+    mData[kPrivateSet].mKeys.Clear();
+    ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
+  }
+
+  if (aUnloadFlags & kUnloadSession) {
+    mData[kSessionSet].mKeys.Clear();
+    ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
+    mSessionOnlyDataSetActive = false;
+  }
+
+#ifdef DOM_STORAGE_TESTS
+  if (aUnloadFlags & kTestReload) {
+    WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
+
+    mData[kDefaultSet].mKeys.Clear();
+    mLoaded = false; // This is only used in testing code
+    Preload();
+  }
+#endif
+}
+
+// DOMStorageCacheBridge
+
+uint32_t
+DOMStorageCache::LoadedCount()
+{
+  MonitorAutoLock monitor(mMonitor);
+  Data& data = mData[kDefaultSet];
+  return data.mKeys.Count();
+}
+
+bool
+DOMStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
+{
+  MonitorAutoLock monitor(mMonitor);
+  if (mLoaded) {
+    return false;
+  }
+
+  Data& data = mData[kDefaultSet];
+  if (data.mKeys.Get(aKey, nullptr)) {
+    return true; // don't stop, just don't override
+  }
+
+  data.mKeys.Put(aKey, aValue);
+  data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
+  return true;
+}
+
+void
+DOMStorageCache::LoadDone(nsresult aRv)
+{
+  // Keep the preloaded cache alive for a time
+  KeepAlive();
+
+  MonitorAutoLock monitor(mMonitor);
+  mLoadResult = aRv;
+  mLoaded = true;
+  monitor.Notify();
+}
+
+void
+DOMStorageCache::LoadWait()
+{
+  MonitorAutoLock monitor(mMonitor);
+  while (!mLoaded) {
+    monitor.Wait();
+  }
+}
+
+// DOMStorageUsage
+
+DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
+  : mScope(aScope)
+{
+  mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
+}
+
+namespace { // anon
+
+class LoadUsageRunnable : public nsRunnable
+{
+public:
+  LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
+    : mTarget(aUsage)
+    , mDelta(aDelta)
+  {}
+
+private:
+  int64_t* mTarget;
+  int64_t mDelta;
+
+  NS_IMETHOD Run() { *mTarget = mDelta; return NS_OK; }
+};
+
+} // anon
+
+void
+DOMStorageUsage::LoadUsage(const int64_t aUsage)
+{
+  // Using kDefaultSet index since it is the index for the persitent data
+  // stored in the database we have just loaded usage for.
+  if (!NS_IsMainThread()) {
+    // In single process scenario we get this call from the DB thread
+    nsRefPtr<LoadUsageRunnable> r =
+      new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
+    NS_DispatchToMainThread(r);
+  } else {
+    // On a child process we get this on the main thread already
+    mUsage[kDefaultSet] += aUsage;
+  }
+}
+
+bool
+DOMStorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, const int64_t aDelta)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
+  if (aDelta > 0 && newUsage > DOMStorageManager::GetQuota()) {
+    return false;
+  }
+
+  mUsage[aDataSetIndex] = newUsage;
+  return true;
+}
+
+
+// static
+DOMStorageDBBridge*
+DOMStorageCache::StartDatabase()
+{
+  if (sDatabase) {
+    return sDatabase;
+  }
+
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    nsAutoPtr<DOMStorageDBThread> db(new DOMStorageDBThread());
+
+    nsresult rv = db->Init();
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
+
+    sDatabase = db.forget();
+  } else {
+    nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
+        DOMLocalStorageManager::Self());
+
+    nsresult rv = db->Init();
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
+
+    db.forget(&sDatabase);
+  }
+
+  return sDatabase;
+}
+
+// static
+DOMStorageDBBridge*
+DOMStorageCache::GetDatabase()
+{
+  return sDatabase;
+}
+
+// static
+nsresult
+DOMStorageCache::StopDatabase()
+{
+  if (!sDatabase) {
+    return NS_OK;
+  }
+
+  nsresult rv = sDatabase->Shutdown();
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    delete sDatabase;
+  } else {
+    DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(sDatabase);
+    NS_RELEASE(child);
+  }
+
+  sDatabase = nullptr;
+  return rv;
+}
+
+} // ::dom
+} // ::mozilla
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageCache.h
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef nsDOMStorageCache_h___
+#define nsDOMStorageCache_h___
+
+#include "nsIPrincipal.h"
+#include "nsITimer.h"
+
+#include "nsString.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Telemetry.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class DOMStorage;
+class DOMStorageManager;
+class DOMStorageDBBridge;
+
+// Interface class on which only the database or IPC may call.
+// Used to populate the cache with DB data.
+class DOMStorageCacheBridge
+{
+public:
+  NS_IMETHOD_(nsrefcnt) AddRef(void);
+  NS_IMETHOD_(void) Release(void);
+
+  virtual ~DOMStorageCacheBridge() {}
+
+  // The scope (origin) in the database usage format (reversed)
+  virtual const nsCString& Scope() const = 0;
+
+  // Whether the cache is already fully loaded
+  virtual bool Loaded() = 0;
+
+  // How many items has so far been loaded into the cache, used
+  // for optimization purposes
+  virtual uint32_t LoadedCount() = 0;
+
+  // Called by the database to load a key and its value to the cache
+  virtual bool LoadItem(const nsAString& aKey, const nsString& aValue) = 0;
+
+  // Called by the database after all keys and values has been loaded
+  // to this cache
+  virtual void LoadDone(nsresult aRv) = 0;
+
+  // Use to synchronously wait until the cache gets fully loaded with data,
+  // this method exits after LoadDone has been called
+  virtual void LoadWait() = 0;
+
+protected:
+  nsAutoRefCnt mRefCnt;
+};
+
+// Implementation of scope cache that is responsible for preloading data
+// for persistent storage (localStorage) and hold data for non-private,
+// private and session-only cookie modes.  It is also responsible for
+// persisting data changes using the database, works as a write-back cache.
+class DOMStorageCache : public DOMStorageCacheBridge
+{
+public:
+  NS_IMETHOD_(void) Release(void);
+
+  DOMStorageCache(const nsACString* aScope);
+  virtual ~DOMStorageCache();
+
+  void Init(DOMStorageManager* aManager, bool aPersistent, nsIPrincipal* aPrincipal,
+            const nsACString& aQuotaScope);
+
+  // Copies all data from the other storage.
+  void CloneFrom(const DOMStorageCache* aThat);
+
+  // Starts async preload of this cache if it persistent and not loaded.
+  void Preload();
+
+  // Keeps the cache alive (i.e. present in the manager's hash table) for a time.
+  void KeepAlive();
+
+  // The set of methods that are invoked by DOM storage web API.
+  // We are passing the DOMStorage object just to let the cache
+  // read properties like mPrivate and mSessionOnly.
+  // 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);
+
+  // 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();
+
+  // Stops the thread and flushes all uncommited data
+  static nsresult StopDatabase();
+
+  // DOMStorageCacheBridge
+
+  virtual const nsCString& Scope() const { return mScope; }
+  virtual bool Loaded() { return mLoaded; }
+  virtual uint32_t LoadedCount();
+  virtual bool LoadItem(const nsAString& aKey, const nsString& aValue);
+  virtual void LoadDone(nsresult aRv);
+  virtual void LoadWait();
+
+  // Cache keeps 3 sets of data: regular, private and session-only.
+  // This class keeps keys and values for a set and also caches
+  // size of the data for quick per-origin quota checking.
+  class Data
+  {
+  public:
+    Data() : mOriginQuotaUsage(0) { mKeys.Init(); }
+    int64_t mOriginQuotaUsage;
+    nsDataHashtable<nsStringHashKey, nsString> mKeys;
+  };
+
+public:
+  // Number of data sets we keep: default, private, session
+  static const uint32_t kDataSetCount = 3;
+
+private:
+  // API to clear the cache data, this is invoked by chrome operations
+  // like cookie deletion.
+  friend class DOMStorageManager;
+
+  static const uint32_t kUnloadDefault = 1 << 0;
+  static const uint32_t kUnloadPrivate = 1 << 1;
+  static const uint32_t kUnloadSession = 1 << 2;
+  static const uint32_t kUnloadComplete =
+    kUnloadDefault | kUnloadPrivate | kUnloadSession;
+
+#ifdef DOM_STORAGE_TESTS
+  static const uint32_t kTestReload    = 1 << 15;
+#endif
+
+  void UnloadItems(uint32_t aUnloadFlags);
+
+private:
+  // Synchronously blocks until the cache is fully loaded from the database
+  void WaitForPreload(mozilla::Telemetry::ID aTelemetryID);
+
+  // Helper to get one of the 3 data sets (regular, private, session)
+  Data& DataSet(const DOMStorage* aStorage);
+
+  // Whether the storage change is about to persist
+  bool Persist(const DOMStorage* aStorage) const;
+
+  // Changes the quota usage on the given data set if it fits the quota.
+  // If not, then false is returned and no change to the set must be done.
+  bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
+  bool ProcessUsageDelta(const DOMStorage* aStorage, const int64_t aDelta);
+
+private:
+  // When a cache is reponsible for its life time (in case of localStorage data
+  // cache) we need to refer our manager since removal of the cache from the hash
+  // table is handled in the destructor by call to the manager.
+  // Cache could potentially overlive the manager, hence the hard ref.
+  nsRefPtr<DOMStorageManager> mManager;
+
+  // Timer that holds this cache alive for a while after it has been preloaded.
+  nsCOMPtr<nsITimer> mKeepAliveTimer;
+
+  // Principal the cache has been initially created for, this is used only
+  // for sessionStorage access checks since sessionStorage objects are strictly
+  // scoped by a principal.  localStorage objects on the other hand are scoped by
+  // origin only.
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
+  // The scope this cache belongs to in the "DB format", i.e. reversed
+  nsCString mScope;
+
+  // The eTLD+1 scope used to count quota usage.
+  nsCString mQuotaScope;
+
+  // Non-private Browsing, Private Browsing and Session Only sets.
+  Data mData[kDataSetCount];
+
+  // This monitor is used to wait for full load of data.
+  mozilla::Monitor mMonitor;
+
+  // Flag that is initially false.  When the cache is about to work with
+  // the database (i.e. it is persistent) this flags is set to true after
+  // all keys and coresponding values are loaded from the database.
+  // This flag never goes from true back to false.
+  bool mLoaded;
+
+  // Result of load from the database.  Valid after mLoaded flag has been set.
+  nsresult mLoadResult;
+
+  // Init() method has been called
+  bool mInitialized : 1;
+
+  // This cache is about to be bound with the database (i.e. it has
+  // to load from the DB first and has to persist when modifying the
+  // default data set.)
+  bool mPersistent : 1;
+
+  // - False when the session-only data set was never used.
+  // - True after access to session-only data has been made for the first time.
+  // We also fill session-only data set with the default one at that moment.
+  // Drops back to false when session-only data are cleared from chrome.
+  bool mSessionOnlyDataSetActive : 1;
+
+  // Whether we have already captured state of the cache preload on our first access.
+  bool mPreloadTelemetryRecorded : 1;
+
+  // DOMStorageDBThread on the parent or single process,
+  // DOMStorageDBChild on the child process.
+  static DOMStorageDBBridge* sDatabase;
+};
+
+// DOMStorageUsage
+// Infrastructure to manage and check eTLD+1 quota
+class DOMStorageUsageBridge
+{
+public:
+  virtual ~DOMStorageUsageBridge() {}
+
+  virtual const nsCString& Scope() = 0;
+  virtual void LoadUsage(const int64_t aUsage) = 0;
+};
+
+class DOMStorageUsage : public DOMStorageUsageBridge
+{
+public:
+  DOMStorageUsage(const nsACString& aScope);
+
+  bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
+
+private:
+  virtual const nsCString& Scope() { return mScope; }
+  virtual void LoadUsage(const int64_t aUsage);
+
+  nsCString mScope;
+  int64_t mUsage[DOMStorageCache::kDataSetCount];
+};
+
+} // ::dom
+} // ::mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageDBThread.cpp
@@ -0,0 +1,1296 @@
+/* -*- Mode: C++; 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 "DOMStorageDBThread.h"
+#include "DOMStorageCache.h"
+
+#include "nsIEffectiveTLDService.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+#include "mozStorageCID.h"
+#include "mozStorageHelper.h"
+#include "mozIStorageService.h"
+#include "mozIStorageBindingParamsArray.h"
+#include "mozIStorageBindingParams.h"
+#include "mozIStorageValueArray.h"
+#include "mozIStorageFunction.h"
+#include "nsIObserverService.h"
+#include "nsIVariant.h"
+#include "mozilla/Services.h"
+
+// How long we collect write oprerations
+// before they are flushed to the database
+// In milliseconds.
+#define FLUSHING_INTERVAL_MS 5000
+
+namespace mozilla {
+namespace dom {
+
+DOMStorageDBBridge::DOMStorageDBBridge()
+{
+  mUsages.Init();
+}
+
+DOMStorageUsage*
+DOMStorageDBBridge::GetScopeUsage(const nsACString& aScope)
+{
+  DOMStorageUsage* usage;
+  if (mUsages.Get(aScope, &usage)) {
+    return usage;
+  }
+
+  usage = new DOMStorageUsage(aScope);
+  AsyncGetUsage(usage);
+  mUsages.Put(aScope, usage);
+
+  return usage;
+}
+
+
+DOMStorageDBThread::DOMStorageDBThread()
+: mThread(nullptr)
+, mMonitor("DOMStorageThreadMonitor")
+, mStopIOThread(false)
+, mDBReady(false)
+, mStatus(NS_OK)
+, mWorkerStatements(mWorkerConnection)
+, mReaderStatements(mReaderConnection)
+, mDirtyEpoch(0)
+, mFlushImmediately(false)
+, mPriorityCounter(0)
+{
+  mScopesHavingData.Init();
+}
+
+nsresult
+DOMStorageDBThread::Init()
+{
+  nsresult rv;
+
+  // Need to determine location on the main thread, since
+  // NS_GetSpecialDirectory access the atom table that can
+  // be accessed only on the main thread.
+  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                              getter_AddRefs(mDatabaseFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mDatabaseFile->Append(NS_LITERAL_STRING("webappsstore.sqlite"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Ensure mozIStorageService init on the main thread first.
+  nsCOMPtr<mozIStorageService> service =
+    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Need to keep the lock to avoid setting mThread later then
+  // the thread body executes.
+  MonitorAutoLock monitor(mMonitor);
+
+  mThread = PR_CreateThread(PR_USER_THREAD, &DOMStorageDBThread::ThreadFunc, this,
+                            PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD,
+                            262144);
+  if (!mThread) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBThread::Shutdown()
+{
+  if (!mThread) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS> timer;
+
+  {
+    MonitorAutoLock monitor(mMonitor);
+
+    // After we stop, no other operations can be accepted
+    mFlushImmediately = true;
+    mStopIOThread = true;
+    monitor.Notify();
+  }
+
+  mReaderStatements.FinalizeStatements();
+  if (mReaderConnection) {
+    mReaderConnection->Close();
+    mReaderConnection = nullptr;
+  }
+
+  PR_JoinThread(mThread);
+  mThread = nullptr;
+
+  return mStatus;
+}
+
+void
+DOMStorageDBThread::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
+{
+  if (!aForceSync && aCache->LoadedCount()) {
+    // Preload already started for this cache, just wait for it to finish.
+    // LoadWait will exit after LoadDone on the cache has been called.
+    SetHigherPriority();
+    aCache->LoadWait();
+    SetDefaultPriority();
+    return;
+  }
+
+  // Bypass sync load when an update is pending in the queue to write, we would
+  // get incosistent data in the cache.  Also don't allow sync main-thread preload
+  // when DB open and init is still pending on the background thread.
+  if (mWALModeEnabled && mDBReady) {
+    bool pendingTasks;
+    {
+      MonitorAutoLock monitor(mMonitor);
+      pendingTasks = mPendingTasks.IsScopeUpdatePending(aCache->Scope()) ||
+                     mPendingTasks.IsScopeClearPending(aCache->Scope());
+    }
+
+    if (!pendingTasks) {
+      // WAL is enabled, thus do the load synchronously on the main thread.
+      DBOperation preload(DBOperation::opPreload, aCache);
+      preload.PerformAndFinalize(this);
+      return;
+    }
+  }
+
+  // Need to go asynchronously since WAL is not allowed or scheduled updates
+  // need to be flushed first.
+  // Schedule preload for this cache as the first operation.
+  nsresult rv = InsertDBOp(new DBOperation(DBOperation::opPreloadUrgent, aCache));
+
+  // LoadWait exits after LoadDone of the cache has been called.
+  if (NS_SUCCEEDED(rv)) {
+    aCache->LoadWait();
+  }
+}
+
+void
+DOMStorageDBThread::AsyncFlush()
+{
+  MonitorAutoLock monitor(mMonitor);
+  mFlushImmediately = true;
+  monitor.Notify();
+}
+
+bool
+DOMStorageDBThread::ShouldPreloadScope(const nsACString& aScope)
+{
+  MonitorAutoLock monitor(mMonitor);
+  return mScopesHavingData.Contains(aScope);
+}
+
+namespace { // anon
+
+PLDHashOperator
+GetScopesHavingDataEnum(nsCStringHashKey* aKey, void* aArg)
+{
+  InfallibleTArray<nsCString>* scopes =
+      static_cast<InfallibleTArray<nsCString>*>(aArg);
+  scopes->AppendElement(aKey->GetKey());
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+void
+DOMStorageDBThread::GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
+{
+  MonitorAutoLock monitor(mMonitor);
+  mScopesHavingData.EnumerateEntries(GetScopesHavingDataEnum, aScopes);
+}
+
+nsresult
+DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
+{
+  MonitorAutoLock monitor(mMonitor);
+
+  // Sentinel to don't forget to delete the operation when we exit early.
+  nsAutoPtr<DOMStorageDBThread::DBOperation> opScope(aOperation);
+
+  if (mStopIOThread) {
+    // Thread use after shutdown demanded.
+    MOZ_ASSERT(false);
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (NS_FAILED(mStatus)) {
+    MonitorAutoUnlock unlock(mMonitor);
+    aOperation->Finalize(mStatus);
+    return mStatus;
+  }
+
+  switch (aOperation->Type()) {
+  case DBOperation::opPreload:
+  case DBOperation::opPreloadUrgent:
+    if (mPendingTasks.IsScopeUpdatePending(aOperation->Scope())) {
+      // If there is a pending update operation for the scope first do the flush
+      // before we preload the cache.  This may happen in an extremely rare case
+      // when a child process throws away its cache before flush on the parent
+      // has finished.  If we would preloaded the cache as a priority operation 
+      // before the pending flush, we would have got an inconsistent cache content.
+      mFlushImmediately = true;
+    } else if (mPendingTasks.IsScopeClearPending(aOperation->Scope())) {
+      // The scope is scheduled to be cleared, so just quickly load as empty.
+      // We need to do this to prevent load of the DB data before the scope has
+      // actually been cleared from the database.  Preloads are processed
+      // immediately before update and clear operations on the database that
+      // are flushed periodically in batches.
+      MonitorAutoUnlock unlock(mMonitor);
+      aOperation->Finalize(NS_OK);
+      return NS_OK;
+    }
+    // NO BREAK
+
+  case DBOperation::opGetUsage:
+    if (aOperation->Type() == DBOperation::opPreloadUrgent) {
+      SetHigherPriority(); // Dropped back after urgent preload execution
+      mPreloads.InsertElementAt(0, aOperation);
+    } else {
+      mPreloads.AppendElement(aOperation);
+    }
+
+    // DB operation adopted, don't delete it.
+    opScope.forget();
+
+    // Immediately start executing this.
+    monitor.Notify();
+    break;
+
+  default:
+    // Update operations are first collected, coalesced and then flushed
+    // after a short time.
+    mPendingTasks.Add(aOperation);
+
+    // DB operation adopted, don't delete it.
+    opScope.forget();
+
+    ScheduleFlush();
+    break;
+  }
+
+  return NS_OK;
+}
+
+void
+DOMStorageDBThread::SetHigherPriority()
+{
+  ++mPriorityCounter;
+  PR_SetThreadPriority(mThread, PR_PRIORITY_URGENT);
+}
+
+void
+DOMStorageDBThread::SetDefaultPriority()
+{
+  if (--mPriorityCounter <= 0) {
+    PR_SetThreadPriority(mThread, PR_PRIORITY_LOW);
+  }
+}
+
+void
+DOMStorageDBThread::ThreadFunc(void* aArg)
+{
+  PR_SetCurrentThreadName("localStorage DB");
+
+  DOMStorageDBThread* thread = static_cast<DOMStorageDBThread*>(aArg);
+  thread->ThreadFunc();
+}
+
+void
+DOMStorageDBThread::ThreadFunc()
+{
+  nsresult rv = InitDatabase();
+
+  MonitorAutoLock lockMonitor(mMonitor);
+
+  if (NS_FAILED(rv)) {
+    mStatus = rv;
+    mStopIOThread = true;
+    return;
+  }
+
+  while (MOZ_LIKELY(!mStopIOThread || mPreloads.Length() || mPendingTasks.HasTasks())) {
+    if (MOZ_UNLIKELY(TimeUntilFlush() == 0)) {
+      // Flush time is up or flush has been forced, do it now.
+      UnscheduleFlush();
+      if (mPendingTasks.Prepare()) {
+        {
+          MonitorAutoUnlock unlockMonitor(mMonitor);
+          rv = mPendingTasks.Execute(this);
+        }
+
+        if (!mPendingTasks.Finalize(rv)) {
+          mStatus = rv;
+          NS_WARNING("localStorage DB access broken");
+        }
+      }
+      NotifyFlushCompletion();
+    } else if (MOZ_LIKELY(mPreloads.Length())) {
+      nsAutoPtr<DBOperation> op(mPreloads[0]);
+      mPreloads.RemoveElementAt(0);
+      {
+        MonitorAutoUnlock unlockMonitor(mMonitor);
+        op->PerformAndFinalize(this);
+      }
+
+      if (op->Type() == DBOperation::opPreloadUrgent) {
+        SetDefaultPriority(); // urgent preload unscheduled
+      }
+    } else if (MOZ_UNLIKELY(!mStopIOThread)) {
+      lockMonitor.Wait(TimeUntilFlush());
+    }
+  } // thread loop
+
+  mStatus = ShutdownDatabase();
+}
+
+extern void
+ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
+
+namespace { // anon
+
+class nsReverseStringSQLFunction MOZ_FINAL : public mozIStorageFunction
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+};
+
+NS_IMPL_ISUPPORTS1(nsReverseStringSQLFunction, mozIStorageFunction)
+
+NS_IMETHODIMP
+nsReverseStringSQLFunction::OnFunctionCall(
+    mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
+{
+  nsresult rv;
+
+  nsAutoCString stringToReverse;
+  rv = aFunctionArguments->GetUTF8String(0, stringToReverse);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString result;
+  ReverseString(stringToReverse, result);
+
+  nsCOMPtr<nsIWritableVariant> outVar(do_CreateInstance(
+      NS_VARIANT_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = outVar->SetAsAUTF8String(result);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aResult = outVar.get();
+  outVar.forget();
+  return NS_OK;
+}
+
+} // anon
+
+nsresult
+DOMStorageDBThread::OpenDatabaseConnection()
+{
+  nsresult rv;
+
+  nsCOMPtr<mozIStorageService> service
+      = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<mozIStorageConnection> connection;
+  rv = service->OpenUnsharedDatabase(mDatabaseFile, getter_AddRefs(connection));
+  if (rv == NS_ERROR_FILE_CORRUPTED) {
+    // delete the db and try opening again
+    rv = mDatabaseFile->Remove(false);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = service->OpenUnsharedDatabase(mDatabaseFile, getter_AddRefs(connection));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (NS_IsMainThread()) {
+    connection.swap(mReaderConnection);
+  } else {
+    connection.swap(mWorkerConnection);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBThread::InitDatabase()
+{
+  Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_INIT_DATABASE_MS> timer;
+
+  nsresult rv;
+
+  // Here we are on the worker thread. This opens the worker connection.
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  rv = OpenDatabaseConnection();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = TryJournalMode();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mozStorageTransaction transaction(mWorkerConnection, false);
+
+  // Ensure Gecko 1.9.1 storage table
+  rv = mWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+         "CREATE TABLE IF NOT EXISTS webappsstore2 ("
+         "scope TEXT, "
+         "key TEXT, "
+         "value TEXT, "
+         "secure INTEGER, "
+         "owner TEXT)"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+        "CREATE UNIQUE INDEX IF NOT EXISTS scope_key_index"
+        " ON webappsstore2(scope, key)"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<mozIStorageFunction> function1(new nsReverseStringSQLFunction());
+  NS_ENSURE_TRUE(function1, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = mWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("REVERSESTRING"), 1, function1);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool exists;
+
+  // Check if there is storage of Gecko 1.9.0 and if so, upgrade that storage
+  // to actual webappsstore2 table and drop the obsolete table. First process
+  // this newer table upgrade to priority potential duplicates from older
+  // storage table.
+  rv = mWorkerConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"),
+                                &exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (exists) {
+    rv = mWorkerConnection->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
+                         "webappsstore2(scope, key, value, secure, owner) "
+                         "SELECT REVERSESTRING(domain) || '.:', key, value, secure, owner "
+                         "FROM webappsstore"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mWorkerConnection->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("DROP TABLE webappsstore"));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Check if there is storage of Gecko 1.8 and if so, upgrade that storage
+  // to actual webappsstore2 table and drop the obsolete table. Potential
+  // duplicates will be ignored.
+  rv = mWorkerConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"),
+                                &exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (exists) {
+    rv = mWorkerConnection->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
+                         "webappsstore2(scope, key, value, secure, owner) "
+                         "SELECT REVERSESTRING(domain) || '.:', key, value, secure, domain "
+                         "FROM moz_webappsstore"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mWorkerConnection->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = transaction.Commit();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Database open and all initiation operation are done.  Switching this flag
+  // to true allow main thread to read directly from the database.
+  // If we would allow this sooner, we would have opened a window where main thread
+  // read might operate on a totaly broken and incosistent database.
+  mDBReady = true;
+
+  // List scopes having any stored data
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = mWorkerConnection->CreateStatement(NS_LITERAL_CSTRING("SELECT DISTINCT scope FROM webappsstore2"),
+                                    getter_AddRefs(stmt));
+  NS_ENSURE_SUCCESS(rv, rv);
+  mozStorageStatementScoper scope(stmt);
+
+  while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
+    nsAutoCString foundScope;
+    rv = stmt->GetUTF8String(0, foundScope);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    MonitorAutoLock monitor(mMonitor);
+    mScopesHavingData.PutEntry(foundScope);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBThread::SetJournalMode(bool aIsWal)
+{
+  nsresult rv;
+
+  nsAutoCString stmtString(
+    MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = ");
+  if (aIsWal) {
+    stmtString.AppendLiteral("wal");
+  } else {
+    stmtString.AppendLiteral("truncate");
+  }
+
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = mWorkerConnection->CreateStatement(stmtString, getter_AddRefs(stmt));
+  NS_ENSURE_SUCCESS(rv, rv);
+  mozStorageStatementScoper scope(stmt);
+
+  bool hasResult = false;
+  rv = stmt->ExecuteStep(&hasResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!hasResult) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoCString journalMode;
+  rv = stmt->GetUTF8String(0, journalMode);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if ((aIsWal && !journalMode.EqualsLiteral("wal")) ||
+      (!aIsWal && !journalMode.EqualsLiteral("truncate"))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBThread::TryJournalMode()
+{
+  nsresult rv;
+
+  rv = SetJournalMode(true);
+  if (NS_FAILED(rv)) {
+    mWALModeEnabled = false;
+
+    rv = SetJournalMode(false);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    mWALModeEnabled = true;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBThread::ShutdownDatabase()
+{
+  nsresult rv = mStatus;
+
+  // Finalize the cached statements.
+  mWorkerStatements.FinalizeStatements();
+  if (mWorkerConnection) {
+    rv = mWorkerConnection->Close();
+    mWorkerConnection = nullptr;
+  }
+
+  return rv;
+}
+
+void
+DOMStorageDBThread::ScheduleFlush()
+{
+  if (mDirtyEpoch) {
+    return; // Already scheduled
+  }
+
+  mDirtyEpoch = PR_IntervalNow() | 1; // Must be non-zero to indicate we are scheduled
+
+  // Wake the monitor from indefinite sleep...
+  mMonitor.Notify();
+}
+
+void
+DOMStorageDBThread::UnscheduleFlush()
+{
+  // We are just about to do the flush, drop flags
+  mFlushImmediately = false;
+  mDirtyEpoch = 0;
+}
+
+PRIntervalTime
+DOMStorageDBThread::TimeUntilFlush()
+{
+  if (mFlushImmediately) {
+    return 0; // Do it now regardless the timeout.
+  }
+
+  MOZ_STATIC_ASSERT(PR_INTERVAL_NO_TIMEOUT != 0,
+      "PR_INTERVAL_NO_TIMEOUT must be non-zero");
+
+  if (!mDirtyEpoch) {
+    return PR_INTERVAL_NO_TIMEOUT; // No pending task...
+  }
+
+  static const PRIntervalTime kMaxAge = PR_MillisecondsToInterval(FLUSHING_INTERVAL_MS);
+
+  PRIntervalTime now = PR_IntervalNow() | 1;
+  PRIntervalTime age = now - mDirtyEpoch;
+  if (age > kMaxAge) {
+    return 0; // It is time.
+  }
+
+  return kMaxAge - age; // Time left, this is used to sleep the monitor
+}
+
+void
+DOMStorageDBThread::NotifyFlushCompletion()
+{
+#ifdef DOM_STORAGE_TESTS
+  if (!NS_IsMainThread()) {
+    nsRefPtr<nsRunnableMethod<DOMStorageDBThread, void, false> > event =
+      NS_NewNonOwningRunnableMethod(this, &DOMStorageDBThread::NotifyFlushCompletion);
+    NS_DispatchToMainThread(event);
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
+  }
+#endif
+}
+
+// DOMStorageDBThread::DBOperation
+
+DOMStorageDBThread::DBOperation::DBOperation(const OperationType aType,
+                                             DOMStorageCacheBridge* aCache,
+                                             const nsAString& aKey,
+                                             const nsAString& aValue)
+: mType(aType)
+, mCache(aCache)
+, mKey(aKey)
+, mValue(aValue)
+{
+  MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
+}
+
+DOMStorageDBThread::DBOperation::DBOperation(const OperationType aType,
+                                             DOMStorageUsageBridge* aUsage)
+: mType(aType)
+, mUsage(aUsage)
+{
+  MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
+}
+
+DOMStorageDBThread::DBOperation::DBOperation(const OperationType aType,
+                                             const nsACString& aScope)
+: mType(aType)
+, mCache(nullptr)
+, mScope(aScope)
+{
+  MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
+}
+
+DOMStorageDBThread::DBOperation::~DBOperation()
+{
+  MOZ_COUNT_DTOR(DOMStorageDBThread::DBOperation);
+}
+
+const nsCString
+DOMStorageDBThread::DBOperation::Scope()
+{
+  if (mCache) {
+    return mCache->Scope();
+  }
+
+  return mScope;
+}
+
+const nsCString
+DOMStorageDBThread::DBOperation::Target()
+{
+  switch (mType) {
+    case opAddItem:
+    case opUpdateItem:
+    case opRemoveItem:
+      return Scope() + NS_LITERAL_CSTRING("|") + NS_ConvertUTF16toUTF8(mKey);
+
+    default:
+      return Scope();
+  }
+}
+
+void
+DOMStorageDBThread::DBOperation::PerformAndFinalize(DOMStorageDBThread* aThread)
+{
+  Finalize(Perform(aThread));
+}
+
+nsresult
+DOMStorageDBThread::DBOperation::Perform(DOMStorageDBThread* aThread)
+{
+  nsresult rv;
+
+  switch (mType) {
+  case opPreload:
+  case opPreloadUrgent:
+  {
+    // Already loaded?
+    if (mCache->Loaded()) {
+      break;
+    }
+
+    StatementCache* statements;
+    if (MOZ_UNLIKELY(NS_IsMainThread())) {
+      if (MOZ_UNLIKELY(!aThread->mReaderConnection)) {
+        // Do lazy main thread connection opening
+        rv = aThread->OpenDatabaseConnection();
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      statements = &aThread->mReaderStatements;
+    } else {
+      statements = &aThread->mWorkerStatements;
+    }
+
+    // OFFSET is an optimization when we have to do a sync load
+    // and cache has already loaded some parts asynchronously.
+    // It skips keys we have already loaded.
+    nsCOMPtr<mozIStorageStatement> stmt = statements->GetCachedStatement(
+        "SELECT key, value FROM webappsstore2 "
+        "WHERE scope = :scope ORDER BY key "
+        "LIMIT -1 OFFSET :offset");
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
+                                    mCache->Scope());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("offset"),
+                               static_cast<int32_t>(mCache->LoadedCount()));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool exists;
+    while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
+      nsAutoString key;
+      rv = stmt->GetString(0, key);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsAutoString value;
+      rv = stmt->GetString(1, value);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (!mCache->LoadItem(key, value)) {
+        break;
+      }
+    }
+
+    mCache->LoadDone(NS_OK);
+    break;
+  }
+
+  case opGetUsage:
+  {
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "SELECT SUM(LENGTH(key) + LENGTH(value)) FROM webappsstore2"
+      " WHERE scope LIKE :scope"
+    );
+    NS_ENSURE_STATE(stmt);
+
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
+                                    mUsage->Scope() + NS_LITERAL_CSTRING("%"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool exists;
+    rv = stmt->ExecuteStep(&exists);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    int64_t usage;
+    if (exists) {
+      rv = stmt->GetInt64(0, &usage);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    mUsage->LoadUsage(usage);
+    break;
+  }
+
+  case opAddItem:
+  case opUpdateItem:
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "INSERT OR REPLACE INTO webappsstore2 (scope, key, value) "
+      "VALUES (:scope, :key, :value) "
+    );
+    NS_ENSURE_STATE(stmt);
+
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
+                                    mCache->Scope());
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
+                                mKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindStringByName(NS_LITERAL_CSTRING("value"),
+                                mValue);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aThread->mScopesHavingData.PutEntry(Scope());
+    break;
+  }
+
+  case opRemoveItem:
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "DELETE FROM webappsstore2 "
+      "WHERE scope = :scope "
+        "AND key = :key "
+    );
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
+                                    mCache->Scope());
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
+                                mKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    break;
+  }
+
+  case opClear:
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "DELETE FROM webappsstore2 "
+      "WHERE scope = :scope"
+    );
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
+                                    mCache->Scope());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aThread->mScopesHavingData.RemoveEntry(Scope());
+    break;
+  }
+
+  case opClearAll:
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "DELETE FROM webappsstore2"
+    );
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aThread->mScopesHavingData.Clear();
+    break;
+  }
+
+  case opClearMatchingScope:
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "DELETE FROM webappsstore2"
+      " WHERE scope GLOB :scope"
+    );
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scope(stmt);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
+                                    mScope + NS_LITERAL_CSTRING("*"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    break;
+  }
+
+  default:
+    NS_ERROR("Unknown task type");
+    break;
+  }
+
+  return NS_OK;
+}
+
+void
+DOMStorageDBThread::DBOperation::Finalize(nsresult aRv)
+{
+  switch (mType) {
+  case opPreloadUrgent:
+  case opPreload:
+    if (NS_FAILED(aRv)) {
+      // When we are here, something failed when loading from the database.
+      // Notify that the storage is loaded to prevent deadlock of the main thread,
+      // even though it is actually empty or incomplete.
+      NS_WARNING("Failed to preload localStorage");
+    }
+
+    mCache->LoadDone(aRv);
+    break;
+
+  case opGetUsage:
+    if (NS_FAILED(aRv)) {
+      mUsage->LoadUsage(0);
+    }
+
+    break;
+
+  default:
+    if (NS_FAILED(aRv)) {
+      NS_WARNING("localStorage update/clear operation failed,"
+                 " data may not persist or clean up");
+    }
+
+    break;
+  }
+}
+
+// DOMStorageDBThread::PendingOperations
+
+DOMStorageDBThread::PendingOperations::PendingOperations()
+: mFlushFailureCount(0)
+{
+  mClears.Init();
+  mUpdates.Init();
+}
+
+bool
+DOMStorageDBThread::PendingOperations::HasTasks()
+{
+  return !!mUpdates.Count() || !!mClears.Count();
+}
+
+namespace { // anon
+
+PLDHashOperator
+ForgetUpdatesForScope(const nsACString& aMapping,
+                      nsAutoPtr<DOMStorageDBThread::DBOperation>& aPendingTask,
+                      void* aArg)
+{
+  DOMStorageDBThread::DBOperation* newOp = static_cast<DOMStorageDBThread::DBOperation*>(aArg);
+
+  if (newOp->Type() == DOMStorageDBThread::DBOperation::opClear &&
+      aPendingTask->Scope() != newOp->Scope()) {
+    return PL_DHASH_NEXT;
+  }
+
+  if (newOp->Type() == DOMStorageDBThread::DBOperation::opClearMatchingScope &&
+      !StringBeginsWith(aPendingTask->Scope(), newOp->Scope())) {
+    return PL_DHASH_NEXT;
+  }
+
+  return PL_DHASH_REMOVE;
+}
+
+} // anon
+
+bool
+DOMStorageDBThread::PendingOperations::CheckForCoalesceOpportunity(DBOperation* aNewOp,
+                                                                   DBOperation::OperationType aPendingType,
+                                                                   DBOperation::OperationType aNewType)
+{
+  if (aNewOp->Type() != aNewType) {
+    return false;
+  }
+
+  DOMStorageDBThread::DBOperation* pendingTask;
+  if (!mUpdates.Get(aNewOp->Target(), &pendingTask)) {
+    return false;
+  }
+
+  if (pendingTask->Type() != aPendingType) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+DOMStorageDBThread::PendingOperations::Add(DOMStorageDBThread::DBOperation* aOperation)
+{
+  // Optimize: when a key to remove has never been written to disk
+  // just bypass this operation.  A kew is new when an operation scheduled
+  // to write it to the database is of type opAddItem.
+  if (CheckForCoalesceOpportunity(aOperation, DBOperation::opAddItem, DBOperation::opRemoveItem)) {
+    mUpdates.Remove(aOperation->Target());
+    delete aOperation;
+    return;
+  }
+
+  // Optimize: when changing a key that is new and has never been
+  // written to disk, keep type of the operation to store it at opAddItem.
+  // This allows optimization to just forget adding a new key when
+  // it is removed from the storage before flush.
+  if (CheckForCoalesceOpportunity(aOperation, DBOperation::opAddItem, DBOperation::opUpdateItem)) {
+    aOperation->mType = DBOperation::opAddItem;
+  }
+
+  // Optimize: to prevent lose of remove operation on a key when doing
+  // remove/set/remove on a previously existing key we have to change
+  // opAddItem to opUpdateItem on the new operation when there is opRemoveItem
+  // pending for the key.
+  if (CheckForCoalesceOpportunity(aOperation, DBOperation::opRemoveItem, DBOperation::opAddItem)) {
+    aOperation->mType = DBOperation::opUpdateItem;
+  }
+
+  switch (aOperation->Type())
+  {
+  // Operations on single keys
+
+  case DBOperation::opAddItem:
+  case DBOperation::opUpdateItem:
+  case DBOperation::opRemoveItem:
+    // Override any existing operation for the target (=scope+key).
+    mUpdates.Put(aOperation->Target(), aOperation);
+    break;
+
+  // Clear operations
+
+  case DBOperation::opClear:
+  case DBOperation::opClearMatchingScope:
+    // Drop all update (insert/remove) operations for equivavelent or matching scope.
+    // We do this as an optimization as well as a must based on the logic,
+    // if we would not delete the update tasks, changes would have been stored
+    // to the database after clear operations have been executed.
+    mUpdates.Enumerate(ForgetUpdatesForScope, aOperation);
+    mClears.Put(aOperation->Target(), aOperation);
+    break;
+
+  case DBOperation::opClearAll:
+    // Drop simply everything, this is a super-operation.
+    mUpdates.Clear();
+    mClears.Clear();
+    mClears.Put(aOperation->Target(), aOperation);
+    break;
+
+  default:
+    MOZ_ASSERT(false);
+    break;
+  }
+}
+
+namespace { // anon
+
+PLDHashOperator
+CollectTasks(const nsACString& aMapping, nsAutoPtr<DOMStorageDBThread::DBOperation>& aOperation, void* aArg)
+{
+  nsTArray<nsAutoPtr<DOMStorageDBThread::DBOperation> >* tasks =
+    static_cast<nsTArray<nsAutoPtr<DOMStorageDBThread::DBOperation> >*>(aArg);
+
+  tasks->AppendElement(aOperation.forget());
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+bool
+DOMStorageDBThread::PendingOperations::Prepare()
+{
+  // Called under the lock
+
+  // First collect clear operations and then updates, we can
+  // do this since whenever a clear operation for a scope is
+  // scheduled, we drop all updates matching that scope. So,
+  // all scope-related update operations we have here now were
+  // scheduled after the clear operations.
+  mClears.Enumerate(CollectTasks, &mExecList);
+  mClears.Clear();
+
+  mUpdates.Enumerate(CollectTasks, &mExecList);
+  mUpdates.Clear();
+
+  return !!mExecList.Length();
+}
+
+nsresult
+DOMStorageDBThread::PendingOperations::Execute(DOMStorageDBThread* aThread)
+{
+  // Called outside the lock
+
+  mozStorageTransaction transaction(aThread->mWorkerConnection, false);
+
+  nsresult rv;
+
+  for (uint32_t i = 0; i < mExecList.Length(); ++i) {
+    DOMStorageDBThread::DBOperation* task = mExecList[i];
+    rv = task->Perform(aThread);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  rv = transaction.Commit();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+bool
+DOMStorageDBThread::PendingOperations::Finalize(nsresult aRv)
+{
+  // Called under the lock
+
+  // The list is kept on a failure to retry it
+  if (NS_FAILED(aRv)) {
+    // XXX Followup: we may try to reopen the database and flush these
+    // pending tasks, however testing showed that even though I/O is actually
+    // broken some amount of operations is left in sqlite+system buffers and
+    // seems like successfully flushed to disk.
+    // Tested by removing a flash card and disconnecting from network while
+    // using a network drive on Windows system.
+    NS_WARNING("Flush operation on localStorage database failed");
+
+    ++mFlushFailureCount;
+
+    return mFlushFailureCount >= 5;
+  }
+
+  mFlushFailureCount = 0;
+  mExecList.Clear();
+  return true;
+}
+
+namespace { // anon
+
+class FindPendingOperationForScopeData
+{
+public:
+  FindPendingOperationForScopeData(const nsACString& aScope) : mScope(aScope), mFound(false) {}
+  nsCString mScope;
+  bool mFound;
+};
+
+PLDHashOperator
+FindPendingClearForScope(const nsACString& aMapping,
+                         DOMStorageDBThread::DBOperation* aPendingOperation,
+                         void* aArg)
+{
+  FindPendingOperationForScopeData* data =
+    static_cast<FindPendingOperationForScopeData*>(aArg);
+
+  if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearAll) {
+    data->mFound = true;
+    return PL_DHASH_STOP;
+  }
+
+  if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClear &&
+      data->mScope == aPendingOperation->Scope()) {
+    data->mFound = true;
+    return PL_DHASH_STOP;
+  }
+
+  if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearMatchingScope &&
+      StringBeginsWith(data->mScope, aPendingOperation->Scope())) {
+    data->mFound = true;
+    return PL_DHASH_STOP;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+bool
+DOMStorageDBThread::PendingOperations::IsScopeClearPending(const nsACString& aScope)
+{
+  // Called under the lock
+
+  FindPendingOperationForScopeData data(aScope);
+  mClears.EnumerateRead(FindPendingClearForScope, &data);
+  if (data.mFound) {
+    return true;
+  }
+
+  for (uint32_t i = 0; i < mExecList.Length(); ++i) {
+    DOMStorageDBThread::DBOperation* task = mExecList[i];
+    FindPendingClearForScope(EmptyCString(), task, &data);
+
+    if (data.mFound) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+namespace { // anon
+
+PLDHashOperator
+FindPendingUpdateForScope(const nsACString& aMapping,
+                          DOMStorageDBThread::DBOperation* aPendingOperation,
+                          void* aArg)
+{
+  FindPendingOperationForScopeData* data =
+    static_cast<FindPendingOperationForScopeData*>(aArg);
+
+  if ((aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opAddItem ||
+       aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opUpdateItem ||
+       aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opRemoveItem) &&
+       data->mScope == aPendingOperation->Scope()) {
+    data->mFound = true;
+    return PL_DHASH_STOP;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+bool
+DOMStorageDBThread::PendingOperations::IsScopeUpdatePending(const nsACString& aScope)
+{
+  // Called under the lock
+
+  FindPendingOperationForScopeData data(aScope);
+  mUpdates.EnumerateRead(FindPendingUpdateForScope, &data);
+  if (data.mFound) {
+    return true;
+  }
+
+  for (uint32_t i = 0; i < mExecList.Length(); ++i) {
+    DOMStorageDBThread::DBOperation* task = mExecList[i];
+    FindPendingUpdateForScope(EmptyCString(), task, &data);
+
+    if (data.mFound) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // ::dom
+} // ::mozilla
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageDBThread.h
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef DOMStorageDBThread_h___
+#define DOMStorageDBThread_h___
+
+#include "prthread.h"
+#include "prinrval.h"
+#include "nsTArray.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/storage/StatementCache.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsClassHashtable.h"
+#include "nsIFile.h"
+
+class mozIStorageConnection;
+
+namespace mozilla {
+namespace dom {
+
+class DOMStorageCacheBridge;
+class DOMStorageUsageBridge;
+class DOMStorageUsage;
+
+typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
+
+// Interface used by the cache to post operations to the asynchronous
+// database thread or process.
+class DOMStorageDBBridge
+{
+public:
+  DOMStorageDBBridge();
+  virtual ~DOMStorageDBBridge() {}
+
+  // Ensures the database engine is started
+  virtual nsresult Init() = 0;
+
+  // Releases the database and disallows its usage
+  virtual nsresult Shutdown() = 0;
+
+  // Asynchronously fills the cache with data from the database for first use.
+  // When |aPriority| is true, the preload operation is scheduled as the first one.
+  // This method is responsible to keep hard reference to the cache for the time of
+  // the preload or, when preload cannot be performed, call LoadDone() immediately.
+  virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) = 0;
+
+  // Asynchronously fill the |usage| object with actual usage of data by its scope.
+  // The scope is eTLD+1 tops, never deeper subdomains.
+  virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage) = 0;
+
+  // Synchronously fills the cache, when |aForceSync| is false and cache already got some
+  // data before, the method waits for the running preload to finish
+  virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false) = 0;
+
+  // Called when an existing key is modified in the storage, schedules update to the database
+  virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0;
+
+  // Called when an existing key is modified in the storage, schedules update to the database
+  virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0;
+
+  // Called when an item is removed from the storage, schedules delete of the key
+  virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) = 0;
+
+  // Called when the whole storage is cleared by the DOM API, schedules delete of the scope
+  virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) = 0;
+
+  // Called when chrome deletes e.g. cookies, schedules delete of the whole database
+  virtual void AsyncClearAll() = 0;
+
+  // Called when only a domain and its subdomains or an app data is about to clear
+  virtual void AsyncClearMatchingScope(const nsACString& aScope) = 0;
+
+  // Forces scheduled DB operations to be early flushed to the disk
+  virtual void AsyncFlush() = 0;
+
+  // Check whether the scope has any data stored on disk and is thus allowed to preload
+  virtual bool ShouldPreloadScope(const nsACString& aScope) = 0;
+
+  // Get the complete list of scopes having data
+  virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0;
+
+  // Returns object keeping usage cache for the scope.
+  DOMStorageUsage* GetScopeUsage(const nsACString& aScope);
+
+protected:
+  // Keeps usage cache objects for eTLD+1 scopes we have touched.
+  nsClassHashtable<nsCStringHashKey, DOMStorageUsage> mUsages;
+};
+
+// The implementation of the the database engine, this directly works
+// with the sqlite or any other db API we are based on
+// This class is resposible for collecting and processing asynchronous 
+// DB operations over caches (DOMStorageCache) communicating though 
+// DOMStorageCacheBridge interface class
+class DOMStorageDBThread MOZ_FINAL : public DOMStorageDBBridge
+{
+public:
+  class PendingOperations;
+
+  // Representation of a singe database task, like adding and removing keys,
+  // (pre)loading the whole origin data, cleaning.
+  class DBOperation
+  {
+  public:
+    typedef enum {
+      // Only operation that reads data from the database
+      opPreload,
+      // The same as opPreload, just executed with highest priority
+      opPreloadUrgent,
+
+      // Load usage of a scope
+      opGetUsage,
+
+      // Operations invoked by the DOM content API
+      opAddItem,
+      opUpdateItem,
+      opRemoveItem,
+      opClear,
+
+      // Operations invoked by chrome
+      opClearAll,
+      opClearMatchingScope,
+    } OperationType;
+
+    DBOperation(const OperationType aType,
+                DOMStorageCacheBridge* aCache = nullptr,
+                const nsAString& aKey = EmptyString(),
+                const nsAString& aValue = EmptyString());
+    DBOperation(const OperationType aType,
+                DOMStorageUsageBridge* aUsage);
+    DBOperation(const OperationType aType,
+                const nsACString& aScope);
+    ~DBOperation();
+
+    // Executes the operation, doesn't necessarity have to be called on the I/O thread
+    void PerformAndFinalize(DOMStorageDBThread* aThread);
+
+    // Finalize the operation, i.e. do any internal cleanup and finish calls
+    void Finalize(nsresult aRv);
+
+    // The operation type
+    OperationType Type() { return mType; }
+
+    // The operation scope (=origin)
+    const nsCString Scope();
+
+    // |Scope + key| the operation is working with
+    const nsCString Target();
+
+  private:
+    // The operation implementation body
+    nsresult Perform(DOMStorageDBThread* aThread);
+
+    friend class PendingOperations;
+    OperationType mType;
+    nsRefPtr<DOMStorageCacheBridge> mCache;
+    DOMStorageUsageBridge* mUsage;
+    nsString mKey;
+    nsString mValue;
+    nsCString mScope;
+  };
+
+  // Encapsulation of collective and coalescing logic for all pending operations
+  // except preloads that are handled separately as priority operations
+  class PendingOperations {
+  public:
+    PendingOperations();
+
+    // Method responsible for coalescing redundant update operations with the same
+    // |Target()| or clear operations with the same or matching |Scope()|
+    void Add(DBOperation* aOperation);
+
+    // True when there are some scheduled operations to flush on disk
+    bool HasTasks();
+
+    // Moves collected operations to a local flat list to allow execution of the operation
+    // list out of the thread lock
+    bool Prepare();
+
+    // Executes the previously |Prepared()'ed| list of operations, retuns result, but doesn't
+    // handle it in any way in case of a failure
+    nsresult Execute(DOMStorageDBThread* aThread);
+
+    // Finalizes the pending operation list, returns false when too many operations failed
+    // to flush what indicates a long standing issue with the database access.
+    bool Finalize(nsresult aRv);
+
+    // true when a clear that deletes the given |scope| is among the pending operations;
+    // when a preload for that scope is being scheduled, it must be finished right away
+    bool IsScopeClearPending(const nsACString& aScope);
+
+    // Checks whether there is a pending update (or clear, actually) operation for this scope.
+    bool IsScopeUpdatePending(const nsACString& aScope);
+
+  private:
+    // Returns true iff new operation is of type newType and there is a pending 
+    // operation of type pendingType for the same key (target).
+    bool CheckForCoalesceOpportunity(DBOperation* aNewOp,
+                                     DBOperation::OperationType aPendingType,
+                                     DBOperation::OperationType aNewType);
+
+    // List of all clearing operations, executed first
+    nsClassHashtable<nsCStringHashKey, DBOperation> mClears;
+
+    // List of all update/insert operations, executed as second
+    nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates;
+
+    // Collection of all tasks, valid only between Prepare() and Execute()
+    nsTArray<nsAutoPtr<DBOperation> > mExecList;
+
+    // Number of failing flush attempts
+    uint32_t mFlushFailureCount;
+  };
+
+public:
+  DOMStorageDBThread();
+  virtual ~DOMStorageDBThread() {}
+
+  virtual nsresult Init();
+  virtual nsresult Shutdown();
+
+  virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false)
+    { InsertDBOp(new DBOperation(aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload, aCache)); }
+
+  virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForce = false);
+
+  virtual void AsyncGetUsage(DOMStorageUsageBridge * aUsage)
+    { InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); }
+
+  virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue)
+    { return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, aValue)); }
+
+  virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue)
+    { return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, aValue)); }
+
+  virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey)
+    { return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); }
+
+  virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache)
+    { return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); }
+
+  virtual void AsyncClearAll()
+    { InsertDBOp(new DBOperation(DBOperation::opClearAll)); }
+
+  virtual void AsyncClearMatchingScope(const nsACString& aScope)
+    { InsertDBOp(new DBOperation(DBOperation::opClearMatchingScope, aScope)); }
+
+  virtual void AsyncFlush();
+
+  virtual bool ShouldPreloadScope(const nsACString& aScope);
+  virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes);
+
+private:
+  nsCOMPtr<nsIFile> mDatabaseFile;
+  PRThread* mThread;
+
+  // The monitor we drive the thread with
+  Monitor mMonitor;
+
+  // Flag to stop, protected by the monitor
+  bool mStopIOThread;
+
+  // Whether WAL is enabled
+  bool mWALModeEnabled;
+
+  // Whether DB has already been open, avoid races between main thread reads
+  // and pending DB init in the background I/O thread
+  bool mDBReady;
+
+  // State of the database initiation
+  nsresult mStatus;
+
+  // List of scopes having data, for optimization purposes only
+  nsTHashtable<nsCStringHashKey> mScopesHavingData;
+
+  StatementCache mWorkerStatements;
+  StatementCache mReaderStatements;
+
+  // Connection used by the worker thread for all read and write ops
+  nsCOMPtr<mozIStorageConnection> mWorkerConnection;
+
+  // Connection used only on the main thread for sync read operations
+  nsCOMPtr<mozIStorageConnection> mReaderConnection;
+
+  // Time the first pending operation has been added to the pending operations
+  // list
+  PRIntervalTime mDirtyEpoch;
+
+  // Flag to force immediate flush of all pending operations
+  bool mFlushImmediately;
+
+  // List of preloading operations, in chronological or priority order.
+  // Executed prioritly over pending update operations.
+  nsTArray<DBOperation*> mPreloads;
+
+  // Collector of pending update operations
+  PendingOperations mPendingTasks;
+
+  // Counter of calls for thread priority rising.
+  int32_t mPriorityCounter;
+
+  // Helper to direct an operation to one of the arrays above;
+  // also checks IsScopeClearPending for preloads
+  nsresult InsertDBOp(DBOperation* aOperation);
+
+  // Opens the database, first thing we do after start of the thread.
+  nsresult OpenDatabaseConnection();
+  nsresult InitDatabase();
+  nsresult ShutdownDatabase();
+
+  // Tries to establish WAL mode
+  nsresult SetJournalMode(bool aIsWal);
+  nsresult TryJournalMode();
+
+  void SetHigherPriority();
+  void SetDefaultPriority();
+
+  // Ensures we flush pending tasks in some reasonble time
+  void ScheduleFlush();
+
+  // Called when flush of pending tasks is being executed
+  void UnscheduleFlush();
+
+  // This method is used for two purposes:
+  // 1. as a value passed to monitor.Wait() method
+  // 2. as in indicator that flush has to be performed
+  //
+  // Return:
+  // - PR_INTERVAL_NO_TIMEOUT when no pending tasks are scheduled
+  // - larger then zero when tasks have been scheduled, but it is
+  //   still not time to perform the flush ; it is actual interval
+  //   time to wait until the flush has to happen
+  // - 0 when it is time to do the flush
+  PRIntervalTime TimeUntilFlush();
+
+  // Notifies to the main thread that flush has completed
+  void NotifyFlushCompletion();
+
+  // Thread loop
+  static void ThreadFunc(void* aArg);
+  void ThreadFunc();
+};
+
+} // ::dom
+} // ::mozilla
+
+#endif /* DOMStorageDBThread_h___ */
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageIPC.cpp
@@ -0,0 +1,712 @@
+/* -*- Mode: C++; 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 "DOMStorageIPC.h"
+
+#include "DOMStorageManager.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace dom {
+
+// ----------------------------------------------------------------------------
+// Child
+// ----------------------------------------------------------------------------
+
+NS_IMPL_THREADSAFE_ADDREF(DOMStorageDBChild)
+
+NS_IMETHODIMP_(nsrefcnt) DOMStorageDBChild::Release(void)
+{
+  NS_PRECONDITION(0 != mRefCnt, "dup release");
+  nsrefcnt count = --mRefCnt;
+  NS_LOG_RELEASE(this, count, "DOMStorageDBChild");
+  if (count == 1 && mIPCOpen) {
+    Send__delete__(this);
+    return 0;
+  }
+  if (count == 0) {
+    mRefCnt = 1;
+    delete this;
+    return 0;
+  }
+  return count;
+}
+
+void
+DOMStorageDBChild::AddIPDLReference()
+{
+  NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
+  mIPCOpen = true;
+  AddRef();
+}
+
+void
+DOMStorageDBChild::ReleaseIPDLReference()
+{
+  NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
+  mIPCOpen = false;
+  Release();
+}
+
+DOMStorageDBChild::DOMStorageDBChild(DOMLocalStorageManager* aManager)
+  : mManager(aManager)
+  , mStatus(NS_OK)
+  , mIPCOpen(false)
+{
+  mLoadingCaches.Init();
+}
+
+DOMStorageDBChild::~DOMStorageDBChild()
+{
+}
+
+nsTHashtable<nsCStringHashKey>&
+DOMStorageDBChild::ScopesHavingData()
+{
+  if (!mScopesHavingData.IsInitialized()) {
+    mScopesHavingData.Init();
+  }
+
+  return mScopesHavingData;
+}
+
+nsresult
+DOMStorageDBChild::Init()
+{
+  ContentChild* child = ContentChild::GetSingleton();
+  AddIPDLReference();
+  child->SendPStorageConstructor(this);
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBChild::Shutdown()
+{
+  // There is nothing to do here, IPC will release automatically and
+  // the actual thread running on the parent process will also stop
+  // automatically in profile-before-change topic observer.
+  return NS_OK;
+}
+
+void
+DOMStorageDBChild::AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority)
+{
+  if (mIPCOpen) {
+    // Adding ref to cache for the time of preload.  This ensures a reference to
+    // to the cache and that all keys will load into this cache object.
+    mLoadingCaches.PutEntry(aCache);
+    SendAsyncPreload(aCache->Scope(), aPriority);
+  } else {
+    // No IPC, no love.  But the LoadDone call is expected.
+    aCache->LoadDone(NS_ERROR_UNEXPECTED);
+  }
+}
+
+void
+DOMStorageDBChild::AsyncGetUsage(DOMStorageUsageBridge* aUsage)
+{
+  if (mIPCOpen) {
+    SendAsyncGetUsage(aUsage->Scope());
+  }
+}
+
+void
+DOMStorageDBChild::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
+{
+  if (NS_FAILED(mStatus)) {
+    aCache->LoadDone(mStatus);
+    return;
+  }
+
+  if (!mIPCOpen) {
+    aCache->LoadDone(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  // There is no way to put the child process to a wait state to receive all
+  // incoming async responses from the parent, hence we have to do a sync preload
+  // instead.  We are smart though, we only demand keys that are left to load in
+  // case the async preload has already loaded some keys.
+  InfallibleTArray<nsString> keys, values;
+  nsresult rv;
+  SendPreload(aCache->Scope(), aCache->LoadedCount(), &keys, &values, &rv);
+
+  for (uint32_t i = 0; i < keys.Length(); ++i) {
+    aCache->LoadItem(keys[i], values[i]);
+  }
+
+  aCache->LoadDone(rv);
+}
+
+nsresult
+DOMStorageDBChild::AsyncAddItem(DOMStorageCacheBridge* aCache,
+                                const nsAString& aKey,
+                                const nsAString& aValue)
+{
+  if (NS_FAILED(mStatus) || !mIPCOpen) {
+    return mStatus;
+  }
+
+  SendAsyncAddItem(aCache->Scope(), nsString(aKey), nsString(aValue));
+  ScopesHavingData().PutEntry(aCache->Scope());
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBChild::AsyncUpdateItem(DOMStorageCacheBridge* aCache,
+                                   const nsAString& aKey,
+                                   const nsAString& aValue)
+{
+  if (NS_FAILED(mStatus) || !mIPCOpen) {
+    return mStatus;
+  }
+
+  SendAsyncUpdateItem(aCache->Scope(), nsString(aKey), nsString(aValue));
+  ScopesHavingData().PutEntry(aCache->Scope());
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBChild::AsyncRemoveItem(DOMStorageCacheBridge* aCache,
+                                   const nsAString& aKey)
+{
+  if (NS_FAILED(mStatus) || !mIPCOpen) {
+    return mStatus;
+  }
+
+  SendAsyncRemoveItem(aCache->Scope(), nsString(aKey));
+  return NS_OK;
+}
+
+nsresult
+DOMStorageDBChild::AsyncClear(DOMStorageCacheBridge* aCache)
+{
+  if (NS_FAILED(mStatus) || !mIPCOpen) {
+    return mStatus;
+  }
+
+  SendAsyncClear(aCache->Scope());
+  ScopesHavingData().RemoveEntry(aCache->Scope());
+  return NS_OK;
+}
+
+bool
+DOMStorageDBChild::ShouldPreloadScope(const nsACString& aScope)
+{
+  // Return true if we didn't receive the aScope list yet.
+  // I tend to rather preserve a bit of early-after-start performance
+  // then a bit of memory here.
+  return !mScopesHavingData.IsInitialized() ||
+         mScopesHavingData.Contains(aScope);
+}
+
+bool
+DOMStorageDBChild::RecvObserve(const nsCString& aTopic,
+                               const nsCString& aScopePrefix)
+{
+  DOMStorageObserver::Self()->Notify(aTopic.get(), aScopePrefix);
+  return true;
+}
+
+bool
+DOMStorageDBChild::RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes)
+{
+  for (uint32_t i = 0; i < aScopes.Length(); ++i) {
+    ScopesHavingData().PutEntry(aScopes[i]);
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBChild::RecvLoadItem(const nsCString& aScope,
+                                const nsString& aKey,
+                                const nsString& aValue)
+{
+  DOMStorageCache* aCache = mManager->GetCache(aScope);
+  if (aCache) {
+    aCache->LoadItem(aKey, aValue);
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBChild::RecvLoadDone(const nsCString& aScope, const nsresult& aRv)
+{
+  DOMStorageCache* aCache = mManager->GetCache(aScope);
+  if (aCache) {
+    aCache->LoadDone(aRv);
+
+    // Just drop reference to this cache now since the load is done.
+    mLoadingCaches.RemoveEntry(static_cast<DOMStorageCacheBridge*>(aCache));
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBChild::RecvLoadUsage(const nsCString& aScope, const int64_t& aUsage)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
+  if (!db) {
+    return false;
+  }
+
+  DOMStorageUsageBridge* scopeUsage = db->GetScopeUsage(aScope);
+  scopeUsage->LoadUsage(aUsage);
+  return true;
+}
+
+bool
+DOMStorageDBChild::RecvError(const nsresult& aRv)
+{
+  mStatus = aRv;
+  return true;
+}
+
+// ----------------------------------------------------------------------------
+// Parent
+// ----------------------------------------------------------------------------
+
+NS_IMPL_THREADSAFE_ADDREF(DOMStorageDBParent)
+NS_IMPL_THREADSAFE_RELEASE(DOMStorageDBParent)
+
+void
+DOMStorageDBParent::AddIPDLReference()
+{
+  NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
+  mIPCOpen = true;
+  AddRef();
+}
+
+void
+DOMStorageDBParent::ReleaseIPDLReference()
+{
+  NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
+  mIPCOpen = false;
+  Release();
+}
+
+namespace { // anon
+
+class SendScopesHavingDataRunnable : public nsRunnable
+{
+public:
+  SendScopesHavingDataRunnable(DOMStorageDBParent* aParent)
+    : mParent(aParent)
+  {}
+
+private:
+  NS_IMETHOD Run()
+  {
+    if (!mParent->IPCOpen()) {
+      return NS_OK;
+    }
+
+    DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
+    if (db) {
+      InfallibleTArray<nsCString> scopes;
+      db->GetScopesHavingData(&scopes);
+      mozilla::unused << mParent->SendScopesHavingData(scopes);
+    }
+
+    return NS_OK;
+  }
+
+  nsRefPtr<DOMStorageDBParent> mParent;
+};
+
+} // anon
+
+DOMStorageDBParent::DOMStorageDBParent()
+: mIPCOpen(false)
+{
+  DOMStorageObserver* observer = DOMStorageObserver::Self();
+  if (observer) {
+    observer->AddSink(this);
+  }
+
+  // We are always open by IPC only
+  AddIPDLReference();
+
+  // Cannot send directly from here since the channel
+  // is not completely built at this moment.
+  nsRefPtr<SendScopesHavingDataRunnable> r =
+    new SendScopesHavingDataRunnable(this);
+  NS_DispatchToCurrentThread(r);
+}
+
+DOMStorageDBParent::~DOMStorageDBParent()
+{
+  DOMStorageObserver* observer = DOMStorageObserver::Self();
+  if (observer) {
+    observer->RemoveSink(this);
+  }
+}
+
+DOMStorageDBParent::CacheParentBridge*
+DOMStorageDBParent::NewCache(const nsACString& aScope)
+{
+  return new CacheParentBridge(this, aScope);
+}
+
+bool
+DOMStorageDBParent::RecvAsyncPreload(const nsCString& aScope, const bool& aPriority)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  db->AsyncPreload(NewCache(aScope), aPriority);
+  return true;
+}
+
+bool
+DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aScope)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  // The object releases it self in LoadUsage method
+  UsageParentBridge* usage = new UsageParentBridge(this, aScope);
+  db->AsyncGetUsage(usage);
+  return true;
+}
+
+namespace { // anon
+
+// We need another implementation of DOMStorageCacheBridge to do
+// synchronous IPC preload.  This class just receives Load* notifications
+// and fills the returning arguments of RecvPreload with the database
+// values for us.
+class SyncLoadCacheHelper : public DOMStorageCacheBridge
+{
+public:
+  SyncLoadCacheHelper(const nsCString& aScope,
+                      uint32_t aAlreadyLoadedCount,
+                      InfallibleTArray<nsString>* aKeys,
+                      InfallibleTArray<nsString>* aValues,
+                      nsresult* rv)
+  : mMonitor("DOM Storage SyncLoad IPC")
+  , mScope(aScope)
+  , mKeys(aKeys)
+  , mValues(aValues)
+  , mRv(rv)
+  , mLoaded(false)
+  , mLoadedCount(aAlreadyLoadedCount)
+  {
+    // Precaution
+    *mRv = NS_ERROR_UNEXPECTED;
+  }
+
+  virtual const nsCString& Scope() const { return mScope; }
+  virtual bool Loaded() { return mLoaded; }
+  virtual uint32_t LoadedCount() { return mLoadedCount; }
+  virtual bool LoadItem(const nsAString& aKey, const nsString& aValue)
+  {
+    // Called on the aCache background thread
+    if (mLoaded) {
+      return false;
+    }
+
+    ++mLoadedCount;
+    mKeys->AppendElement(aKey);
+    mValues->AppendElement(aValue);
+    return true;
+  }
+
+  virtual void LoadDone(nsresult aRv)
+  {
+    // Called on the aCache background thread
+    MonitorAutoLock monitor(mMonitor);
+    mLoaded = true;
+    *mRv = aRv;
+    monitor.Notify();
+  }
+
+  virtual void LoadWait()
+  {
+    // Called on the main thread, exits after LoadDone() call
+    MonitorAutoLock monitor(mMonitor);
+    while (!mLoaded) {
+      monitor.Wait();
+    }
+  }
+
+private:
+  Monitor mMonitor;
+  nsCString mScope;
+  InfallibleTArray<nsString>* mKeys;
+  InfallibleTArray<nsString>* mValues;
+  nsresult* mRv;
+  bool mLoaded;
+  uint32_t mLoadedCount;
+};
+
+} // anon
+
+bool
+DOMStorageDBParent::RecvPreload(const nsCString& aScope,
+                                const uint32_t& aAlreadyLoadedCount,
+                                InfallibleTArray<nsString>* aKeys,
+                                InfallibleTArray<nsString>* aValues,
+                                nsresult* aRv)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  nsRefPtr<SyncLoadCacheHelper> cache(
+    new SyncLoadCacheHelper(aScope, aAlreadyLoadedCount, aKeys, aValues, aRv));
+
+  db->SyncPreload(cache, true);
+  return true;
+}
+
+bool
+DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aScope,
+                                     const nsString& aKey,
+                                     const nsString& aValue)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  nsresult rv = db->AsyncAddItem(NewCache(aScope), aKey, aValue);
+  if (NS_FAILED(rv) && mIPCOpen) {
+    mozilla::unused << SendError(rv);
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aScope,
+                                        const nsString& aKey,
+                                        const nsString& aValue)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  nsresult rv = db->AsyncUpdateItem(NewCache(aScope), aKey, aValue);
+  if (NS_FAILED(rv) && mIPCOpen) {
+    mozilla::unused << SendError(rv);
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aScope,
+                                        const nsString& aKey)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  nsresult rv = db->AsyncRemoveItem(NewCache(aScope), aKey);
+  if (NS_FAILED(rv) && mIPCOpen) {
+    mozilla::unused << SendError(rv);
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBParent::RecvAsyncClear(const nsCString& aScope)
+{
+  DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+  if (!db) {
+    return false;
+  }
+
+  nsresult rv = db->AsyncClear(NewCache(aScope));
+  if (NS_FAILED(rv) && mIPCOpen) {
+    mozilla::unused << SendError(rv);
+  }
+
+  return true;
+}
+
+bool
+DOMStorageDBParent::RecvAsyncFlush()
+{
+  DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
+  if (!db) {
+    return false;
+  }
+
+  db->AsyncFlush();
+  return true;
+}
+
+// DOMStorageObserverSink
+
+nsresult
+DOMStorageDBParent::Observe(const char* aTopic,
+                            const nsACString& aScopePrefix)
+{
+  if (mIPCOpen) {
+    mozilla::unused << SendObserve(nsDependentCString(aTopic),
+                                   nsCString(aScopePrefix));
+  }
+
+  return NS_OK;
+}
+
+namespace { // anon
+
+// Results must be sent back on the main thread
+class LoadRunnable : public nsRunnable
+{
+public:
+  enum TaskType {
+    loadItem,
+    loadDone
+  };
+
+  LoadRunnable(DOMStorageDBParent* aParent,
+               TaskType aType,
+               const nsACString& aScope,
+               const nsAString& aKey = EmptyString(),
+               const nsAString& aValue = EmptyString())
+  : mParent(aParent)
+  , mType(aType)
+  , mScope(aScope)
+  , mKey(aKey)
+  , mValue(aValue)
+  { }
+
+  LoadRunnable(DOMStorageDBParent* aParent,
+               TaskType aType,
+               const nsACString& aScope,
+               nsresult aRv)
+  : mParent(aParent)
+  , mType(aType)
+  , mScope(aScope)
+  , mRv(aRv)
+  { }
+
+private:
+  nsRefPtr<DOMStorageDBParent> mParent;
+  TaskType mType;
+  nsCString mScope;
+  nsString mKey;
+  nsString mValue;
+  nsresult mRv;
+
+  NS_IMETHOD Run()
+  {
+    if (!mParent->IPCOpen()) {
+      return NS_OK;
+    }
+
+    switch (mType)
+    {
+    case loadItem:
+      mozilla::unused << mParent->SendLoadItem(mScope, mKey, mValue);
+      break;
+    case loadDone:
+      mozilla::unused << mParent->SendLoadDone(mScope, mRv);
+      break;
+    }
+
+    return NS_OK;
+  }
+};
+
+} // anon
+
+// DOMStorageDBParent::CacheParentBridge
+
+bool
+DOMStorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, const nsString& aValue)
+{
+  if (mLoaded) {
+    return false;
+  }
+
+  ++mLoadedCount;
+
+  nsRefPtr<LoadRunnable> r =
+    new LoadRunnable(mParent, LoadRunnable::loadItem, mScope, aKey, aValue);
+  NS_DispatchToMainThread(r);
+  return true;
+}
+
+void
+DOMStorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
+{
+  // Prevent send of duplicate LoadDone.
+  if (mLoaded) {
+    return;
+  }
+
+  mLoaded = true;
+
+  nsRefPtr<LoadRunnable> r =
+    new LoadRunnable(mParent, LoadRunnable::loadDone, mScope, aRv);
+  NS_DispatchToMainThread(r);
+}
+
+void
+DOMStorageDBParent::CacheParentBridge::LoadWait()
+{
+  // Should never be called on this implementation
+  MOZ_ASSERT(false);
+}
+
+// DOMStorageDBParent::UsageParentBridge
+
+namespace { // anon
+
+class UsageRunnable : public nsRunnable
+{
+public:
+  UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aScope, const int64_t& aUsage)
+  : mParent(aParent)
+  , mScope(aScope)
+  , mUsage(aUsage)
+  {}
+
+private:
+  NS_IMETHOD Run()
+  {
+    if (!mParent->IPCOpen()) {
+      return NS_OK;
+    }
+
+    mozilla::unused << mParent->SendLoadUsage(mScope, mUsage);
+    return NS_OK;
+  }
+
+  nsRefPtr<DOMStorageDBParent> mParent;
+  nsCString mScope;
+  int64_t mUsage;
+};
+
+} // anon
+
+void
+DOMStorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
+{
+  nsRefPtr<UsageRunnable> r = new UsageRunnable(mParent, mScope, aUsage);
+  NS_DispatchToMainThread(r);
+  delete this;
+}
+
+} // ::dom
+} // ::mozilla
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageIPC.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef nsDOMStorageIPC_h___
+#define nsDOMStorageIPC_h___
+
+#include "mozilla/dom/PStorageChild.h"
+#include "mozilla/dom/PStorageParent.h"
+#include "DOMStorageDBThread.h"
+#include "DOMStorageCache.h"
+#include "DOMStorageObserver.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+namespace dom {
+
+class DOMLocalStorageManager;
+
+// Child side of the IPC protocol, exposes as DB interface but
+// is responsible to send all requests to the parent process
+// and expects asynchronous answers. Those are then transparently
+// forwarded back to consumers on the child process.
+class DOMStorageDBChild MOZ_FINAL : public DOMStorageDBBridge
+                                  , public PStorageChild
+{
+public:
+  DOMStorageDBChild(DOMLocalStorageManager* aManager);
+  virtual ~DOMStorageDBChild();
+
+  NS_IMETHOD_(nsrefcnt) AddRef(void);
+  NS_IMETHOD_(nsrefcnt) Release(void);
+
+  void AddIPDLReference();
+  void ReleaseIPDLReference();
+
+  virtual nsresult Init();
+  virtual nsresult Shutdown();
+
+  virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false);
+  virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage);
+
+  virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false);
+
+  virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue);
+  virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue);
+  virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey);
+  virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache);
+
+  virtual void AsyncClearAll()
+    { mScopesHavingData.Clear(); /* NO-OP on the child process otherwise */ }
+
+  virtual void AsyncClearMatchingScope(const nsACString& aScope)
+    { /* NO-OP on the child process */ }
+
+  virtual void AsyncFlush()
+    { SendAsyncFlush(); }
+
+  virtual bool ShouldPreloadScope(const nsACString& aScope);
+  virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
+    { NS_NOTREACHED("Not implemented for child process"); }
+
+private:
+  bool RecvObserve(const nsCString& aTopic,
+                   const nsCString& aScopePrefix);
+  bool RecvLoadItem(const nsCString& aScope,
+                    const nsString& aKey,
+                    const nsString& aValue);
+  bool RecvLoadDone(const nsCString& aScope,
+                    const nsresult& aRv);
+  bool RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes);
+  bool RecvLoadUsage(const nsCString& aScope,
+                     const int64_t& aUsage);
+  bool RecvError(const nsresult& aRv);
+
+  nsTHashtable<nsCStringHashKey>& ScopesHavingData();
+
+  nsAutoRefCnt mRefCnt;
+
+  // Held to get caches to forward answers to.
+  nsRefPtr<DOMLocalStorageManager> mManager;
+
+  // Scopes having data hash, for optimization purposes only
+  nsTHashtable<nsCStringHashKey> mScopesHavingData;
+
+  // List of caches waiting for preload.  This ensures the contract that
+  // AsyncPreload call references the cache for time of the preload.
+  nsTHashtable<nsRefPtrHashKey<DOMStorageCacheBridge> > mLoadingCaches;
+
+  // Status of the remote database
+  nsresult mStatus;
+
+  bool mIPCOpen;
+};
+
+
+// Receives async requests from child processes and is responsible
+// to send back responses from the DB thread.  Exposes as a fake
+// DOMStorageCache consumer.
+// Also responsible for forwardning all chrome operation notifications
+// such as cookie cleaning etc to the child process.
+class DOMStorageDBParent MOZ_FINAL : public PStorageParent
+                                   , public DOMStorageObserverSink
+{
+public:
+  DOMStorageDBParent();
+  virtual ~DOMStorageDBParent();
+
+  NS_IMETHOD_(nsrefcnt) AddRef(void);
+  NS_IMETHOD_(nsrefcnt) Release(void);
+
+  void AddIPDLReference();
+  void ReleaseIPDLReference();
+
+  bool IPCOpen() { return mIPCOpen; }
+
+public:
+  // Fake cache class receiving async callbacks from DB thread, sending
+  // them back to appropriate cache object on the child process.
+  class CacheParentBridge : public DOMStorageCacheBridge {
+  public:
+    CacheParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aScope)
+      : mParent(aParentDB), mScope(aScope), mLoaded(false), mLoadedCount(0) {}
+    virtual ~CacheParentBridge() {}
+
+    // DOMStorageCacheBridge
+    virtual const nsCString& Scope() const
+      { return mScope; }
+    virtual bool Loaded()
+      { return mLoaded; }
+    virtual uint32_t LoadedCount()
+      { return mLoadedCount; }
+
+    virtual bool LoadItem(const nsAString& aKey, const nsString& aValue);
+    virtual void LoadDone(nsresult aRv);
+    virtual void LoadWait();
+
+  private:
+    nsRefPtr<DOMStorageDBParent> mParent;
+    nsCString mScope;
+    bool mLoaded;
+    uint32_t mLoadedCount;
+  };
+
+  // Fake usage class receiving async callbacks from DB thread
+  class UsageParentBridge : public DOMStorageUsageBridge
+  {
+  public:
+    UsageParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aScope)
+      : mParent(aParentDB), mScope(aScope) {}
+    virtual ~UsageParentBridge() {}
+
+    // DOMStorageUsageBridge
+    virtual const nsCString& Scope() { return mScope; }
+    virtual void LoadUsage(const int64_t usage);
+
+  private:
+    nsRefPtr<DOMStorageDBParent> mParent;
+    nsCString mScope;
+  };
+
+private:
+  // IPC
+  bool RecvAsyncPreload(const nsCString& aScope, const bool& aPriority);
+  bool RecvPreload(const nsCString& aScope, const uint32_t& aAlreadyLoadedCount,
+                   InfallibleTArray<nsString>* aKeys, InfallibleTArray<nsString>* aValues,
+                   nsresult* aRv);
+  bool RecvAsyncGetUsage(const nsCString& aScope);
+  bool RecvAsyncAddItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue);
+  bool RecvAsyncUpdateItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue);
+  bool RecvAsyncRemoveItem(const nsCString& aScope, const nsString& aKey);
+  bool RecvAsyncClear(const nsCString& aScope);
+  bool RecvAsyncFlush();
+
+  // DOMStorageObserverSink
+  virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix);
+
+private:
+  CacheParentBridge* NewCache(const nsACString& aScope);
+
+  nsAutoRefCnt mRefCnt;
+	
+	// True when IPC channel is open and Send*() methods are OK to use.
+  bool mIPCOpen;
+};
+
+} // ::dom
+} // ::mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageManager.cpp
@@ -0,0 +1,631 @@
+/* -*- Mode: C++; 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 "DOMStorageManager.h"
+#include "DOMStorage.h"
+#include "DOMStorageDBThread.h"
+
+#include "nsIScriptSecurityManager.h"
+#include "nsIEffectiveTLDService.h"
+
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "mozilla/Preferences.h"
+
+// Only allow relatively small amounts of data since performance of
+// the synchronous IO is very bad.
+// We are enforcing simple per-origin quota only.
+#define DEFAULT_QUOTA_LIMIT (5 * 1024)
+
+namespace mozilla {
+namespace dom {
+
+namespace { // anon
+
+int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
+
+} // anon
+
+DOMLocalStorageManager*
+DOMLocalStorageManager::sSelf = nullptr;
+
+// static
+uint32_t
+DOMStorageManager::GetQuota()
+{
+  static bool preferencesInitialized = false;
+  if (!preferencesInitialized) {
+    mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
+                                         DEFAULT_QUOTA_LIMIT);
+    preferencesInitialized = true;
+  }
+
+  return gQuotaLimit * 1024; // pref is in kBs
+}
+
+void
+ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
+{
+  nsACString::const_iterator sourceBegin, sourceEnd;
+  aSource.BeginReading(sourceBegin);
+  aSource.EndReading(sourceEnd);
+
+  aResult.SetLength(aSource.Length());
+  nsACString::iterator destEnd;
+  aResult.EndWriting(destEnd);
+
+  while (sourceBegin != sourceEnd) {
+    *(--destEnd) = *sourceBegin;
+    ++sourceBegin;
+  }
+}
+
+nsresult
+CreateReversedDomain(const nsACString& aAsciiDomain,
+                     nsACString& aKey)
+{
+  if (aAsciiDomain.IsEmpty()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  ReverseString(aAsciiDomain, aKey);
+
+  aKey.AppendLiteral(".");
+  return NS_OK;
+}
+
+bool
+PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
+{
+  if (!aSubjectPrincipal) {
+    return true;
+  }
+
+  if (!aObjectPrincipal) {
+    return false;
+  }
+
+  bool equals;
+  nsresult rv = aSubjectPrincipal->EqualsIgnoringDomain(aObjectPrincipal, &equals);
+
+  NS_ASSERTION(NS_SUCCEEDED(rv) && equals,
+               "Trying to get DOM storage for wrong principal!");
+
+  if (NS_FAILED(rv) || !equals) {
+    return false;
+  }
+
+  return true;
+}
+
+NS_IMPL_ISUPPORTS1(DOMStorageManager,
+                   nsIDOMStorageManager)
+
+DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
+  : mType(aType)
+{
+  mCaches.Init(10);
+  DOMStorageObserver* observer = DOMStorageObserver::Self();
+  NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
+
+  if (observer) {
+    observer->AddSink(this);
+  }
+}
+
+DOMStorageManager::~DOMStorageManager()
+{
+  DOMStorageObserver* observer = DOMStorageObserver::Self();
+  if (observer) {
+    observer->RemoveSink(this);
+  }
+}
+
+namespace { // anon
+
+nsresult
+CreateScopeKey(nsIPrincipal* aPrincipal,
+               nsACString& aKey)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!uri) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoCString domainScope;
+  rv = uri->GetAsciiHost(domainScope);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (domainScope.IsEmpty()) {
+    // About pages have an empty host but a valid path.  Since they are handled
+    // internally by our own redirector, we can trust them and use path as key.
+    // if file:/// protocol, let's make the exact directory the domain
+    bool isScheme = false;
+    if ((NS_SUCCEEDED(uri->SchemeIs("about", &isScheme)) && isScheme) ||
+        (NS_SUCCEEDED(uri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
+      rv = uri->GetPath(domainScope);
+      NS_ENSURE_SUCCESS(rv, rv);
+      // While the host is always canonicalized to lowercase, the path is not,
+      // thus need to force the casing.
+      ToLowerCase(domainScope);
+    } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
+      nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = url->GetDirectory(domainScope);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  nsAutoCString key;
+
+  rv = CreateReversedDomain(domainScope, key);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsAutoCString scheme;
+  rv = uri->GetScheme(scheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  key.Append(NS_LITERAL_CSTRING(":") + scheme);
+
+  int32_t port = NS_GetRealPort(uri);
+  if (port != -1) {
+    key.Append(nsPrintfCString(":%d", port));
+  }
+
+  bool unknownAppId;
+  rv = aPrincipal->GetUnknownAppId(&unknownAppId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!unknownAppId) {
+    uint32_t appId;
+    rv = aPrincipal->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool isInBrowserElement;
+    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
+      aKey.Assign(key);
+      return NS_OK;
+    }
+
+    aKey.Truncate();
+    aKey.AppendInt(appId);
+    aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
+                NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
+                NS_LITERAL_CSTRING(":") + key);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+CreateQuotaDBKey(nsIPrincipal* aPrincipal,
+                 nsACString& aKey)
+{
+  nsresult rv;
+
+  nsAutoCString subdomainsDBKey;
+  nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
+    NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
+
+  nsAutoCString eTLDplusOne;
+  rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
+  if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
+    // XXX bug 357323 - what to do for localhost/file exactly?
+    rv = uri->GetAsciiHost(eTLDplusOne);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
+
+  bool unknownAppId;
+  rv = aPrincipal->GetUnknownAppId(&unknownAppId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!unknownAppId) {
+    uint32_t appId;
+    rv = aPrincipal->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool isInBrowserElement;
+    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
+      aKey.Assign(subdomainsDBKey);
+      return NS_OK;
+    }
+
+    aKey.Truncate();
+    aKey.AppendInt(appId);
+    aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
+                NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
+                NS_LITERAL_CSTRING(":") + subdomainsDBKey);
+  }
+
+  return NS_OK;
+}
+
+} // anon
+
+DOMStorageCache*
+DOMStorageManager::GetCache(const nsACString& aScope) const
+{
+  DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
+  if (!entry) {
+    return nullptr;
+  }
+
+  return entry->cache();
+}
+
+already_AddRefed<DOMStorageCache>
+DOMStorageManager::PutCache(const nsACString& aScope,
+                            nsIPrincipal* aPrincipal)
+{
+  DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
+  nsRefPtr<DOMStorageCache> cache = entry->cache();
+
+  nsAutoCString quotaScope;
+  CreateQuotaDBKey(aPrincipal, quotaScope);
+
+  switch (mType) {
+  case SessionStorage:
+    // Lifetime handled by the manager, don't persist
+    entry->HardRef();
+    cache->Init(nullptr, false, aPrincipal, quotaScope);
+    break;
+
+  case LocalStorage:
+    // Lifetime handled by the cache, do persist
+    cache->Init(this, true, aPrincipal, quotaScope);
+    break;
+
+  default:
+    MOZ_ASSERT(false);
+  }
+
+  return cache.forget();
+}
+
+void
+DOMStorageManager::DropCache(DOMStorageCache* aCache)
+{
+  if (!NS_IsMainThread()) {
+    NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
+  }
+
+  mCaches.RemoveEntry(aCache->Scope());
+}
+
+nsresult
+DOMStorageManager::GetStorageInternal(bool aCreate,
+                                      nsIPrincipal* aPrincipal,
+                                      const nsAString& aDocumentURI,
+                                      bool aPrivate,
+                                      nsIDOMStorage** aRetval)
+{
+  nsresult rv;
+
+  nsAutoCString scope;
+  rv = CreateScopeKey(aPrincipal, scope);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsRefPtr<DOMStorageCache> cache = GetCache(scope);
+
+  // Get or create a cache for the given scope
+  if (!cache) {
+    if (!aCreate) {
+      *aRetval = nullptr;
+      return NS_OK;
+    }
+
+    if (!aRetval) {
+      // This is demand to just preload the cache, if the scope has
+      // no data stored, bypass creation and preload of the cache.
+      DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
+      if (db) {
+        if (!db->ShouldPreloadScope(scope)) {
+          return NS_OK;
+        }
+      } else {
+        if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) {
+          return NS_OK;
+        }
+      }
+    }
+
+    // There is always a single instance of a cache per scope
+    // in a single instance of a DOM storage manager.
+    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);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
+{
+  return GetStorageInternal(true, aPrincipal, EmptyString(), false, nullptr);
+}
+
+NS_IMETHODIMP
+DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
+                                 const nsAString& aDocumentURI,
+                                 bool aPrivate,
+                                 nsIDOMStorage** aRetval)
+{
+  return GetStorageInternal(true, aPrincipal, aDocumentURI, aPrivate, aRetval);
+}
+
+NS_IMETHODIMP
+DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
+                              bool aPrivate,
+                              nsIDOMStorage** aRetval)
+{
+  return GetStorageInternal(false, 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) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  const DOMStorageCache* origCache = pstorage->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
+  // the cache in our hashtable.
+  nsRefPtr<DOMStorageCache> newCache = PutCache(origCache->Scope(),
+                                                origCache->Principal());
+
+  newCache->CloneFrom(origCache);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
+                                nsIDOMStorage* aStorage,
+                                bool* aRetval)
+{
+  nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
+  if (!pstorage) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  *aRetval = false;
+
+  if (!aPrincipal) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsAutoCString scope;
+  nsresult rv = CreateScopeKey(aPrincipal, scope);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  DOMStorageCache* cache = GetCache(scope);
+  if (cache != pstorage->GetCache()) {
+    return NS_OK;
+  }
+
+  if (!pstorage->PrincipalEquals(aPrincipal)) {
+    return NS_OK;
+  }
+
+  *aRetval = true;
+  return NS_OK;
+}
+
+// Obsolete nsIDOMStorageManager methods
+
+NS_IMETHODIMP
+DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
+                                               const nsAString& aDocumentURI,
+                                               bool aPrivate,
+                                               nsIDOMStorage** aRetval)
+{
+  if (mType != LocalStorage) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
+}
+
+namespace { // anon
+
+class ClearCacheEnumeratorData
+{
+public:
+  ClearCacheEnumeratorData(uint32_t aFlags)
+    : mUnloadFlags(aFlags)
+  {}
+
+  uint32_t mUnloadFlags;
+  nsCString mKeyPrefix;
+};
+
+} // anon
+
+PLDHashOperator
+DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure)
+{
+  DOMStorageCache* cache = aEntry->cache();
+  nsCString& key = const_cast<nsCString&>(cache->Scope());
+
+  ClearCacheEnumeratorData* data = static_cast<ClearCacheEnumeratorData*>(aClosure);
+
+  if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) {
+    cache->UnloadItems(data->mUnloadFlags);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+nsresult
+DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
+{
+  // Clear everything, caches + database
+  if (!strcmp(aTopic, "cookie-cleared")) {
+    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+
+    return NS_OK;
+  }
+
+  // Clear from caches everything that has been stored
+  // while in session-only mode
+  if (!strcmp(aTopic, "session-only-cleared")) {
+    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession);
+    data.mKeyPrefix = aScopePrefix;
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+
+    return NS_OK;
+  }
+
+  // Clear everything (including so and pb data) from caches and database
+  // for the gived domain and subdomains.
+  if (!strcmp(aTopic, "domain-data-cleared")) {
+    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
+    data.mKeyPrefix = aScopePrefix;
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+
+    return NS_OK;
+  }
+
+  // Clear all private-browsing caches
+  if (!strcmp(aTopic, "private-browsing-data-cleared")) {
+    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate);
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+
+    return NS_OK;
+  }
+
+  // Clear localStorage data beloging to an app.
+  if (!strcmp(aTopic, "app-data-cleared")) {
+
+    // sessionStorage is expected to stay
+    if (mType == SessionStorage) {
+      return NS_OK;
+    }
+
+    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
+    data.mKeyPrefix = aScopePrefix;
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "profile-change")) {
+    // For case caches are still referenced - clear them completely
+    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+
+    mCaches.Clear();
+    return NS_OK;
+  }
+
+#ifdef DOM_STORAGE_TESTS
+  if (!strcmp(aTopic, "test-reload")) {
+    if (mType != LocalStorage) {
+      return NS_OK;
+    }
+
+    // This immediately completely reloads all caches from the database.
+    ClearCacheEnumeratorData data(DOMStorageCache::kTestReload);
+    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "test-flushed")) {
+    if (XRE_GetProcessType() != GeckoProcessType_Default) {
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      if (obs) {
+        obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
+      }
+    }
+
+    return NS_OK;
+  }
+#endif
+
+  NS_ERROR("Unexpected topic");
+  return NS_ERROR_UNEXPECTED;
+}
+
+// DOMLocalStorageManager
+
+DOMLocalStorageManager::DOMLocalStorageManager()
+  : DOMStorageManager(LocalStorage)
+{
+  NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
+  sSelf = this;
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    // Do this only on the child process.  The thread IPC bridge
+    // is also used to communicate chrome observer notifications.
+    // Note: must be called after we set sSelf
+    DOMStorageCache::StartDatabase();
+  }
+}
+
+DOMLocalStorageManager::~DOMLocalStorageManager()
+{
+  sSelf = nullptr;
+}
+
+// DOMSessionStorageManager
+
+DOMSessionStorageManager::DOMSessionStorageManager()
+  : DOMStorageManager(SessionStorage)
+{
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    // Do this only on the child process.  The thread IPC bridge
+    // is also used to communicate chrome observer notifications.
+    DOMStorageCache::StartDatabase();
+  }
+}
+
+} // ::dom
+} // ::mozilla
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageManager.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef nsDOMStorageManager_h__
+#define nsDOMStorageManager_h__
+
+#include "nsIDOMStorageManager.h"
+#include "DOMStorageObserver.h"
+
+#include "nsPIDOMStorage.h"
+#include "DOMStorageCache.h"
+
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace dom {
+
+const nsPIDOMStorage::StorageType SessionStorage = nsPIDOMStorage::SessionStorage;
+const nsPIDOMStorage::StorageType LocalStorage = nsPIDOMStorage::LocalStorage;
+
+class DOMStorage;
+
+class DOMStorageManager : public nsIDOMStorageManager
+                        , public DOMStorageObserverSink
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSTORAGEMANAGER
+
+public:
+  virtual nsPIDOMStorage::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;
+
+protected:
+  DOMStorageManager(nsPIDOMStorage::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.
+  class DOMStorageCacheHashKey : public nsCStringHashKey
+  {
+  public:
+    DOMStorageCacheHashKey(const nsACString* aKey)
+      : nsCStringHashKey(aKey)
+      , mCache(new DOMStorageCache(aKey))
+    {}
+
+    DOMStorageCacheHashKey(const DOMStorageCacheHashKey& aOther)
+      : nsCStringHashKey(aOther)
+    {
+      NS_ERROR("Shouldn't be called");
+    }
+
+    DOMStorageCache* cache() { return mCache; }
+    // Keep the cache referenced forever, used for sessionStorage.
+    void HardRef() { mCacheRef = mCache; }
+
+  private:
+    // weak ref only since cache references its manager.
+    DOMStorageCache* mCache;
+    // hard ref when this is sessionStorage to keep it alive forever.
+    nsRefPtr<DOMStorageCache> mCacheRef;
+  };
+
+  // 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,
+                              nsIPrincipal* aPrincipal,
+                              const nsAString& aDocumentURI,
+                              bool aPrivate,
+                              nsIDOMStorage** aRetval);
+
+  // Scope->cache map
+  nsTHashtable<DOMStorageCacheHashKey> mCaches;
+  const nsPIDOMStorage::StorageType mType;
+
+  static PLDHashOperator ClearCacheEnumerator(DOMStorageCacheHashKey* aCache,
+                                              void* aClosure);
+
+protected:
+  friend class DOMStorageCache;
+  // Releases cache since it is no longer referrered by any DOMStorage object.
+  virtual void DropCache(DOMStorageCache* aCache);
+};
+
+// Derived classes to allow two different contract ids, one for localStorage and
+// one for sessionStorage management.  localStorage manager is used as service
+// scoped to the application while sessionStorage managers are instantiated by each
+// top doc shell in the application since sessionStorages are isolated per top level
+// browsing context.  The code may easily by shared by both.
+
+class DOMLocalStorageManager MOZ_FINAL : public DOMStorageManager
+{
+public:
+  DOMLocalStorageManager();
+  virtual ~DOMLocalStorageManager();
+
+  // Global getter of localStorage manager service
+  static DOMLocalStorageManager* Self() { return sSelf; }
+
+private:
+  static DOMLocalStorageManager* sSelf;
+};
+
+class DOMSessionStorageManager MOZ_FINAL : public DOMStorageManager
+{
+public:
+  DOMSessionStorageManager();
+};
+
+} // ::dom
+} // ::mozilla
+
+#endif /* nsDOMStorageManager_h__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageObserver.cpp
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; 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 "DOMStorageObserver.h"
+
+#include "DOMStorageDBThread.h"
+#include "DOMStorageCache.h"
+
+#include "nsIObserverService.h"
+#include "nsIURI.h"
+#include "nsIURL.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIPermission.h"
+#include "nsIIDNService.h"
+#include "mozIApplicationClearPrivateDataParams.h"
+#include "nsICookiePermission.h"
+
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "nsEscape.h"
+#include "nsNetCID.h"
+#include "mozilla/Services.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+static const char kStartupTopic[] = "sessionstore-windows-restored";
+static const uint32_t kStartupDelay = 0;
+
+NS_IMPL_ISUPPORTS2(DOMStorageObserver,
+                   nsIObserver,
+                   nsISupportsWeakReference)
+
+DOMStorageObserver* DOMStorageObserver::sSelf = nullptr;
+
+extern nsresult
+CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
+
+// static
+nsresult
+DOMStorageObserver::Init()
+{
+  if (sSelf) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  sSelf = new DOMStorageObserver();
+  NS_ADDREF(sSelf);
+
+  // Chrome clear operations.
+  obs->AddObserver(sSelf, kStartupTopic, true);
+  obs->AddObserver(sSelf, "cookie-changed", true);
+  obs->AddObserver(sSelf, "perm-changed", true);
+  obs->AddObserver(sSelf, "browser:purge-domain-data", true);
+  obs->AddObserver(sSelf, "last-pb-context-exited", true);
+  obs->AddObserver(sSelf, "webapps-clear-data", true);
+
+  // Shutdown
+  obs->AddObserver(sSelf, "profile-after-change", true);
+  obs->AddObserver(sSelf, "profile-before-change", true);
+  obs->AddObserver(sSelf, "xpcom-shutdown", true);
+
+#ifdef DOM_STORAGE_TESTS
+  // Testing
+  obs->AddObserver(sSelf, "domstorage-test-flush-force", true);
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    // Only to forward to child process.
+    obs->AddObserver(sSelf, "domstorage-test-flushed", true);
+  }
+
+  obs->AddObserver(sSelf, "domstorage-test-reload", true);
+#endif
+
+  return NS_OK;
+}
+
+// static
+nsresult
+DOMStorageObserver::Shutdown()
+{
+  if (!sSelf) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  NS_RELEASE(sSelf);
+  return NS_OK;
+}
+
+void
+DOMStorageObserver::AddSink(DOMStorageObserverSink* aObs)
+{
+  mSinks.AppendElement(aObs);
+}
+
+void
+DOMStorageObserver::RemoveSink(DOMStorageObserverSink* aObs)
+{
+  mSinks.RemoveElement(aObs);
+}
+
+void
+DOMStorageObserver::Notify(const char* aTopic, const nsACString& aData)
+{
+  for (uint32_t i = 0; i < mSinks.Length(); ++i) {
+    DOMStorageObserverSink* sink = mSinks[i];
+    sink->Observe(aTopic, aData);
+  }
+}
+
+NS_IMETHODIMP
+DOMStorageObserver::Observe(nsISupports* aSubject,
+                            const char* aTopic,
+                            const PRUnichar* aData)
+{
+  nsresult rv;
+
+  // Start the thread that opens the database.
+  if (!strcmp(aTopic, kStartupTopic)) {
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->RemoveObserver(this, kStartupTopic);
+
+    mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    if (!mDBThreadStartDelayTimer) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay);
+
+    return NS_OK;
+  }
+
+  // Timer callback used to start the database a short timer after startup
+  if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
+    nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
+    if (!timer) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    if (timer == mDBThreadStartDelayTimer) {
+      mDBThreadStartDelayTimer = nullptr;
+
+      DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+      NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
+    }
+
+    return NS_OK;
+  }
+
+  // Clear everything, caches + database
+  if (!strcmp(aTopic, "cookie-changed")) {
+    if (!NS_LITERAL_STRING("cleared").Equals(aData)) {
+      return NS_OK;
+    }
+
+    DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+    NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
+
+    db->AsyncClearAll();
+
+    Notify("cookie-cleared");
+
+    return NS_OK;
+  }
+
+  // Clear from caches everything that has been stored
+  // while in session-only mode
+  if (!strcmp(aTopic, "perm-changed")) {
+    // Check for cookie permission change
+    nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
+    if (!perm) {
+      return NS_OK;
+    }
+
+    nsAutoCString type;
+    perm->GetType(type);
+    if (type != NS_LITERAL_CSTRING("cookie")) {
+      return NS_OK;
+    }
+
+    uint32_t cap = 0;
+    perm->GetCapability(&cap);
+    if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
+        !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) {
+      return NS_OK;
+    }
+
+    nsAutoCString host;
+    perm->GetHost(host);
+    if (host.IsEmpty()) {
+      return NS_OK;
+    }
+
+    nsAutoCString scope;
+    rv = CreateReversedDomain(host, scope);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    Notify("session-only-cleared", scope);
+
+    return NS_OK;
+  }
+
+  // Clear everything (including so and pb data) from caches and database
+  // for the gived domain and subdomains.
+  if (!strcmp(aTopic, "browser:purge-domain-data")) {
+    // Convert the domain name to the ACE format
+    nsAutoCString aceDomain;
+    nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
+    if (converter) {
+      rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } else {
+      // In case the IDN service is not available, this is the best we can come up with!
+      NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
+                   esc_OnlyNonASCII | esc_AlwaysCopy,
+                   aceDomain);
+    }
+
+    nsAutoCString scopePrefix;
+    rv = CreateReversedDomain(aceDomain, scopePrefix);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+    NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
+
+    db->AsyncClearMatchingScope(scopePrefix);
+
+    Notify("domain-data-cleared", scopePrefix);
+
+    return NS_OK;
+  }
+
+  // Clear all private-browsing caches
+  if (!strcmp(aTopic, "last-pb-context-exited")) {
+    Notify("private-browsing-data-cleared");
+
+    return NS_OK;
+  }
+
+  // Clear data beloging to an app.
+  if (!strcmp(aTopic, "webapps-clear-data")) {
+    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
+      do_QueryInterface(aSubject);
+    if (!params) {
+      NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    uint32_t appId;
+    bool browserOnly;
+
+    rv = params->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = params->GetBrowserOnly(&browserOnly);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
+
+    DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
+    NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
+
+    nsAutoCString scope;
+    scope.AppendInt(appId);
+    scope.Append(NS_LITERAL_CSTRING(":t:"));
+    db->AsyncClearMatchingScope(scope);
+    Notify("app-data-cleared", scope);
+
+    if (!browserOnly) {
+      scope.Truncate();
+      scope.AppendInt(appId);
+      scope.Append(NS_LITERAL_CSTRING(":f:"));
+      db->AsyncClearMatchingScope(scope);
+      Notify("app-data-cleared", scope);
+    }
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "profile-after-change")) {
+    Notify("profile-change");
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "profile-before-change") ||
+      !strcmp(aTopic, "xpcom-shutdown")) {
+    rv = DOMStorageCache::StopDatabase();
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Error while stopping DOMStorage DB background thread");
+    }
+
+    return NS_OK;
+  }
+
+#ifdef DOM_STORAGE_TESTS
+  if (!strcmp(aTopic, "domstorage-test-flush-force")) {
+    DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
+    if (db) {
+      db->AsyncFlush();
+    }
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "domstorage-test-flushed")) {
+    // Only used to propagate to IPC children
+    Notify("test-flushed");
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "domstorage-test-reload")) {
+    Notify("test-reload");
+
+    return NS_OK;
+  }
+#endif
+
+  NS_ERROR("Unexpected topic");
+  return NS_ERROR_UNEXPECTED;
+}
+
+} // ::dom
+} // ::mozilla
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/DOMStorageObserver.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef nsIDOMStorageObserver_h__
+#define nsIDOMStorageObserver_h__
+
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsWeakReference.h"
+#include "nsTArray.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+class DOMStorageObserver;
+
+// Implementers are DOMStorageManager and DOMStorageDBParent to forward to
+// child processes.
+class DOMStorageObserverSink
+{
+public:
+  virtual ~DOMStorageObserverSink() {}
+
+private:
+  friend class DOMStorageObserver;
+  virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix) = 0;
+};
+
+// Statically (though layout statics) initialized observer receiving and processing
+// chrome clearing notifications, such as cookie deletion etc.
+class DOMStorageObserver : public nsIObserver
+                         , public nsSupportsWeakReference
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  virtual ~DOMStorageObserver() {}
+
+  static nsresult Init();
+  static nsresult Shutdown();
+  static DOMStorageObserver* Self() { return sSelf; }
+
+  void AddSink(DOMStorageObserverSink* aObs);
+  void RemoveSink(DOMStorageObserverSink* aObs);
+  void Notify(const char* aTopic, const nsACString& aData = EmptyCString());
+
+private:
+  static DOMStorageObserver* sSelf;
+
+  // Weak references
+  nsTArray<DOMStorageObserverSink*> mSinks;
+  nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;
+};
+
+} // ::dom
+} // ::mozilla
+
+#endif
--- a/dom/src/storage/Makefile.in
+++ b/dom/src/storage/Makefile.in
@@ -10,33 +10,35 @@ VPATH          = @srcdir@
 FAIL_ON_WARNINGS := 1
 
 include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME   = jsdomstorage_s
 LIBXUL_LIBRARY = 1
 
 CPPSRCS = \
-       nsDOMStorage.cpp \
-       nsDOMStorageBaseDB.cpp \
-       nsDOMStorageDBWrapper.cpp \
-       nsLocalStorageCache.cpp \
-       nsDOMStoragePersistentDB.cpp \
-       nsDOMStorageMemoryDB.cpp \
-       StorageChild.cpp \
-       StorageParent.cpp \
+       DOMStorage.cpp \
+       DOMStorageCache.cpp \
+       DOMStorageDBThread.cpp \
+       DOMStorageObserver.cpp \
+       DOMStorageManager.cpp \
+       DOMStorageIPC.cpp \
        $(NULL)
 
 EXPORTS_NAMESPACES = mozilla/dom
-EXPORTS_mozilla/dom = StorageChild.h StorageParent.h
+EXPORTS_mozilla/dom = DOMStorageIPC.h
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 LOCAL_INCLUDES = \
                 -I$(topsrcdir)/dom/base \
 		-I$(topsrcdir)/content/events/src
 
 DEFINES += -D_IMPL_NS_LAYOUT
 
+ifdef ENABLE_TESTS
+DEFINES += -DDOM_STORAGE_TESTS
+endif
+
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
--- a/dom/src/storage/PStorage.ipdl
+++ b/dom/src/storage/PStorage.ipdl
@@ -1,67 +1,43 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 protocol PContent;
 
-using mozilla::null_t;
-
 namespace mozilla {
 namespace dom {
 
-struct ItemData
-{
-  nsString value;
-  bool secure;
-};
-
-// A cross-process GetValue result -- either null, or containing the parameters
-// with which to initialize an nsIDOMStorageItem.
-union StorageItem
-{
-  null_t;
-  ItemData;
-};
-
-// This protocol is little more than a thin wrapper around the DOMStorageBase
-// class in nsDOMStorage.h.  The child implementation simply forwards the
-// arguments for any given call to the parent, and returns the result.
+/* This protocol bridges async access to the database thread running on the parent process
+ * and caches running on the child process.
+ */
 sync protocol PStorage
 {
   manager PContent;
 
 parent:
-  __delete__();
+  async __delete__();
+
+  sync  Preload(nsCString scope, uint32_t alreadyLoadedCount)
+    returns (nsString[] keys, nsString[] values, nsresult rv);
 
-  Init(bool useDB, bool sessionOnly, bool isPrivate,
-       nsCString scopeDBKey, nsCString quotaDBKey, uint32_t storageType);
+  async AsyncPreload(nsCString scope, bool priority);
+  async AsyncGetUsage(nsCString scope);
+  async AsyncAddItem(nsCString scope, nsString key, nsString value);
+  async AsyncUpdateItem(nsCString scope, nsString key, nsString value);
+  async AsyncRemoveItem(nsCString scope, nsString key);
+  async AsyncClear(nsCString scope);
+  async AsyncFlush();
   
-  sync GetKeys(bool callerSecure)
-      returns (nsString[] keys);
-  sync GetLength(bool callerSecure, bool sessionOnly)
-      returns (uint32_t length, nsresult rv);
-  sync GetKey(bool callerSecure, bool sessionOnly, uint32_t index)
-      returns (nsString key, nsresult rv);
-  sync GetValue(bool callerSecure, bool sessionOnly, nsString key)
-      returns (StorageItem item, nsresult rv);
-  sync SetValue(bool callerSecure, bool sessionOnly, nsString key, nsString data)
-      returns (nsString oldValue, nsresult rv);
-  sync RemoveValue(bool callerSecure, bool sessionOnly, nsString key)
-      returns (nsString oldValue, nsresult rv);
-  sync Clear(bool callerSecure, bool sessionOnly)
-      returns (int32_t oldCount, nsresult rv);
-
-  sync GetDBValue(nsString key)
-      returns (nsString value, bool secure, nsresult rv);
-  sync SetDBValue(nsString key, nsString value, bool secure)
-      returns (nsresult rv);
-  sync SetSecure(nsString key, bool secure)
-      returns (nsresult rv);
-
-  UpdatePrivateState(bool enabled);
+child:
+  async Observe(nsCString topic, nsCString scopePrefix);
+  async ScopesHavingData(nsCString[] scopes);
+  async LoadItem(nsCString scope, nsString key, nsString value);
+  async LoadDone(nsCString scope, nsresult rv);
+  async LoadUsage(nsCString scope, int64_t usage);
+  async Error(nsresult rv);
 };
 
 }
 }
deleted file mode 100644
--- a/dom/src/storage/StorageChild.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
-/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
-/* 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 "StorageChild.h"
-#include "mozilla/dom/ContentChild.h"
-#include "nsError.h"
-
-#include "GeckoProfiler.h"
-
-namespace mozilla {
-namespace dom {
-
-NS_IMPL_CYCLE_COLLECTION_1(StorageChild, mStorage)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageChild)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageChild)
-  NS_INTERFACE_MAP_ENTRY(nsIPrivacyTransitionObserver)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrivacyTransitionObserver)
-NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP_(nsrefcnt) StorageChild::Release(void)
-{
-  NS_PRECONDITION(0 != mRefCnt, "dup release");
-  NS_ASSERT_OWNINGTHREAD(StorageChild);
-  nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(StorageChild)::Upcast(this);
-  nsrefcnt count = mRefCnt.decr(base);
-  NS_LOG_RELEASE(this, count, "StorageChild");
-  if (count == 1 && mIPCOpen) {
-    Send__delete__(this);
-    return 0;
-  }
-  if (count == 0) {
-    mRefCnt.stabilizeForDeletion();
-    delete this;
-    return 0;
-  }
-  return count;
-}
-
-StorageChild::StorageChild(nsDOMStorage* aOwner)
-: mStorage(aOwner)
-, mIPCOpen(false)
-{
-}
-
-StorageChild::StorageChild(nsDOMStorage* aOwner, StorageChild& aOther)
-: DOMStorageBase(aOther)
-, mStorage(aOwner)
-, mIPCOpen(false)
-{
-}
-
-void
-StorageChild::AddIPDLReference()
-{
-  NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
-  mIPCOpen = true;
-  AddRef();
-}
-
-void
-StorageChild::ReleaseIPDLReference()
-{
-  NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
-  mIPCOpen = false;
-  Release();
-}
-
-bool
-StorageChild::CacheStoragePermissions()
-{
-  nsDOMStorage* storage = static_cast<nsDOMStorage*>(mStorage.get());
-  return storage->CacheStoragePermissions();
-}
-
-void
-StorageChild::InitRemote()
-{
-  ContentChild* child = ContentChild::GetSingleton();
-  AddIPDLReference();
-  child->SendPStorageConstructor(this, null_t());
-  SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing, mScopeDBKey,
-           mQuotaDBKey, mStorageType);
-}
-
-void
-StorageChild::InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate)
-{
-  DOMStorageBase::InitAsSessionStorage(aPrincipal, aPrivate);
-  InitRemote();
-}
-
-void
-StorageChild::InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate)
-{
-  DOMStorageBase::InitAsLocalStorage(aPrincipal, aPrivate);
-  InitRemote();
-}
-
-nsTArray<nsString>*
-StorageChild::GetKeys(bool aCallerSecure)
-{
-  InfallibleTArray<nsString> remoteKeys;
-  SendGetKeys(aCallerSecure, &remoteKeys);
-  nsTArray<nsString>* keys = new nsTArray<nsString>;
-  *keys = remoteKeys;
-  return keys;
-}
-
-nsresult
-StorageChild::GetLength(bool aCallerSecure, uint32_t* aLength)
-{
-  nsresult rv;
-  SendGetLength(aCallerSecure, mSessionOnly, aLength, &rv);
-  return rv;
-}
-
-nsresult
-StorageChild::GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey)
-{
-  nsresult rv;
-  nsString key;
-  SendGetKey(aCallerSecure, mSessionOnly, aIndex, &key, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  aKey = key;
-  return NS_OK;
-}
-
-// Unlike other cross-process forwarding methods, GetValue needs to replicate
-// the following behaviour of DOMStorageImpl::GetValue:
-//
-// - if a security error occurs, or the item isn't found, return null without
-//   propogating the error.
-//
-// If DOMStorageImpl::GetValue ever changes its behaviour, this should be kept
-// in sync.
-nsIDOMStorageItem*
-StorageChild::GetValue(bool aCallerSecure, const nsAString& aKey, nsresult* rv)
-{
-  PROFILER_LABEL("StorageChild", "GetValue");
-  nsresult rv2 = *rv = NS_OK;
-  StorageItem storageItem;
-  SendGetValue(aCallerSecure, mSessionOnly, nsString(aKey), &storageItem, &rv2);
-  if (rv2 == NS_ERROR_DOM_SECURITY_ERR || rv2 == NS_ERROR_DOM_NOT_FOUND_ERR)
-    return nullptr;
-  *rv = rv2;
-  if (NS_FAILED(*rv) || storageItem.type() == StorageItem::Tnull_t)
-    return nullptr;
-  const ItemData& data = storageItem.get_ItemData();
-  nsIDOMStorageItem* item = new nsDOMStorageItem(this, aKey, data.value(),
-                                                 data.secure());
-  return item;
-}
-
-nsresult
-StorageChild::SetValue(bool aCallerSecure, const nsAString& aKey,
-                       const nsAString& aData, nsAString& aOldData)
-{
-  nsresult rv;
-  nsString oldData;
-  SendSetValue(aCallerSecure, mSessionOnly, nsString(aKey), nsString(aData),
-               &oldData, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  aOldData = oldData;
-  return NS_OK;
-}
-
-nsresult
-StorageChild::RemoveValue(bool aCallerSecure, const nsAString& aKey,
-                          nsAString& aOldData)
-{
-  nsresult rv;
-  nsString oldData;
-  SendRemoveValue(aCallerSecure, mSessionOnly, nsString(aKey), &oldData, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  aOldData = oldData;
-  return NS_OK;
-}
-
-nsresult
-StorageChild::Clear(bool aCallerSecure, int32_t* aOldCount)
-{
-  nsresult rv;
-  int32_t oldCount;
-  SendClear(aCallerSecure, mSessionOnly, &oldCount, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  *aOldCount = oldCount;
-  return NS_OK;
-}
-
-nsresult
-StorageChild::GetDBValue(const nsAString& aKey, nsAString& aValue,
-                         bool* aSecure)
-{
-  nsresult rv;
-  nsString value;
-  SendGetDBValue(nsString(aKey), &value, aSecure, &rv);
-  aValue = value;
-  return rv;
-}
-
-nsresult
-StorageChild::SetDBValue(const nsAString& aKey,
-                         const nsAString& aValue,
-                         bool aSecure)
-{
-  nsresult rv;
-  SendSetDBValue(nsString(aKey), nsString(aValue), aSecure, &rv);
-  return rv;
-}
-
-nsresult
-StorageChild::SetSecure(const nsAString& aKey, bool aSecure)
-{
-  nsresult rv;
-  SendSetSecure(nsString(aKey), aSecure, &rv);
-  return rv;
-}
-
-nsresult
-StorageChild::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
-{
-  StorageChild* other = static_cast<StorageChild*>(aThat);
-  ContentChild* child = ContentChild::GetSingleton();
-  StorageClone clone(nullptr, other, aCallerSecure);
-  AddIPDLReference();
-  child->SendPStorageConstructor(this, clone);
-  SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing,
-           mScopeDBKey, mQuotaDBKey, mStorageType);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-StorageChild::PrivateModeChanged(bool enabled)
-{
-  mInPrivateBrowsing = enabled;
-  SendUpdatePrivateState(enabled);
-  return NS_OK;
-}
-
-}
-}
deleted file mode 100644
--- a/dom/src/storage/StorageChild.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
-/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
-/* 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 mozilla_dom_StorageChild_h
-#define mozilla_dom_StorageChild_h
-
-#include "mozilla/dom/PStorageChild.h"
-#include "nsDOMStorage.h"
-#include "nsCycleCollectionParticipant.h"
-
-namespace mozilla {
-namespace dom {
-
-class StorageChild : public PStorageChild
-                   , public DOMStorageBase
-                   , public nsSupportsWeakReference
-{
-public:
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(StorageChild, nsIPrivacyTransitionObserver)
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
-  
-  StorageChild(nsDOMStorage* aOwner);
-  StorageChild(nsDOMStorage* aOwner, StorageChild& aOther);
-
-  virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
-  virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
-
-  virtual bool CacheStoragePermissions();
-  
-  virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
-  virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
-  virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
-  virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
-                                      nsresult* rv);
-  virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
-                            const nsAString& aData, nsAString& aOldValue);
-  virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
-                               nsAString& aOldValue);
-  virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount);
-
-  virtual nsresult GetDBValue(const nsAString& aKey,
-                              nsAString& aValue,
-                              bool* aSecure);
-  virtual nsresult SetDBValue(const nsAString& aKey,
-                              const nsAString& aValue,
-                              bool aSecure);
-  virtual nsresult SetSecure(const nsAString& aKey, bool aSecure);
-
-  virtual nsresult CloneFrom(bool aCallerSecure, DOMStorageBase* aThat);
-
-  void AddIPDLReference();
-  void ReleaseIPDLReference();
-
-private:
-  void InitRemote();
-
-  // Unimplemented
-  StorageChild(const StorageChild&);
-
-  nsCOMPtr<nsIDOMStorageObsolete> mStorage;
-  bool mIPCOpen;
-};
-
-}
-}
-
-#endif
deleted file mode 100644
--- a/dom/src/storage/StorageParent.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
-/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
-/* 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 "StorageParent.h"
-#include "mozilla/dom/PContentParent.h"
-#include "mozilla/unused.h"
-#include "nsDOMString.h"
-
-using mozilla::unused;
-
-namespace mozilla {
-namespace dom {
-
-StorageParent::StorageParent(const StorageConstructData& aData)
-{
-  if (aData.type() == StorageConstructData::Tnull_t) {
-    mStorage = new DOMStorageImpl(nullptr);
-  } else {
-    const StorageClone& clone = aData.get_StorageClone();
-    StorageParent* other = static_cast<StorageParent*>(clone.actorParent());
-    mStorage = new DOMStorageImpl(nullptr, *other->mStorage.get());
-    mStorage->CloneFrom(clone.callerSecure(), other->mStorage);
-  }
-}
-
-bool
-StorageParent::RecvInit(const bool& aUseDB,
-                        const bool& aSessionOnly,
-                        const bool& aPrivate,
-                        const nsCString& aScopeDBKey,
-                        const nsCString& aQuotaDBKey,
-                        const uint32_t& aStorageType)
-{
-  mStorage->InitFromChild(aUseDB, aSessionOnly, aPrivate,
-                          aScopeDBKey, aQuotaDBKey,
-                          aStorageType);
-  return true;
-}
-
-bool
-StorageParent::RecvUpdatePrivateState(const bool& aEnabled)
-{
-  mStorage->PrivateModeChanged(aEnabled);
-  return true;
-}
-
-bool
-StorageParent::RecvGetKeys(const bool& aCallerSecure, InfallibleTArray<nsString>* aKeys)
-{
-  // Callers are responsible for deallocating the array returned by mStorage->GetKeys
-  nsAutoPtr<nsTArray<nsString> > keys(mStorage->GetKeys(aCallerSecure));
-  aKeys->SwapElements(*keys);
-  return true;
-}
-
-bool
-StorageParent::RecvGetLength(const bool& aCallerSecure, const bool& aSessionOnly,
-                             uint32_t* aLength, nsresult* rv)
-{
-  mStorage->SetSessionOnly(aSessionOnly);
-  *rv = mStorage->GetLength(aCallerSecure, aLength);
-  return true;
-}
-
-bool
-StorageParent::RecvGetKey(const bool& aCallerSecure, const bool& aSessionOnly,
-                          const uint32_t& aIndex, nsString* aKey, nsresult* rv)
-{
-  mStorage->SetSessionOnly(aSessionOnly);
-  *rv = mStorage->GetKey(aCallerSecure, aIndex, *aKey);
-  return true;
-}
-
-bool
-StorageParent::RecvGetValue(const bool& aCallerSecure, const bool& aSessionOnly,
-                            const nsString& aKey, StorageItem* aItem,
-                            nsresult* rv)
-{
-  mStorage->SetSessionOnly(aSessionOnly);
-
-  // We need to ensure that a proper null representation is sent to the child
-  // if no item is found or an error occurs.
-
-  *rv = NS_OK;
-  nsCOMPtr<nsIDOMStorageItem> item = mStorage->GetValue(aCallerSecure, aKey, rv);
-  if (NS_FAILED(*rv) || !item) {
-    *aItem = null_t();
-    return true;
-  }
-
-  ItemData data(EmptyString(), false);
-  nsDOMStorageItem* internalItem = static_cast<nsDOMStorageItem*>(item.get());
-  data.value() = internalItem->GetValueInternal();
-  if (aCallerSecure)
-    data.secure() = internalItem->IsSecure();
-  *aItem = data;
-  return true;
-}
-
-bool
-StorageParent::RecvSetValue(const bool& aCallerSecure, const bool& aSessionOnly,
-                            const nsString& aKey, const nsString& aData,
-                            nsString* aOldValue, nsresult* rv)
-{
-  mStorage->SetSessionOnly(aSessionOnly);
-  *rv = mStorage->SetValue(aCallerSecure, aKey, aData, *aOldValue);
-  return true;
-}
-
-bool
-StorageParent::RecvRemoveValue(const bool& aCallerSecure, const bool& aSessionOnly,
-                               const nsString& aKey, nsString* aOldValue,
-                               nsresult* rv)
-{
-  mStorage->SetSessionOnly(aSessionOnly);
-  *rv = mStorage->RemoveValue(aCallerSecure, aKey, *aOldValue);
-  return true;
-}
-
-bool
-StorageParent::RecvClear(const bool& aCallerSecure, const bool& aSessionOnly,
-                         int32_t* aOldCount, nsresult* rv)
-{
-  mStorage->SetSessionOnly(aSessionOnly);
-  *rv = mStorage->Clear(aCallerSecure, aOldCount);
-  return true;
-}
-
-bool
-StorageParent::RecvGetDBValue(const nsString& aKey, nsString* aValue,
-                              bool* aSecure, nsresult* rv)
-{
-  *rv = mStorage->GetDBValue(aKey, *aValue, aSecure);
-  return true;
-}
-
-bool
-StorageParent::RecvSetDBValue(const nsString& aKey, const nsString& aValue,
-                              const bool& aSecure, nsresult* rv)
-{
-  *rv = mStorage->SetDBValue(aKey, aValue, aSecure);
-  return true;
-}
-
-bool
-StorageParent::RecvSetSecure(const nsString& aKey, const bool& aSecure,
-                             nsresult* rv)
-{
-  *rv = mStorage->SetSecure(aKey, aSecure);
-  return true;
-}
-
-}
-}
deleted file mode 100644
--- a/dom/src/storage/StorageParent.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
-/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
-/* 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 mozilla_dom_StorageParent_h
-#define mozilla_dom_StorageParent_h
-
-#include "mozilla/dom/PStorageParent.h"
-#include "nsDOMStorage.h"
-
-namespace mozilla {
-namespace dom {
-
-class StorageConstructData;
-
-class StorageParent : public PStorageParent
-{
-public:
-  StorageParent(const StorageConstructData& aData);
-
-private:
-  bool RecvGetKeys(const bool& aCallerSecure, InfallibleTArray<nsString>* aKeys);
-  bool RecvGetLength(const bool& aCallerSecure, const bool& aSessionOnly,
-                     uint32_t* aLength, nsresult* rv);
-  bool RecvGetKey(const bool& aCallerSecure, const bool& aSessionOnly,
-                  const uint32_t& aIndex,nsString* aKey, nsresult* rv);
-  bool RecvGetValue(const bool& aCallerSecure, const bool& aSessionOnly,
-                    const nsString& aKey, StorageItem* aItem, nsresult* rv);
-  bool RecvSetValue(const bool& aCallerSecure, const bool& aSessionOnly,
-                    const nsString& aKey, const nsString& aData,
-                    nsString* aOldValue, nsresult* rv);
-  bool RecvRemoveValue(const bool& aCallerSecure, const bool& aSessionOnly,
-                       const nsString& aKey, nsString* aOldData, nsresult* rv);
-  bool RecvClear(const bool& aCallerSecure, const bool& aSessionOnly,
-                 int32_t* aOldCount, nsresult* rv);
-
-  bool RecvGetDBValue(const nsString& aKey, nsString* aValue, bool* aSecure,
-                      nsresult* rv);
-  bool RecvSetDBValue(const nsString& aKey, const nsString& aValue,
-                      const bool& aSecure, nsresult* rv);
-  bool RecvSetSecure(const nsString& aKey, const bool& aSecure, nsresult* rv);
-
-  bool RecvInit(const bool& aUseDB,
-                const bool& aSessionOnly,
-                const bool& aPrivate,
-                const nsCString& aScopeDBKey,
-                const nsCString& aQuotaDBKey,
-                const uint32_t& aStorageType);
-
-  bool RecvUpdatePrivateState(const bool& aEnabled);
-
-  nsRefPtr<DOMStorageImpl> mStorage;
-};
-
-}
-}
-
-#endif
deleted file mode 100644
--- a/dom/src/storage/nsDOMStorage.cpp
+++ /dev/null
@@ -1,1808 +0,0 @@
-/* -*- Mode: C++; 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 "mozilla/DebugOnly.h"
-
-#include "StorageChild.h"
-#include "StorageParent.h"
-#include "mozilla/dom/ContentChild.h"
-#include "nsXULAppAPI.h"
-using mozilla::dom::StorageChild;
-using mozilla::dom::ContentChild;
-
-#include "prnetdb.h"
-#include "nsCOMPtr.h"
-#include "nsError.h"
-#include "nsDOMClassInfoID.h"
-#include "nsDOMJSUtils.h"
-#include "nsUnicharUtils.h"
-#include "nsDOMStorage.h"
-#include "nsEscape.h"
-#include "nsContentUtils.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsIPrincipal.h"
-#include "nsIURI.h"
-#include "nsReadableUtils.h"
-#include "nsIObserverService.h"
-#include "nsNetUtil.h"
-#include "nsICookiePermission.h"
-#include "nsIPermission.h"
-#include "nsIPermissionManager.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsIJSContextStack.h"
-#include "nsDOMString.h"
-#include "nsNetCID.h"
-#include "mozilla/Preferences.h"
-#include "nsThreadUtils.h"
-#include "mozilla/Telemetry.h"
-#include "DictionaryHelpers.h"
-#include "GeneratedEvents.h"
-#include "mozIApplicationClearPrivateDataParams.h"
-
-// calls FlushAndEvictFromCache(false)
-#define NS_DOMSTORAGE_FLUSH_TIMER_TOPIC "domstorage-flush-timer"
-
-// calls FlushAndEvictFromCache(false)
-#define NS_DOMSTORAGE_FLUSH_FORCE_TOPIC "domstorage-flush-force"
-
-using namespace mozilla;
-
-static const uint32_t ASK_BEFORE_ACCEPT = 1;
-static const uint32_t ACCEPT_SESSION = 2;
-static const uint32_t BEHAVIOR_REJECT = 2;
-
-static const char kPermissionType[] = "cookie";
-static const char kStorageEnabled[] = "dom.storage.enabled";
-static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
-static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
-
-//
-// Helper that tells us whether the caller is secure or not.
-//
-
-static bool
-IsCallerSecure()
-{
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv = nsContentUtils::GetSecurityManager()->
-                  GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  if (!subjectPrincipal) {
-    // No subject principal means no code is running. Default to not
-    // being secure in that case.
-
-    return false;
-  }
-
-  nsCOMPtr<nsIURI> codebase;
-  subjectPrincipal->GetURI(getter_AddRefs(codebase));
-
-  if (!codebase) {
-    return false;
-  }
-
-  nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(codebase);
-
-  if (!innerUri) {
-    return false;
-  }
-
-  bool isHttps = false;
-  rv = innerUri->SchemeIs("https", &isHttps);
-
-  return NS_SUCCEEDED(rv) && isHttps;
-}
-
-nsSessionStorageEntry::nsSessionStorageEntry(KeyTypePointer aStr)
-  : nsStringHashKey(aStr), mItem(nullptr)
-{
-}
-
-nsSessionStorageEntry::nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy)
-  : nsStringHashKey(aToCopy), mItem(nullptr)
-{
-  NS_ERROR("We're horked.");
-}
-
-nsSessionStorageEntry::~nsSessionStorageEntry()
-{
-}
-
-//
-// nsDOMStorageManager
-//
-
-nsDOMStorageManager* nsDOMStorageManager::gStorageManager;
-
-nsDOMStorageManager::nsDOMStorageManager()
-{
-}
-
-NS_IMPL_ISUPPORTS3(nsDOMStorageManager,
-                   nsIDOMStorageManager,
-                   nsIObserver,
-                   nsISupportsWeakReference)
-
-//static
-nsresult
-nsDOMStorageManager::Initialize()
-{
-  gStorageManager = new nsDOMStorageManager();
-  if (!gStorageManager)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  gStorageManager->mStorages.Init();
-  NS_ADDREF(gStorageManager);
-
-  // No observers needed in non-chrome
-  if (XRE_GetProcessType() != GeckoProcessType_Default)
-    return NS_OK;
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (!os)
-    return NS_OK;
-
-  nsresult rv;
-  rv = os->AddObserver(gStorageManager, "cookie-changed", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = os->AddObserver(gStorageManager, "profile-after-change", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = os->AddObserver(gStorageManager, "perm-changed", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = os->AddObserver(gStorageManager, "browser:purge-domain-data", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  // Used for temporary table flushing
-  rv = os->AddObserver(gStorageManager, "profile-before-change", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = os->AddObserver(gStorageManager, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC, true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = os->AddObserver(gStorageManager, "last-pb-context-exited", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = os->AddObserver(gStorageManager, "webapps-clear-data", true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-//static
-nsDOMStorageManager*
-nsDOMStorageManager::GetInstance()
-{
-  NS_ASSERTION(gStorageManager,
-               "nsDOMStorageManager::GetInstance() called before Initialize()");
-  NS_IF_ADDREF(gStorageManager);
-  return gStorageManager;
-}
-
-//static
-void
-nsDOMStorageManager::Shutdown()
-{
-  NS_IF_RELEASE(gStorageManager);
-  gStorageManager = nullptr;
-
-  ShutdownDB();
-}
-
-//static
-void
-nsDOMStorageManager::ShutdownDB()
-{
-  delete DOMStorageImpl::gStorageDB;
-  DOMStorageImpl::gStorageDB = nullptr;
-}
-
-static PLDHashOperator
-ClearStorage(nsDOMStorageEntry* aEntry, void* userArg)
-{
-  aEntry->mStorage->ClearAll();
-  return PL_DHASH_REMOVE;
-}
-
-static PLDHashOperator
-ClearStorageIfDomainMatches(nsDOMStorageEntry* aEntry, void* userArg)
-{
-  nsAutoCString* aKey = static_cast<nsAutoCString*> (userArg);
-  if (StringBeginsWith(aEntry->mStorage->GetScopeDBKey(), *aKey)) {
-    aEntry->mStorage->ClearAll();
-  }
-  return PL_DHASH_REMOVE;
-}
-
-nsresult
-nsDOMStorageManager::Observe(nsISupports *aSubject,
-                             const char *aTopic,
-                             const PRUnichar *aData)
-{
-  if (!strcmp(aTopic, "profile-after-change")) {
-  } else if (!strcmp(aTopic, "cookie-changed") &&
-             !nsCRT::strcmp(aData, NS_LITERAL_STRING("cleared").get())) {
-    mStorages.EnumerateEntries(ClearStorage, nullptr);
-
-    nsresult rv = DOMStorageImpl::InitDB();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return DOMStorageImpl::gStorageDB->RemoveAll();
-  } else if (!strcmp(aTopic, "perm-changed")) {
-    // Check for cookie permission change
-    nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
-    if (perm) {
-      nsAutoCString type;
-      perm->GetType(type);
-      if (type != NS_LITERAL_CSTRING("cookie"))
-        return NS_OK;
-
-      uint32_t cap = 0;
-      perm->GetCapability(&cap);
-      if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
-          nsDependentString(aData) != NS_LITERAL_STRING("deleted"))
-        return NS_OK;
-
-      nsAutoCString host;
-      perm->GetHost(host);
-      if (host.IsEmpty())
-        return NS_OK;
-
-      nsresult rv = DOMStorageImpl::InitDB();
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      return DOMStorageImpl::gStorageDB->DropSessionOnlyStoragesForHost(host);
-    }
-  } else if (!strcmp(aTopic, "timer-callback")) {
-    nsCOMPtr<nsIObserverService> obsserv = mozilla::services::GetObserverService();
-    if (obsserv)
-      obsserv->NotifyObservers(nullptr, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC, nullptr);
-  } else if (!strcmp(aTopic, "browser:purge-domain-data")) {
-    // Convert the domain name to the ACE format
-    nsAutoCString aceDomain;
-    nsresult rv;
-    nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
-    if (converter) {
-      rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else {
-      // In case the IDN service is not available, this is the best we can come up with!
-      NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
-                   esc_OnlyNonASCII | esc_AlwaysCopy,
-                   aceDomain);
-    }
-
-    nsAutoCString key;
-    rv = nsDOMStorageDBWrapper::CreateReversedDomain(aceDomain, key);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Clear the storage entries for matching domains
-    mStorages.EnumerateEntries(ClearStorageIfDomainMatches, &key);
-
-    rv = DOMStorageImpl::InitDB();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    DOMStorageImpl::gStorageDB->RemoveOwner(aceDomain);
-  } else if (!strcmp(aTopic, "profile-before-change")) {
-    if (DOMStorageImpl::gStorageDB) {
-      DebugOnly<nsresult> rv =
-        DOMStorageImpl::gStorageDB->FlushAndEvictFromCache(true);
-      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                       "DOMStorage: cache commit failed");
-      DOMStorageImpl::gStorageDB->Close();
-      nsDOMStorageManager::ShutdownDB();
-    }
-  } else if (!strcmp(aTopic, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC)) {
-    if (DOMStorageImpl::gStorageDB) {
-      DebugOnly<nsresult> rv =
-        DOMStorageImpl::gStorageDB->FlushAndEvictFromCache(false);
-      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                       "DOMStorage: cache commit failed");
-    }
-  } else if (!strcmp(aTopic, NS_DOMSTORAGE_FLUSH_FORCE_TOPIC)) {
-    if (DOMStorageImpl::gStorageDB) {
-      DebugOnly<nsresult> rv =
-        DOMStorageImpl::gStorageDB->FlushAndEvictFromCache(false);
-      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                       "DOMStorage: cache  commit failed");
-    }
-  } else if (!strcmp(aTopic, "last-pb-context-exited")) {
-    if (DOMStorageImpl::gStorageDB) {
-      return DOMStorageImpl::gStorageDB->DropPrivateBrowsingStorages();
-    }
-  } else if (!strcmp(aTopic, "webapps-clear-data")) {
-    if (!DOMStorageImpl::gStorageDB) {
-      return NS_OK;
-    }
-
-    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
-      do_QueryInterface(aSubject);
-    if (!params) {
-      NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    uint32_t appId;
-    bool browserOnly;
-
-    nsresult rv = params->GetAppId(&appId);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = params->GetBrowserOnly(&browserOnly);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
-
-    return DOMStorageImpl::gStorageDB->RemoveAllForApp(appId, browserOnly);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMStorageManager::GetUsage(const nsAString& aDomain,
-                              int32_t *aUsage)
-{
-  nsresult rv = DOMStorageImpl::InitDB();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return DOMStorageImpl::gStorageDB->GetUsage(NS_ConvertUTF16toUTF8(aDomain),
-                                              aUsage, false);
-}
-
-NS_IMETHODIMP
-nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal *aPrincipal,
-                                                 const nsSubstring &aDocumentURI,
-                                                 bool aPrivate,
-                                                 nsIDOMStorage **aResult)
-{
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-  *aResult = nullptr;
-
-  nsresult rv;
-
-  nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
-  if (!storage)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  rv = storage->InitAsLocalStorage(aPrincipal, aDocumentURI, aPrivate);
-  if (NS_FAILED(rv))
-    return rv;
-
-  *aResult = storage.get();
-  storage.forget();
-
-  return NS_OK;
-}
-
-void
-nsDOMStorageManager::AddToStoragesHash(DOMStorageImpl* aStorage)
-{
-  nsDOMStorageEntry* entry = mStorages.PutEntry(aStorage);
-  if (entry)
-    entry->mStorage = aStorage;
-}
-
-void
-nsDOMStorageManager::RemoveFromStoragesHash(DOMStorageImpl* aStorage)
-{
-  nsDOMStorageEntry* entry = mStorages.GetEntry(aStorage);
-  if (entry)
-    mStorages.RemoveEntry(aStorage);
-}
-
-//
-// nsDOMStorage
-//
-
-nsDOMStorageDBWrapper* DOMStorageImpl::gStorageDB = nullptr;
-
-nsDOMStorageEntry::nsDOMStorageEntry(KeyTypePointer aStr)
-  : nsPtrHashKey<const void>(aStr), mStorage(nullptr)
-{
-}
-
-nsDOMStorageEntry::nsDOMStorageEntry(const nsDOMStorageEntry& aToCopy)
-  : nsPtrHashKey<const void>(aToCopy), mStorage(nullptr)
-{
-  NS_ERROR("DOMStorage horked.");
-}
-
-nsDOMStorageEntry::~nsDOMStorageEntry()
-{
-}
-
-NS_IMPL_CYCLE_COLLECTION_1(nsDOMStorage, mStorageImpl)
-
-DOMCI_DATA(StorageObsolete, nsDOMStorage)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorage)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorage)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageObsolete)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMStorageObsolete)
-  NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
-  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageObsolete)
-NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP
-nsDOMStorage::GetInterface(const nsIID & aIID, void **result)
-{
-  nsresult rv = mStorageImpl->QueryInterface(aIID, result);
-  if (NS_SUCCEEDED(rv))
-    return rv;
-  return QueryInterface(aIID, result);;
-}
-
-nsresult
-NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult)
-{
-  nsDOMStorage2* storage = new nsDOMStorage2();
-  return storage->QueryInterface(aIID, aResult);
-}
-
-DOMStorageBase::DOMStorageBase()
-  : mStorageType(nsPIDOMStorage::Unknown)
-  , mUseDB(false)
-  , mSessionOnly(true)
-  , mInPrivateBrowsing(false)
-{
-}
-
-DOMStorageBase::DOMStorageBase(DOMStorageBase& aThat)
-  : mStorageType(aThat.mStorageType)
-  , mUseDB(false) // Clones don't use the DB
-  , mSessionOnly(true)
-  , mScopeDBKey(aThat.mScopeDBKey)
-  , mQuotaDBKey(aThat.mQuotaDBKey)
-  , mInPrivateBrowsing(aThat.mInPrivateBrowsing)
-{
-}
-
-void
-DOMStorageBase::InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate)
-{
-  MOZ_ASSERT(mQuotaDBKey.IsEmpty());
-  mUseDB = false;
-  mScopeDBKey.Truncate();
-  mStorageType = nsPIDOMStorage::SessionStorage;
-  mInPrivateBrowsing = aPrivate;
-}
-
-void
-DOMStorageBase::InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate)
-{
-  nsDOMStorageDBWrapper::CreateScopeDBKey(aPrincipal, mScopeDBKey);
-
-  // XXX Bug 357323, we have to solve the issue how to define
-  // origin for file URLs. In that case CreateOriginScopeDBKey
-  // fails (the result is empty) and we must avoid database use
-  // in that case because it produces broken entries w/o owner.
-  mUseDB = !mScopeDBKey.IsEmpty();
-
-  nsDOMStorageDBWrapper::CreateQuotaDBKey(aPrincipal, mQuotaDBKey);
-  mStorageType = nsPIDOMStorage::LocalStorage;
-  mInPrivateBrowsing = aPrivate;
-}
-
-PLDHashOperator
-SessionStorageTraverser(nsSessionStorageEntry* aEntry, void* userArg) {
-  nsCycleCollectionTraversalCallback *cb = 
-      static_cast<nsCycleCollectionTraversalCallback*>(userArg);
-
-  cb->NoteXPCOMChild((nsIDOMStorageItem *) aEntry->mItem);
-
-  return PL_DHASH_NEXT;
-}
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_0(DOMStorageImpl)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMStorageImpl)
-{
-  if (tmp->mItems.IsInitialized()) {
-    tmp->mItems.EnumerateEntries(SessionStorageTraverser, &cb);
-  }
-}
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMStorageImpl)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMStorageImpl)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStorageImpl)
-  NS_INTERFACE_MAP_ENTRY(nsIPrivacyTransitionObserver)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrivacyTransitionObserver)
-NS_INTERFACE_MAP_END
-
-DOMStorageImpl::DOMStorageImpl(nsDOMStorage* aStorage)
-{
-  Init(aStorage);
-}
-
-DOMStorageImpl::DOMStorageImpl(nsDOMStorage* aStorage, DOMStorageImpl& aThat)
-  : DOMStorageBase(aThat)
-{
-  Init(aStorage);
-}
-
-void
-DOMStorageImpl::Init(nsDOMStorage* aStorage)
-{
-  mItemsCachedVersion = 0;
-  mItems.Init(8);
-  mOwner = aStorage;
-  if (nsDOMStorageManager::gStorageManager)
-    nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
-}
-
-DOMStorageImpl::~DOMStorageImpl()
-{
-  if (nsDOMStorageManager::gStorageManager)
-    nsDOMStorageManager::gStorageManager->RemoveFromStoragesHash(this);
-}
-
-nsresult
-DOMStorageImpl::InitDB()
-{
-  if (!gStorageDB) {
-    gStorageDB = new nsDOMStorageDBWrapper();
-    if (!gStorageDB)
-      return NS_ERROR_OUT_OF_MEMORY;
-
-    nsresult rv = gStorageDB->Init();
-    if (NS_FAILED(rv)) {
-      // Failed to initialize the DB, delete it and null out the
-      // pointer so we don't end up attempting to use an
-      // un-initialized DB later on.
-
-      delete gStorageDB;
-      gStorageDB = nullptr;
-
-      return rv;
-    }
-  }
-
-  return NS_OK;
-}
-
-void
-DOMStorageImpl::InitFromChild(bool aUseDB,
-                              bool aSessionOnly, bool aPrivate,
-                              const nsACString& aScopeDBKey,
-                              const nsACString& aQuotaDBKey,
-                              uint32_t aStorageType)
-{
-  mUseDB = aUseDB;
-  mSessionOnly = aSessionOnly;
-  mInPrivateBrowsing = aPrivate;
-  mScopeDBKey = aScopeDBKey;
-  mQuotaDBKey = aQuotaDBKey;
-  mStorageType = static_cast<nsPIDOMStorage::nsDOMStorageType>(aStorageType);
-}
-
-void
-DOMStorageImpl::SetSessionOnly(bool aSessionOnly)
-{
-  mSessionOnly = aSessionOnly;
-}
-
-bool
-DOMStorageImpl::CacheStoragePermissions()
-{
-  // If this is a cross-process situation, we don't have a real storage owner.
-  // All the correct checks have been done on the child, so we just need to
-  // make sure that our session-only status is correctly updated.
-  if (!mOwner)
-    return CanUseStorage();
-  
-  return mOwner->CacheStoragePermissions();
-}
-
-nsresult
-DOMStorageImpl::GetCachedValue(const nsAString& aKey, nsAString& aValue,
-                               bool* aSecure)
-{
-  aValue.Truncate();
-  *aSecure = false;
-
-  nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
-  if (!entry)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  aValue = entry->mItem->GetValueInternal();
-  *aSecure = entry->mItem->IsSecure();
-
-  return NS_OK;
-}
-
-nsresult
-DOMStorageImpl::GetDBValue(const nsAString& aKey, nsAString& aValue,
-                           bool* aSecure)
-{
-  aValue.Truncate();
-
-  if (!UseDB())
-    return NS_OK;
-
-  nsresult rv = InitDB();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString value;
-  rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure);
-
-  if (rv == NS_ERROR_DOM_NOT_FOUND_ERR) {
-    SetDOMStringToNull(aValue);
-  }
-
-  if (NS_FAILED(rv))
-    return rv;
-
-  aValue.Assign(value);
-
-  return NS_OK;
-}
-
-nsresult
-DOMStorageImpl::SetDBValue(const nsAString& aKey,
-                           const nsAString& aValue,
-                           bool aSecure)
-{
-  if (!UseDB())
-    return NS_OK;
-
-  nsresult rv = InitDB();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  CacheKeysFromDB();
-
-  rv = gStorageDB->SetKey(this, aKey, aValue, aSecure);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-DOMStorageImpl::SetSecure(const nsAString& aKey, bool aSecure)
-{
-  if (UseDB()) {
-    nsresult rv = InitDB();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return gStorageDB->SetSecure(this, aKey, aSecure);
-  }
-
-  nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
-  NS_ASSERTION(entry, "Don't use SetSecure() with nonexistent keys!");
-
-  if (entry) {
-    entry->mItem->SetSecureInternal(aSecure);
-  }  
-
-  return NS_OK;
-}
-
-static PLDHashOperator
-ClearStorageItem(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  aEntry->mItem->SetValueInternal(EmptyString());
-  return PL_DHASH_NEXT;
-}
-
-void
-DOMStorageImpl::ClearAll()
-{
-  mItems.EnumerateEntries(ClearStorageItem, nullptr);
-  mItemsCachedVersion = 0;
-}
-
-struct CopyArgs {
-  DOMStorageImpl* storage;
-  bool callerSecure;
-};
-
-static PLDHashOperator
-CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  // When copying items from one impl to another, we may not
-  // have an mOwner that we can call SetItem on. Therefore we need
-  // to replicate its behaviour.
-  
-  CopyArgs* args = static_cast<CopyArgs*>(userArg);
-
-  nsAutoString unused;
-  nsresult rv = args->storage->SetValue(args->callerSecure, aEntry->GetKey(),
-                                        aEntry->mItem->GetValueInternal(), unused);
-  if (NS_FAILED(rv))
-    return PL_DHASH_NEXT;
-
-  if (aEntry->mItem->IsSecure()) {
-    args->storage->SetSecure(aEntry->GetKey(), true);
-  }
-
-  return PL_DHASH_NEXT;
-}
-
-nsresult
-DOMStorageImpl::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
-{
-  // For various reasons, we no longer call SetItem in CopyStorageItems,
-  // so we need to ensure that the storage permissions are correct.
-  if (!CacheStoragePermissions())
-    return NS_ERROR_DOM_SECURITY_ERR;
-  
-  DOMStorageImpl* that = static_cast<DOMStorageImpl*>(aThat);
-  CopyArgs args = { this, aCallerSecure };
-  that->mItems.EnumerateEntries(CopyStorageItems, &args);
-  return NS_OK;
-}
-
-nsresult
-DOMStorageImpl::CacheKeysFromDB()
-{
-  // cache all the keys in the hash. This is used by the Length and Key methods
-  // use this cache for better performance. The disadvantage is that the
-  // order may break if someone changes the keys in the database directly.
-  if (gStorageDB->IsScopeDirty(this)) {
-    nsresult rv = InitDB();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mItems.Clear();
-
-    rv = gStorageDB->GetAllKeys(this, &mItems);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    gStorageDB->MarkScopeCached(this);
-  }
-
-  return NS_OK;
-}
-
-struct KeysArrayBuilderStruct
-{
-  bool callerIsSecure;
-  nsTArray<nsString> *keys;
-};
-
-static PLDHashOperator
-KeysArrayBuilder(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  KeysArrayBuilderStruct *keystruct = (KeysArrayBuilderStruct *)userArg;
-  
-  if (keystruct->callerIsSecure || !aEntry->mItem->IsSecure())
-    keystruct->keys->AppendElement(aEntry->GetKey());
-
-  return PL_DHASH_NEXT;
-}
-
-nsTArray<nsString>*
-DOMStorageImpl::GetKeys(bool aCallerSecure)
-{
-  if (UseDB())
-    CacheKeysFromDB();
-
-  KeysArrayBuilderStruct keystruct;
-  keystruct.callerIsSecure = aCallerSecure;
-  keystruct.keys = new nsTArray<nsString>();
-  if (keystruct.keys)
-    mItems.EnumerateEntries(KeysArrayBuilder, &keystruct);
- 
-  return keystruct.keys;
-}
-
-class ItemCounterState
-{
- public:
-  ItemCounterState(bool aIsCallerSecure)
-  : mIsCallerSecure(aIsCallerSecure), mCount(0)
-  {
-  }
-
-  bool mIsCallerSecure;
-  uint32_t mCount;
- private:
-  ItemCounterState(); // Not to be implemented
-};
-
-static PLDHashOperator
-ItemCounter(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  ItemCounterState *state = (ItemCounterState *)userArg;
-
-  if (state->mIsCallerSecure || !aEntry->mItem->IsSecure()) {
-    ++state->mCount;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
-nsresult
-DOMStorageImpl::GetLength(bool aCallerSecure, uint32_t* aLength)
-{
-  // Force reload of items from database.  This ensures sync localStorages for
-  // same origins among different windows.
-  if (UseDB())
-    CacheKeysFromDB();
-
-  ItemCounterState state(aCallerSecure);
-
-  mItems.EnumerateEntries(ItemCounter, &state);
-
-  *aLength = state.mCount;
-  return NS_OK;
-}
-
-class IndexFinderData
-{
- public:
-  IndexFinderData(bool aIsCallerSecure, uint32_t aWantedIndex)
-  : mIsCallerSecure(aIsCallerSecure), mIndex(0), mWantedIndex(aWantedIndex),
-    mItem(nullptr)
-  {
-  }
-
-  bool mIsCallerSecure;
-  uint32_t mIndex;
-  uint32_t mWantedIndex;
-  nsSessionStorageEntry *mItem;
-
- private:
-  IndexFinderData(); // Not to be implemented
-};
-
-static PLDHashOperator
-IndexFinder(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  IndexFinderData *data = (IndexFinderData *)userArg;
-
-  if (data->mIndex == data->mWantedIndex &&
-      (data->mIsCallerSecure || !aEntry->mItem->IsSecure())) {
-    data->mItem = aEntry;
-
-    return PL_DHASH_STOP;
-  }
-
-  ++data->mIndex;
-
-  return PL_DHASH_NEXT;
-}
-
-nsresult
-DOMStorageImpl::GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey)
-{
-  // XXX: This does a linear search for the key at index, which would
-  // suck if there's a large numer of indexes. Do we care? If so,
-  // maybe we need to have a lazily populated key array here or
-  // something?
-
-  if (UseDB()) {
-    CacheKeysFromDB();
-  }
-
-  IndexFinderData data(aCallerSecure, aIndex);
-  mItems.EnumerateEntries(IndexFinder, &data);
-
-  if (!data.mItem) {
-    // aIndex was larger than the number of accessible keys. Return null.
-    aKey.SetIsVoid(true);
-    return NS_OK;
-  }
-
-  aKey = data.mItem->GetKey();
-  return NS_OK;
-}
-
-// The behaviour of this function must be kept in sync with StorageChild::GetValue.
-// See the explanatory comment there for more details.
-nsIDOMStorageItem*
-DOMStorageImpl::GetValue(bool aCallerSecure, const nsAString& aKey,
-                         nsresult* aResult)
-{
-  nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
-  nsIDOMStorageItem* item = nullptr;
-  if (entry) {
-    if (aCallerSecure || !entry->mItem->IsSecure()) {
-      item = entry->mItem;
-    }
-  }
-  else if (UseDB()) {
-    bool secure;
-    nsAutoString value;
-    nsresult rv = GetDBValue(aKey, value, &secure);
-    // return null if access isn't allowed or the key wasn't found
-    if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR ||
-        (!aCallerSecure && secure))
-      return nullptr;
-
-    *aResult = rv;
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    nsRefPtr<nsDOMStorageItem> newitem =
-        new nsDOMStorageItem(this, aKey, value, secure);
-    if (newitem && (entry = mItems.PutEntry(aKey))) {
-      item = entry->mItem = newitem;
-    }
-    else {
-      *aResult = NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-  return item;
-}
-
-nsresult
-DOMStorageImpl::SetValue(bool aIsCallerSecure, const nsAString& aKey,
-                         const nsAString& aData, nsAString& aOldValue)
-{
-  nsresult rv;
-  nsString oldValue;
-  SetDOMStringToNull(oldValue);
-
-  // First store the value to the database, we need to do this before we update
-  // the mItems cache.  SetDBValue is using the old cached value to decide
-  // on quota checking.
-  if (UseDB()) {
-    rv = SetDBValue(aKey, aData, aIsCallerSecure);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
-  if (entry) {
-    if (entry->mItem->IsSecure() && !aIsCallerSecure) {
-      return NS_ERROR_DOM_SECURITY_ERR;
-    }
-    oldValue = entry->mItem->GetValueInternal();
-    entry->mItem->SetValueInternal(aData);
-  }
-  else {
-    nsRefPtr<nsDOMStorageItem> newitem =
-        new nsDOMStorageItem(this, aKey, aData, aIsCallerSecure);
-    if (!newitem)
-      return NS_ERROR_OUT_OF_MEMORY;
-    entry = mItems.PutEntry(aKey);
-    NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
-    entry->mItem = newitem;
-  }
-  aOldValue = oldValue;
-  return NS_OK;
-}
-
-nsresult
-DOMStorageImpl::RemoveValue(bool aCallerSecure, const nsAString& aKey,
-                            nsAString& aOldValue)
-{
-  nsString oldValue;
-  nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
-
-  if (entry && entry->mItem->IsSecure() && !aCallerSecure) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  if (UseDB()) {
-    nsresult rv = InitDB();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    CacheKeysFromDB();
-    entry = mItems.GetEntry(aKey);
-
-    nsAutoString value;
-    bool secureItem;
-    rv = GetDBValue(aKey, value, &secureItem);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!aCallerSecure && secureItem)
-      return NS_ERROR_DOM_SECURITY_ERR;
-
-    oldValue = value;
-
-    rv = gStorageDB->RemoveKey(this, aKey);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else if (entry) {
-    // clear string as StorageItems may be referencing this item
-    oldValue = entry->mItem->GetValueInternal();
-    entry->mItem->ClearValue();
-  }
-
-  if (entry) {
-    mItems.RawRemoveEntry(entry);
-  }
-  aOldValue = oldValue;
-  return NS_OK;
-}
-
-static PLDHashOperator
-CheckSecure(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  bool* secure = (bool*)userArg;
-  if (aEntry->mItem->IsSecure()) {
-    *secure = true;
-    return PL_DHASH_STOP;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
-nsresult
-DOMStorageImpl::Clear(bool aCallerSecure, int32_t* aOldCount)
-{
-  if (UseDB())
-    CacheKeysFromDB();
-
-  int32_t oldCount = mItems.Count();
-
-  bool foundSecureItem = false;
-  mItems.EnumerateEntries(CheckSecure, &foundSecureItem);
-
-  if (foundSecureItem && !aCallerSecure) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  if (UseDB()) {
-    nsresult rv = InitDB();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = gStorageDB->ClearStorage(this);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  *aOldCount = oldCount;
-  mItems.Clear();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMStorageImpl::PrivateModeChanged(bool enabled)
-{
-  mInPrivateBrowsing = enabled;
-  CanUseStorage(); // cause mSessionOnly to update as well
-  mItems.Clear();
-  mItemsCachedVersion = 0;
-  return NS_OK;
-}
-
-nsDOMStorage::nsDOMStorage()
-  : mStorageType(nsPIDOMStorage::Unknown)
-  , mEventBroadcaster(nullptr)
-{
-  if (XRE_GetProcessType() != GeckoProcessType_Default)
-    mStorageImpl = new StorageChild(this);
-  else
-    mStorageImpl = new DOMStorageImpl(this);
-}
-
-nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
-  : mStorageType(aThat.mStorageType)
-  , mPrincipal(aThat.mPrincipal)
-  , mEventBroadcaster(nullptr)
-{
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    StorageChild* other = static_cast<StorageChild*>(aThat.mStorageImpl.get());
-    mStorageImpl = new StorageChild(this, *other);
-  } else {
-    DOMStorageImpl* other = static_cast<DOMStorageImpl*>(aThat.mStorageImpl.get());
-    mStorageImpl = new DOMStorageImpl(this, *other);
-  }
-}
-
-nsDOMStorage::~nsDOMStorage()
-{
-}
-
-nsresult
-nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                   bool aPrivate)
-{
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!uri) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  mDocumentURI = aDocumentURI;
-  mPrincipal = aPrincipal;
-
-  mStorageType = SessionStorage;
-
-  mStorageImpl->InitAsSessionStorage(mPrincipal, aPrivate);
-  return NS_OK;
-}
-
-nsresult
-nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                 bool aPrivate)
-{
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!uri) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  mDocumentURI = aDocumentURI;
-  mPrincipal = aPrincipal;
-
-  mStorageType = LocalStorage;
-
-  mStorageImpl->InitAsLocalStorage(aPrincipal, aPrivate);
-  return NS_OK;
-}
-
-bool
-DOMStorageBase::CanUseStorage()
-{
-  return nsDOMStorage::CanUseStorage(this);
-}
-
-//static
-bool
-nsDOMStorage::CanUseStorage(DOMStorageBase* aStorage /* = NULL */)
-{
-  if (aStorage) {
-    // check if the calling domain can use storage. Downgrade to session
-    // only if only session storage may be used.
-    aStorage->mSessionOnly = false;
-  }
-
-  if (!Preferences::GetBool(kStorageEnabled)) {
-    return false;
-  }
-
-  // chrome can always use storage regardless of permission preferences
-  if (nsContentUtils::IsCallerChrome())
-    return true;
-
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv = nsContentUtils::GetSecurityManager()->
-                  GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  // if subjectPrincipal were null we'd have returned after
-  // IsCallerChrome().
-
-  nsCOMPtr<nsIPermissionManager> permissionManager =
-    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-  if (!permissionManager)
-    return false;
-
-  uint32_t perm;
-  permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
-                                                 kPermissionType, &perm);
-
-  if (perm == nsIPermissionManager::DENY_ACTION)
-    return false;
-
-  // In private browsing mode we ougth to behave as in session-only cookies
-  // mode to prevent detection of being in private browsing mode and ensuring
-  // that there will be no traces left.
-  if (perm == nsICookiePermission::ACCESS_SESSION ||
-      (aStorage && aStorage->IsPrivate())) {
-    if (aStorage)
-      aStorage->mSessionOnly = true;
-  }
-  else if (perm != nsIPermissionManager::ALLOW_ACTION) {
-    uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior);
-    uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
-
-    // Treat "ask every time" as "reject always".
-    if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT))
-      return false;
-
-    if (lifetimePolicy == ACCEPT_SESSION && aStorage)
-      aStorage->mSessionOnly = true;
-  }
-
-  return true;
-}
-
-bool
-nsDOMStorage::CacheStoragePermissions()
-{
-  // Bug 488446, disallowing storage use when in session only mode.
-  // This is temporary fix before we find complete solution for storage
-  // behavior in private browsing mode or session-only cookies mode.
-  if (!mStorageImpl->CanUseStorage())
-    return false;
-
-  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  if (!ssm)
-    return false;
-
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  return CanAccess(subjectPrincipal);
-}
-
-NS_IMETHODIMP
-nsDOMStorage::GetLength(uint32_t *aLength)
-{
-  if (!CacheStoragePermissions())
-    return NS_ERROR_DOM_SECURITY_ERR;
-  
-  return mStorageImpl->GetLength(IsCallerSecure(), aLength);
-}
-
-NS_IMETHODIMP
-nsDOMStorage::Key(uint32_t aIndex, nsAString& aKey)
-{
-  if (!CacheStoragePermissions())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
-  return mStorageImpl->GetKey(IsCallerSecure(), aIndex, aKey);
-}
-
-nsIDOMStorageItem*
-nsDOMStorage::GetNamedItem(const nsAString& aKey, nsresult* aResult)
-{
-  if (!CacheStoragePermissions()) {
-    *aResult = NS_ERROR_DOM_SECURITY_ERR;
-    return nullptr;
-  }
-
-  *aResult = NS_OK;
-  return mStorageImpl->GetValue(IsCallerSecure(), aKey, aResult);
-}
-
-nsresult
-nsDOMStorage::GetItem(const nsAString& aKey, nsAString &aData)
-{
-  nsresult rv;
-
-  // IMPORTANT:
-  // CacheStoragePermissions() is called inside of
-  // GetItem(nsAString, nsIDOMStorageItem)
-  // To call it particularly in this method would just duplicate
-  // the call. If the code changes, make sure that call to
-  // CacheStoragePermissions() is put here!
-
-  nsCOMPtr<nsIDOMStorageItem> item;
-  rv = GetItem(aKey, getter_AddRefs(item));
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (item) {
-    rv = item->GetValue(aData);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else
-    SetDOMStringToNull(aData);
-
-  return NS_OK;
-}
-
-static Telemetry::ID
-TelemetryIDForKey(nsPIDOMStorage::nsDOMStorageType type)
-{
-  switch (type) {
-  default:
-    MOZ_ASSERT(false);
-    // We need to return something to satisfy the compiler.
-    // Fallthrough.
-  case nsPIDOMStorage::LocalStorage:
-    return Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES;
-  case nsPIDOMStorage::SessionStorage:
-    return Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES;
-  }
-}
-
-static Telemetry::ID
-TelemetryIDForValue(nsPIDOMStorage::nsDOMStorageType type)
-{
-  switch (type) {
-  default:
-    MOZ_ASSERT(false);
-    // We need to return something to satisfy the compiler.
-    // Fallthrough.
-  case nsPIDOMStorage::LocalStorage:
-    return Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES;
-  case nsPIDOMStorage::SessionStorage:
-    return Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES;
-  }
-}
-
-NS_IMETHODIMP
-nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
-{
-  nsresult rv;
-
-  NS_IF_ADDREF(*aItem = GetNamedItem(aKey, &rv));
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
-{
-  if (!CacheStoragePermissions())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
-  Telemetry::Accumulate(TelemetryIDForKey(mStorageType), aKey.Length());
-  Telemetry::Accumulate(TelemetryIDForValue(mStorageType), aData.Length());
-
-  nsString oldValue;
-  nsresult rv = mStorageImpl->SetValue(IsCallerSecure(), aKey, aData, oldValue);
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (oldValue != aData && mEventBroadcaster)
-    mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, aData);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
-{
-  if (!CacheStoragePermissions())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
-  nsString oldValue;
-  nsresult rv = mStorageImpl->RemoveValue(IsCallerSecure(), aKey, oldValue);
-  if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
-    return NS_OK;
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (!oldValue.IsEmpty() && mEventBroadcaster) {
-    nsAutoString nullString;
-    SetDOMStringToNull(nullString);
-    mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, nullString);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsDOMStorage::Clear()
-{
-  if (!CacheStoragePermissions())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
-  int32_t oldCount;
-  nsresult rv = mStorageImpl->Clear(IsCallerSecure(), &oldCount);
-  if (NS_FAILED(rv))
-    return rv;
-  
-  if (oldCount && mEventBroadcaster) {
-    nsAutoString nullString;
-    SetDOMStringToNull(nullString);
-    mEventBroadcaster->BroadcastChangeNotification(nullString, nullString, nullString);
-  }
-
-  return NS_OK;
-}
-
-already_AddRefed<nsIDOMStorage>
-nsDOMStorage::Clone()
-{
-  NS_ASSERTION(false, "Old DOMStorage doesn't implement cloning");
-  return nullptr;
-}
-
-already_AddRefed<nsIDOMStorage>
-nsDOMStorage::Fork(const nsSubstring &aDocumentURI)
-{
-  NS_ASSERTION(false, "Old DOMStorage doesn't implement forking");
-  return nullptr;
-}
-
-bool nsDOMStorage::IsForkOf(nsIDOMStorage* aThat)
-{
-  NS_ASSERTION(false, "Old DOMStorage doesn't implement forking");
-  return false;
-}
-
-nsresult
-nsDOMStorage::CloneFrom(nsDOMStorage* aThat)
-{
-  return mStorageImpl->CloneFrom(IsCallerSecure(), aThat->mStorageImpl);
-}
-
-nsTArray<nsString> *
-nsDOMStorage::GetKeys()
-{
-  return mStorageImpl->GetKeys(IsCallerSecure());
-}
-
-nsIPrincipal*
-nsDOMStorage::Principal()
-{
-  return nullptr;
-}
-
-bool
-nsDOMStorage::CanAccessSystem(nsIPrincipal *aPrincipal)
-{
-  if (!aPrincipal)
-    return true;
-
-  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  if (!ssm)
-    return false;
-
-  bool isSystem;
-  nsresult rv = ssm->IsSystemPrincipal(aPrincipal, &isSystem);
-
-  return NS_SUCCEEDED(rv) && isSystem;
-}
-
-bool
-nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
-{
-  // Allow C++ callers to access the storage
-  if (!aPrincipal)
-    return true;
-
-  // Allow more powerful principals (e.g. system) to access the storage
-
-  // For content, either the code base or domain must be the same.  When code
-  // base is the same, this is enough to say it is safe for a page to access
-  // this storage.
-
-  bool subsumes;
-  nsresult rv = aPrincipal->SubsumesIgnoringDomain(mPrincipal, &subsumes);
-  if (NS_FAILED(rv))
-    return false;
-
-  if (!subsumes) {
-    nsresult rv = aPrincipal->Subsumes(mPrincipal, &subsumes);
-    if (NS_FAILED(rv))
-      return false;
-  }
-
-  return subsumes;
-}
-
-nsPIDOMStorage::nsDOMStorageType
-nsDOMStorage::StorageType()
-{
-  return mStorageType;
-}
-
-//
-// nsDOMStorage2
-//
-
-NS_IMPL_CYCLE_COLLECTION_1(nsDOMStorage2, mStorage)
-
-DOMCI_DATA(Storage, nsDOMStorage2)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorage2)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorage2)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
-  NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
-  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
-NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP
-nsDOMStorage2::GetInterface(const nsIID & aIID, void **result)
-{
-  nsresult rv = mStorage->GetInterface(aIID, result);
-  if (NS_SUCCEEDED(rv))
-    return rv;
-  return QueryInterface(aIID, result);;
-}
-
-nsDOMStorage2::nsDOMStorage2()
-{
-}
-
-nsDOMStorage2::nsDOMStorage2(nsDOMStorage2& aThat)
-{
-  mStorage = new nsDOMStorage(*aThat.mStorage.get());
-  mPrincipal = aThat.mPrincipal;
-}
-
-nsresult
-nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                    bool aPrivate)
-{
-  mStorage = new nsDOMStorage();
-  if (!mStorage)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  mPrincipal = aPrincipal;
-  mDocumentURI = aDocumentURI;
-
-  return mStorage->InitAsSessionStorage(aPrincipal, aDocumentURI, aPrivate);
-}
-
-nsresult
-nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                  bool aPrivate)
-{
-  mStorage = new nsDOMStorage();
-  if (!mStorage)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  mPrincipal = aPrincipal;
-  mDocumentURI = aDocumentURI;
-
-  return mStorage->InitAsLocalStorage(aPrincipal, aDocumentURI, aPrivate);
-}
-
-already_AddRefed<nsIDOMStorage>
-nsDOMStorage2::Clone()
-{
-  nsDOMStorage2* storage = new nsDOMStorage2(*this);
-  if (!storage)
-    return nullptr;
-
-  storage->mStorage->CloneFrom(mStorage);
-  NS_ADDREF(storage);
-
-  return storage;
-}
-
-already_AddRefed<nsIDOMStorage>
-nsDOMStorage2::Fork(const nsSubstring &aDocumentURI)
-{
-  nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
-  storage->InitAsSessionStorageFork(mPrincipal, aDocumentURI, mStorage);
-  return storage.forget();
-}
-
-bool nsDOMStorage2::IsForkOf(nsIDOMStorage* aThat)
-{
-  if (!aThat)
-    return false;
-
-  nsDOMStorage2* storage = static_cast<nsDOMStorage2*>(aThat);
-  return mStorage == storage->mStorage;
-}
-
-void
-nsDOMStorage2::InitAsSessionStorageFork(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI, nsDOMStorage* aStorage)
-{
-  mPrincipal = aPrincipal;
-  mDocumentURI = aDocumentURI;
-  mStorage = aStorage;
-}
-
-nsTArray<nsString> *
-nsDOMStorage2::GetKeys()
-{
-  return mStorage->GetKeys();
-}
-
-nsIPrincipal*
-nsDOMStorage2::Principal()
-{
-  return mPrincipal;
-}
-
-bool
-nsDOMStorage2::CanAccess(nsIPrincipal *aPrincipal)
-{
-  return mStorage->CanAccess(aPrincipal);
-}
-
-nsPIDOMStorage::nsDOMStorageType
-nsDOMStorage2::StorageType()
-{
-  if (mStorage)
-    return mStorage->StorageType();
-
-  return nsPIDOMStorage::Unknown;
-}
-
-bool
-nsDOMStorage2::IsPrivate()
-{
-  return mStorage && mStorage->IsPrivate();
-}
-
-namespace {
-
-class StorageNotifierRunnable : public nsRunnable
-{
-public:
-  StorageNotifierRunnable(nsISupports* aSubject)
-    : mSubject(aSubject)
-  { }
-
-  NS_DECL_NSIRUNNABLE
-
-private:
-  nsCOMPtr<nsISupports> mSubject;
-};
-
-NS_IMETHODIMP
-StorageNotifierRunnable::Run()
-{
-  nsCOMPtr<nsIObserverService> observerService =
-    mozilla::services::GetObserverService();
-  if (observerService) {
-    observerService->NotifyObservers(mSubject, "dom-storage2-changed", nullptr);
-  }
-  return NS_OK;
-}
-
-} // anonymous namespace
-
-void
-nsDOMStorage2::BroadcastChangeNotification(const nsSubstring &aKey,
-                                           const nsSubstring &aOldValue,
-                                           const nsSubstring &aNewValue)
-{
-  nsresult rv;
-  nsCOMPtr<nsIDOMEvent> domEvent;
-  // Note, this DOM event should never reach JS. It is cloned later in
-  // nsGlobalWindow.
-  NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr);
-  nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent);
-  rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
-                               false,
-                               false,
-                               aKey,
-                               aOldValue,
-                               aNewValue,
-                               mDocumentURI,
-                               static_cast<nsIDOMStorage*>(this));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  nsRefPtr<StorageNotifierRunnable> r = new StorageNotifierRunnable(event);
-  NS_DispatchToMainThread(r);
-}
-
-NS_IMETHODIMP
-nsDOMStorage2::GetLength(uint32_t *aLength)
-{
-  return mStorage->GetLength(aLength);
-}
-
-NS_IMETHODIMP
-nsDOMStorage2::Key(uint32_t aIndex, nsAString& aKey)
-{
-  return mStorage->Key(aIndex, aKey);
-}
-
-NS_IMETHODIMP
-nsDOMStorage2::GetItem(const nsAString& aKey, nsAString &aData)
-{
-  return mStorage->GetItem(aKey, aData);
-}
-
-NS_IMETHODIMP
-nsDOMStorage2::SetItem(const nsAString& aKey, const nsAString& aData)
-{
-  mStorage->mEventBroadcaster = this;
-  return mStorage->SetItem(aKey, aData);
-}
-
-NS_IMETHODIMP
-nsDOMStorage2::RemoveItem(const nsAString& aKey)
-{
-  mStorage->mEventBroadcaster = this;
-  return mStorage->RemoveItem(aKey);
-}
-
-NS_IMETHODIMP
-nsDOMStorage2::Clear()
-{
-  mStorage->mEventBroadcaster = this;
-  return mStorage->Clear();
-}
-
-//
-// nsDOMStorageItem
-//
-
-NS_IMPL_CYCLE_COLLECTION_1(nsDOMStorageItem, mStorage)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorageItem)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorageItem)
-
-DOMCI_DATA(StorageItem, nsDOMStorageItem)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorageItem)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageItem)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMStorageItem)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMToString)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageItem)
-NS_INTERFACE_MAP_END
-
-nsDOMStorageItem::nsDOMStorageItem(DOMStorageBase* aStorage,
-                                   const nsAString& aKey,
-                                   const nsAString& aValue,
-                                   bool aSecure)
-  : mSecure(aSecure),
-    mKey(aKey),
-    mValue(aValue),
-    mStorage(aStorage)
-{
-}
-
-nsDOMStorageItem::~nsDOMStorageItem()
-{
-}
-
-NS_IMETHODIMP
-nsDOMStorageItem::GetSecure(bool* aSecure)
-{
-  if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
-    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
-  }
-
-  if (mStorage->UseDB()) {
-    nsAutoString value;
-    return mStorage->GetDBValue(mKey, value, aSecure);
-  }
-
-  *aSecure = IsSecure();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMStorageItem::SetSecure(bool aSecure)
-{
-  if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
-    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
-  }
-
-  if (mStorage->UseDB()) {
-    nsresult rv = mStorage->SetSecure(mKey, aSecure);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  mSecure = aSecure;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMStorageItem::GetValue(nsAString& aValue)
-{
-  if (!mStorage->CacheStoragePermissions())
-    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
-
-  if (mStorage->UseDB()) {
-    bool secure;
-    nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure);
-    if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
-      return NS_OK;
-    if (NS_SUCCEEDED(rv) && !IsCallerSecure() && secure)
-      return NS_ERROR_DOM_SECURITY_ERR;
-    return rv;
-  }
-
-  if (IsSecure() && !IsCallerSecure()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  aValue = mValue;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMStorageItem::SetValue(const nsAString& aValue)
-{
-  if (!mStorage->CacheStoragePermissions())
-    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
-
-  bool secureCaller = IsCallerSecure();
-
-  if (mStorage->UseDB()) {
-    // SetDBValue() does the security checks for us.
-    return mStorage->SetDBValue(mKey, aValue, secureCaller);
-  }
-
-  bool secureItem = IsSecure();
-
-  if (!secureCaller && secureItem) {
-    // The item is secure, but the caller isn't. Throw.
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  mValue = aValue;
-  mSecure = secureCaller;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMStorageItem::ToString(nsAString& aStr)
-{
-  return GetValue(aStr);
-}
-
deleted file mode 100644
--- a/dom/src/storage/nsDOMStorage.h
+++ /dev/null
@@ -1,486 +0,0 @@
-/* -*- Mode: C++; 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/. */
-
-#ifndef nsDOMStorage_h___
-#define nsDOMStorage_h___
-
-#include "nscore.h"
-#include "nsAutoPtr.h"
-#include "nsIDOMStorageObsolete.h"
-#include "nsIDOMStorage.h"
-#include "nsIDOMStorageItem.h"
-#include "nsIPermissionManager.h"
-#include "nsIPrivacyTransitionObserver.h"
-#include "nsInterfaceHashtable.h"
-#include "nsVoidArray.h"
-#include "nsTArray.h"
-#include "nsPIDOMStorage.h"
-#include "nsIDOMToString.h"
-#include "nsDOMEvent.h"
-#include "nsIDOMStorageEvent.h"
-#include "nsIDOMStorageManager.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsIObserver.h"
-#include "nsITimer.h"
-#include "nsWeakReference.h"
-#include "nsIInterfaceRequestor.h"
-#include "mozilla/Attributes.h"
-
-#include "nsDOMStorageDBWrapper.h"
-
-class nsDOMStorage;
-class nsIDOMStorage;
-class nsDOMStorageItem;
-class nsDOMStoragePersistentDB;
-
-namespace mozilla {
-namespace dom {
-class StorageParent;
-}
-}
-using mozilla::dom::StorageParent;
-
-class DOMStorageImpl;
-
-class nsDOMStorageEntry : public nsPtrHashKey<const void>
-{
-public:
-  nsDOMStorageEntry(KeyTypePointer aStr);
-  nsDOMStorageEntry(const nsDOMStorageEntry& aToCopy);
-  ~nsDOMStorageEntry();
-
-  // weak reference so that it can be deleted when no longer used
-  DOMStorageImpl* mStorage;
-};
-
-class nsSessionStorageEntry : public nsStringHashKey
-{
-public:
-  nsSessionStorageEntry(KeyTypePointer aStr);
-  nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy);
-  ~nsSessionStorageEntry();
-
-  nsRefPtr<nsDOMStorageItem> mItem;
-};
-
-class nsDOMStorageManager MOZ_FINAL : public nsIDOMStorageManager
-                                    , public nsIObserver
-                                    , public nsSupportsWeakReference
-{
-public:
-  // nsISupports
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMStorageManager
-  NS_DECL_NSIDOMSTORAGEMANAGER
-
-  // nsIObserver
-  NS_DECL_NSIOBSERVER
-
-  nsDOMStorageManager();
-
-  void AddToStoragesHash(DOMStorageImpl* aStorage);
-  void RemoveFromStoragesHash(DOMStorageImpl* aStorage);
-
-  nsresult ClearAllStorages();
-
-  static nsresult Initialize();
-  static nsDOMStorageManager* GetInstance();
-  static void Shutdown();
-  static void ShutdownDB();
-
-  /**
-   * Checks whether there is any data waiting to be flushed from a temp table.
-   */
-  bool UnflushedDataExists();
-
-  static nsDOMStorageManager* gStorageManager;
-
-protected:
-
-  nsTHashtable<nsDOMStorageEntry> mStorages;
-};
-
-class DOMStorageBase : public nsIPrivacyTransitionObserver
-{
-public:
-  DOMStorageBase();
-  DOMStorageBase(DOMStorageBase&);
-
-  virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
-  virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
-
-  virtual nsTArray<nsString>* GetKeys(bool aCallerSecure) = 0;
-  virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength) = 0;
-  virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey) = 0;
-  virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
-                                      nsresult* rv) = 0;
-  virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
-                            const nsAString& aData, nsAString& aOldValue) = 0;
-  virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
-                               nsAString& aOldValue) = 0;
-  virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount) = 0;
-
-  // Call nsDOMStorage::CanUseStorage with |this|
-  bool CanUseStorage();
-
-  // If true, the contents of the storage should be stored in the
-  // database, otherwise this storage should act like a session
-  // storage.
-  // This call relies on mSessionOnly, and should only be used
-  // after a CacheStoragePermissions() call.  See the comments
-  // for mSessionOnly below.
-  bool UseDB() {
-    return mUseDB;
-  }
-
-  bool IsPrivate() {
-    return mInPrivateBrowsing;
-  }
-
-  // retrieve the value and secure state corresponding to a key out of storage.
-  virtual nsresult
-  GetDBValue(const nsAString& aKey,
-             nsAString& aValue,
-             bool* aSecure) = 0;
-
-  // set the value corresponding to a key in the storage. If
-  // aSecure is false, then attempts to modify a secure value
-  // throw NS_ERROR_DOM_INVALID_ACCESS_ERR
-  virtual nsresult
-  SetDBValue(const nsAString& aKey,
-             const nsAString& aValue,
-             bool aSecure) = 0;
-
-  // set the value corresponding to a key as secure.
-  virtual nsresult
-  SetSecure(const nsAString& aKey, bool aSecure) = 0;
-
-  virtual nsresult
-  CloneFrom(bool aCallerSecure, DOMStorageBase* aThat) = 0;
-
-  // e.g. "moc.rab.oof.:" or "moc.rab.oof.:http:80" depending
-  // on association with a domain (globalStorage) or
-  // an origin (localStorage).
-  nsCString& GetScopeDBKey() {return mScopeDBKey;}
-
-  // e.g. "moc.rab.%" - reversed eTLD+1 subpart of the domain.
-  nsCString& GetQuotaDBKey()
-  {
-    return mQuotaDBKey;
-  }
-
-  virtual bool CacheStoragePermissions() = 0;
-
-protected:
-  friend class nsDOMStorageManager;
-  friend class nsDOMStorage;
-
-  nsPIDOMStorage::nsDOMStorageType mStorageType;
-  
-  // true if the storage database should be used for values
-  bool mUseDB;
-
-  // true if the preferences indicates that this storage should be
-  // session only. This member is updated by
-  // CacheStoragePermissions(), using the current principal.
-  // CacheStoragePermissions() must be called at each entry point to
-  // make sure this stays up to date.
-  bool mSessionOnly;
-
-  // keys are used for database queries.
-  // see comments of the getters bellow.
-  nsCString mScopeDBKey;
-  nsCString mQuotaDBKey;
-
-  bool mInPrivateBrowsing;
-};
-
-class DOMStorageImpl MOZ_FINAL : public DOMStorageBase
-                               , public nsSupportsWeakReference
-{
-public:
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DOMStorageImpl, nsIPrivacyTransitionObserver)
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
-
-  DOMStorageImpl(nsDOMStorage*);
-  DOMStorageImpl(nsDOMStorage*, DOMStorageImpl&);
-  ~DOMStorageImpl();
-
-  bool SessionOnly() {
-    return mSessionOnly;
-  }
-
-  virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
-  virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
-  virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
-  virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
-                                      nsresult* rv);
-  virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
-                            const nsAString& aData, nsAString& aOldValue);
-  virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
-                               nsAString& aOldValue);
-  virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount);
-
-  // cache the keys from the database for faster lookup
-  nsresult CacheKeysFromDB();
-
-  uint64_t CachedVersion() { return mItemsCachedVersion; }
-  void SetCachedVersion(uint64_t version) { mItemsCachedVersion = version; }
-  
-  // retrieve the value and secure state corresponding to a key out of storage
-  // that has been cached in mItems hash table.
-  nsresult
-  GetCachedValue(const nsAString& aKey,
-                 nsAString& aValue,
-                 bool* aSecure);
-
-  // retrieve the value and secure state corresponding to a key out of storage.
-  virtual nsresult
-  GetDBValue(const nsAString& aKey,
-             nsAString& aValue,
-             bool* aSecure);
-
-  // set the value corresponding to a key in the storage. If
-  // aSecure is false, then attempts to modify a secure value
-  // throw NS_ERROR_DOM_INVALID_ACCESS_ERR
-  virtual nsresult
-  SetDBValue(const nsAString& aKey,
-             const nsAString& aValue,
-             bool aSecure);
-
-  // set the value corresponding to a key as secure.
-  virtual nsresult
-  SetSecure(const nsAString& aKey, bool aSecure);
-
-  // clear all values from the store
-  void ClearAll();
-
-  virtual nsresult
-  CloneFrom(bool aCallerSecure, DOMStorageBase* aThat);
-
-  virtual bool CacheStoragePermissions();
-
-private:
-  static nsDOMStorageDBWrapper* gStorageDB;
-  friend class nsDOMStorageManager;
-  friend class nsDOMStoragePersistentDB;
-  friend class StorageParent;
-
-  void Init(nsDOMStorage*);
-
-  // Cross-process storage implementations never have InitAs(Session|Local|Global)Storage
-  // called, so the appropriate initialization needs to happen from the child.
-  void InitFromChild(bool aUseDB, bool aSessionOnly,
-                     bool aPrivate,
-                     const nsACString& aScopeDBKey,
-                     const nsACString& aQuotaDBKey,
-                     uint32_t aStorageType);
-  void SetSessionOnly(bool aSessionOnly);
-
-  static nsresult InitDB();
-
-  // 0 initially or a positive data version number assigned by gStorageDB
-  // after keys have been cached from the database
-  uint64_t mItemsCachedVersion;
-
-  // the key->value item pairs
-  nsTHashtable<nsSessionStorageEntry> mItems;
-
-  // Weak reference to the owning storage instance
-  nsDOMStorage* mOwner;
-};
-
-class nsDOMStorage2;
-
-class nsDOMStorage : public nsIDOMStorageObsolete,
-                     public nsPIDOMStorage,
-                     public nsIInterfaceRequestor
-{
-public:
-  nsDOMStorage();
-  nsDOMStorage(nsDOMStorage& aThat);
-  virtual ~nsDOMStorage();
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage, nsIDOMStorageObsolete)
-
-  NS_DECL_NSIDOMSTORAGEOBSOLETE
-  NS_DECL_NSIINTERFACEREQUESTOR
-
-  // Helpers for implementing nsIDOMStorage
-  nsresult GetItem(const nsAString& key, nsAString& aData);
-  nsresult Clear();
-
-  // nsPIDOMStorage
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                        bool aPrivate);
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                      bool aPrivate);
-  virtual already_AddRefed<nsIDOMStorage> Clone();
-  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
-  virtual bool IsForkOf(nsIDOMStorage* aThat);
-  virtual nsTArray<nsString> *GetKeys();
-  virtual nsIPrincipal* Principal();
-  virtual bool CanAccess(nsIPrincipal *aPrincipal);
-  virtual nsDOMStorageType StorageType();
-  virtual bool IsPrivate() {
-    return mStorageImpl && mStorageImpl->IsPrivate();
-  }
-
-  // Check whether storage may be used by the caller, and whether it
-  // is session only.  Returns true if storage may be used.
-  static bool
-  CanUseStorage(DOMStorageBase* aStorage = nullptr);
-
-  // Check whether storage may be used.  Updates mSessionOnly based on
-  // the result of CanUseStorage.
-  bool
-  CacheStoragePermissions();
-
-  nsIDOMStorageItem* GetNamedItem(const nsAString& aKey, nsresult* aResult);
-
-  nsresult SetSecure(const nsAString& aKey, bool aSecure)
-  {
-    return mStorageImpl->SetSecure(aKey, aSecure);
-  }
-
-  nsresult CloneFrom(nsDOMStorage* aThat);
-
- protected:
-  friend class nsDOMStorage2;
-  friend class nsDOMStoragePersistentDB;
-
-  nsRefPtr<DOMStorageBase> mStorageImpl;
-
-  bool CanAccessSystem(nsIPrincipal *aPrincipal);
-
-  // document URI of the document this storage is bound to
-  nsString mDocumentURI;
-
-  // true if this storage was initialized as a localStorage object.  localStorage
-  // objects are scoped to scheme/host/port in the database, while globalStorage
-  // objects are scoped just to host.  this flag also tells the manager to map
-  // this storage also in mLocalStorages hash table.
-  nsDOMStorageType mStorageType;
-
-  friend class nsIDOMStorage2;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsDOMStorage2* mEventBroadcaster;
-};
-
-class nsDOMStorage2 MOZ_FINAL : public nsIDOMStorage,
-                                public nsPIDOMStorage,
-                                public nsIInterfaceRequestor
-{
-public:
-  // nsISupports
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage)
-
-  nsDOMStorage2(nsDOMStorage2& aThat);
-  nsDOMStorage2();
-
-  NS_DECL_NSIDOMSTORAGE
-  NS_DECL_NSIINTERFACEREQUESTOR
-
-  // nsPIDOMStorage
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                        bool aPrivate);
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
-                                      bool aPrivate);
-  virtual already_AddRefed<nsIDOMStorage> Clone();
-  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
-  virtual bool IsForkOf(nsIDOMStorage* aThat);
-  virtual nsTArray<nsString> *GetKeys();
-  virtual nsIPrincipal* Principal();
-  virtual bool CanAccess(nsIPrincipal *aPrincipal);
-  virtual nsDOMStorageType StorageType();
-  virtual bool IsPrivate();
-
-  void BroadcastChangeNotification(const nsSubstring &aKey,
-                                   const nsSubstring &aOldValue,
-                                   const nsSubstring &aNewValue);
-  void InitAsSessionStorageFork(nsIPrincipal *aPrincipal,
-                                const nsSubstring &aDocumentURI,
-                                nsDOMStorage* aStorage);
-
-private:
-  // storages bound to an origin hold the principal to
-  // make security checks against it
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-
-  // Needed for the storage event, this is address of the document this storage
-  // is bound to
-  nsString mDocumentURI;
-  nsRefPtr<nsDOMStorage> mStorage;
-};
-
-class nsDOMStorageItem : public nsIDOMStorageItem,
-                         public nsIDOMToString
-{
-public:
-  nsDOMStorageItem(DOMStorageBase* aStorage,
-                   const nsAString& aKey,
-                   const nsAString& aValue,
-                   bool aSecure);
-  virtual ~nsDOMStorageItem();
-
-  // nsISupports
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorageItem, nsIDOMStorageItem)
-
-  // nsIDOMStorageObsolete
-  NS_DECL_NSIDOMSTORAGEITEM
-
-  // nsIDOMToString
-  NS_DECL_NSIDOMTOSTRING
-
-  bool IsSecure()
-  {
-    return mSecure;
-  }
-
-  void SetSecureInternal(bool aSecure)
-  {
-    mSecure = aSecure;
-  }
-
-  const nsAString& GetValueInternal()
-  {
-    return mValue;
-  }
-
-  const void SetValueInternal(const nsAString& aValue)
-  {
-    mValue = aValue;
-  }
-
-  void ClearValue()
-  {
-    mValue.Truncate();
-  }
-
-protected:
-
-  // true if this value is for secure sites only
-  bool mSecure;
-
-  // key for the item
-  nsString mKey;
-
-  // value of the item
-  nsString mValue;
-
-  // If this item came from the db, mStorage points to the storage
-  // object where this item came from.
-  nsRefPtr<DOMStorageBase> mStorage;
-};
-
-nsresult
-NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult);
-
-#endif /* nsDOMStorage_h___ */
deleted file mode 100644
--- a/dom/src/storage/nsDOMStorageBaseDB.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/* -*- Mode: C++; 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 "nsDOMStorageBaseDB.h"
-#include "nsDOMStorage.h"
-#include "mozilla/Preferences.h"
-
-// Only allow relatively small amounts of data since performance of
-// the synchronous IO is very bad.
-#define DEFAULT_QUOTA_LIMIT (5 * 1024)
-
-uint64_t nsDOMStorageBaseDB::sGlobalVersion = 1;
-int32_t nsDOMStorageBaseDB::gQuotaLimit = DEFAULT_QUOTA_LIMIT * 1024;
-
-using namespace mozilla;
-
-/* static */
-void
-nsDOMStorageBaseDB::Init()
-{
-  Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
-                              DEFAULT_QUOTA_LIMIT);
-}
-
-nsDOMStorageBaseDB::nsDOMStorageBaseDB()
-{
-  mScopesVersion.Init(8);
-}
-
-// public
-
-void
-nsDOMStorageBaseDB::MarkScopeCached(DOMStorageImpl* aStorage)
-{
-  aStorage->SetCachedVersion(CachedScopeVersion(aStorage));
-}
-
-bool
-nsDOMStorageBaseDB::IsScopeDirty(DOMStorageImpl* aStorage)
-{
-  return !aStorage->CachedVersion() ||
-         (aStorage->CachedVersion() != CachedScopeVersion(aStorage));
-}
-
-// protected
-
-// static
-uint64_t
-nsDOMStorageBaseDB::NextGlobalVersion()
-{
-  sGlobalVersion++;
-  if (sGlobalVersion == 0) // Control overlap, never return 0
-    sGlobalVersion = 1;
-  return sGlobalVersion;
-}
-
-uint64_t
-nsDOMStorageBaseDB::CachedScopeVersion(DOMStorageImpl* aStorage)
-{
-  uint64_t currentVersion;
-  if (mScopesVersion.Get(aStorage->GetScopeDBKey(), &currentVersion))
-    return currentVersion;
-
-  mScopesVersion.Put(aStorage->GetScopeDBKey(), sGlobalVersion);
-  return sGlobalVersion;
-}
-
-void
-nsDOMStorageBaseDB::MarkScopeDirty(DOMStorageImpl* aStorage)
-{
-  uint64_t nextVersion = NextGlobalVersion();
-  mScopesVersion.Put(aStorage->GetScopeDBKey(), nextVersion);
-
-  // We may do this because the storage updates its cache along with
-  // updating the database.
-  aStorage->SetCachedVersion(nextVersion);
-}
-
-void
-nsDOMStorageBaseDB::MarkAllScopesDirty()
-{
-  mScopesVersion.Clear();
-  NextGlobalVersion();
-}
deleted file mode 100644
--- a/dom/src/storage/nsDOMStorageBaseDB.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -*- Mode: C++; 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/. */
-
-#ifndef nsDOMStorageBaseDB_h___
-#define nsDOMStorageBaseDB_h___
-
-#include "nscore.h"
-#include "nsDataHashtable.h"
-
-class DOMStorageImpl;
-
-class nsDOMStorageBaseDB
-{
-public:
-  static void Init();
-
-  nsDOMStorageBaseDB();
-  virtual ~nsDOMStorageBaseDB() {}
-
-  /**
-   * Marks the storage as "cached" after the DOMStorageImpl object has loaded
-   * all items to its memory copy of the entries - IsScopeDirty returns false
-   * after call of this method for this storage.
-   *
-   * When a key is changed or deleted in the storage, the storage scope is
-   * marked as "dirty" again and makes the DOMStorageImpl object recache its
-   * keys on next access, because IsScopeDirty returns true again.
-   */
-  void MarkScopeCached(DOMStorageImpl* aStorage);
-
-  /**
-   * Test whether the storage for the scope (i.e. origin or host) has been
-   * changed since the last MarkScopeCached call.
-   */
-  bool IsScopeDirty(DOMStorageImpl* aStorage);
-
-  int32_t GetQuota() {
-    return gQuotaLimit * 1024;
-  }
-
-protected:
-  nsDataHashtable<nsCStringHashKey, uint64_t> mScopesVersion;
-