Bug 1165214 - Use OriginAttributes in DOM Storage. r=smaug, r=bholley
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 05 Jan 2016 07:25:00 -0500
changeset 315658 35794bec70ba6a805f2e8031b53d7c43e9ea9206
parent 315657 7b7b730f7f8ef0b69950cd259150dcdb41ef5e26
child 315659 cd8dbe86837fa2c1911a1b84698d32fe36c14d33
push id1079
push userjlund@mozilla.com
push dateFri, 15 Apr 2016 21:02:33 +0000
treeherdermozilla-release@575fbf6786d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, bholley
bugs1165214
milestone46.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 1165214 - Use OriginAttributes in DOM Storage. r=smaug, r=bholley
browser/components/contextualidentity/test/browser/browser_usercontext.js
caps/BasePrincipal.cpp
dom/storage/DOMStorageCache.cpp
dom/storage/DOMStorageCache.h
dom/storage/DOMStorageDBThread.cpp
dom/storage/DOMStorageDBThread.h
dom/storage/DOMStorageDBUpdater.cpp
dom/storage/DOMStorageDBUpdater.h
dom/storage/DOMStorageIPC.cpp
dom/storage/DOMStorageIPC.h
dom/storage/DOMStorageManager.cpp
dom/storage/DOMStorageManager.h
dom/storage/DOMStorageObserver.cpp
dom/storage/DOMStorageObserver.h
dom/storage/PStorage.ipdl
dom/storage/moz.build
--- a/browser/components/contextualidentity/test/browser/browser_usercontext.js
+++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js
@@ -75,17 +75,16 @@ add_task(function* test() {
     yield BrowserTestUtils.browserLoaded(browser);
 
     // get the title
     let title = browser.contentDocument.title.trim().split("|");
 
     // check each item in the title and validate it meets expectatations
     for (let part of title) {
       let [storageMethodName, value] = part.split("=");
-      let is_f = storageMethodName == "cookie" ? is : todo_is;
-      is_f(value, expectedContext,
+      is(value, expectedContext,
             "the title reflects the expected contextual identity of " +
             expectedContext + " for method " + storageMethodName + ": " + value);
     }
 
     gBrowser.removeTab(tab);
   }
 });
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -127,16 +127,17 @@ OriginAttributes::CreateSuffix(nsACStrin
 
   if (mUserContextId != nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
     value.Truncate();
     value.AppendInt(mUserContextId);
     params->Set(NS_LITERAL_STRING("userContextId"), value);
   }
 
   if (!mSignedPkg.IsEmpty()) {
+    MOZ_RELEASE_ASSERT(mSignedPkg.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
     params->Set(NS_LITERAL_STRING("signedPkg"), mSignedPkg);
   }
 
   aStr.Truncate();
 
   params->Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("^");
--- a/dom/storage/DOMStorageCache.cpp
+++ b/dom/storage/DOMStorageCache.cpp
@@ -71,18 +71,18 @@ NS_IMETHODIMP_(void) DOMStorageCacheBrid
     /* enable this to find non-threadsafe destructors: */
     /* NS_ASSERT_OWNINGTHREAD(_class); */
     delete (this);
   }
 }
 
 // DOMStorageCache
 
-DOMStorageCache::DOMStorageCache(const nsACString* aScope)
-: mScope(*aScope)
+DOMStorageCache::DOMStorageCache(const nsACString* aOriginNoSuffix)
+: mOriginNoSuffix(*aOriginNoSuffix)
 , mMonitor("DOMStorageCache")
 , mLoaded(false)
 , mLoadResult(NS_OK)
 , mInitialized(false)
 , mPersistent(false)
 , mSessionOnlyDataSetActive(false)
 , mPreloadTelemetryRecorded(false)
 {
@@ -119,43 +119,60 @@ DOMStorageCache::Release(void)
     DOMStorageCacheBridge::Release();
   }
 }
 
 void
 DOMStorageCache::Init(DOMStorageManager* aManager,
                       bool aPersistent,
                       nsIPrincipal* aPrincipal,
-                      const nsACString& aQuotaScope)
+                      const nsACString& aQuotaOriginScope)
 {
   if (mInitialized) {
     return;
   }
 
   mInitialized = true;
   mPrincipal = aPrincipal;
+  BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(mOriginSuffix);
   mPersistent = aPersistent;
-  mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
+  if (aQuotaOriginScope.IsEmpty()) {
+    mQuotaOriginScope = Origin();
+  } else {
+    mQuotaOriginScope = aQuotaOriginScope;
+  }
 
   if (mPersistent) {
     mManager = aManager;
     Preload();
   }
 
-  mUsage = aManager->GetScopeUsage(mQuotaScope);
+  // Check the quota string has (or has not) the identical origin suffix as
+  // this storage cache is bound to.
+  MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
+  MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope, 
+                                                         NS_LITERAL_CSTRING("^")));
+
+  mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
 }
 
 inline bool
 DOMStorageCache::Persist(const DOMStorage* aStorage) const
 {
   return mPersistent &&
          !aStorage->IsSessionOnly() &&
          !aStorage->IsPrivate();
 }
 
+const nsCString
+DOMStorageCache::Origin() const
+{
+  return DOMStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix);
+}
+
 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.
@@ -653,18 +670,18 @@ DOMStorageCache::LoadWait()
   MonitorAutoLock monitor(mMonitor);
   while (!mLoaded) {
     monitor.Wait();
   }
 }
 
 // DOMStorageUsage
 
-DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
-  : mScope(aScope)
+DOMStorageUsage::DOMStorageUsage(const nsACString& aOriginScope)
+  : mOriginScope(aOriginScope)
 {
   mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
 }
 
 namespace {
 
 class LoadUsageRunnable : public nsRunnable
 {
--- a/dom/storage/DOMStorageCache.h
+++ b/dom/storage/DOMStorageCache.h
@@ -28,18 +28,26 @@ 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_(MozExternalRefCountType) AddRef(void);
   NS_IMETHOD_(void) Release(void);
 
-  // The scope (origin) in the database usage format (reversed)
-  virtual const nsCString& Scope() const = 0;
+  // The origin of the cache, result is concatenation of OriginNoSuffix() and OriginSuffix(),
+  // see below.
+  virtual const nsCString Origin() const = 0;
+
+  // The origin attributes suffix alone, this is usually passed as an |aOriginSuffix|
+  // argument to various methods
+  virtual const nsCString& OriginSuffix() const = 0;
+
+  // The origin in the database usage format (reversed) and without the suffix
+  virtual const nsCString& OriginNoSuffix() 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;
 
@@ -65,24 +73,27 @@ protected:
 // 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);
 
-  explicit DOMStorageCache(const nsACString* aScope);
+  // Note: We pass aOriginNoSuffix through the ctor here, because 
+  // DOMStorageCacheHashKey's ctor is creating this class and 
+  // accepts reversed-origin-no-suffix as an argument - the hashing key.
+  explicit DOMStorageCache(const nsACString* aOriginNoSuffix);
 
 protected:
   virtual ~DOMStorageCache();
 
 public:
   void Init(DOMStorageManager* aManager, bool aPersistent, nsIPrincipal* aPrincipal,
-            const nsACString& aQuotaScope);
+            const nsACString& aQuotaOriginScope);
 
   // 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.
@@ -109,17 +120,19 @@ public:
   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 const nsCString Origin() const;
+  virtual const nsCString& OriginNoSuffix() const { return mOriginNoSuffix; }
+  virtual const nsCString& OriginSuffix() const { return mOriginSuffix; }
   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
@@ -183,21 +196,25 @@ private:
   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 origin this cache belongs to in the "DB format", i.e. reversed
+  nsCString mOriginNoSuffix;
 
-  // The eTLD+1 scope used to count quota usage.
-  nsCString mQuotaScope;
+  // The origin attributes suffix
+  nsCString mOriginSuffix;
+
+  // The eTLD+1 scope used to count quota usage.  It is in the reversed format 
+  // and contains the origin attributes suffix.
+  nsCString mQuotaOriginScope;
 
   // 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
@@ -236,35 +253,35 @@ private:
 
 // DOMStorageUsage
 // Infrastructure to manage and check eTLD+1 quota
 class DOMStorageUsageBridge
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DOMStorageUsageBridge)
 
