Bug 1322316 - Split SessionStorage and LocalStorage implementation - part 5 - Shared broadcasting of changes, r=asuth
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 17 May 2017 07:01:14 +0200
changeset 409203 fc8021ded92b04ded69778a7b1f2857bb7f61741
parent 409202 d6d373c78235d36229ef3b355fbaefa48d6dfa05
child 409204 6937455b609899c63f7aa9c77f5b8fa7df213127
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1322316
milestone55.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 1322316 - Split SessionStorage and LocalStorage implementation - part 5 - Shared broadcasting of changes, r=asuth
dom/ipc/ContentChild.cpp
dom/storage/LocalStorage.cpp
dom/storage/LocalStorageManager.cpp
dom/storage/LocalStorageManager.h
dom/storage/SessionStorage.cpp
dom/storage/SessionStorageManager.cpp
dom/storage/Storage.cpp
dom/storage/Storage.h
dom/storage/StorageCache.cpp
dom/storage/StorageDBThread.cpp
dom/storage/StorageDBUpdater.cpp
dom/storage/StorageObserver.cpp
dom/storage/StorageUtils.cpp
dom/storage/StorageUtils.h
dom/storage/moz.build
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -30,17 +30,17 @@
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/PushNotifier.h"
-#include "mozilla/dom/Storage.h"
+#include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/psm/PSMContentListener.h"
@@ -3180,19 +3180,18 @@ ContentChild::RecvBlobURLUnregistration(
 mozilla::ipc::IPCResult
 ContentChild::RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
                                              const nsString& aKey,
                                              const nsString& aOldValue,
                                              const nsString& aNewValue,
                                              const IPC::Principal& aPrincipal,
                                              const bool& aIsPrivate)
 {
-  Storage::DispatchStorageEvent(Storage::LocalStorage,
-                                aDocumentURI, aKey, aOldValue, aNewValue,
-                                aPrincipal, aIsPrivate, nullptr, true);
+  LocalStorage::DispatchStorageEvent(aDocumentURI, aKey, aOldValue, aNewValue,
+                                     aPrincipal, aIsPrivate, nullptr, true);
   return IPC_OK();
 }
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 bool
 ContentChild::SendGetA11yContentId()
 {
   return PContentChild::SendGetA11yContentId(&mMsaaID);
--- a/dom/storage/LocalStorage.cpp
+++ b/dom/storage/LocalStorage.cpp
@@ -2,16 +2,17 @@
 /* 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 "LocalStorage.h"
 #include "LocalStorageManager.h"
 #include "StorageCache.h"
+#include "StorageUtils.h"
 
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsICookiePermission.h"
 
 #include "mozilla/dom/ContentChild.h"
@@ -169,51 +170,16 @@ LocalStorage::Clear(nsIPrincipal& aSubje
     return;
   }
 
   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
     BroadcastChangeNotification(NullString(), NullString(), NullString());
   }
 }
 
-namespace {
-
-class StorageNotifierRunnable : public Runnable
-{
-public:
-  StorageNotifierRunnable(nsISupports* aSubject, bool aPrivateBrowsing)
-    : Runnable("StorageNotifierRunnable")
-    , mSubject(aSubject)
-    , mPrivateBrowsing(aPrivateBrowsing)
-  { }
-
-  NS_DECL_NSIRUNNABLE
-
-private:
-  nsCOMPtr<nsISupports> mSubject;
-  const bool mPrivateBrowsing;
-};
-
-NS_IMETHODIMP
-StorageNotifierRunnable::Run()
-{
-  nsCOMPtr<nsIObserverService> observerService =
-    mozilla::services::GetObserverService();
-  if (observerService) {
-    observerService->NotifyObservers(mSubject,
-                                     mPrivateBrowsing
-                                       ? "dom-private-storage2-changed"
-                                       : "dom-storage2-changed",
-                                     u"localStorage");
-  }
-  return NS_OK;
-}
-
-} // namespace
-
 void
 LocalStorage::BroadcastChangeNotification(const nsSubstring& aKey,
                                           const nsSubstring& aOldValue,
                                           const nsSubstring& aNewValue)
 {
   if (!XRE_IsParentProcess() && Principal()) {
     // If we are in a child process, we want to send a message to the parent in
     // order to broadcast the StorageEvent correctly to any child process.
@@ -232,40 +198,18 @@ LocalStorage::DispatchStorageEvent(const
                                    const nsAString& aKey,
                                    const nsAString& aOldValue,
                                    const nsAString& aNewValue,
                                    nsIPrincipal* aPrincipal,
                                    bool aIsPrivate,
                                    Storage* aStorage,
                                    bool aImmediateDispatch)
 {
-  StorageEventInit dict;
-  dict.mBubbles = false;
-  dict.mCancelable = false;
-  dict.mKey = aKey;
-  dict.mNewValue = aNewValue;
-  dict.mOldValue = aOldValue;
-  dict.mStorageArea = aStorage;
-  dict.mUrl = aDocumentURI;
-
-  // Note, this DOM event should never reach JS. It is cloned later in
-  // nsGlobalWindow.
-  RefPtr<StorageEvent> event =
-    StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
-
-  event->SetPrincipal(aPrincipal);
-
-  RefPtr<StorageNotifierRunnable> r =
-    new StorageNotifierRunnable(event, aIsPrivate);
-
-  if (aImmediateDispatch) {
-    Unused << r->Run();
-  } else {
-    NS_DispatchToMainThread(r);
-  }
+  NotifyChange(aStorage, aPrincipal, aKey, aOldValue, aNewValue,
+               u"localStorage", aDocumentURI, aIsPrivate, aImmediateDispatch);
 
   // If we are in the parent process and we have the principal, we want to
   // broadcast this event to every other process.
   if (XRE_IsParentProcess() && aPrincipal) {
     for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
       Unused << cp->SendDispatchLocalStorageChange(
         nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
         nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
@@ -322,25 +266,20 @@ LocalStorage::CanUseStorage(nsIPrincipal
   if (access == nsContentUtils::StorageAccess::eDeny) {
     return false;
   }
 
   mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
   return CanAccess(&aSubjectPrincipal);
 }
 
-// Defined in StorageManager.cpp
-extern bool
-PrincipalsEqual(nsIPrincipal* aObjectPrincipal,
-                nsIPrincipal* aSubjectPrincipal);
-
 bool
 LocalStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
 {
-  return PrincipalsEqual(mPrincipal, aPrincipal);
+  return StorageUtils::PrincipalsEqual(mPrincipal, aPrincipal);
 }
 
 void
 LocalStorage::GetSupportedNames(nsTArray<nsString>& aKeys)
 {
   if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
     // return just an empty array
     aKeys.Clear();
--- a/dom/storage/LocalStorageManager.cpp
+++ b/dom/storage/LocalStorageManager.cpp
@@ -2,16 +2,17 @@
 /* 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 "LocalStorageManager.h"
 #include "LocalStorage.h"
 #include "StorageDBThread.h"
+#include "StorageUtils.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIEffectiveTLDService.h"
 
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsIURL.h"
 #include "nsPrintfCString.h"
@@ -24,16 +25,18 @@
 // Only allow relatively small amounts of data since performance of
 // the synchronous IO is very bad.
 // We are enforcing simple per-origin quota only.
 #define DEFAULT_QUOTA_LIMIT (5 * 1024)
 
 namespace mozilla {
 namespace dom {
 
+using namespace StorageUtils;
+
 namespace {
 
 int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
 
 } // namespace
 
 LocalStorageManager* LocalStorageManager::sSelf = nullptr;
 
@@ -47,61 +50,16 @@ LocalStorageManager::GetQuota()
                                          "dom.storage.default_quota",
                                          DEFAULT_QUOTA_LIMIT);
     preferencesInitialized = true;
   }
 
   return gQuotaLimit * 1024; // pref is in kBs
 }
 
-void
-ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
-{
-  nsACString::const_iterator sourceBegin, sourceEnd;
-  aSource.BeginReading(sourceBegin);
-  aSource.EndReading(sourceEnd);
-
-  aResult.SetLength(aSource.Length());
-  nsACString::iterator destEnd;
-  aResult.EndWriting(destEnd);
-
-  while (sourceBegin != sourceEnd) {
-    *(--destEnd) = *sourceBegin;
-    ++sourceBegin;
-  }
-}
-
-nsresult
-CreateReversedDomain(const nsACString& aAsciiDomain,
-                     nsACString& aKey)
-{
-  if (aAsciiDomain.IsEmpty()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  ReverseString(aAsciiDomain, aKey);
-
-  aKey.Append('.');
-  return NS_OK;
-}
-
-bool
-PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
-{
-  if (!aSubjectPrincipal) {
-    return true;
-  }
-
-  if (!aObjectPrincipal) {
-    return false;
-  }
-
-  return aSubjectPrincipal->Equals(aObjectPrincipal);
-}
-
 NS_IMPL_ISUPPORTS(LocalStorageManager,
                   nsIDOMStorageManager)
 
 LocalStorageManager::LocalStorageManager()
   : mCaches(8)
   , mLowDiskSpace(false)
 {
   StorageObserver* observer = StorageObserver::Self();
@@ -130,69 +88,16 @@ LocalStorageManager::~LocalStorageManage
   }
 
   sSelf = nullptr;
 }
 
 namespace {
 
 nsresult
-AppendOriginNoSuffix(nsIPrincipal* aPrincipal, nsACString& aKey)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIURI> uri;
-  rv = aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!uri) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  nsAutoCString domainOrigin;
-  rv = uri->GetAsciiHost(domainOrigin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  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(domainOrigin);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  }
-
-  // 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);
-
-  aKey.Append(':');
-  aKey.Append(scheme);
-
-  // Append port if any
-  int32_t port = NS_GetRealPort(uri);
-  if (port != -1) {
-    aKey.Append(nsPrintfCString(":%d", port));
-  }
-
-  return NS_OK;
-}
-
-nsresult
 CreateQuotaDBKey(nsIPrincipal* aPrincipal,
                  nsACString& aKey)
 {
   nsresult rv;
 
   nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
     NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -304,25 +209,22 @@ LocalStorageManager::DropCache(StorageCa
 nsresult
 LocalStorageManager::GetStorageInternal(CreateMode aCreateMode,
                                         mozIDOMWindow* aWindow,
                                         nsIPrincipal* aPrincipal,
                                         const nsAString& aDocumentURI,
                                         bool aPrivate,
                                         nsIDOMStorage** aRetval)
 {
-  nsresult rv;
-
   nsAutoCString originAttrSuffix;
-  aPrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
+  nsAutoCString originKey;
 
-  nsAutoCString originKey;
-  rv = AppendOriginNoSuffix(aPrincipal, originKey);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_NOT_AVAILABLE;
+  nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   RefPtr<StorageCache> cache = GetCache(originAttrSuffix, originKey);
 
   // Get or create a cache for the given scope
   if (!cache) {
     if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
       *aRetval = nullptr;
@@ -410,21 +312,19 @@ LocalStorageManager::CheckStorage(nsIPri
 
   *aRetval = false;
 
   if (!aPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoCString suffix;
-  aPrincipal->OriginAttributesRef().CreateSuffix(suffix);
-
   nsAutoCString origin;
-  rv = AppendOriginNoSuffix(aPrincipal, origin);
-  if (NS_FAILED(rv)) {
+  rv = GenerateOriginKey(aPrincipal, suffix, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   StorageCache* cache = GetCache(suffix, origin);
   if (cache != storage->GetCache()) {
     return NS_OK;
   }
 
--- a/dom/storage/LocalStorageManager.h
+++ b/dom/storage/LocalStorageManager.h
@@ -27,29 +27,33 @@ namespace dom {
 
 class LocalStorageManager final : public nsIDOMStorageManager
                                 , public StorageObserverSink
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMSTORAGEMANAGER
 
 public:
+  LocalStorageManager();
+
   // Reads the preference for DOM storage quota
   static uint32_t GetQuota();
+
   // Gets (but not ensures) cache for the given scope
   StorageCache* GetCache(const nsACString& aOriginSuffix,
                          const nsACString& aOriginNoSuffix);
+
   // Returns object keeping usage cache for the scope.
-  already_AddRefed<StorageUsage> GetOriginUsage(const nsACString& aOriginNoSuffix);
+  already_AddRefed<StorageUsage>
+  GetOriginUsage(const nsACString& aOriginNoSuffix);
 
   static nsCString CreateOrigin(const nsACString& aOriginSuffix,
                                 const nsACString& aOriginNoSuffix);
 
 private:
-  LocalStorageManager();
   ~LocalStorageManager();
 
   // StorageObserverSink, handler to various chrome clearing notification
   nsresult Observe(const char* aTopic,
                    const nsAString& aOriginAttributesPattern,
                    const nsACString& aOriginScope) override;
 
   // Since nsTHashtable doesn't like multiple inheritance, we have to aggregate
--- a/dom/storage/SessionStorage.cpp
+++ b/dom/storage/SessionStorage.cpp
@@ -191,17 +191,18 @@ SessionStorage::CanUseStorage(nsIPrincip
   return CanAccess(&aSubjectPrincipal);
 }
 
 void
 SessionStorage::BroadcastChangeNotification(const nsAString& aKey,
                                             const nsAString& aOldValue,
                                             const nsAString& aNewValue)
 {
-  // TODO
+  NotifyChange(this, Principal(), aKey, aOldValue, aNewValue, u"sessionStorage",
+               mDocumentURI, mIsPrivate, false);
 }
 
 // ----------------------------------------------------------------------------
 // SessionStorageCache
 // ----------------------------------------------------------------------------
 
 SessionStorageCache::SessionStorageCache()
   : mOriginQuotaUsage(0)
--- a/dom/storage/SessionStorageManager.cpp
+++ b/dom/storage/SessionStorageManager.cpp
@@ -175,21 +175,19 @@ SessionStorageManager::CheckStorage(nsIP
   }
 
   RefPtr<SessionStorage> sessionStorage =
     static_cast<SessionStorage*>(aStorage);
   if (sessionStorage->Cache() != cache) {
     return NS_OK;
   }
 
-/* TODO
-  if (!storage->PrincipalEquals(aPrincipal)) {
+  if (!StorageUtils::PrincipalsEqual(storage->Principal(), aPrincipal)) {
     return NS_OK;
   }
-*/
 
   *aRetval = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SessionStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
                                                    const nsAString& aDocumentURI,
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -41,10 +41,80 @@ Storage::CanAccess(nsIPrincipal* aPrinci
 }
 
 /* virtual */ JSObject*
 Storage::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return StorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
+namespace {
+
+class StorageNotifierRunnable : public Runnable
+{
+public:
+  StorageNotifierRunnable(nsISupports* aSubject, const char16_t *aStorageType,
+                          bool aPrivateBrowsing)
+    : Runnable("StorageNotifierRunnable")
+    , mSubject(aSubject)
+    , mStorageType(aStorageType)
+    , mPrivateBrowsing(aPrivateBrowsing)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    if (observerService) {
+      observerService->NotifyObservers(mSubject,
+                                       mPrivateBrowsing
+                                         ? "dom-private-storage2-changed"
+                                         : "dom-storage2-changed",
+                                       mStorageType);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsISupports> mSubject;
+  const char16_t* mStorageType;
+  const bool mPrivateBrowsing;
+};
+
+} // namespace
+
+/* static */ void
+Storage::NotifyChange(Storage* aStorage, nsIPrincipal* aPrincipal,
+                      const nsAString& aKey,
+                      const nsAString& aOldValue, const nsAString& aNewValue,
+                      const char16_t* aStorageType,
+                      const nsAString& aDocumentURI, bool aIsPrivate,
+                      bool aImmediateDispatch)
+{
+  StorageEventInit dict;
+  dict.mBubbles = false;
+  dict.mCancelable = false;
+  dict.mKey = aKey;
+  dict.mNewValue = aNewValue;
+  dict.mOldValue = aOldValue;
+  dict.mStorageArea = aStorage;
+  dict.mUrl = aDocumentURI;
+
+  // Note, this DOM event should never reach JS. It is cloned later in
+  // nsGlobalWindow.
+  RefPtr<StorageEvent> event =
+    StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
+
+  event->SetPrincipal(aPrincipal);
+
+  RefPtr<StorageNotifierRunnable> r =
+    new StorageNotifierRunnable(event, aStorageType, aIsPrivate);
+
+  if (aImmediateDispatch) {
+    Unused << r->Run();
+  } else {
+    NS_DispatchToMainThread(r, NS_DISPATCH_NORMAL);
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/storage/Storage.h
+++ b/dom/storage/Storage.h
@@ -104,16 +104,23 @@ public:
     aFound = !aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION);
   }
 
   virtual void
   Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) = 0;
 
   virtual bool IsSessionOnly() const = 0;
 
+  static void
+  NotifyChange(Storage* aStorage, nsIPrincipal* aPrincipal,
+               const nsAString& aKey, const nsAString& aOldValue,
+               const nsAString& aNewValue, const char16_t* aStorageType,
+               const nsAString& aDocumentURI, bool aIsPrivate,
+               bool aImmediateDispatch);
+
 protected:
   virtual ~Storage();
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
--- a/dom/storage/StorageCache.cpp
+++ b/dom/storage/StorageCache.cpp
@@ -4,16 +4,17 @@
  * 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 "StorageCache.h"
 
 #include "Storage.h"
 #include "StorageDBThread.h"
 #include "StorageIPC.h"
+#include "StorageUtils.h"
 #include "LocalStorageManager.h"
 
 #include "nsAutoPtr.h"
 #include "nsDOMString.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Unused.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
@@ -541,25 +542,20 @@ StorageCache::CloneFrom(const StorageCac
 }
 
 int64_t
 StorageCache::GetOriginQuotaUsage(const LocalStorage* aStorage) const
 {
   return mData[GetDataSetIndex(aStorage)].mOriginQuotaUsage;
 }
 
-// Defined in StorageManager.cpp
-extern bool
-PrincipalsEqual(nsIPrincipal* aObjectPrincipal,
-                nsIPrincipal* aSubjectPrincipal);
-
 bool
 StorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
 {
-  return PrincipalsEqual(mPrincipal, aPrincipal);
+  return StorageUtils::PrincipalsEqual(mPrincipal, aPrincipal);
 }
 
 void
 StorageCache::UnloadItems(uint32_t aUnloadFlags)
 {
   if (aUnloadFlags & kUnloadDefault) {
     // Must wait for preload to pass correct usage to ProcessUsageDelta
     // XXX this is not technically needed right now since there is just
--- a/dom/storage/StorageDBThread.cpp
+++ b/dom/storage/StorageDBThread.cpp
@@ -2,16 +2,17 @@
 /* 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 "StorageDBThread.h"
 #include "StorageDBUpdater.h"
 #include "StorageCache.h"
+#include "StorageUtils.h"
 #include "LocalStorageManager.h"
 
 #include "nsIEffectiveTLDService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "mozStorageCID.h"
@@ -38,16 +39,18 @@
 #define MAX_WAL_SIZE_BYTES 512 * 1024
 
 // Current version of the database schema
 #define CURRENT_SCHEMA_VERSION 2
 
 namespace mozilla {
 namespace dom {
 
+using namespace StorageUtils;
+
 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(StorageCacheBridge* aCache)
 {
   nsCString result;
@@ -441,20 +444,16 @@ StorageDBThread::ThreadObserver::OnProce
 
 NS_IMETHODIMP
 StorageDBThread::ThreadObserver::AfterProcessNextEvent(nsIThreadInternal* aThread,
                                                        bool eventWasProcessed)
 {
   return NS_OK;
 }
 
-
-extern void
-ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
-
 nsresult
 StorageDBThread::OpenDatabaseConnection()
 {
   nsresult rv;
 
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<mozIStorageService> service
--- a/dom/storage/StorageDBUpdater.cpp
+++ b/dom/storage/StorageDBUpdater.cpp
@@ -1,33 +1,33 @@
 /* -*- 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 "LocalStorageManager.h"
+#include "StorageUtils.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 2
 
 namespace mozilla {
 namespace dom {
 
-extern void
-ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
+using namespace StorageUtils;
 
 namespace {
 
 class nsReverseStringSQLFunction final : public mozIStorageFunction
 {
   ~nsReverseStringSQLFunction() {}
 
   NS_DECL_ISUPPORTS
--- a/dom/storage/StorageObserver.cpp
+++ b/dom/storage/StorageObserver.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StorageObserver.h"
 
 #include "StorageDBThread.h"
 #include "StorageCache.h"
+#include "StorageUtils.h"
 
 #include "mozilla/BasePrincipal.h"
 #include "nsIObserverService.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermission.h"
 #include "nsIIDNService.h"
@@ -24,30 +25,29 @@
 #include "nsNetCID.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+using namespace StorageUtils;
+
 static const char kStartupTopic[] = "sessionstore-windows-restored";
 static const uint32_t kStartupDelay = 0;
 
 const char kTestingPref[] = "dom.storage.testing";
 
 NS_IMPL_ISUPPORTS(StorageObserver,
                   nsIObserver,
                   nsISupportsWeakReference)
 
 StorageObserver* StorageObserver::sSelf = nullptr;
 
-extern nsresult
-CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
-
 // static
 nsresult
 StorageObserver::Init()
 {
   if (sSelf) {
     return NS_OK;
   }
 
--- a/dom/storage/StorageUtils.cpp
+++ b/dom/storage/StorageUtils.cpp
@@ -61,11 +61,57 @@ GenerateOriginKey(nsIPrincipal* aPrincip
   int32_t port = NS_GetRealPort(uri);
   if (port != -1) {
     aOriginKey.Append(nsPrintfCString(":%d", port));
   }
 
   return NS_OK;
 }
 
+bool
+PrincipalsEqual(nsIPrincipal* aObjectPrincipal,
+                nsIPrincipal* aSubjectPrincipal)
+{
+  if (!aSubjectPrincipal) {
+    return true;
+  }
+
+  if (!aObjectPrincipal) {
+    return false;
+  }
+
+  return aSubjectPrincipal->Equals(aObjectPrincipal);
+}
+
+void
+ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
+{
+  nsACString::const_iterator sourceBegin, sourceEnd;
+  aSource.BeginReading(sourceBegin);
+  aSource.EndReading(sourceEnd);
+
+  aResult.SetLength(aSource.Length());
+  nsACString::iterator destEnd;
+  aResult.EndWriting(destEnd);
+
+  while (sourceBegin != sourceEnd) {
+    *(--destEnd) = *sourceBegin;
+    ++sourceBegin;
+  }
+}
+
+nsresult
+CreateReversedDomain(const nsACString& aAsciiDomain,
+                     nsACString& aKey)
+{
+  if (aAsciiDomain.IsEmpty()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  ReverseString(aAsciiDomain, aKey);
+
+  aKey.Append('.');
+  return NS_OK;
+}
+
 } // StorageUtils namespace
 } // dom namespace
 } // mozilla namespace
--- a/dom/storage/StorageUtils.h
+++ b/dom/storage/StorageUtils.h
@@ -12,13 +12,24 @@ class nsIPrincipal;
 namespace mozilla {
 namespace dom {
 namespace StorageUtils {
 
 nsresult
 GenerateOriginKey(nsIPrincipal* aPrincipal, nsACString& aOriginAttrSuffix,
                   nsACString& aOriginKey);
 
+bool
+PrincipalsEqual(nsIPrincipal* aObjectPrincipal,
+                nsIPrincipal* aSubjectPrincipal);
+
+void
+ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
+
+nsresult
+CreateReversedDomain(const nsACString& aAsciiDomain,
+                     nsACString& aKey);
+
 } // StorageUtils namespace
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_StorageUtils_h
--- a/dom/storage/moz.build
+++ b/dom/storage/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 EXPORTS.mozilla.dom += [
+    'LocalStorage.h',
     'LocalStorageManager.h',
     'SessionStorageManager.h',
     'Storage.h',
     'StorageIPC.h',
 ]
 
 UNIFIED_SOURCES += [
     'LocalStorage.cpp',