Bug 683316 - DOMStorageImpl::GetKey performance regression, r=bz
☠☠ backed out by 1e41259daf67 ☠ ☠
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 20 Sep 2011 20:44:11 +0200
changeset 77210 f94b4d73777fdf543a620a6f6744cdf32cca14fa
parent 77209 21a39c2f9060c50afcc1e3e593bd49b55345e19d
child 77211 69fdc6af563d6679be07ee8aeb2e9714b6697fb8
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersbz
bugs683316
milestone9.0a1
Bug 683316 - DOMStorageImpl::GetKey performance regression, r=bz
dom/src/storage/Makefile.in
dom/src/storage/nsDOMStorage.cpp
dom/src/storage/nsDOMStorage.h
dom/src/storage/nsDOMStorageBaseDB.cpp
dom/src/storage/nsDOMStorageBaseDB.h
dom/src/storage/nsDOMStorageDBWrapper.cpp
dom/src/storage/nsDOMStorageDBWrapper.h
dom/src/storage/nsDOMStorageMemoryDB.cpp
dom/src/storage/nsDOMStorageMemoryDB.h
dom/src/storage/nsDOMStoragePersistentDB.cpp
dom/src/storage/nsDOMStoragePersistentDB.h
--- a/dom/src/storage/Makefile.in
+++ b/dom/src/storage/Makefile.in
@@ -43,16 +43,17 @@ VPATH          = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 LIBRARY_NAME   = jsdomstorage_s
 LIBXUL_LIBRARY = 1
 
 CPPSRCS = \
        nsDOMStorage.cpp \