-  virtual const nsCString& Scope() = 0;
+  virtual const nsCString& OriginScope() = 0;
   virtual void LoadUsage(const int64_t aUsage) = 0;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~DOMStorageUsageBridge() {}
 };
 
 class DOMStorageUsage : public DOMStorageUsageBridge
 {
 public:
-  explicit DOMStorageUsage(const nsACString& aScope);
+  explicit DOMStorageUsage(const nsACString& aOriginScope);
 
   bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
 
 private:
-  virtual const nsCString& Scope() { return mScope; }
+  virtual const nsCString& OriginScope() { return mOriginScope; }
   virtual void LoadUsage(const int64_t aUsage);
 
-  nsCString mScope;
+  nsCString mOriginScope;
   int64_t mUsage[DOMStorageCache::kDataSetCount];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/storage/DOMStorageDBThread.cpp
+++ b/dom/storage/DOMStorageDBThread.cpp
@@ -1,45 +1,83 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMStorageDBThread.h"
+#include "DOMStorageDBUpdater.h"
 #include "DOMStorageCache.h"
+#include "DOMStorageManager.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 "mozilla/BasePrincipal.h"
 #include "nsIObserverService.h"
 #include "nsVariant.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/Services.h"
+#include "mozilla/Tokenizer.h"
 
 // How long we collect write oprerations
 // before they are flushed to the database
 // In milliseconds.
 #define FLUSHING_INTERVAL_MS 5000
 
 // Write Ahead Log's maximum size is 512KB
 #define MAX_WAL_SIZE_BYTES 512 * 1024
 
+// Current version of the database schema
+#define CURRENT_SCHEMA_VERSION 1
+
 namespace mozilla {
 namespace dom {
 
+namespace { // anon
+
+// This is only a compatibility code for schema version 0.  Returns the 'scope' key
+// in the schema version 0 format for the scope column.
+nsCString
+Scheme0Scope(DOMStorageCacheBridge* aCache)
+{
+  nsCString result;
+
+  nsCString suffix = aCache->OriginSuffix();
+
+  PrincipalOriginAttributes oa;
+  if (!suffix.IsEmpty()) {
+    oa.PopulateFromSuffix(suffix);
+  }
+
+  if (oa.mAppId != nsIScriptSecurityManager::NO_APP_ID || oa.mInBrowser) {
+    result.AppendInt(oa.mAppId);
+    result.Append(':');
+    result.Append(oa.mInBrowser ? 't' : 'f');
+    result.Append(':');
+  }
+
+  result.Append(aCache->OriginNoSuffix());
+
+  return result;
+}
+
+} // anon
+
+
 DOMStorageDBBridge::DOMStorageDBBridge()
 {
 }
 
 
 DOMStorageDBThread::DOMStorageDBThread()
 : mThread(nullptr)
 , mThreadObserver(new ThreadObserver())
@@ -127,18 +165,18 @@ DOMStorageDBThread::SyncPreload(DOMStora
 
   // 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 (mDBReady && mWALModeEnabled) {
     bool pendingTasks;
     {
       MonitorAutoLock monitor(mThreadObserver->GetMonitor());
-      pendingTasks = mPendingTasks.IsScopeUpdatePending(aCache->Scope()) ||
-                     mPendingTasks.IsScopeClearPending(aCache->Scope());
+      pendingTasks = mPendingTasks.IsOriginUpdatePending(aCache->OriginSuffix(), aCache->OriginNoSuffix()) ||
+                     mPendingTasks.IsOriginClearPending(aCache->OriginSuffix(), aCache->OriginNoSuffix());
     }
 
     if (!pendingTasks) {
       // WAL is enabled, thus do the load synchronously on the main thread.
       DBOperation preload(DBOperation::opPreload, aCache);
       preload.PerformAndFinalize(this);
       return;
     }
@@ -159,28 +197,28 @@ void
 DOMStorageDBThread::AsyncFlush()
 {
   MonitorAutoLock monitor(mThreadObserver->GetMonitor());
   mFlushImmediately = true;
   monitor.Notify();
 }
 
 bool
-DOMStorageDBThread::ShouldPreloadScope(const nsACString& aScope)
+DOMStorageDBThread::ShouldPreloadOrigin(const nsACString& aOrigin)
 {
   MonitorAutoLock monitor(mThreadObserver->GetMonitor());
-  return mScopesHavingData.Contains(aScope);
+  return mOriginsHavingData.Contains(aOrigin);
 }
 
 void
-DOMStorageDBThread::GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
+DOMStorageDBThread::GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins)
 {
   MonitorAutoLock monitor(mThreadObserver->GetMonitor());
-  for (auto iter = mScopesHavingData.Iter(); !iter.Done(); iter.Next()) {
-    aScopes->AppendElement(iter.Get()->GetKey());
+  for (auto iter = mOriginsHavingData.Iter(); !iter.Done(); iter.Next()) {
+    aOrigins->AppendElement(iter.Get()->GetKey());
   }
 }
 
 nsresult
 DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
 {
   MonitorAutoLock monitor(mThreadObserver->GetMonitor());
 
@@ -197,24 +235,24 @@ DOMStorageDBThread::InsertDBOp(DOMStorag
     // Thread use after shutdown demanded.
     MOZ_ASSERT(false);
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   switch (aOperation->Type()) {
   case DBOperation::opPreload:
   case DBOperation::opPreloadUrgent:
-    if (mPendingTasks.IsScopeUpdatePending(aOperation->Scope())) {
+    if (mPendingTasks.IsOriginUpdatePending(aOperation->OriginSuffix(), aOperation->OriginNoSuffix())) {
       // 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())) {
+    } else if (mPendingTasks.IsOriginClearPending(aOperation->OriginSuffix(), aOperation->OriginNoSuffix())) {
       // 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(mThreadObserver->GetMonitor());
       aOperation->Finalize(NS_OK);
       return NS_OK;
@@ -373,51 +411,16 @@ DOMStorageDBThread::ThreadObserver::Afte
 {
   return NS_OK;
 }
 
 
 extern void
 ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
 
-namespace {
-
-class nsReverseStringSQLFunction final : public mozIStorageFunction
-{
-  ~nsReverseStringSQLFunction() {}
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_MOZISTORAGEFUNCTION
-};
-
-NS_IMPL_ISUPPORTS(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);
-
-  RefPtr<nsVariant> outVar(new nsVariant());
-  rv = outVar->SetAsAUTF8String(result);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  outVar.forget(aResult);
-  return NS_OK;
-}
-
-} // namespace
-
 nsresult
 DOMStorageDBThread::OpenDatabaseConnection()
 {
   nsresult rv;
 
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<mozIStorageService> service
@@ -452,105 +455,42 @@ DOMStorageDBThread::InitDatabase()
 
   rv = TryJournalMode();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create a read-only clone
   (void)mWorkerConnection->Clone(true, getter_AddRefs(mReaderConnection));
   NS_ENSURE_TRUE(mReaderConnection, NS_ERROR_FAILURE);
 
-  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();
+  rv = DOMStorageDBUpdater::Update(mWorkerConnection);
   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));
+  // Note: result of this select must match DOMStorageManager::CreateOrigin()
+  rv = mWorkerConnection->CreateStatement(NS_LITERAL_CSTRING(
+        "SELECT DISTINCT originAttributes || ':' || originKey FROM webappsstore2"),
+        getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
   mozStorageStatementScoper scope(stmt);
 
+  bool exists;
   while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
-    nsAutoCString foundScope;
-    rv = stmt->GetUTF8String(0, foundScope);
+    nsAutoCString foundOrigin;
+    rv = stmt->GetUTF8String(0, foundOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
 
     MonitorAutoLock monitor(mThreadObserver->GetMonitor());
-    mScopesHavingData.PutEntry(foundScope);
+    mOriginsHavingData.PutEntry(foundOrigin);
   }
 
   return NS_OK;
 }
 
 nsresult
 DOMStorageDBThread::SetJournalMode(bool aIsWal)
 {
@@ -733,73 +673,157 @@ DOMStorageDBThread::NotifyFlushCompletio
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
   }
 #endif
 }
 
+// Helper SQL function classes
+
+namespace {
+
+class OriginAttrsPatternMatchSQLFunction final : public mozIStorageFunction
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+
+  explicit OriginAttrsPatternMatchSQLFunction(OriginAttributesPattern const& aPattern)
+    : mPattern(aPattern) {}
+
+private:
+  OriginAttrsPatternMatchSQLFunction() = delete;
+  ~OriginAttrsPatternMatchSQLFunction() {}
+
+  OriginAttributesPattern mPattern;
+};
+
+NS_IMPL_ISUPPORTS(OriginAttrsPatternMatchSQLFunction, mozIStorageFunction)
+
+NS_IMETHODIMP
+OriginAttrsPatternMatchSQLFunction::OnFunctionCall(
+    mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
+{
+  nsresult rv;
+
+  nsAutoCString suffix;
+  rv = aFunctionArguments->GetUTF8String(0, suffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PrincipalOriginAttributes oa;
+  oa.PopulateFromSuffix(suffix);
+  bool result = mPattern.Matches(oa);
+
+  RefPtr<nsVariant> outVar(new nsVariant());
+  rv = outVar->SetAsBool(result);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  outVar.forget(aResult);
+  return NS_OK;
+}
+
+} // namespace
+
 // 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_ASSERT(mType == opPreload ||
+             mType == opPreloadUrgent ||
+             mType == opAddItem ||
+             mType == opUpdateItem ||
+             mType == opRemoveItem ||
+             mType == opClear ||
+             mType == opClearAll);
   MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
 }
 
 DOMStorageDBThread::DBOperation::DBOperation(const OperationType aType,
                                              DOMStorageUsageBridge* aUsage)
 : mType(aType)
 , mUsage(aUsage)
 {
+  MOZ_ASSERT(mType == opGetUsage);
   MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
 }
 
 DOMStorageDBThread::DBOperation::DBOperation(const OperationType aType,
-                                             const nsACString& aScope)
+                                             const nsACString& aOriginNoSuffix)
 : mType(aType)
 , mCache(nullptr)
-, mScope(aScope)
+, mOrigin(aOriginNoSuffix)
 {
+  MOZ_ASSERT(mType == opClearMatchingOrigin);
+  MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
+}
+
+DOMStorageDBThread::DBOperation::DBOperation(const OperationType aType,
+                                             const OriginAttributesPattern& aOriginNoSuffix)
+: mType(aType)
+, mCache(nullptr)
+, mOriginPattern(aOriginNoSuffix)
+{
+  MOZ_ASSERT(mType == opClearMatchingOriginAttributes);
   MOZ_COUNT_CTOR(DOMStorageDBThread::DBOperation);
 }
 
 DOMStorageDBThread::DBOperation::~DBOperation()
 {
   MOZ_COUNT_DTOR(DOMStorageDBThread::DBOperation);
 }
 
 const nsCString
-DOMStorageDBThread::DBOperation::Scope()
+DOMStorageDBThread::DBOperation::OriginNoSuffix() const
 {
   if (mCache) {
-    return mCache->Scope();
+    return mCache->OriginNoSuffix();
   }
 
-  return mScope;
+  return EmptyCString();
 }
 
 const nsCString
-DOMStorageDBThread::DBOperation::Target()
+DOMStorageDBThread::DBOperation::OriginSuffix() const
+{
+  if (mCache) {
+    return mCache->OriginSuffix();
+  }
+
+  return EmptyCString();
+}
+
+const nsCString
+DOMStorageDBThread::DBOperation::Origin() const
+{
+  if (mCache) {
+    return mCache->Origin();
+  }
+
+  return mOrigin;
+}
+
+const nsCString
+DOMStorageDBThread::DBOperation::Target() const
 {
   switch (mType) {
     case opAddItem:
     case opUpdateItem:
     case opRemoveItem:
-      return Scope() + NS_LITERAL_CSTRING("|") + NS_ConvertUTF16toUTF8(mKey);
+      return Origin() + NS_LITERAL_CSTRING("|") + NS_ConvertUTF16toUTF8(mKey);
 
     default:
-      return Scope();
+      return Origin();
   }
 }
 
 void
 DOMStorageDBThread::DBOperation::PerformAndFinalize(DOMStorageDBThread* aThread)
 {
   Finalize(Perform(aThread));
 }
@@ -825,23 +849,27 @@ DOMStorageDBThread::DBOperation::Perform
       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");
+        "WHERE originAttributes = :originAttributes AND originKey = :originKey "
+        "ORDER BY key LIMIT -1 OFFSET :offset");
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scope(stmt);
 
-    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
-                                    mCache->Scope());
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originAttributes"),
+                                    mCache->OriginSuffix());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originKey"),
+                                    mCache->OriginNoSuffix());
     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) {
@@ -860,25 +888,25 @@ DOMStorageDBThread::DBOperation::Perform
 
     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"
+      "SELECT SUM(LENGTH(key) + LENGTH(value)) FROM webappsstore2 "
+      "WHERE (originAttributes || ':' || originKey) LIKE :usageOrigin"
     );
     NS_ENSURE_STATE(stmt);
 
     mozStorageStatementScoper scope(stmt);
 
-    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
-                                    mUsage->Scope() + NS_LITERAL_CSTRING("%"));
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("usageOrigin"),
+                                    mUsage->OriginScope());
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool exists;
     rv = stmt->ExecuteStep(&exists);
     NS_ENSURE_SUCCESS(rv, rv);
 
     int64_t usage = 0;
     if (exists) {
@@ -891,54 +919,65 @@ DOMStorageDBThread::DBOperation::Perform
   }
 
   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) "
+      "INSERT OR REPLACE INTO webappsstore2 (originAttributes, originKey, scope, key, value) "
+      "VALUES (:originAttributes, :originKey, :scope, :key, :value) "
     );
     NS_ENSURE_STATE(stmt);
 
     mozStorageStatementScoper scope(stmt);
 
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originAttributes"),
+                                    mCache->OriginSuffix());
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originKey"),
+                                    mCache->OriginNoSuffix());
+    NS_ENSURE_SUCCESS(rv, rv);
+    // Filling the 'scope' column just for downgrade compatibility reasons
     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
-                                    mCache->Scope());
+                                    Scheme0Scope(mCache));
     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());
+    MonitorAutoLock monitor(aThread->mThreadObserver->GetMonitor());
+    aThread->mOriginsHavingData.PutEntry(Origin());
     break;
   }
 
   case opRemoveItem:
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
       "DELETE FROM webappsstore2 "
-      "WHERE scope = :scope "
+      "WHERE originAttributes = :originAttributes AND originKey = :originKey "
         "AND key = :key "
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scope(stmt);
 
-    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
-                                    mCache->Scope());
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originAttributes"),
+                                    mCache->OriginSuffix());
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originKey"),
+                                    mCache->OriginNoSuffix());
     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);
 
@@ -946,67 +985,111 @@ DOMStorageDBThread::DBOperation::Perform
   }
 
   case opClear:
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
       "DELETE FROM webappsstore2 "
-      "WHERE scope = :scope"
+      "WHERE originAttributes = :originAttributes AND originKey = :originKey"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scope(stmt);
 
-    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
-                                    mCache->Scope());
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originAttributes"),
+                                    mCache->OriginSuffix());
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("originKey"),
+                                    mCache->OriginNoSuffix());
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
 
-    aThread->mScopesHavingData.RemoveEntry(Scope());
+    MonitorAutoLock monitor(aThread->mThreadObserver->GetMonitor());
+    aThread->mOriginsHavingData.RemoveEntry(Origin());
     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();
+    MonitorAutoLock monitor(aThread->mThreadObserver->GetMonitor());
+    aThread->mOriginsHavingData.Clear();
     break;
   }
 
-  case opClearMatchingScope:
+  case opClearMatchingOrigin:
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
       "DELETE FROM webappsstore2"
-      " WHERE scope GLOB :scope"
+      " WHERE originKey GLOB :scope"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scope(stmt);
 
     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
-                                    mScope + NS_LITERAL_CSTRING("*"));
+                                    mOrigin + NS_LITERAL_CSTRING("*"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // No need to selectively clear mOriginsHavingData here.  That hashtable only
+    // prevents preload for scopes with no data.  Leaving a false record in it has
+    // a negligible effect on performance.
+    break;
+  }
+
+  case opClearMatchingOriginAttributes:
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    // Register the ORIGIN_ATTRS_PATTERN_MATCH function, initialized with the pattern
+    nsCOMPtr<mozIStorageFunction> patternMatchFunction(
+      new OriginAttrsPatternMatchSQLFunction(mOriginPattern));
+
+    rv = aThread->mWorkerConnection->CreateFunction(
+      NS_LITERAL_CSTRING("ORIGIN_ATTRS_PATTERN_MATCH"), 1, patternMatchFunction);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
+      "DELETE FROM webappsstore2"
+      " WHERE ORIGIN_ATTRS_PATTERN_MATCH(originAttributes)"
+    );
+
+    if (stmt) {
+      mozStorageStatementScoper scope(stmt);
+      rv = stmt->Execute();
+    } else {
+      rv = NS_ERROR_UNEXPECTED;
+    }
+
+    // Always remove the function
+    aThread->mWorkerConnection->RemoveFunction(
+      NS_LITERAL_CSTRING("ORIGIN_ATTRS_PATTERN_MATCH"));
+
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // No need to selectively clear mOriginsHavingData here.  That hashtable only
+    // prevents preload for scopes with no data.  Leaving a false record in it has
+    // a negligible effect on performance.
     break;
   }
 
   default:
     NS_ERROR("Unknown task type");
     break;
   }
 
@@ -1049,37 +1132,51 @@ DOMStorageDBThread::DBOperation::Finaliz
 // DOMStorageDBThread::PendingOperations
 
 DOMStorageDBThread::PendingOperations::PendingOperations()
 : mFlushFailureCount(0)
 {
 }
 
 bool
-DOMStorageDBThread::PendingOperations::HasTasks()
+DOMStorageDBThread::PendingOperations::HasTasks() const
 {
   return !!mUpdates.Count() || !!mClears.Count();
 }
 
 namespace {
 
+bool OriginPatternMatches(const nsACString& aOriginSuffix, const OriginAttributesPattern& aPattern)
+{
+  PrincipalOriginAttributes oa;
+  DebugOnly<bool> rv = oa.PopulateFromSuffix(aOriginSuffix);
+  MOZ_ASSERT(rv);
+  return aPattern.Matches(oa);
+}
+
 PLDHashOperator
-ForgetUpdatesForScope(const nsACString& aMapping,
+ForgetUpdatesForOrigin(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()) {
+      (aPendingTask->OriginNoSuffix() != newOp->OriginNoSuffix() ||
+       aPendingTask->OriginSuffix() != newOp->OriginSuffix())) {
     return PL_DHASH_NEXT;
   }
 
-  if (newOp->Type() == DOMStorageDBThread::DBOperation::opClearMatchingScope &&
-      !StringBeginsWith(aPendingTask->Scope(), newOp->Scope())) {
+  if (newOp->Type() == DOMStorageDBThread::DBOperation::opClearMatchingOrigin &&
+      !StringBeginsWith(aPendingTask->OriginNoSuffix(), newOp->Origin())) {
+    return PL_DHASH_NEXT;
+  }
+
+  if (newOp->Type() == DOMStorageDBThread::DBOperation::opClearMatchingOriginAttributes &&
+      !OriginPatternMatches(aPendingTask->OriginSuffix(), newOp->OriginPattern())) {
     return PL_DHASH_NEXT;
   }
 
   return PL_DHASH_REMOVE;
 }
 
 } // namespace
 
@@ -1103,17 +1200,17 @@ DOMStorageDBThread::PendingOperations::C
 
   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
+  // just bypass this operation.  A key 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
@@ -1141,22 +1238,23 @@ DOMStorageDBThread::PendingOperations::A
   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:
+  case DBOperation::opClearMatchingOrigin:
+  case DBOperation::opClearMatchingOriginAttributes:
     // 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);
+    mUpdates.Enumerate(ForgetUpdatesForOrigin, 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);
@@ -1249,89 +1347,98 @@ DOMStorageDBThread::PendingOperations::F
   mFlushFailureCount = 0;
   mExecList.Clear();
   return true;
 }
 
 namespace {
 
 bool
-FindPendingClearForScope(const nsACString& aScope,
+FindPendingClearForOrigin(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix,
                          DOMStorageDBThread::DBOperation* aPendingOperation)
 {
   if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearAll) {
     return true;
   }
 
   if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClear &&
-      aScope == aPendingOperation->Scope()) {
+      aOriginNoSuffix == aPendingOperation->OriginNoSuffix() &&
+      aOriginSuffix == aPendingOperation->OriginSuffix()) {
     return true;
   }
 
-  if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearMatchingScope &&
-      StringBeginsWith(aScope, aPendingOperation->Scope())) {
+  if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearMatchingOrigin &&
+      StringBeginsWith(aOriginNoSuffix, aPendingOperation->Origin())) {
+    return true;
+  }
+
+  if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearMatchingOriginAttributes &&
+      OriginPatternMatches(aOriginSuffix, aPendingOperation->OriginPattern())) {
     return true;
   }
 
   return false;
 }
 
 } // namespace
 
 bool
-DOMStorageDBThread::PendingOperations::IsScopeClearPending(const nsACString& aScope)
+DOMStorageDBThread::PendingOperations::IsOriginClearPending(const nsACString& aOriginSuffix,
+                                                            const nsACString& aOriginNoSuffix) const
 {
   // Called under the lock
 
-  for (auto iter = mClears.Iter(); !iter.Done(); iter.Next()) {
-    if (FindPendingClearForScope(aScope, iter.UserData())) {
+  for (auto iter = mClears.ConstIter(); !iter.Done(); iter.Next()) {
+    if (FindPendingClearForOrigin(aOriginSuffix, aOriginNoSuffix, iter.UserData())) {
       return true;
     }
   }
 
   for (uint32_t i = 0; i < mExecList.Length(); ++i) {
-    if (FindPendingClearForScope(aScope, mExecList[i])) {
+    if (FindPendingClearForOrigin(aOriginSuffix, aOriginNoSuffix, mExecList[i])) {
       return true;
     }
   }
 
   return false;
 }
 
 namespace {
 
 bool
-FindPendingUpdateForScope(const nsACString& aScope,
-                          DOMStorageDBThread::DBOperation* aPendingOperation)
+FindPendingUpdateForOrigin(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix,
+                           DOMStorageDBThread::DBOperation* aPendingOperation)
 {
   if ((aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opAddItem ||
        aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opUpdateItem ||
        aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opRemoveItem) &&
-       aScope == aPendingOperation->Scope()) {
+       aOriginNoSuffix == aPendingOperation->OriginNoSuffix() &&
+       aOriginSuffix == aPendingOperation->OriginSuffix()) {
     return true;
   }
 
   return false;
 }
 
 } // namespace
 
 bool
-DOMStorageDBThread::PendingOperations::IsScopeUpdatePending(const nsACString& aScope)
+DOMStorageDBThread::PendingOperations::IsOriginUpdatePending(const nsACString& aOriginSuffix,
+                                                             const nsACString& aOriginNoSuffix) const
 {
   // Called under the lock
 
-  for (auto iter = mUpdates.Iter(); !iter.Done(); iter.Next()) {
-    if (FindPendingUpdateForScope(aScope, iter.UserData())) {
+  for (auto iter = mUpdates.ConstIter(); !iter.Done(); iter.Next()) {
+    if (FindPendingUpdateForOrigin(aOriginSuffix, aOriginNoSuffix, iter.UserData())) {
       return true;
     }
   }
 
   for (uint32_t i = 0; i < mExecList.Length(); ++i) {
-    if (FindPendingUpdateForScope(aScope, mExecList[i])) {
+    if (FindPendingUpdateForOrigin(aOriginSuffix, aOriginNoSuffix, mExecList[i])) {
       return true;
     }
   }
 
   return false;
 }
 
 } // namespace dom
--- a/dom/storage/DOMStorageDBThread.h
+++ b/dom/storage/DOMStorageDBThread.h
@@ -7,16 +7,17 @@
 #ifndef DOMStorageDBThread_h___
 #define DOMStorageDBThread_h___
 
 #include "prthread.h"
 #include "prinrval.h"
 #include "nsTArray.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/storage/StatementCache.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsClassHashtable.h"
 #include "nsIFile.h"
 #include "nsIThreadInternal.h"
 
 class mozIStorageConnection;
@@ -68,27 +69,30 @@ public:
   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;
+  // Called when only a domain and its subdomains is about to clear
+  virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) = 0;
+
+  // Called when data matching an origin pattern have to be cleared
+  virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern) = 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;
+  virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix) = 0;
 
   // Get the complete list of scopes having data
-  virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0;
+  virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins) = 0;
 };
 
 // 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 final : public DOMStorageDBBridge
@@ -109,92 +113,112 @@ public:
 
       // Load usage of a scope
       opGetUsage,
 
       // Operations invoked by the DOM content API
       opAddItem,
       opUpdateItem,
       opRemoveItem,
+      // Clears a specific single origin data
       opClear,
 
       // Operations invoked by chrome
+
+      // Clear all the data stored in the database, for all scopes, no exceptions
       opClearAll,
-      opClearMatchingScope,
+      // Clear data under a domain and all its subdomains regardless OriginAttributes value
+      opClearMatchingOrigin,
+      // Clear all data matching an OriginAttributesPattern regardless a domain
+      opClearMatchingOriginAttributes,
     } OperationType;
 
     explicit 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);
+                const nsACString& aOriginNoSuffix);
+    DBOperation(const OperationType aType,
+                const OriginAttributesPattern& aOriginNoSuffix);
     ~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; }
+    OperationType Type() const { return mType; }
+
+    // The origin in the database usage format (reversed)
+    const nsCString OriginNoSuffix() const;
+
+    // The origin attributes suffix
+    const nsCString OriginSuffix() const;
 
-    // The operation scope (=origin)
-    const nsCString Scope();
+    // |origin suffix + origin key| the operation is working with
+    // or a scope pattern to delete with simple SQL's "LIKE %" from the database.
+    const nsCString Origin() const;
 
-    // |Scope + key| the operation is working with
-    const nsCString Target();
+    // |origin suffix + origin key + key| the operation is working with
+    const nsCString Target() const;
+
+    // Pattern to delete matching data with this op
+    const OriginAttributesPattern& OriginPattern() const { return mOriginPattern; }
 
   private:
     // The operation implementation body
     nsresult Perform(DOMStorageDBThread* aThread);
 
     friend class PendingOperations;
     OperationType mType;
     RefPtr<DOMStorageCacheBridge> mCache;
     RefPtr<DOMStorageUsageBridge> mUsage;
-    nsString mKey;
-    nsString mValue;
-    nsCString mScope;
+    nsString const mKey;
+    nsString const mValue;
+    nsCString const mOrigin;
+    OriginAttributesPattern const mOriginPattern;
   };
 
   // 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()|
+    // |Target()| or clear operations with the same or matching |Origin()|
     void Add(DBOperation* aOperation);
 
     // True when there are some scheduled operations to flush on disk
-    bool HasTasks();
+    bool HasTasks() const;
 
     // 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);
+    // true when a clear that deletes the given origin attr pattern and/or origin key
+    // is among the pending operations; when a preload for that scope is being scheduled,
+    // it must be finished right away
+    bool IsOriginClearPending(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) const;
 
-    // Checks whether there is a pending update (or clear, actually) operation for this scope.
-    bool IsScopeUpdatePending(const nsACString& aScope);
+    // Checks whether there is a pending update operation for this scope.
+    bool IsOriginUpdatePending(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) const;
 
   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);
 
@@ -264,23 +288,26 @@ public:
     { 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 AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix)