+       nsDOMStorageBaseDB.cpp \
        nsDOMStorageDBWrapper.cpp \
        nsDOMStoragePersistentDB.cpp \
        nsDOMStorageMemoryDB.cpp \
        StorageChild.cpp \
        StorageParent.cpp \
        $(NULL)
 
 EXPORTS_NAMESPACES = mozilla/dom
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -721,17 +721,17 @@ DOMStorageImpl::DOMStorageImpl(nsDOMStor
   : DOMStorageBase(aThat)
 {
   Init(aStorage);
 }
 
 void
 DOMStorageImpl::Init(nsDOMStorage* aStorage)
 {
-  mItemsCached = PR_FALSE;
+  mItemsCachedVersion = 0;
   mItems.Init(8);
   mOwner = aStorage;
   if (nsDOMStorageManager::gStorageManager)
     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
 }
 
 DOMStorageImpl::~DOMStorageImpl()
 {
@@ -887,18 +887,16 @@ DOMStorageImpl::SetDBValue(const nsAStri
                                   CanUseChromePersist());
 
   PRInt32 usage;
   rv = gStorageDB->SetKey(this, aKey, aValue, aSecure, quota,
                          !IS_PERMISSION_ALLOWED(offlineAppPermission),
                          &usage);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Before bug 536544 got fixed we were dropping mItemsCached flag here
-
   if (warnQuota >= 0 && usage > warnQuota) {
     // try to include the window that exceeded the warn quota
     nsCOMPtr<nsIDOMWindow> window;
     JSContext *cx;
     nsCOMPtr<nsIJSContextStack> stack =
         do_GetService("@mozilla.org/js/xpc/ContextStack;1");
     if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
       nsCOMPtr<nsIScriptContext> scriptContext;
@@ -942,17 +940,17 @@ ClearStorageItem(nsSessionStorageEntry* 
   aEntry->mItem->SetValueInternal(EmptyString());
   return PL_DHASH_NEXT;
 }
 
 void
 DOMStorageImpl::ClearAll()
 {
   mItems.EnumerateEntries(ClearStorageItem, nsnull);
-  mItemsCached = PR_FALSE;
+  mItemsCachedVersion = 0;
 }
 
 struct CopyArgs {
   DOMStorageImpl* storage;
   bool callerSecure;
 };
 
 static PLDHashOperator
@@ -992,26 +990,26 @@ DOMStorageImpl::CloneFrom(bool aCallerSe
 }
 
 nsresult
 DOMStorageImpl::CacheKeysFromDB()
 {
   // cache all the keys in the hash. This is used by the Length and Key methods
   // use this cache for better performance. The disadvantage is that the
   // order may break if someone changes the keys in the database directly.
-  if (!mItemsCached) {
+  if (gStorageDB->IsScopeDirty(this)) {
     nsresult rv = InitDB();
     NS_ENSURE_SUCCESS(rv, rv);
 
     mItems.Clear();
 
     rv = gStorageDB->GetAllKeys(this, &mItems);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mItemsCached = PR_TRUE;
+    gStorageDB->MarkScopeCached(this);
   }
 
   return NS_OK;
 }
 
 struct KeysArrayBuilderStruct
 {
   PRBool callerIsSecure;
@@ -1130,17 +1128,16 @@ DOMStorageImpl::GetKey(bool aCallerSecur
   // passed in.
 
   // XXX: This does a linear search for the key at index, which would
   // suck if there's a large numer of indexes. Do we care? If so,
   // maybe we need to have a lazily populated key array here or
   // something?
 
   if (UseDB()) {
-    mItemsCached = PR_FALSE;
     CacheKeysFromDB();
   }
 
   IndexFinderData data(aCallerSecure, aIndex);
   mItems.EnumerateEntries(IndexFinder, &data);
 
   if (!data.mItem) {
     // aIndex was larger than the number of accessible keys. Throw.
@@ -1250,18 +1247,16 @@ DOMStorageImpl::RemoveValue(bool aCaller
     if (!aCallerSecure && secureItem)
       return NS_ERROR_DOM_SECURITY_ERR;
 
     oldValue = value;
 
     rv = gStorageDB->RemoveKey(this, aKey, !IsOfflineAllowed(mDomain),
                                aKey.Length() + value.Length());
     NS_ENSURE_SUCCESS(rv, rv);
-
-    // Before bug 536544 got fixed we were dropping mItemsCached flag here
   }
   else if (entry) {
     // clear string as StorageItems may be referencing this item
     oldValue = entry->mItem->GetValueInternal();
     entry->mItem->ClearValue();
   }
 
   if (entry) {
--- a/dom/src/storage/nsDOMStorage.h
+++ b/dom/src/storage/nsDOMStorage.h
@@ -265,16 +265,19 @@ public:
   virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
                             const nsAString& aData, nsAString& aOldValue);
   virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
                                nsAString& aOldValue);
   virtual nsresult Clear(bool aCallerSecure, PRInt32* aOldCount);
 
   // cache the keys from the database for faster lookup
   nsresult CacheKeysFromDB();
+
+  PRUint64 CachedVersion() { return mItemsCachedVersion; }
+  void SetCachedVersion(PRUint64 version) { mItemsCachedVersion = version; }
   
   // Some privileged internal pages can use a persistent storage even in
   // session-only or private-browsing modes.
   bool CanUseChromePersist();
 
   // retrieve the value and secure state corresponding to a key out of storage
   // that has been cached in mItems hash table.
   nsresult
@@ -323,18 +326,19 @@ private:
                      const nsACString& aScopeDBKey,
                      const nsACString& aQuotaDomainDBKey,
                      const nsACString& aQuotaETLDplus1DomainDBKey,
                      PRUint32 aStorageType);
   void SetSessionOnly(bool aSessionOnly);
 
   static nsresult InitDB();
 
-  // true if items from the database are cached
-  PRPackedBool mItemsCached;
+  // 0 initially or a positive data version number assigned by gStorageDB
+  // after keys have been cached from the database
+  PRUint64 mItemsCachedVersion;
 
   // the key->value item pairs
   nsTHashtable<nsSessionStorageEntry> mItems;
 
   // Weak reference to the owning storage instance
   nsDOMStorage* mOwner;
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/nsDOMStorageBaseDB.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMStorageBaseDB.h"
+#include "nsDOMStorage.h"
+
+PRUint64 nsDOMStorageBaseDB::sGlobalVersion = 1;
+
+nsDOMStorageBaseDB::nsDOMStorageBaseDB()
+{
+  mScopesVersion.Init(8);
+}
+
+// public
+
+void
+nsDOMStorageBaseDB::MarkScopeCached(DOMStorageImpl* aStorage)
+{
+  aStorage->SetCachedVersion(CachedScopeVersion(aStorage));
+}
+
+bool
+nsDOMStorageBaseDB::IsScopeDirty(DOMStorageImpl* aStorage)
+{
+  return !aStorage->CachedVersion() ||
+         (aStorage->CachedVersion() != CachedScopeVersion(aStorage));
+}
+
+// protected
+
+// static
+PRUint64
+nsDOMStorageBaseDB::NextGlobalVersion()
+{
+  sGlobalVersion++;
+  if (sGlobalVersion == 0) // Control overlap, never return 0
+    sGlobalVersion = 1;
+  return sGlobalVersion;
+}
+
+PRUint64
+nsDOMStorageBaseDB::CachedScopeVersion(DOMStorageImpl* aStorage)
+{
+  PRUint64 currentVersion;
+  if (mScopesVersion.Get(aStorage->GetScopeDBKey(), &currentVersion))
+    return currentVersion;
+
+  mScopesVersion.Put(aStorage->GetScopeDBKey(), sGlobalVersion);
+  return sGlobalVersion;
+}
+
+void
+nsDOMStorageBaseDB::MarkScopeDirty(DOMStorageImpl* aStorage)
+{
+  PRUint64 nextVersion = NextGlobalVersion();
+  mScopesVersion.Put(aStorage->GetScopeDBKey(), nextVersion);
+
+  // We may do this because the storage updates its cache along with
+  // updating the database.
+  aStorage->SetCachedVersion(nextVersion);
+}
+
+void
+nsDOMStorageBaseDB::MarkAllScopesDirty()
+{
+  mScopesVersion.Clear();
+  NextGlobalVersion();
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/storage/nsDOMStorageBaseDB.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsDOMStorageBaseDB_h___
+#define nsDOMStorageBaseDB_h___
+
+#include "nscore.h"
+#include "nsDataHashtable.h"
+
+class DOMStorageImpl;
+
+class nsDOMStorageBaseDB
+{
+public:
+  nsDOMStorageBaseDB();
+  virtual ~nsDOMStorageBaseDB() {}
+
+  /**
+   * Marks the storage as "cached" after the DOMStorageImpl object has loaded
+   * all items to its memory copy of the entries - IsScopeDirty returns false
+   * after call of this method for this storage.
+   *
+   * When a key is changed or deleted in the storage, the storage scope is
+   * marked as "dirty" again and makes the DOMStorageImpl object recache its
+   * keys on next access, because IsScopeDirty returns true again.
+   */
+  void MarkScopeCached(DOMStorageImpl* aStorage);
+
+  /**
+   * Test whether the storage for the scope (i.e. origin or host) has been
+   * changed since the last MarkScopeCached call.
+   */
+  bool IsScopeDirty(DOMStorageImpl* aStorage);
+
+protected:
+  nsDataHashtable<nsCStringHashKey, PRUint64> mScopesVersion;
+
+  static PRUint64 NextGlobalVersion();
+  PRUint64 CachedScopeVersion(DOMStorageImpl* aStorage);
+
+  void MarkScopeDirty(DOMStorageImpl* aStorage);
+  void MarkAllScopesDirty();
+
+private:
+  static PRUint64 sGlobalVersion;
+};
+
+#endif /* nsDOMStorageDB_h___ */
--- a/dom/src/storage/nsDOMStorageDBWrapper.cpp
+++ b/dom/src/storage/nsDOMStorageDBWrapper.cpp
@@ -108,27 +108,33 @@ nsDOMStorageDBWrapper::FlushAndDeleteTem
   // Everything flushed?  Then no need for a timer.
   if (!mChromePersistentDB.mTempTableLoads.Count() && 
       !mPersistentDB.mTempTableLoads.Count())
     StopTempTableFlushTimer();
 
   return NS_FAILED(rv1) ? rv1 : rv2;
 }
 
-#define IMPL_FORWARDER(_code)                                         \
+#define IMPL_FORWARDER_GUTS(_return, _code)                                \
   PR_BEGIN_MACRO                                                      \
   if (aStorage->CanUseChromePersist())                                \
-    return mChromePersistentDB._code;                                 \
+    _return mChromePersistentDB._code;                                \
   if (nsDOMStorageManager::gStorageManager->InPrivateBrowsingMode())  \
-    return mPrivateBrowsingDB._code;                                  \
+    _return mPrivateBrowsingDB._code;                                 \
   if (aStorage->SessionOnly())                                        \
-    return mSessionOnlyDB._code;                                      \
-  return mPersistentDB._code;                                         \
+    _return mSessionOnlyDB._code;                                     \
+  _return mPersistentDB._code;                                        \
   PR_END_MACRO
 
+#define IMPL_FORWARDER(_code)                                  \
+  IMPL_FORWARDER_GUTS(return, _code)
+
+#define IMPL_VOID_FORWARDER(_code)                                    \
+  IMPL_FORWARDER_GUTS((void), _code)
+
 nsresult
 nsDOMStorageDBWrapper::GetAllKeys(DOMStorageImpl* aStorage,
                                   nsTHashtable<nsSessionStorageEntry>* aKeys)
 {
   IMPL_FORWARDER(GetAllKeys(aStorage, aKeys));
 }
 
 nsresult
@@ -171,16 +177,28 @@ nsDOMStorageDBWrapper::RemoveKey(DOMStor
 }
 
 nsresult
 nsDOMStorageDBWrapper::ClearStorage(DOMStorageImpl* aStorage)
 {
   IMPL_FORWARDER(ClearStorage(aStorage));
 }
 
+void
+nsDOMStorageDBWrapper::MarkScopeCached(DOMStorageImpl* aStorage)
+{
+  IMPL_VOID_FORWARDER(MarkScopeCached(aStorage));
+}
+
+bool
+nsDOMStorageDBWrapper::IsScopeDirty(DOMStorageImpl* aStorage)
+{
+  IMPL_FORWARDER(IsScopeDirty(aStorage));
+}
+
 nsresult
 nsDOMStorageDBWrapper::DropSessionOnlyStoragesForHost(const nsACString& aHostName)
 {
   return mSessionOnlyDB.RemoveOwner(aHostName, PR_TRUE);
 }
 
 nsresult
 nsDOMStorageDBWrapper::DropPrivateBrowsingStorages()
--- a/dom/src/storage/nsDOMStorageDBWrapper.h
+++ b/dom/src/storage/nsDOMStorageDBWrapper.h
@@ -186,16 +186,35 @@ public:
 
   /**
     * Returns usage of the domain and optionaly by any subdomain.
     */
   nsresult
   GetUsage(const nsACString& aDomain, PRBool aIncludeSubDomains, PRInt32 *aUsage);
 
   /**
+   * Marks the storage as "cached" after the DOMStorageImpl object has loaded
+   * all items to its memory copy of the entries - IsScopeDirty returns false
+   * after call of this method for this storage.
+   *
+   * When a key is changed or deleted in the storage, the storage scope is
+   * marked as "dirty" again and makes the DOMStorageImpl object recache its
+   * keys on next access, because IsScopeDirty returns true again.
+   */
+  void
+  MarkScopeCached(DOMStorageImpl* aStorage);
+
+  /**
+   * Test whether the storage for the scope (i.e. origin or host) has been
+   * changed since the last MarkScopeCached call.
+   */
+  bool
+  IsScopeDirty(DOMStorageImpl* aStorage);
+
+  /**
     * Turns "http://foo.bar.com:80" to "moc.rab.oof.:http:80",
     * i.e. reverses the host, appends a dot, appends the schema
     * and a port number.
     */
   static nsresult CreateOriginScopeDBKey(nsIURI* aUri, nsACString& aKey);
 
   /**
     * Turns "http://foo.bar.com" to "moc.rab.oof.",
--- a/dom/src/storage/nsDOMStorageMemoryDB.cpp
+++ b/dom/src/storage/nsDOMStorageMemoryDB.cpp
@@ -230,16 +230,18 @@ nsDOMStorageMemoryDB::SetKey(DOMStorageI
 
   storage->mUsageDelta += aValue.Length() - item->mValue.Length();
 
   item->mValue = aValue;
   item->mSecure = aSecure;
 
   *aNewUsage = usage;
 
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStorageMemoryDB::SetSecure(DOMStorageImpl* aStorage,
                                 const nsAString& aKey,
                                 const PRBool aSecure)
 {
@@ -250,16 +252,18 @@ nsDOMStorageMemoryDB::SetSecure(DOMStora
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsInMemoryItem* item;
   if (!storage->mTable.Get(aKey, &item))
     return NS_ERROR_DOM_NOT_FOUND_ERR;
 
   item->mSecure = aSecure;
 
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStorageMemoryDB::RemoveKey(DOMStorageImpl* aStorage,
                                 const nsAString& aKey,
                                 PRBool aExcludeOfflineFromUsage,
                                 PRInt32 aKeyUsage)
@@ -272,16 +276,18 @@ nsDOMStorageMemoryDB::RemoveKey(DOMStora
 
   nsInMemoryItem* item;
   if (!storage->mTable.Get(aKey, &item))
     return NS_ERROR_DOM_NOT_FOUND_ERR;
 
   storage->mUsageDelta -= aKey.Length() + item->mValue.Length();
   storage->mTable.Remove(aKey);
 
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 static PLDHashOperator
 RemoveAllKeysEnum(const nsAString& keyname,
                   nsAutoPtr<nsDOMStorageMemoryDB::nsInMemoryItem>& item,
                   void *closure)
 {
@@ -297,23 +303,27 @@ nsDOMStorageMemoryDB::ClearStorage(DOMSt
 {
   nsresult rv;
 
   nsInMemoryStorage* storage;
   rv = GetItemsTable(aStorage, &storage);
   NS_ENSURE_SUCCESS(rv, rv);
 
   storage->mTable.Enumerate(RemoveAllKeysEnum, storage);
+
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStorageMemoryDB::DropStorage(DOMStorageImpl* aStorage)
 {
   mData.Remove(aStorage->GetScopeDBKey());
+  MarkScopeDirty(aStorage);
   return NS_OK;
 }
 
 struct RemoveOwnersStruc
 {
   nsCString* mSubDomain;
   PRBool mMatch;
 };
@@ -341,16 +351,18 @@ nsDOMStorageMemoryDB::RemoveOwner(const 
   if (!aIncludeSubDomains)
     subdomainsDBKey.AppendLiteral(":");
 
   RemoveOwnersStruc struc;
   struc.mSubDomain = &subdomainsDBKey;
   struc.mMatch = PR_TRUE;
   mData.Enumerate(RemoveOwnersEnum, &struc);
 
+  MarkAllScopesDirty();
+
   return NS_OK;
 }
 
 
 nsresult
 nsDOMStorageMemoryDB::RemoveOwners(const nsTArray<nsString> &aOwners,
                                    PRBool aIncludeSubDomains,
                                    PRBool aMatch)
@@ -373,23 +385,28 @@ nsDOMStorageMemoryDB::RemoveOwners(const
       quotaKey.AppendLiteral(":");
 
     RemoveOwnersStruc struc;
     struc.mSubDomain = &quotaKey;
     struc.mMatch = aMatch;
     mData.Enumerate(RemoveOwnersEnum, &struc);
   }
 
+  MarkAllScopesDirty();
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStorageMemoryDB::RemoveAll()
 {
   mData.Clear(); // XXX Check this releases all instances
+
+  MarkAllScopesDirty();
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStorageMemoryDB::GetUsage(DOMStorageImpl* aStorage,
                                PRBool aExcludeOfflineFromUsage, PRInt32 *aUsage)
 {
   return GetUsageInternal(aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage),
--- a/dom/src/storage/nsDOMStorageMemoryDB.h
+++ b/dom/src/storage/nsDOMStorageMemoryDB.h
@@ -35,22 +35,23 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsDOMStorageMemoryDB_h___
 #define nsDOMStorageMemoryDB_h___
 
 #include "nscore.h"
+#include "nsDOMStorageBaseDB.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 
 class nsDOMStoragePersistentDB;
 
-class nsDOMStorageMemoryDB
+class nsDOMStorageMemoryDB : public nsDOMStorageBaseDB
 {
 public:
   nsDOMStorageMemoryDB() : mPreloading(PR_FALSE) {}
   ~nsDOMStorageMemoryDB() {}
 
   class nsInMemoryItem
   {
   public:
--- a/dom/src/storage/nsDOMStoragePersistentDB.cpp
+++ b/dom/src/storage/nsDOMStoragePersistentDB.cpp
@@ -721,16 +721,18 @@ nsDOMStoragePersistentDB::SetKey(DOMStor
 
   if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
     mCachedOwner = aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage);
     mCachedUsage = usage;
   }
 
   *aNewUsage = usage;
 
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::SetSecure(DOMStorageImpl* aStorage,
                                     const nsAString& aKey,
                                     const PRBool aSecure)
 {
@@ -755,17 +757,22 @@ nsDOMStoragePersistentDB::SetSecure(DOMS
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
                                aSecure ? 1 : 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return mSetSecureStatement->Execute();
+  rv = mSetSecureStatement->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  MarkScopeDirty(aStorage);
+
+  return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::RemoveKey(DOMStorageImpl* aStorage,
                                     const nsAString& aKey,
                                     PRBool aExcludeOfflineFromUsage,
                                     PRInt32 aKeyUsage)
 {
@@ -791,16 +798,18 @@ nsDOMStoragePersistentDB::RemoveKey(DOMS
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mRemoveKeyStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::ClearStorage(DOMStorageImpl* aStorage)
 {
   nsresult rv;
 
@@ -820,16 +829,18 @@ nsDOMStoragePersistentDB::ClearStorage(D
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mRemoveStorageStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  MarkScopeDirty(aStorage);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
                                       PRBool aIncludeSubDomains)
 {
   nsresult rv;
@@ -859,16 +870,18 @@ nsDOMStoragePersistentDB::RemoveOwner(co
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mRemoveOwnerStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  MarkAllScopesDirty();
+
   return NS_OK;
 }
 
 
 nsresult
 nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
                                        PRBool aIncludeSubDomains,
                                        PRBool aMatch)
@@ -936,32 +949,36 @@ nsDOMStoragePersistentDB::RemoveOwners(c
   }
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  MarkAllScopesDirty();
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::RemoveAll()
 {
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mozStorageStatementScoper scope(mRemoveAllStatement);
 
   rv = mRemoveAllStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  MarkAllScopesDirty();
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::GetUsage(DOMStorageImpl* aStorage,
                                    PRBool aExcludeOfflineFromUsage,
                                    PRInt32 *aUsage)
 {
--- a/dom/src/storage/nsDOMStoragePersistentDB.h
+++ b/dom/src/storage/nsDOMStoragePersistentDB.h
@@ -35,29 +35,30 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsDOMStoragePersistentDB_h___
 #define nsDOMStoragePersistentDB_h___
 
 #include "nscore.h"
+#include "nsDOMStorageBaseDB.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "nsTHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 
 class DOMStorageImpl;
 class nsSessionStorageEntry;
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
-class nsDOMStoragePersistentDB
+class nsDOMStoragePersistentDB : public nsDOMStorageBaseDB
 {
 public:
   nsDOMStoragePersistentDB();
   ~nsDOMStoragePersistentDB() {}
 
   nsresult
   Init(const nsString& aDatabaseName);