+    { InsertDBOp(new DBOperation(DBOperation::opClearMatchingOrigin, aOriginNoSuffix)); }
+
+  virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern)
+    { InsertDBOp(new DBOperation(DBOperation::opClearMatchingOriginAttributes, aPattern)); }
 
   virtual void AsyncFlush();
 
-  virtual bool ShouldPreloadScope(const nsACString& aScope);
-  virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes);
+  virtual bool ShouldPreloadOrigin(const nsACString& aOrigin);
+  virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins);
 
 private:
   nsCOMPtr<nsIFile> mDatabaseFile;
   PRThread* mThread;
 
   // Used to observe runnables dispatched to our thread and to monitor it.
   RefPtr<ThreadObserver> mThreadObserver;
 
@@ -293,18 +320,18 @@ private:
 
   // Whether DB has already been open, avoid races between main thread reads
   // and pending DB init in the background I/O thread
   Atomic<bool, ReleaseAcquire> mDBReady;
 
   // State of the database initiation
   nsresult mStatus;
 
-  // List of scopes having data, for optimization purposes only
-  nsTHashtable<nsCStringHashKey> mScopesHavingData;
+  // List of origins (including origin attributes suffix) having data, for optimization purposes only
+  nsTHashtable<nsCStringHashKey> mOriginsHavingData;
 
   // 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;
 
   StatementCache mWorkerStatements;
@@ -323,17 +350,17 @@ private:
 
   // 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
+  // also checks IsOriginClearPending 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
new file mode 100644
--- /dev/null
+++ b/dom/storage/DOMStorageDBUpdater.cpp
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DOMStorageManager.h"
+
+#include "mozIStorageBindingParamsArray.h"
+#include "mozIStorageBindingParams.h"
+#include "mozIStorageValueArray.h"
+#include "mozIStorageFunction.h"
+#include "mozilla/BasePrincipal.h"
+#include "nsVariant.h"
+#include "mozilla/Services.h"
+#include "mozilla/Tokenizer.h"
+
+// Current version of the database schema
+#define CURRENT_SCHEMA_VERSION 1
+
+namespace mozilla {
+namespace dom {
+
+extern void
+ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
+
+namespace {
+
+class nsReverseStringSQLFunction final : public mozIStorageFunction
+{
+  ~nsReverseStringSQLFunction() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+};
+
+NS_IMPL_ISUPPORTS(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);
+
+  RefPtr<nsVariant> outVar(new nsVariant());
+  rv = outVar->SetAsAUTF8String(result);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  outVar.forget(aResult);
+  return NS_OK;
+}
+
+// "scope" to "origin attributes suffix" and "origin key" convertor
+
+class ExtractOriginData : protected mozilla::Tokenizer
+{
+public:
+  ExtractOriginData(const nsACString& scope, nsACString& suffix, nsACString& origin)
+    : mozilla::Tokenizer(scope)
+  {
+    using mozilla::OriginAttributes;
+
+    // Parse optional appId:isInBrowserElement: string, in case
+    // we don't find it, the scope is our new origin key and suffix
+    // is empty.
+    suffix.Truncate();
+    origin.Assign(scope);
+
+    // Bail out if it isn't appId.
+    uint32_t appId;
+    if (!ReadInteger(&appId)) {
+      return;
+    }
+
+    // Should be followed by a colon.
+    if (!CheckChar(':')) {
+      return;
+    }
+
+    // Bail out if it isn't 'browserFlag'.
+    nsDependentCSubstring browserFlag;
+    if (!ReadWord(browserFlag)) {
+      return;
+    }
+
+    bool inBrowser = browserFlag == "t";
+    bool notInBrowser = browserFlag == "f";
+    if (!inBrowser && !notInBrowser) {
+      return;
+    }
+
+    // Should be followed by a colon.
+    if (!CheckChar(':')) {
+      return;
+    }
+
+    // OK, we have found appId and inBrowser flag, create the suffix from it
+    // and take the rest as the origin key.
+
+    PrincipalOriginAttributes attrs(appId, inBrowser);
+    attrs.CreateSuffix(suffix);
+
+    // Consume the rest of the input as "origin".
+    origin.Assign(Substring(mCursor, mEnd));
+  }
+};
+
+class GetOriginParticular final : public mozIStorageFunction
+{
+public:
+  enum EParticular {
+    ORIGIN_ATTRIBUTES_SUFFIX,
+    ORIGIN_KEY
+  };
+
+  explicit GetOriginParticular(EParticular aParticular)
+    : mParticular(aParticular) {}
+
+private:
+  GetOriginParticular() = delete;
+  ~GetOriginParticular() {}
+
+  EParticular mParticular;
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+};
+
+NS_IMPL_ISUPPORTS(GetOriginParticular, mozIStorageFunction)
+
+NS_IMETHODIMP
+GetOriginParticular::OnFunctionCall(
+    mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
+{
+  nsresult rv;
+
+  nsAutoCString scope;
+  rv = aFunctionArguments->GetUTF8String(0, scope);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString suffix, origin;
+  ExtractOriginData(scope, suffix, origin);
+
+  nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
+
+  switch (mParticular) {
+  case EParticular::ORIGIN_ATTRIBUTES_SUFFIX:
+    rv = outVar->SetAsAUTF8String(suffix);
+    break;
+  case EParticular::ORIGIN_KEY:
+    rv = outVar->SetAsAUTF8String(origin);
+    break;
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  outVar.forget(aResult);
+  return NS_OK;
+}
+
+} // namespace
+
+namespace DOMStorageDBUpdater {
+
+nsresult CreateSchema1Tables(mozIStorageConnection *aWorkerConnection)
+{
+  nsresult rv;
+
+  rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+          "CREATE TABLE IF NOT EXISTS webappsstore2 ("
+          "originAttributes TEXT, "
+          "originKey TEXT, "
+          "scope TEXT, " // Only for schema0 downgrade compatibility
+          "key TEXT, "
+          "value TEXT)"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+        "CREATE UNIQUE INDEX IF NOT EXISTS origin_key_index"
+        " ON webappsstore2(originAttributes, originKey, key)"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult Update(mozIStorageConnection *aWorkerConnection)
+{
+  nsresult rv;
+
+  mozStorageTransaction transaction(aWorkerConnection, false);
+
+  bool doVacuum = false;
+
+  int32_t schemaVer;
+  rv = aWorkerConnection->GetSchemaVersion(&schemaVer);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // downgrade (v0) -> upgrade (v1+) specific code
+  if (schemaVer >= 1) {
+    bool schema0IndexExists;
+    rv = aWorkerConnection->IndexExists(NS_LITERAL_CSTRING("scope_key_index"),
+                                        &schema0IndexExists);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (schema0IndexExists) {
+      // If this index exists, the database (already updated to schema >1)
+      // has been run again on schema 0 code.  That recreated that index
+      // and might store some new rows while updating only the 'scope' column.
+      // For such added rows we must fill the new 'origin*' columns correctly
+      // otherwise there would be a data loss.  The safest way to do it is to
+      // simply run the whole update to schema 1 again.
+      schemaVer = 0;
+    }
+  }
+
+  switch (schemaVer) {
+  case 0: {
+    bool webappsstore2Exists, webappsstoreExists, moz_webappsstoreExists;
+
+    rv = aWorkerConnection->TableExists(NS_LITERAL_CSTRING("webappsstore2"),
+                                        &webappsstore2Exists);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aWorkerConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"),
+                                        &webappsstoreExists);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aWorkerConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"),
+                                        &moz_webappsstoreExists);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!webappsstore2Exists && !webappsstoreExists && !moz_webappsstoreExists) {
+      // The database is empty, this is the first start.  Just create the schema table
+      // and break to the next version to update to, i.e. bypass update from the old version.
+
+      rv = CreateSchema1Tables(aWorkerConnection);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = aWorkerConnection->SetSchemaVersion(CURRENT_SCHEMA_VERSION);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      break;
+    }
+
+    doVacuum = true;
+
+    // Ensure Gecko 1.9.1 storage table
+    rv = aWorkerConnection->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 = aWorkerConnection->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 = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("REVERSESTRING"), 1, function1);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // 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.
+    if (webappsstoreExists) {
+      rv = aWorkerConnection->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 = aWorkerConnection->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.
+    if (moz_webappsstoreExists) {
+      rv = aWorkerConnection->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 = aWorkerConnection->ExecuteSimpleSQL(
+        NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("REVERSESTRING"));
+
+    // Update the scoping to match the new implememntation: split to oa suffix and origin key
+    // First rename the old table, we want to remove some columns no longer needed.
+    rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+          "ALTER TABLE webappsstore2 RENAME TO webappsstore2_old"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<mozIStorageFunction> oaSuffixFunc(
+      new GetOriginParticular(GetOriginParticular::ORIGIN_ATTRIBUTES_SUFFIX));
+    rv = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("GET_ORIGIN_SUFFIX"), 1, oaSuffixFunc);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<mozIStorageFunction> originKeyFunc(
+      new GetOriginParticular(GetOriginParticular::ORIGIN_KEY));
+    rv = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("GET_ORIGIN_KEY"), 1, originKeyFunc);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Here we ensure this schema tables when we are updating.
+    rv = CreateSchema1Tables(aWorkerConnection);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+          "INSERT OR IGNORE INTO "
+          "webappsstore2 (originAttributes, originKey, scope, key, value) "
+          "SELECT GET_ORIGIN_SUFFIX(scope), GET_ORIGIN_KEY(scope), scope, key, value "
+          "FROM webappsstore2_old"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aWorkerConnection->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("DROP TABLE webappsstore2_old"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("GET_ORIGIN_SUFFIX"));
+    aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("GET_ORIGIN_KEY"));
+
+    rv = aWorkerConnection->SetSchemaVersion(1);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    MOZ_FALLTHROUGH;
+  }
+  case CURRENT_SCHEMA_VERSION:
+    // Nothing more to do here, this is the current schema version
+    break;
+
+  default:
+    MOZ_ASSERT(false);
+    break;
+  } // switch
+
+  rv = transaction.Commit();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (doVacuum) {
+    // In some cases this can make the disk file of the database significantly smaller.
+    // VACUUM cannot be executed inside a transaction.
+    rv = aWorkerConnection->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("VACUUM"));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+} // namespace DOMStorageDBUpdater
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/storage/DOMStorageDBUpdater.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DOMStorageDBUpdater_h___
+#define DOMStorageDBUpdater_h___
+
+namespace mozilla {
+namespace dom {
+
+namespace DOMStorageDBUpdater {
+
+nsresult Update(mozIStorageConnection *aWorkerConnection);
+
+} // DOMStorageDBUpdater
+
+} // dom
+} // mozilla
+
+#endif
--- a/dom/storage/DOMStorageIPC.cpp
+++ b/dom/storage/DOMStorageIPC.cpp
@@ -63,23 +63,23 @@ DOMStorageDBChild::DOMStorageDBChild(DOM
 {
 }
 
 DOMStorageDBChild::~DOMStorageDBChild()
 {
 }
 
 nsTHashtable<nsCStringHashKey>&
-DOMStorageDBChild::ScopesHavingData()
+DOMStorageDBChild::OriginsHavingData()
 {
-  if (!mScopesHavingData) {
-    mScopesHavingData = new nsTHashtable<nsCStringHashKey>;
+  if (!mOriginsHavingData) {
+    mOriginsHavingData = new nsTHashtable<nsCStringHashKey>;
   }
 
-  return *mScopesHavingData;
+  return *mOriginsHavingData;
 }
 
 nsresult
 DOMStorageDBChild::Init()
 {
   ContentChild* child = ContentChild::GetSingleton();
   AddIPDLReference();
   child->SendPStorageConstructor(this);
@@ -97,28 +97,28 @@ DOMStorageDBChild::Shutdown()
 
 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);
+    SendAsyncPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(), 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());
+    SendAsyncGetUsage(aUsage->OriginScope());
   }
 }
 
 void
 DOMStorageDBChild::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
 {
   if (NS_FAILED(mStatus)) {
     aCache->LoadDone(mStatus);
@@ -131,17 +131,18 @@ DOMStorageDBChild::SyncPreload(DOMStorag
   }
 
   // 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);
+  SendPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(), aCache->LoadedCount(),
+              &keys, &values, &rv);
 
   for (uint32_t i = 0; i < keys.Length(); ++i) {
     aCache->LoadItem(keys[i], values[i]);
   }
 
   aCache->LoadDone(rv);
 }
 
@@ -149,117 +150,125 @@ 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());
+  SendAsyncAddItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
+                   nsString(aKey), nsString(aValue));
+  OriginsHavingData().PutEntry(aCache->Origin());
   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());
+  SendAsyncUpdateItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
+                      nsString(aKey), nsString(aValue));
+  OriginsHavingData().PutEntry(aCache->Origin());
   return NS_OK;
 }
 
 nsresult
 DOMStorageDBChild::AsyncRemoveItem(DOMStorageCacheBridge* aCache,
                                    const nsAString& aKey)
 {
   if (NS_FAILED(mStatus) || !mIPCOpen) {
     return mStatus;
   }
 
-  SendAsyncRemoveItem(aCache->Scope(), nsString(aKey));
+  SendAsyncRemoveItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
+                      nsString(aKey));
   return NS_OK;
 }
 
 nsresult
 DOMStorageDBChild::AsyncClear(DOMStorageCacheBridge* aCache)
 {
   if (NS_FAILED(mStatus) || !mIPCOpen) {
     return mStatus;
   }
 
-  SendAsyncClear(aCache->Scope());
-  ScopesHavingData().RemoveEntry(aCache->Scope());
+  SendAsyncClear(aCache->OriginSuffix(), aCache->OriginNoSuffix());
+  OriginsHavingData().RemoveEntry(aCache->Origin());
   return NS_OK;
 }
 
 bool
-DOMStorageDBChild::ShouldPreloadScope(const nsACString& aScope)
+DOMStorageDBChild::ShouldPreloadOrigin(const nsACString& aOrigin)
 {
-  // Return true if we didn't receive the aScope list yet.
+  // Return true if we didn't receive the origins list yet.
   // I tend to rather preserve a bit of early-after-start performance
-  // then a bit of memory here.
-  return !mScopesHavingData || mScopesHavingData->Contains(aScope);
+  // than a bit of memory here.
+  return !mOriginsHavingData || mOriginsHavingData->Contains(aOrigin);
 }
 
 bool
 DOMStorageDBChild::RecvObserve(const nsCString& aTopic,
-                               const nsCString& aScopePrefix)
+                               const nsString& aOriginAttributesPattern,
+                               const nsCString& aOriginScope)
 {
-  DOMStorageObserver::Self()->Notify(aTopic.get(), aScopePrefix);
+  DOMStorageObserver::Self()->Notify(
+    aTopic.get(), aOriginAttributesPattern, aOriginScope);
   return true;
 }
 
 bool
-DOMStorageDBChild::RecvScopesHavingData(nsTArray<nsCString>&& aScopes)
+DOMStorageDBChild::RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins)
 {
-  for (uint32_t i = 0; i < aScopes.Length(); ++i) {
-    ScopesHavingData().PutEntry(aScopes[i]);
+  for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
+    OriginsHavingData().PutEntry(aOrigins[i]);
   }
 
   return true;
 }
 
 bool
-DOMStorageDBChild::RecvLoadItem(const nsCString& aScope,
+DOMStorageDBChild::RecvLoadItem(const nsCString& aOriginSuffix,
+                                const nsCString& aOriginNoSuffix,
                                 const nsString& aKey,
                                 const nsString& aValue)
 {
-  DOMStorageCache* aCache = mManager->GetCache(aScope);
+  DOMStorageCache* aCache = mManager->GetCache(aOriginSuffix, aOriginNoSuffix);
   if (aCache) {
     aCache->LoadItem(aKey, aValue);
   }
 
   return true;
 }
 
 bool
-DOMStorageDBChild::RecvLoadDone(const nsCString& aScope, const nsresult& aRv)
+DOMStorageDBChild::RecvLoadDone(const nsCString& aOriginSuffix,
+                                const nsCString& aOriginNoSuffix,
+                                const nsresult& aRv)
 {
-  DOMStorageCache* aCache = mManager->GetCache(aScope);
+  DOMStorageCache* aCache = mManager->GetCache(aOriginSuffix, aOriginNoSuffix);
   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)
+DOMStorageDBChild::RecvLoadUsage(const nsCString& aOriginNoSuffix, const int64_t& aUsage)
 {
-  RefPtr<DOMStorageUsageBridge> scopeUsage = mManager->GetScopeUsage(aScope);
+  RefPtr<DOMStorageUsageBridge> scopeUsage = mManager->GetOriginUsage(aOriginNoSuffix);
   scopeUsage->LoadUsage(aUsage);
   return true;
 }
 
 bool
 DOMStorageDBChild::RecvError(const nsresult& aRv)
 {
   mStatus = aRv;
@@ -303,33 +312,34 @@ private:
   {
     if (!mParent->IPCOpen()) {
       return NS_OK;
     }
 
     DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
     if (db) {
       InfallibleTArray<nsCString> scopes;
-      db->GetScopesHavingData(&scopes);
-      mozilla::Unused << mParent->SendScopesHavingData(scopes);
+      db->GetOriginsHavingData(&scopes);
+      mozilla::Unused << mParent->SendOriginsHavingData(scopes);
     }
 
     // We need to check if the device is in a low disk space situation, so
     // we can forbid in that case any write in localStorage.
     nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcher =
       do_GetService("@mozilla.org/toolkit/disk-space-watcher;1");
     if (!diskSpaceWatcher) {
-      NS_WARNING("Could not get disk information from DiskSpaceWatcher");
       return NS_OK;
     }
+
     bool lowDiskSpace = false;
     diskSpaceWatcher->GetIsDiskFull(&lowDiskSpace);
+
     if (lowDiskSpace) {
       mozilla::Unused << mParent->SendObserve(
-        nsDependentCString("low-disk-space"), EmptyCString());
+        nsDependentCString("low-disk-space"), EmptyString(), EmptyCString());
     }
 
     return NS_OK;
   }
 
   RefPtr<DOMStorageDBParent> mParent;
 };
 
@@ -369,80 +379,89 @@ DOMStorageDBParent::CloneProtocol(Channe
   nsAutoPtr<PStorageParent> actor(contentParent->AllocPStorageParent());
   if (!actor || !contentParent->RecvPStorageConstructor(actor)) {
     return nullptr;
   }
   return actor.forget();
 }
 
 DOMStorageDBParent::CacheParentBridge*
-DOMStorageDBParent::NewCache(const nsACString& aScope)
+DOMStorageDBParent::NewCache(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix)
 {
-  return new CacheParentBridge(this, aScope);
+  return new CacheParentBridge(this, aOriginSuffix, aOriginNoSuffix);
 }
 
 void
 DOMStorageDBParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Implement me! Bug 1005169
 }
 
 bool
-DOMStorageDBParent::RecvAsyncPreload(const nsCString& aScope, const bool& aPriority)
+DOMStorageDBParent::RecvAsyncPreload(const nsCString& aOriginSuffix,
+                                     const nsCString& aOriginNoSuffix,
+                                     const bool& aPriority)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
-  db->AsyncPreload(NewCache(aScope), aPriority);
+  db->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix), aPriority);
   return true;
 }
 
 bool
-DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aScope)
+DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aOriginNoSuffix)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
   // The object releases it self in LoadUsage method
-  RefPtr<UsageParentBridge> usage = new UsageParentBridge(this, aScope);
+  RefPtr<UsageParentBridge> usage = new UsageParentBridge(this, aOriginNoSuffix);
   db->AsyncGetUsage(usage);
   return true;
 }
 
 namespace {
 
 // 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,
+  SyncLoadCacheHelper(const nsCString& aOriginSuffix,
+                      const nsCString& aOriginNoSuffix,
                       uint32_t aAlreadyLoadedCount,
                       InfallibleTArray<nsString>* aKeys,
                       InfallibleTArray<nsString>* aValues,
                       nsresult* rv)
   : mMonitor("DOM Storage SyncLoad IPC")
-  , mScope(aScope)
+  , mSuffix(aOriginSuffix)
+  , mOrigin(aOriginNoSuffix)
   , mKeys(aKeys)
   , mValues(aValues)
   , mRv(rv)
   , mLoaded(false)
   , mLoadedCount(aAlreadyLoadedCount)
   {
     // Precaution
     *mRv = NS_ERROR_UNEXPECTED;
   }
 
-  virtual const nsCString& Scope() const { return mScope; }
+  virtual const nsCString Origin() const
+  {
+    return DOMStorageManager::CreateOrigin(mSuffix, mOrigin);
+  }
+  virtual const nsCString& OriginNoSuffix() const { return mOrigin; }
+  virtual const nsCString& OriginSuffix() const { return mSuffix; }
   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;
     }
@@ -468,107 +487,112 @@ public:
     MonitorAutoLock monitor(mMonitor);
     while (!mLoaded) {
       monitor.Wait();
     }
   }
 
 private:
   Monitor mMonitor;
-  nsCString mScope;
+  nsCString mSuffix, mOrigin;
   InfallibleTArray<nsString>* mKeys;
   InfallibleTArray<nsString>* mValues;
   nsresult* mRv;
   bool mLoaded;
   uint32_t mLoadedCount;
 };
 
 } // namespace
 
 bool
-DOMStorageDBParent::RecvPreload(const nsCString& aScope,
+DOMStorageDBParent::RecvPreload(const nsCString& aOriginSuffix,
+                                const nsCString& aOriginNoSuffix,
                                 const uint32_t& aAlreadyLoadedCount,
                                 InfallibleTArray<nsString>* aKeys,
                                 InfallibleTArray<nsString>* aValues,
                                 nsresult* aRv)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
   RefPtr<SyncLoadCacheHelper> cache(
-    new SyncLoadCacheHelper(aScope, aAlreadyLoadedCount, aKeys, aValues, aRv));
+    new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix, aAlreadyLoadedCount, aKeys, aValues, aRv));
 
   db->SyncPreload(cache, true);
   return true;
 }
 
 bool
-DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aScope,
+DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aOriginSuffix,
+                                     const nsCString& aOriginNoSuffix,
                                      const nsString& aKey,
                                      const nsString& aValue)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
-  nsresult rv = db->AsyncAddItem(NewCache(aScope), aKey, aValue);
+  nsresult rv = db->AsyncAddItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return true;
 }
 
 bool
-DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aScope,
+DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aOriginSuffix,
+                                        const nsCString& aOriginNoSuffix,
                                         const nsString& aKey,
                                         const nsString& aValue)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
-  nsresult rv = db->AsyncUpdateItem(NewCache(aScope), aKey, aValue);
+  nsresult rv = db->AsyncUpdateItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return true;
 }
 
 bool
-DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aScope,
+DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aOriginSuffix,
+                                        const nsCString& aOriginNoSuffix,
                                         const nsString& aKey)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
-  nsresult rv = db->AsyncRemoveItem(NewCache(aScope), aKey);
+  nsresult rv = db->AsyncRemoveItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey);
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return true;
 }
 
 bool
-DOMStorageDBParent::RecvAsyncClear(const nsCString& aScope)
+DOMStorageDBParent::RecvAsyncClear(const nsCString& aOriginSuffix,
+                                   const nsCString& aOriginNoSuffix)
 {
   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   if (!db) {
     return false;
   }
 
-  nsresult rv = db->AsyncClear(NewCache(aScope));
+  nsresult rv = db->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return true;
 }
 
 bool
@@ -582,25 +606,27 @@ DOMStorageDBParent::RecvAsyncFlush()
   db->AsyncFlush();
   return true;
 }
 
 // DOMStorageObserverSink
 
 nsresult
 DOMStorageDBParent::Observe(const char* aTopic,
-                            const nsACString& aScopePrefix)
+                            const nsAString& aOriginAttributesPattern,
+                            const nsACString& aOriginScope)
 {
   if (mIPCOpen) {
 #ifdef MOZ_NUWA_PROCESS
     if (!(static_cast<ContentParent*>(Manager())->IsNuwaProcess() &&
           ContentParent::IsNuwaReady())) {
 #endif
       mozilla::Unused << SendObserve(nsDependentCString(aTopic),
-                                     nsCString(aScopePrefix));
+                                     nsString(aOriginAttributesPattern),
+                                     nsCString(aOriginScope));
 #ifdef MOZ_NUWA_PROCESS
     }
 #endif
   }
 
   return NS_OK;
 }
 
@@ -612,95 +638,105 @@ class LoadRunnable : public nsRunnable
 public:
   enum TaskType {
     loadItem,
     loadDone
   };
 
   LoadRunnable(DOMStorageDBParent* aParent,
                TaskType aType,
-               const nsACString& aScope,
+               const nsACString& aOriginSuffix,
+               const nsACString& aOriginNoSuffix,
                const nsAString& aKey = EmptyString(),
                const nsAString& aValue = EmptyString())
   : mParent(aParent)
   , mType(aType)
-  , mScope(aScope)
+  , mSuffix(aOriginSuffix)
+  , mOrigin(aOriginNoSuffix)
   , mKey(aKey)
   , mValue(aValue)
   { }
 
   LoadRunnable(DOMStorageDBParent* aParent,
                TaskType aType,
-               const nsACString& aScope,
+               const nsACString& aOriginSuffix,
+               const nsACString& aOriginNoSuffix,
                nsresult aRv)
   : mParent(aParent)
   , mType(aType)
-  , mScope(aScope)
+  , mSuffix(aOriginSuffix)
+  , mOrigin(aOriginNoSuffix)
   , mRv(aRv)
   { }
 
 private:
   RefPtr<DOMStorageDBParent> mParent;
   TaskType mType;
-  nsCString mScope;
+  nsCString mSuffix, mOrigin;
   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);
+      mozilla::Unused << mParent->SendLoadItem(mSuffix, mOrigin, mKey, mValue);
       break;
     case loadDone:
-      mozilla::Unused << mParent->SendLoadDone(mScope, mRv);
+      mozilla::Unused << mParent->SendLoadDone(mSuffix, mOrigin, mRv);
       break;
     }
 
     return NS_OK;
   }
 };
 
 } // namespace
 
 // DOMStorageDBParent::CacheParentBridge
 
+const nsCString
+DOMStorageDBParent::CacheParentBridge::Origin() const
+{
+  return DOMStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix);
+}
+
 bool
 DOMStorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, const nsString& aValue)
 {
   if (mLoaded) {
     return false;
   }
 
   ++mLoadedCount;
 
   RefPtr<LoadRunnable> r =
-    new LoadRunnable(mParent, LoadRunnable::loadItem, mScope, aKey, aValue);
+    new LoadRunnable(mParent, LoadRunnable::loadItem, mOriginSuffix, mOriginNoSuffix, aKey, aValue);
   NS_DispatchToMainThread(r);
   return true;
 }
 
 void
 DOMStorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
 {
   // Prevent send of duplicate LoadDone.
   if (mLoaded) {
     return;
   }
 
   mLoaded = true;
 
   RefPtr<LoadRunnable> r =
-    new LoadRunnable(mParent, LoadRunnable::loadDone, mScope, aRv);
+    new LoadRunnable(mParent, LoadRunnable::loadDone, mOriginSuffix, mOriginNoSuffix, aRv);
   NS_DispatchToMainThread(r);
 }
 
 void
 DOMStorageDBParent::CacheParentBridge::LoadWait()
 {
   // Should never be called on this implementation
   MOZ_ASSERT(false);
@@ -708,41 +744,41 @@ DOMStorageDBParent::CacheParentBridge::L
 
 // DOMStorageDBParent::UsageParentBridge
 
 namespace {
 
 class UsageRunnable : public nsRunnable
 {
 public:
-  UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aScope, const int64_t& aUsage)
+  UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aOriginScope, const int64_t& aUsage)
   : mParent(aParent)
-  , mScope(aScope)
+  , mOriginScope(aOriginScope)
   , mUsage(aUsage)
   {}
 
 private:
   NS_IMETHOD Run()
   {
     if (!mParent->IPCOpen()) {
       return NS_OK;
     }
 
-    mozilla::Unused << mParent->SendLoadUsage(mScope, mUsage);
+    mozilla::Unused << mParent->SendLoadUsage(mOriginScope, mUsage);
     return NS_OK;
   }
 
   RefPtr<DOMStorageDBParent> mParent;
-  nsCString mScope;
+  nsCString mOriginScope;
   int64_t mUsage;
 };
 
 } // namespace
 
 void
 DOMStorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
 {
-  RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mScope, aUsage);
+  RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mOriginScope, aUsage);
   NS_DispatchToMainThread(r);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/storage/DOMStorageIPC.h
+++ b/dom/storage/DOMStorageIPC.h
@@ -10,16 +10,19 @@
 #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 {
+
+class OriginAttributesPattern;
+
 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.
@@ -47,58 +50,64 @@ public:
 
   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()
   {
-    if (mScopesHavingData) {
-      mScopesHavingData->Clear(); /* NO-OP on the child process otherwise */
+    if (mOriginsHavingData) {
+      mOriginsHavingData->Clear(); /* NO-OP on the child process otherwise */
     }
   }
 
-  virtual void AsyncClearMatchingScope(const nsACString& aScope)
+  virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix)
+    { /* NO-OP on the child process */ }
+
+  virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern)
     { /* NO-OP on the child process */ }
 
   virtual void AsyncFlush()
     { SendAsyncFlush(); }
 
-  virtual bool ShouldPreloadScope(const nsACString& aScope);
-  virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
+  virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix);
+  virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins)
     { NS_NOTREACHED("Not implemented for child process"); }
 
 private:
   bool RecvObserve(const nsCString& aTopic,
-                   const nsCString& aScopePrefix);
-  bool RecvLoadItem(const nsCString& aScope,
+                   const nsString& aOriginAttributesPattern,
+                   const nsCString& aOriginScope);
+  bool RecvLoadItem(const nsCString& aOriginSuffix,
+                    const nsCString& aOriginNoSuffix,
                     const nsString& aKey,
                     const nsString& aValue);
-  bool RecvLoadDone(const nsCString& aScope,
+  bool RecvLoadDone(const nsCString& aOriginSuffix,
+                    const nsCString& aOriginNoSuffix,
                     const nsresult& aRv);
-  bool RecvScopesHavingData(nsTArray<nsCString>&& aScopes);
-  bool RecvLoadUsage(const nsCString& aScope,
+  bool RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins);
+  bool RecvLoadUsage(const nsCString& aOriginNoSuffix,
                      const int64_t& aUsage);
   bool RecvError(const nsresult& aRv);
 
-  nsTHashtable<nsCStringHashKey>& ScopesHavingData();
+  nsTHashtable<nsCStringHashKey>& OriginsHavingData();
 
   ThreadSafeAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
   // Held to get caches to forward answers to.
   RefPtr<DOMLocalStorageManager> mManager;
 
-  // Scopes having data hash, for optimization purposes only
-  nsAutoPtr<nsTHashtable<nsCStringHashKey> > mScopesHavingData;
+  // Origins having data hash, for optimization purposes only
+  nsAutoPtr<nsTHashtable<nsCStringHashKey>> mOriginsHavingData;
 
   // 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;
+  nsTHashtable<nsRefPtrHashKey<DOMStorageCacheBridge>> mLoadingCaches;
 
   // Status of the remote database
   nsresult mStatus;
 
   bool mIPCOpen;
 };
 
 
@@ -127,75 +136,82 @@ public:
 
   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) {}
+    CacheParentBridge(DOMStorageDBParent* aParentDB,
+                      const nsACString& aOriginSuffix,
+                      const nsACString& aOriginNoSuffix)
+      : mParent(aParentDB)
+      , mOriginSuffix(aOriginSuffix), mOriginNoSuffix(aOriginNoSuffix)
+      , mLoaded(false), mLoadedCount(0) {}
     virtual ~CacheParentBridge() {}
 
     // DOMStorageCacheBridge
-    virtual const nsCString& Scope() const
-      { return mScope; }
+    virtual const nsCString Origin() const;
+    virtual const nsCString& OriginNoSuffix() const
+      { return mOriginNoSuffix; }
+    virtual const nsCString& OriginSuffix() const
+      { return mOriginSuffix; }
     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:
     RefPtr<DOMStorageDBParent> mParent;
-    nsCString mScope;
+    nsCString mOriginSuffix, mOriginNoSuffix;
     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) {}
+    UsageParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aOriginScope)
+      : mParent(aParentDB), mOriginScope(aOriginScope) {}
     virtual ~UsageParentBridge() {}
 
     // DOMStorageUsageBridge
-    virtual const nsCString& Scope() { return mScope; }
+    virtual const nsCString& OriginScope() { return mOriginScope; }
     virtual void LoadUsage(const int64_t usage);
 
   private:
     RefPtr<DOMStorageDBParent> mParent;
-    nsCString mScope;
+    nsCString mOriginScope;
   };
 
 private:
   // IPC
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-  bool RecvAsyncPreload(const nsCString& aScope, const bool& aPriority) override;
-  bool RecvPreload(const nsCString& aScope, const uint32_t& aAlreadyLoadedCount,
+  bool RecvAsyncPreload(const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix, const bool& aPriority) override;
+  bool RecvPreload(const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix, const uint32_t& aAlreadyLoadedCount,
                    InfallibleTArray<nsString>* aKeys, InfallibleTArray<nsString>* aValues,
                    nsresult* aRv) override;
-  bool RecvAsyncGetUsage(const nsCString& aScope) override;
-  bool RecvAsyncAddItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue) override;
-  bool RecvAsyncUpdateItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue) override;
-  bool RecvAsyncRemoveItem(const nsCString& aScope, const nsString& aKey) override;
-  bool RecvAsyncClear(const nsCString& aScope) override;
+  bool RecvAsyncGetUsage(const nsCString& aOriginNoSuffix) override;
+  bool RecvAsyncAddItem(const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix, const nsString& aKey, const nsString& aValue) override;
+  bool RecvAsyncUpdateItem(const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix, const nsString& aKey, const nsString& aValue) override;
+  bool RecvAsyncRemoveItem(const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix, const nsString& aKey) override;
+  bool RecvAsyncClear(const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix) override;
   bool RecvAsyncFlush() override;
 
   // DOMStorageObserverSink
-  virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix) override;
+  virtual nsresult Observe(const char* aTopic, const nsAString& aOriginAttrPattern, const nsACString& aOriginScope) override;
 
 private:
-  CacheParentBridge* NewCache(const nsACString& aScope);
+  CacheParentBridge* NewCache(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix);
 
   ThreadSafeAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
   // True when IPC channel is open and Send*() methods are OK to use.
   bool mIPCOpen;
 };
 
--- a/dom/storage/DOMStorageManager.cpp
+++ b/dom/storage/DOMStorageManager.cpp
@@ -119,86 +119,75 @@ DOMStorageManager::~DOMStorageManager()
   if (observer) {
     observer->RemoveSink(this);
   }
 }
 
 namespace {
 
 nsresult
-CreateScopeKey(nsIPrincipal* aPrincipal,
-               nsACString& aKey)
+AppendOriginNoSuffix(nsIPrincipal* aPrincipal,
+                nsACString& aKey)
 {
+  nsresult rv;
+
   nsCOMPtr<nsIURI> uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
   if (!uri) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsAutoCString domainScope;
-  rv = uri->GetAsciiHost(domainScope);
+  nsAutoCString domainOrigin;
+  rv = uri->GetAsciiHost(domainOrigin);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (domainScope.IsEmpty()) {
+  if (domainOrigin.IsEmpty()) {
     // For the file:/// protocol use the exact directory as domain.
     bool isScheme = false;
     if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
       nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
-      rv = url->GetDirectory(domainScope);
+      rv = url->GetDirectory(domainOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  nsAutoCString key;
-
-  rv = CreateReversedDomain(domainScope, key);
+  // Append reversed domain
+  nsAutoCString reverseDomain;
+  rv = CreateReversedDomain(domainOrigin, reverseDomain);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  aKey.Append(reverseDomain);
+
+  // Append scheme
   nsAutoCString scheme;
   rv = uri->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  key.Append(':');
-  key.Append(scheme);
+  aKey.Append(':');
+  aKey.Append(scheme);
 
+  // Append port if any
   int32_t port = NS_GetRealPort(uri);
   if (port != -1) {
-    key.Append(nsPrintfCString(":%d", port));
-  }
-
-  if (!aPrincipal->GetUnknownAppId()) {
-    uint32_t appId = aPrincipal->GetAppId();
-    bool isInBrowserElement = aPrincipal->GetIsInBrowserElement();
-    if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
-      aKey.Assign(key);
-      return NS_OK;
-    }
-
-    aKey.Truncate();
-    aKey.AppendInt(appId);
-    aKey.Append(':');
-    aKey.Append(isInBrowserElement ? 't' : 'f');
-    aKey.Append(':');
-    aKey.Append(key);
+    aKey.Append(nsPrintfCString(":%d", port));
   }
 
   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);
@@ -206,154 +195,167 @@ CreateQuotaDBKey(nsIPrincipal* aPrincipa
   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);
 
+  aKey.Truncate();
+  BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(aKey);
+
+  nsAutoCString subdomainsDBKey;
   CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
 
-  if (!aPrincipal->GetUnknownAppId()) {
-    uint32_t appId = aPrincipal->GetAppId();
-    bool isInBrowserElement = aPrincipal->GetIsInBrowserElement();
-    if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
-      aKey.Assign(subdomainsDBKey);
-      return NS_OK;
-    }
-
-    aKey.Truncate();
-    aKey.AppendInt(appId);
-    aKey.Append(':');
-    aKey.Append(isInBrowserElement ? 't' : 'f');
-    aKey.Append(':');
-    aKey.Append(subdomainsDBKey);
-  }
+  aKey.Append(':');
+  aKey.Append(subdomainsDBKey);
 
   return NS_OK;
 }
 
 } // namespace
 
+// static
+nsCString
+DOMStorageManager::CreateOrigin(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix)
+{
+  // Note: some hard-coded sqlite statements are dependent on the format this
+  // method returns.  Changing this without updating those sqlite statements
+  // will cause malfunction.
+
+  nsAutoCString scope;
+  scope.Append(aOriginSuffix);
+  scope.Append(':');
+  scope.Append(aOriginNoSuffix);
+  return scope;
+}
+
 DOMStorageCache*
-DOMStorageManager::GetCache(const nsACString& aScope) const
+DOMStorageManager::GetCache(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix)
 {
-  DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
+  CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
+  DOMStorageCacheHashKey* entry = table->GetEntry(aOriginNoSuffix);
   if (!entry) {
     return nullptr;
   }
 
   return entry->cache();
 }
 
 already_AddRefed<DOMStorageUsage>
-DOMStorageManager::GetScopeUsage(const nsACString& aScope)
+DOMStorageManager::GetOriginUsage(const nsACString& aOriginNoSuffix)
 {
   RefPtr<DOMStorageUsage> usage;
-  if (mUsages.Get(aScope, &usage)) {
+  if (mUsages.Get(aOriginNoSuffix, &usage)) {
     return usage.forget();
   }
 
-  usage = new DOMStorageUsage(aScope);
+  usage = new DOMStorageUsage(aOriginNoSuffix);
 
   if (mType == LocalStorage) {
     DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
     if (db) {
       db->AsyncGetUsage(usage);
     }
   }
 
-  mUsages.Put(aScope, usage);
+  mUsages.Put(aOriginNoSuffix, usage);
 
   return usage.forget();
 }
 
 already_AddRefed<DOMStorageCache>
-DOMStorageManager::PutCache(const nsACString& aScope,
+DOMStorageManager::PutCache(const nsACString& aOriginSuffix,
+                            const nsACString& aOriginNoSuffix,
                             nsIPrincipal* aPrincipal)
 {
-  DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
+  CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
+  DOMStorageCacheHashKey* entry = table->PutEntry(aOriginNoSuffix);
   RefPtr<DOMStorageCache> cache = entry->cache();
 
-  nsAutoCString quotaScope;
-  CreateQuotaDBKey(aPrincipal, quotaScope);
+  nsAutoCString quotaOrigin;
+  CreateQuotaDBKey(aPrincipal, quotaOrigin);
 
   switch (mType) {
   case SessionStorage:
     // Lifetime handled by the manager, don't persist
     entry->HardRef();
-    cache->Init(this, false, aPrincipal, quotaScope);
+    cache->Init(this, false, aPrincipal, quotaOrigin);
     break;
 
   case LocalStorage:
     // Lifetime handled by the cache, do persist
-    cache->Init(this, true, aPrincipal, quotaScope);
+    cache->Init(this, true, aPrincipal, quotaOrigin);
     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());
+  CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
+  table->RemoveEntry(aCache->OriginNoSuffix());
 }
 
 nsresult
 DOMStorageManager::GetStorageInternal(bool aCreate,
                                       nsIDOMWindow* aWindow,
                                       nsIPrincipal* aPrincipal,
                                       const nsAString& aDocumentURI,
                                       bool aPrivate,
                                       nsIDOMStorage** aRetval)
 {
   nsresult rv;
 
-  nsAutoCString scope;
-  rv = CreateScopeKey(aPrincipal, scope);
+  nsAutoCString originAttrSuffix;
+  BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originAttrSuffix);
+
+  nsAutoCString originKey;
+  rv = AppendOriginNoSuffix(aPrincipal, originKey);
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  RefPtr<DOMStorageCache> cache = GetCache(scope);
+  RefPtr<DOMStorageCache> cache = GetCache(originAttrSuffix, originKey);
 
   // 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
+      // This is a 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)) {
+        if (!db->ShouldPreloadOrigin(DOMStorageManager::CreateOrigin(originAttrSuffix, originKey))) {
           return NS_OK;
         }
       } else {
-        if (scope.EqualsLiteral("knalb.:about")) {
+        if (originKey.EqualsLiteral("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);
+    cache = PutCache(originAttrSuffix, originKey, aPrincipal);
   } else if (mType == SessionStorage) {
     if (!cache->CheckPrincipal(aPrincipal)) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
   }
 
   if (aRetval) {
     nsCOMPtr<nsIDOMStorage> storage = new DOMStorage(
@@ -402,54 +404,61 @@ DOMStorageManager::CloneStorage(nsIDOMSt
 
   RefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
   if (!storage) {
     return NS_ERROR_UNEXPECTED;
   }
 
   const DOMStorageCache* origCache = storage->GetCache();
 
-  DOMStorageCache* existingCache = GetCache(origCache->Scope());
+  DOMStorageCache* existingCache = GetCache(origCache->OriginSuffix(),
+                                            origCache->OriginNoSuffix());
   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.
-  RefPtr<DOMStorageCache> newCache = PutCache(origCache->Scope(),
-                                                origCache->Principal());
+  RefPtr<DOMStorageCache> newCache = PutCache(origCache->OriginSuffix(),
+                                              origCache->OriginNoSuffix(),
+                                              origCache->Principal());
 
   newCache->CloneFrom(origCache);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
                                 nsIDOMStorage* aStorage,
                                 bool* aRetval)
 {
+  nsresult rv;
+
   RefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
   if (!storage) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *aRetval = false;
 
   if (!aPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsAutoCString scope;
-  nsresult rv = CreateScopeKey(aPrincipal, scope);
-  if (NS_FAILED(rv)) {
+  nsAutoCString suffix;
+  BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(suffix);
+
+  nsAutoCString origin;
+  rv = AppendOriginNoSuffix(aPrincipal, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  DOMStorageCache* cache = GetCache(scope);
+  DOMStorageCache* cache = GetCache(suffix, origin);
   if (cache != storage->GetCache()) {
     return NS_OK;
   }
 
   if (!storage->PrincipalEquals(aPrincipal)) {
     return NS_OK;
   }
 
@@ -469,72 +478,89 @@ DOMStorageManager::GetLocalStorageForPri
     return NS_ERROR_UNEXPECTED;
   }
 
   return CreateStorage(nullptr, aPrincipal, aDocumentURI, aPrivate, aRetval);
 }
 
 void
 DOMStorageManager::ClearCaches(uint32_t aUnloadFlags,
-                               const nsACString& aKeyPrefix)
+                               const OriginAttributesPattern& aPattern,
+                               const nsACString& aOriginScope)
 {
-  for (auto iter = mCaches.Iter(); !iter.Done(); iter.Next()) {
-    DOMStorageCache* cache = iter.Get()->cache();
-    nsCString& key = const_cast<nsCString&>(cache->Scope());
+  for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) {
+    PrincipalOriginAttributes oa;
+    DebugOnly<bool> rv = oa.PopulateFromSuffix(iter1.Key());
+    MOZ_ASSERT(rv);
+    if (!aPattern.Matches(oa)) {
+      // This table doesn't match the given origin attributes pattern
+      continue;
+    }
 
-    if (aKeyPrefix.IsEmpty() || StringBeginsWith(key, aKeyPrefix)) {
-      cache->UnloadItems(aUnloadFlags);
+    CacheOriginHashtable* table = iter1.Data();
+
+    for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
+      DOMStorageCache* cache = iter2.Get()->cache();
+
+      if (aOriginScope.IsEmpty() ||
+          StringBeginsWith(cache->OriginNoSuffix(), aOriginScope)) {
+        cache->UnloadItems(aUnloadFlags);
+      }
     }
   }
 }
 
 nsresult
-DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
+DOMStorageManager::Observe(const char* aTopic,
+                           const nsAString& aOriginAttributesPattern,
+                           const nsACString& aOriginScope)
 {
+  OriginAttributesPattern pattern;
+  pattern.Init(aOriginAttributesPattern);
+
   // Clear everything, caches + database
   if (!strcmp(aTopic, "cookie-cleared")) {
-    ClearCaches(DOMStorageCache::kUnloadComplete, EmptyCString());
+    ClearCaches(DOMStorageCache::kUnloadComplete, pattern, EmptyCString());
     return NS_OK;
   }
 
   // Clear from caches everything that has been stored
   // while in session-only mode
   if (!strcmp(aTopic, "session-only-cleared")) {
-    ClearCaches(DOMStorageCache::kUnloadSession, aScopePrefix);
+    ClearCaches(DOMStorageCache::kUnloadSession, pattern, aOriginScope);
     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")) {
-    ClearCaches(DOMStorageCache::kUnloadComplete, aScopePrefix);
+    ClearCaches(DOMStorageCache::kUnloadComplete, pattern, aOriginScope);
     return NS_OK;
   }
 
   // Clear all private-browsing caches
   if (!strcmp(aTopic, "private-browsing-data-cleared")) {
-    ClearCaches(DOMStorageCache::kUnloadPrivate, EmptyCString());
+    ClearCaches(DOMStorageCache::kUnloadPrivate, pattern, EmptyCString());
     return NS_OK;
   }
 
-  // Clear localStorage data beloging to an app.
-  if (!strcmp(aTopic, "app-data-cleared")) {
-
+  // Clear localStorage data beloging to an origin pattern
+  if (!strcmp(aTopic, "origin-attr-pattern-cleared")) {
     // sessionStorage is expected to stay
     if (mType == SessionStorage) {
       return NS_OK;
     }
 
-    ClearCaches(DOMStorageCache::kUnloadComplete, aScopePrefix);
+    ClearCaches(DOMStorageCache::kUnloadComplete, pattern, EmptyCString());
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "profile-change")) {
     // For case caches are still referenced - clear them completely
-    ClearCaches(DOMStorageCache::kUnloadComplete, EmptyCString());
+    ClearCaches(DOMStorageCache::kUnloadComplete, pattern, EmptyCString());
     mCaches.Clear();
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "low-disk-space")) {
     if (mType == LocalStorage) {
       mLowDiskSpace = true;
     }
@@ -552,17 +578,17 @@ DOMStorageManager::Observe(const char* a
 
 #ifdef DOM_STORAGE_TESTS
   if (!strcmp(aTopic, "test-reload")) {
     if (mType != LocalStorage) {
       return NS_OK;
     }
 
     // This immediately completely reloads all caches from the database.
-    ClearCaches(DOMStorageCache::kTestReload, EmptyCString());
+    ClearCaches(DOMStorageCache::kTestReload, pattern, EmptyCString());
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "test-flushed")) {
     if (!XRE_IsParentProcess()) {
       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
       if (obs) {
         obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
--- a/dom/storage/DOMStorageManager.h
+++ b/dom/storage/DOMStorageManager.h
@@ -10,21 +10,25 @@
 #include "nsIDOMStorageManager.h"
 #include "DOMStorageObserver.h"
 
 #include "DOMStorageCache.h"
 #include "mozilla/dom/DOMStorage.h"
 
 #include "nsTHashtable.h"
 #include "nsDataHashtable.h"
+#include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 
 class nsIDOMWindow;
 
 namespace mozilla {
+
+class OriginAttributesPattern;
+
 namespace dom {
 
 const DOMStorage::StorageType SessionStorage = DOMStorage::SessionStorage;
 const DOMStorage::StorageType LocalStorage = DOMStorage::LocalStorage;
 
 class DOMStorageManager : public nsIDOMStorageManager
                         , public DOMStorageObserverSink
 {
@@ -32,27 +36,31 @@ class DOMStorageManager : public nsIDOMS
   NS_DECL_NSIDOMSTORAGEMANAGER
 
 public:
   virtual DOMStorage::StorageType Type() { return mType; }
 
   // Reads the preference for DOM storage quota
   static uint32_t GetQuota();
   // Gets (but not ensures) cache for the given scope
-  DOMStorageCache* GetCache(const nsACString& aScope) const;
+  DOMStorageCache* GetCache(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix);
   // Returns object keeping usage cache for the scope.
-  already_AddRefed<DOMStorageUsage> GetScopeUsage(const nsACString& aScope);
+  already_AddRefed<DOMStorageUsage> GetOriginUsage(const nsACString& aOriginNoSuffix);
+
+  static nsCString CreateOrigin(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix);
 
 protected:
   explicit DOMStorageManager(DOMStorage::StorageType aType);
   virtual ~DOMStorageManager();
 
 private:
   // DOMStorageObserverSink, handler to various chrome clearing notification
-  virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix) override;
+  virtual nsresult Observe(const char* aTopic,
+                           const nsAString& aOriginAttributesPattern,
+                           const nsACString& aOriginScope) override;
 
   // Since nsTHashtable doesn't like multiple inheritance, we have to aggregate
   // DOMStorageCache into the entry.
   class DOMStorageCacheHashKey : public nsCStringHashKey
   {
   public:
     explicit DOMStorageCacheHashKey(const nsACString* aKey)
       : nsCStringHashKey(aKey)
@@ -73,38 +81,43 @@ private:
     // weak ref only since cache references its manager.
     DOMStorageCache* mCache;
     // hard ref when this is sessionStorage to keep it alive forever.
     RefPtr<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,
+  already_AddRefed<DOMStorageCache> PutCache(const nsACString& aOriginSuffix,
+                                             const nsACString& aOriginNoSuffix,
                                              nsIPrincipal* aPrincipal);
 
   // Helper for creation of DOM storage objects
   nsresult GetStorageInternal(bool aCreate,
                               nsIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
                               const nsAString& aDocumentURI,
                               bool aPrivate,
                               nsIDOMStorage** aRetval);
 
-  // Scope->cache map
-  nsTHashtable<DOMStorageCacheHashKey> mCaches;
+  // Suffix->origin->cache map
+  typedef nsTHashtable<DOMStorageCacheHashKey> CacheOriginHashtable;
+  nsClassHashtable<nsCStringHashKey, CacheOriginHashtable> mCaches;
+
   const DOMStorage::StorageType mType;
 
   // If mLowDiskSpace is true it indicates a low device storage situation and
   // so no localStorage writes are allowed. sessionStorage writes are still
   // allowed.
   bool mLowDiskSpace;
   bool IsLowDiskSpace() const { return mLowDiskSpace; };
 
-  void ClearCaches(uint32_t aUnloadFlags, const nsACString& aKeyPrefix);
+  void ClearCaches(uint32_t aUnloadFlags,
+                   const OriginAttributesPattern& aPattern,
+                   const nsACString& aKeyPrefix);
 
 protected:
   // Keeps usage cache objects for eTLD+1 scopes we have touched.
   nsDataHashtable<nsCStringHashKey, RefPtr<DOMStorageUsage> > mUsages;
 
   friend class DOMStorageCache;
   // Releases cache since it is no longer referrered by any DOMStorage object.
   virtual void DropCache(DOMStorageCache* aCache);
--- a/dom/storage/DOMStorageObserver.cpp
+++ b/dom/storage/DOMStorageObserver.cpp
@@ -4,23 +4,23 @@
  * 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 "mozilla/BasePrincipal.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"
@@ -57,17 +57,17 @@ DOMStorageObserver::Init()
   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);
+  obs->AddObserver(sSelf, "clear-origin-data", true);
 
   // Shutdown
   obs->AddObserver(sSelf, "profile-after-change", true);
   obs->AddObserver(sSelf, "profile-before-change", true);
   obs->AddObserver(sSelf, "xpcom-shutdown", true);
 
   // Observe low device storage notifications.
   obs->AddObserver(sSelf, "disk-space-watcher", true);
@@ -106,21 +106,23 @@ DOMStorageObserver::AddSink(DOMStorageOb
 
 void
 DOMStorageObserver::RemoveSink(DOMStorageObserverSink* aObs)
 {
   mSinks.RemoveElement(aObs);
 }
 
 void
-DOMStorageObserver::Notify(const char* aTopic, const nsACString& aData)
+DOMStorageObserver::Notify(const char* aTopic,
+                           const nsAString& aOriginAttributesPattern,
+                           const nsACString& aOriginScope)
 {
   for (uint32_t i = 0; i < mSinks.Length(); ++i) {
     DOMStorageObserverSink* sink = mSinks[i];
-    sink->Observe(aTopic, aData);
+    sink->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
   }
 }
 
 NS_IMETHODIMP
 DOMStorageObserver::Observe(nsISupports* aSubject,
                             const char* aTopic,
                             const char16_t* aData)
 {
@@ -197,33 +199,36 @@ DOMStorageObserver::Observe(nsISupports*
     }
 
     nsCOMPtr<nsIPrincipal> principal;
     perm->GetPrincipal(getter_AddRefs(principal));
     if (!principal) {
       return NS_OK;
     }
 
+    nsAutoCString originSuffix;
+    BasePrincipal::Cast(principal)->OriginAttributesRef().CreateSuffix(originSuffix);
+
     nsCOMPtr<nsIURI> origin;
     principal->GetURI(getter_AddRefs(origin));
     if (!origin) {
       return NS_OK;
     }
 
     nsAutoCString host;
     origin->GetHost(host);
     if (host.IsEmpty()) {
       return NS_OK;
     }
 
-    nsAutoCString scope;
-    rv = CreateReversedDomain(host, scope);
+    nsAutoCString originScope;
+    rv = CreateReversedDomain(host, originScope);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    Notify("session-only-cleared", scope);
+    Notify("session-only-cleared", NS_ConvertUTF8toUTF16(originSuffix), originScope);
 
     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
@@ -234,73 +239,51 @@ DOMStorageObserver::Observe(nsISupports*
       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);
+    nsAutoCString originScope;
+    rv = CreateReversedDomain(aceDomain, originScope);
     NS_ENSURE_SUCCESS(rv, rv);
 
     DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
     NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
 
-    db->AsyncClearMatchingScope(scopePrefix);
+    db->AsyncClearMatchingOrigin(originScope);
 
-    Notify("domain-data-cleared", scopePrefix);
+    Notify("domain-data-cleared", EmptyString(), originScope);
 
     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;
+  // Clear data of the origins whose prefixes will match the suffix.
+  if (!strcmp(aTopic, "clear-origin-data")) {
+    OriginAttributesPattern pattern;
+    if (!pattern.Init(nsDependentString(aData))) {
+      NS_ERROR("Cannot parse origin attributes pattern");
+      return NS_ERROR_FAILURE;
     }
 
-    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.AppendLiteral(":t:");
-    db->AsyncClearMatchingScope(scope);
-    Notify("app-data-cleared", scope);
+    db->AsyncClearMatchingOriginAttributes(pattern);
 
-    if (!browserOnly) {
-      scope.Truncate();
-      scope.AppendInt(appId);
-      scope.AppendLiteral(":f:");
-      db->AsyncClearMatchingScope(scope);
-      Notify("app-data-cleared", scope);
-    }
+    Notify("origin-attr-pattern-cleared", nsDependentString(aData));
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "profile-after-change")) {
     Notify("profile-change");
 
     return NS_OK;
--- a/dom/storage/DOMStorageObserver.h
+++ b/dom/storage/DOMStorageObserver.h
@@ -22,17 +22,19 @@ class DOMStorageObserver;
 // child processes.
 class DOMStorageObserverSink
 {
 public:
   virtual ~DOMStorageObserverSink() {}
 
 private:
   friend class DOMStorageObserver;
-  virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix) = 0;
+  virtual nsresult Observe(const char* aTopic,
+                           const nsAString& aOriginAttributesPattern,
+                           const nsACString& aOriginScope) = 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:
@@ -40,17 +42,19 @@ public:
   NS_DECL_NSIOBSERVER
 
   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());
+  void Notify(const char* aTopic,
+              const nsAString& aOriginAttributesPattern = EmptyString(),
+              const nsACString& aOriginScope = EmptyCString());
 
 private:
   virtual ~DOMStorageObserver() {}
 
   static DOMStorageObserver* sSelf;
 
   // Weak references
   nsTArray<DOMStorageObserverSink*> mSinks;
--- a/dom/storage/PStorage.ipdl
+++ b/dom/storage/PStorage.ipdl
@@ -14,30 +14,32 @@ namespace dom {
  */
 prio(normal upto urgent) sync protocol PStorage
 {
   manager PContent;
 
 parent:
   async __delete__();
 
-  prio(urgent) sync Preload(nsCString scope, uint32_t alreadyLoadedCount)
+  prio(urgent) sync Preload(nsCString originSuffix, nsCString originNoSuffix, uint32_t alreadyLoadedCount)
     returns (nsString[] keys, nsString[] values, nsresult rv);
 
-  async AsyncPreload(nsCString scope, bool priority);
+  async AsyncPreload(nsCString originSuffix, nsCString originNoSuffix, 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 AsyncAddItem(nsCString originSuffix, nsCString originNoSuffix, nsString key, nsString value);
+  async AsyncUpdateItem(nsCString originSuffix, nsCString originNoSuffix, nsString key, nsString value);
+  async AsyncRemoveItem(nsCString originSuffix, nsCString originNoSuffix, nsString key);
+  async AsyncClear(nsCString originSuffix, nsCString originNoSuffix);
   async AsyncFlush();
   
 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 Observe(nsCString topic,
+                nsString originAttributesPattern,
+                nsCString originScope);
+  async OriginsHavingData(nsCString[] origins);
+  async LoadItem(nsCString originSuffix, nsCString originNoSuffix, nsString key, nsString value);
+  async LoadDone(nsCString originSuffix, nsCString originNoSuffix, nsresult rv);
   async LoadUsage(nsCString scope, int64_t usage);
   async Error(nsresult rv);
 };
 
 }
 }
--- a/dom/storage/moz.build
+++ b/dom/storage/moz.build
@@ -8,16 +8,17 @@ EXPORTS.mozilla.dom += [
     'DOMStorage.h',
     'DOMStorageIPC.h',
 ]
 
 UNIFIED_SOURCES += [
     'DOMStorage.cpp',
     'DOMStorageCache.cpp',
     'DOMStorageDBThread.cpp',
+    'DOMStorageDBUpdater.cpp',
     'DOMStorageIPC.cpp',
     'DOMStorageManager.cpp',
     'DOMStorageObserver.cpp',
 ]
 
 IPDL_SOURCES += [
     'PStorage.ipdl',
 ]