Merge m-c to autoland
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 02 Feb 2017 22:11:54 -0800
changeset 332349 26268dace54f614d06554596c3c8967b957418db
parent 332348 460ffe0d627154d12deff6c96278b42cb5501101 (current diff)
parent 332312 2aede0a97bc685e163196cc451b947a04ae6a598 (diff)
child 332350 b3d2a4cdecd15085d59d2d5fc7661392fb771787
push id31303
push usercbook@mozilla.com
push dateFri, 03 Feb 2017 12:23:51 +0000
treeherdermozilla-central@28bb04d0338d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.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
Merge m-c to autoland
dom/base/nsGlobalWindow.cpp
dom/tests/browser/browser_localStorage_e10s.js
dom/tests/browser/page_localstorage_e10s.html
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3206,21 +3206,17 @@ nsGlobalWindow::PreloadLocalStorage()
     do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
   if (NS_FAILED(rv)) {
     return;
   }
 
   // private browsing windows do not persist local storage to disk so we should
   // only try to precache storage when we're not a private browsing window.
   if (principal->GetPrivateBrowsingId() == 0) {
-    nsCOMPtr<nsIDOMStorage> storage;
-    rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
-    if (NS_SUCCEEDED(rv)) {
-      mLocalStorage = static_cast<Storage*>(storage.get());
-    }
+    storageManager->PrecacheStorage(principal);
   }
 }
 
 void
 nsGlobalWindow::DispatchDOMWindowCreated()
 {
   MOZ_ASSERT(IsOuterWindow());
 
@@ -11839,53 +11835,58 @@ nsGlobalWindow::Observe(nsISupports* aSu
     } else if (AsInner()->IsCurrentInnerWindow()) {
       MOZ_ASSERT(IsInnerWindow());
       ScheduleActiveTimerCallback();
     }
     return NS_OK;
   }
 
   bool isPrivateBrowsing = IsPrivateBrowsing();
-  // In addition to determining if this is a storage event, the
-  // isPrivateBrowsing checks here enforce that the source storage area's
-  // private browsing state matches this window's state.  These flag checks
-  // and their maintenance independent from the principal's OriginAttributes
-  // matter because chrome docshells that are part of private browsing windows
-  // can be private browsing without having their OriginAttributes set (because
-  // they have the system principal).
   if ((!nsCRT::strcmp(aTopic, "dom-storage2-changed") && !isPrivateBrowsing) ||
       (!nsCRT::strcmp(aTopic, "dom-private-storage2-changed") && isPrivateBrowsing)) {
     if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow()) {
       return NS_OK;
     }
 
-    nsIPrincipal *principal = GetPrincipal();
-    if (!principal) {
-      return NS_OK;
-    }
+    nsIPrincipal *principal;
+    nsresult rv;
 
     RefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
     if (!event) {
       return NS_ERROR_FAILURE;
     }
 
+    RefPtr<Storage> changingStorage = event->GetStorageArea();
+    if (!changingStorage) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
+
     bool fireMozStorageChanged = false;
     nsAutoString eventType;
     eventType.AssignLiteral("storage");
-
-    if (!NS_strcmp(aData, u"sessionStorage")) {
-      nsCOMPtr<nsIDOMStorage> changingStorage = event->GetStorageArea();
-      MOZ_ASSERT(changingStorage);
-
+    principal = GetPrincipal();
+    if (!principal) {
+      return NS_OK;
+    }
+
+    if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
+      return NS_OK;
+    }
+
+    switch (changingStorage->GetType())
+    {
+    case Storage::SessionStorage:
+    {
       bool check = false;
 
       nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
       if (storageManager) {
-        nsresult rv = storageManager->CheckStorage(principal, changingStorage,
-                                                   &check);
+        rv = storageManager->CheckStorage(principal, istorage, &check);
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
 
       if (!check) {
         // This storage event is not coming from our storage or is coming
         // from a different docshell, i.e. it is a clone, ignore this event.
@@ -11896,67 +11897,68 @@ nsGlobalWindow::Observe(nsISupports* aSu
         PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p",
                     this, mSessionStorage.get(), changingStorage.get());
       }
 
       fireMozStorageChanged = mSessionStorage == changingStorage;
       if (fireMozStorageChanged) {
         eventType.AssignLiteral("MozSessionStorageChanged");
       }
-    }
-
-    else {
-      MOZ_ASSERT(!NS_strcmp(aData, u"localStorage"));
-      nsIPrincipal* storagePrincipal = event->GetPrincipal();
-      if (!storagePrincipal) {
-        return NS_OK;
-      }
+      break;
+    }
+
+    case Storage::LocalStorage:
+    {
+      // Allow event fire only for the same principal storages
+      // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
+      nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
 
       bool equals = false;
-      nsresult rv = storagePrincipal->Equals(principal, &equals);
+      rv = storagePrincipal->Equals(principal, &equals);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (!equals) {
+      if (!equals)
         return NS_OK;
-      }
-
-      fireMozStorageChanged = mLocalStorage == event->GetStorageArea();
-
+
+      fireMozStorageChanged = mLocalStorage == changingStorage;
       if (fireMozStorageChanged) {
         eventType.AssignLiteral("MozLocalStorageChanged");
       }
+      break;
+    }
+    default:
+      return NS_OK;
     }
 
     // Clone the storage event included in the observer notification. We want
     // to dispatch clones rather than the original event.
     ErrorResult error;
-    RefPtr<StorageEvent> clonedEvent =
-      CloneStorageEvent(eventType, event, error);
+    RefPtr<StorageEvent> newEvent = CloneStorageEvent(eventType, event, error);
     if (error.Failed()) {
       return error.StealNSResult();
     }
 
-    clonedEvent->SetTrusted(true);
+    newEvent->SetTrusted(true);
 
     if (fireMozStorageChanged) {
-      WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
+      WidgetEvent* internalEvent = newEvent->WidgetEventPtr();
       internalEvent->mFlags.mOnlyChromeDispatch = true;
     }
 
     if (IsFrozen()) {
       // This window is frozen, rather than firing the events here,
       // store the domain in which the change happened and fire the
       // events if we're ever thawed.
 
-      mPendingStorageEvents.AppendElement(clonedEvent);
+      mPendingStorageEvents.AppendElement(newEvent);
       return NS_OK;
     }
 
     bool defaultActionEnabled;
-    DispatchEvent(clonedEvent, &defaultActionEnabled);
+    DispatchEvent(newEvent, &defaultActionEnabled);
 
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
     if (mApplicationCache)
       return NS_OK;
 
@@ -12037,41 +12039,32 @@ nsGlobalWindow::CloneStorageEvent(const 
   dict.mBubbles = aEvent->Bubbles();
   dict.mCancelable = aEvent->Cancelable();
   aEvent->GetKey(dict.mKey);
   aEvent->GetOldValue(dict.mOldValue);
   aEvent->GetNewValue(dict.mNewValue);
   aEvent->GetUrl(dict.mUrl);
 
   RefPtr<Storage> storageArea = aEvent->GetStorageArea();
+  MOZ_ASSERT(storageArea);
 
   RefPtr<Storage> storage;
-
-  // If null, this is a localStorage event received by IPC.
-  if (!storageArea) {
-    storage = GetLocalStorage(aRv);
-    if (aRv.Failed() || !storage) {
-      return nullptr;
-    }
-
-    // We must apply the current change to the 'local' localStorage.
-    storage->ApplyEvent(aEvent);
-  } else if (storageArea->GetType() == Storage::LocalStorage) {
+  if (storageArea->GetType() == Storage::LocalStorage) {
     storage = GetLocalStorage(aRv);
   } else {
     MOZ_ASSERT(storageArea->GetType() == Storage::SessionStorage);
     storage = GetSessionStorage(aRv);
   }
 
   if (aRv.Failed() || !storage) {
     return nullptr;
   }
 
   MOZ_ASSERT(storage);
-  MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
+  MOZ_ASSERT(storage->IsForkOf(storageArea));
 
   dict.mStorageArea = storage;
 
   RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
   return event.forget();
 }
 
 void
@@ -13224,23 +13217,16 @@ nsGlobalWindow::EventListenerAdded(nsIAt
 {
   if (aType == nsGkAtoms::onvrdisplayactivate ||
       aType == nsGkAtoms::onvrdisplayconnect ||
       aType == nsGkAtoms::onvrdisplaydeactivate ||
       aType == nsGkAtoms::onvrdisplaydisconnect ||
       aType == nsGkAtoms::onvrdisplaypresentchange) {
     NotifyVREventListenerAdded();
   }
-
-  // We need to initialize localStorage in order to receive notifications.
-  if (aType == nsGkAtoms::onstorage) {
-    ErrorResult rv;
-    GetLocalStorage(rv);
-    rv.SuppressException();
-  }
 }
 
 void
 nsGlobalWindow::NotifyVREventListenerAdded()
 {
   MOZ_ASSERT(IsInnerWindow());
   mHasVREvents = true;
   EnableVRUpdates();
--- a/dom/events/StorageEvent.h
+++ b/dom/events/StorageEvent.h
@@ -8,18 +8,16 @@
 #define mozilla_dom_StorageEvent_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/StorageEventBinding.h"
 
-class nsIPrincipal;
-
 namespace mozilla {
 namespace dom {
 
 class Storage;
 
 class StorageEvent : public Event
 {
 public:
@@ -31,17 +29,16 @@ public:
 protected:
   virtual ~StorageEvent();
 
   nsString mKey;
   nsString mOldValue;
   nsString mNewValue;
   nsString mUrl;
   RefPtr<Storage> mStorageArea;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
 
 public:
   virtual StorageEvent* AsStorageEvent();
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<StorageEvent>
   Constructor(EventTarget* aOwner, const nsAString& aType,
@@ -77,26 +74,14 @@ public:
   {
     aRetVal = mUrl;
   }
 
   Storage* GetStorageArea() const
   {
     return mStorageArea;
   }
-
-  // Non WebIDL methods
-  void SetPrincipal(nsIPrincipal* aPrincipal)
-  {
-    MOZ_ASSERT(!mPrincipal);
-    mPrincipal = aPrincipal;
-  }
-
-  nsIPrincipal* GetPrincipal() const
-  {
-    return mPrincipal;
-  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_StorageEvent_h
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -15,25 +15,18 @@ interface mozIDOMWindow;
  * "@mozilla.org/dom/sessionStorage-manager;1" contract IDs.
  */
 [scriptable, uuid(a20c742e-3ed1-44fb-b897-4080a75b1662)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
    * This starts async preloading of a storage cache for scope
    * defined by the principal.
-   *
-   * Because of how multi-e10s support was implemented in bug 1285898, the
-   * StorageCache instance can no longer use a timer to keep itself alive.  So a
-   * Storage instance is returned if precaching believes the principal may have
-   * localStorage data.  (Previously the StorageCache would be brought into
-   * existence and kept alive by the timer so that it could be returned if a
-   * call to createStorage was made due to a request by the page.)
    */
-  nsIDOMStorage precacheStorage(in nsIPrincipal aPrincipal);
+  void precacheStorage(in nsIPrincipal aPrincipal);
 
   /**
    * Returns instance of DOM storage object for given principal.
    * A new object is always returned and it is ensured there is
    * a storage for the scope created.
    *
    * @param aWindow
    *    The parent window.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -30,17 +30,16 @@
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/PushNotifier.h"
-#include "mozilla/dom/Storage.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"
@@ -3036,30 +3035,16 @@ ContentChild::RecvBlobURLRegistration(co
 
 mozilla::ipc::IPCResult
 ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
 {
   nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
   return IPC_OK();
 }
 
-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);
-  return IPC_OK();
-}
-
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 bool
 ContentChild::SendGetA11yContentId()
 {
   return PContentChild::SendGetA11yContentId(&mMsaaID);
 }
 #endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -389,24 +389,16 @@ public:
   const nsAString& GetRemoteType() const;
 
   virtual mozilla::ipc::IPCResult
   RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override;
 
   virtual mozilla::ipc::IPCResult
   RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
 
-  virtual mozilla::ipc::IPCResult
-  RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
-                                 const nsString& aKey,
-                                 const nsString& aOldValue,
-                                 const nsString& aNewValue,
-                                 const IPC::Principal& aPrincipal,
-                                 const bool& aIsPrivate) override;
-
   virtual mozilla::ipc::IPCResult RecvLastPrivateDocShellDestroyed() override;
 
   virtual mozilla::ipc::IPCResult RecvFilePathUpdate(const nsString& aStorageType,
                                                      const nsString& aStorageName,
                                                      const nsString& aPath,
                                                      const nsCString& aReason) override;
 
   virtual mozilla::ipc::IPCResult
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -46,17 +46,16 @@
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
-#include "mozilla/dom/Storage.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/PresentationParent.h"
 #include "mozilla/dom/PPresentationParent.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
@@ -4749,35 +4748,16 @@ ContentParent::RecvUnstoreAndBroadcastBl
                                                false /* Don't broadcast */);
   BroadcastBlobURLUnregistration(aURI, this);
   mBlobURLs.RemoveElement(aURI);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
-                                               const nsString& aKey,
-                                               const nsString& aOldValue,
-                                               const nsString& aNewValue,
-                                               const Principal& aPrincipal,
-                                               const bool& aIsPrivate)
-{
-  for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
-    if (cp != this) {
-      Unused << cp->SendDispatchLocalStorageChange(
-        nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
-        nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
-    }
-  }
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
 {
 #if defined(XP_WIN32) && defined(ACCESSIBILITY)
   *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID());
   MOZ_ASSERT(*aContentId);
   return IPC_OK();
 #else
   return IPC_FAIL_NO_REASON(this);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -574,24 +574,16 @@ public:
   RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
                                            PBlobParent* aBlobParent,
                                            const Principal& aPrincipal) override;
 
   virtual mozilla::ipc::IPCResult
   RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
 
   virtual mozilla::ipc::IPCResult
-  RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
-                                  const nsString& aKey,
-                                  const nsString& aOldValue,
-                                  const nsString& aNewValue,
-                                  const IPC::Principal& aPrincipal,
-                                  const bool& aIsPrivate) override;
-
-  virtual mozilla::ipc::IPCResult
   RecvGetA11yContentId(uint32_t* aContentId) override;
 
   virtual int32_t Pid() const override;
 
   virtual PURLClassifierParent*
   AllocPURLClassifierParent(const Principal& aPrincipal,
                             const bool& aUseTrackingProtection,
                             bool* aSuccess) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -639,22 +639,16 @@ child:
 
     async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
 
     async BlobURLRegistration(nsCString aURI, PBlob aBlob,
                               Principal aPrincipal);
 
     async BlobURLUnregistration(nsCString aURI);
 
-    async DispatchLocalStorageChange(nsString documentURI,
-                                     nsString key,
-                                     nsString oldValue,
-                                     nsString newValue,
-                                     Principal principal,
-                                     bool isPrivate);
 
     async GMPsChanged(GMPCapabilityData[] capabilities);
 
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     /**
      * Tell the content process some attributes of itself.  This is
@@ -1154,23 +1148,16 @@ parent:
      async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
      async DeleteGetFilesRequest(nsID aID);
 
      async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob,
                                                 Principal principal);
 
      async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
 
-     async BroadcastLocalStorageChange(nsString documentURI,
-                                       nsString key,
-                                       nsString oldValue,
-                                       nsString newValue,
-                                       Principal principal,
-                                       bool isPrivate);
-
     /**
      * Messages for communicating child Telemetry to the parent process
      */
     async AccumulateChildHistogram(Accumulation[] accumulations);
     async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
     async UpdateChildScalars(ScalarAction[] updates);
     async UpdateChildKeyedScalars(KeyedScalarAction[] updates);
 
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -9,33 +9,27 @@
 #include "StorageManager.h"
 
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsICookiePermission.h"
 
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/StorageBinding.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/EnumSet.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
-
-using namespace ipc;
-
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mManager, mPrincipal, mWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Storage)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Storage)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Storage)
@@ -59,16 +53,17 @@ Storage::Storage(nsPIDOMWindowInner* aWi
   , mIsPrivate(aIsPrivate)
   , mIsSessionOnly(false)
 {
   mCache->Preload();
 }
 
 Storage::~Storage()
 {
+  mCache->KeepAlive();
 }
 
 /* virtual */ JSObject*
 Storage::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return StorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -213,107 +208,37 @@ StorageNotifierRunnable::Run()
 
 } // namespace
 
 void
 Storage::BroadcastChangeNotification(const nsSubstring& aKey,
                                      const nsSubstring& aOldValue,
                                      const nsSubstring& aNewValue)
 {
-  if (!XRE_IsParentProcess() && GetType() == LocalStorage && mPrincipal) {
-    // 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.
-    dom::ContentChild* cc = dom::ContentChild::GetSingleton();
-    Unused << NS_WARN_IF(!cc->SendBroadcastLocalStorageChange(
-      mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
-      IPC::Principal(mPrincipal), mIsPrivate));
-  }
-
-  DispatchStorageEvent(GetType(), mDocumentURI, aKey, aOldValue, aNewValue,
-                       mPrincipal, mIsPrivate, this, false);
-}
-
-/* static */ void
-Storage::DispatchStorageEvent(StorageType aStorageType,
-                              const nsAString& aDocumentURI,
-                              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;
+  dict.mStorageArea = this;
+  dict.mUrl = mDocumentURI;
 
   // 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 == LocalStorage
+                                GetType() == LocalStorage
                                   ? u"localStorage"
                                   : u"sessionStorage",
-                                aIsPrivate);
-
-  if (aImmediateDispatch) {
-    Unused << r->Run();
-  } else {
-    NS_DispatchToMainThread(r);
-  }
-
-  // If we are in the parent process and we have the principal, we want to
-  // broadcast this event to every other process.
-  if (aStorageType == LocalStorage && 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);
-    }
-  }
-}
-
-void
-Storage::ApplyEvent(StorageEvent* aStorageEvent)
-{
-  MOZ_ASSERT(aStorageEvent);
-
-  nsAutoString key;
-  nsAutoString old;
-  nsAutoString value;
-
-  aStorageEvent->GetKey(key);
-  aStorageEvent->GetNewValue(value);
-
-  // No key means clearing the full storage.
-  if (key.IsVoid()) {
-    MOZ_ASSERT(value.IsVoid());
-    mCache->Clear(this, StorageCache::E10sPropagated);
-    return;
-  }
-
-  // No new value means removing the key.
-  if (value.IsVoid()) {
-    mCache->RemoveItem(this, key, old, StorageCache::E10sPropagated);
-    return;
-  }
-
-  // Otherwise, we set the new value.
-  mCache->SetItem(this, key, value, old, StorageCache::E10sPropagated);
+                                IsPrivate());
+  NS_DispatchToMainThread(r);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 bool
 Storage::CanUseStorage(nsIPrincipal& aSubjectPrincipal)
 {
--- a/dom/storage/Storage.h
+++ b/dom/storage/Storage.h
@@ -19,17 +19,16 @@
 class nsIPrincipal;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class StorageManagerBase;
 class StorageCache;
-class StorageEvent;
 
 class Storage final
   : public nsIDOMStorage
   , public nsSupportsWeakReference
   , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -125,38 +124,16 @@ public:
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
   bool IsForkOf(const Storage* aOther) const
   {
     MOZ_ASSERT(aOther);
     return mCache == aOther->mCache;
   }
 
-  // aStorage can be null if this method is called by ContentChild.
-  //
-  // aImmediateDispatch is for use by (main-thread) IPC code so that PContent
-  // ordering can be maintained.  Without this, the event would be enqueued and
-  // run in a future turn of the event loop, potentially allowing other PContent
-  // Recv* methods to trigger script that wants to assume our localstorage
-  // changes have already been applied.  This is the case for message manager
-  // messages which are used by ContentTask testing logic and webextensions.
-  static void
-  DispatchStorageEvent(StorageType aStorageType,
-                       const nsAString& aDocumentURI,
-                       const nsAString& aKey,
-                       const nsAString& aOldValue,
-                       const nsAString& aNewValue,
-                       nsIPrincipal* aPrincipal,
-                       bool aIsPrivate,
-                       Storage* aStorage,
-                       bool aImmediateDispatch);
-
-  void
-  ApplyEvent(StorageEvent* aStorageEvent);
-
 protected:
   // The method checks whether the caller can use a storage.
   // CanUseStorage is called before any DOM initiated operation
   // on a storage is about to happen and ensures that the storage's
   // session-only flag is properly set according the current settings.
   // It is an optimization since the privileges check and session only
   // state determination are complex and share the code (comes hand in
   // hand together).
--- a/dom/storage/StorageCache.cpp
+++ b/dom/storage/StorageCache.cpp
@@ -54,17 +54,17 @@ GetDataSetIndex(const Storage* aStorage)
 }
 
 } // namespace
 
 // StorageCacheBridge
 
 NS_IMPL_ADDREF(StorageCacheBridge)
 
-// Since there is no consumer of return value of Release, we can turn this
+// Since there is no consumer of return value of Release, we can turn this 
 // method to void to make implementation of asynchronous StorageCache::Release
 // much simpler.
 NS_IMETHODIMP_(void) StorageCacheBridge::Release(void)
 {
   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
   nsrefcnt count = --mRefCnt;
   NS_LOG_RELEASE(this, count, "StorageCacheBridge");
   if (0 == count) {
@@ -144,17 +144,17 @@ StorageCache::Init(StorageManagerBase* a
   if (mPersistent) {
     mManager = aManager;
     Preload();
   }
 
   // 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,
+  MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope, 
                                                          NS_LITERAL_CSTRING("^")));
 
   mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
 }
 
 inline bool
 StorageCache::Persist(const Storage* aStorage) const
 {
@@ -193,43 +193,38 @@ StorageCache::DataSet(const Storage* aSt
     // for all session only data
     ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
   }
 
   return mData[index];
 }
 
 bool
-StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta,
-                                const MutationSource aSource)
+StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta)
 {
-  return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource);
+  return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
 }
 
 bool
-StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
-                                const MutationSource aSource)
+StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
 {
   // Check if we are in a low disk space situation
-  if (aSource == ContentMutation &&
-      aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
+  if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
     return false;
   }
 
   // Check limit per this origin
   Data& data = mData[aGetDataSetIndex];
   uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
-  if (aSource == ContentMutation &&
-      aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
+  if (aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
     return false;
   }
 
   // Now check eTLD+1 limit
-  if (mUsage &&
-      !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta, aSource)) {
+  if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
     return false;
   }
 
   // Update size in our data set
   data.mOriginQuotaUsage = newOriginUsage;
   return true;
 }
 
@@ -246,16 +241,70 @@ StorageCache::Preload()
     return;
   }
 
   sDatabase->AsyncPreload(this);
 }
 
 namespace {
 
+// This class is passed to timer as a tick observer.  It refers the cache
+// and keeps it alive for a time.
+class StorageCacheHolder : public nsITimerCallback
+{
+  virtual ~StorageCacheHolder() {}
+
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD
+  Notify(nsITimer* aTimer) override
+  {
+    mCache = nullptr;
+    return NS_OK;
+  }
+
+  RefPtr<StorageCache> mCache;
+
+public:
+  explicit StorageCacheHolder(StorageCache* aCache) : mCache(aCache) {}
+};
+
+NS_IMPL_ISUPPORTS(StorageCacheHolder, nsITimerCallback)
+
+} // namespace
+
+void
+StorageCache::KeepAlive()
+{
+  // Missing reference back to the manager means the cache is not responsible
+  // for its lifetime.  Used for keeping sessionStorage live forever.
+  if (!mManager) {
+    return;
+  }
+
+  if (!NS_IsMainThread()) {
+    // Timer and the holder must be initialized on the main thread.
+    NS_DispatchToMainThread(NewRunnableMethod(this, &StorageCache::KeepAlive));
+    return;
+  }
+
+  nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+  if (!timer) {
+    return;
+  }
+
+  RefPtr<StorageCacheHolder> holder = new StorageCacheHolder(this);
+  timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
+                          nsITimer::TYPE_ONE_SHOT);
+
+  mKeepAliveTimer.swap(timer);
+}
+
+namespace {
+
 // The AutoTimer provided by telemetry headers is only using static,
 // i.e. compile time known ID, but here we know the ID only at run time.
 // Hence a new class.
 class TelemetryAutoTimer
 {
 public:
   explicit TelemetryAutoTimer(Telemetry::ID aId)
     : id(aId), start(TimeStamp::Now())
@@ -385,18 +434,17 @@ StorageCache::GetItem(const Storage* aSt
 
   aRetval = value;
 
   return NS_OK;
 }
 
 nsresult
 StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
-                      const nsString& aValue, nsString& aOld,
-                      const MutationSource aSource)
+                      const nsString& aValue, nsString& aOld)
 {
   // Size of the cache that will change after this action.
   int64_t delta = 0;
 
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
     if (NS_FAILED(mLoadResult)) {
       return mLoadResult;
@@ -409,27 +457,27 @@ StorageCache::SetItem(const Storage* aSt
 
     // We only consider key size if the key doesn't exist before.
     delta += static_cast<int64_t>(aKey.Length());
   }
 
   delta += static_cast<int64_t>(aValue.Length()) -
            static_cast<int64_t>(aOld.Length());
 
-  if (!ProcessUsageDelta(aStorage, delta, aSource)) {
+  if (!ProcessUsageDelta(aStorage, delta)) {
     return NS_ERROR_DOM_QUOTA_REACHED;
   }
 
   if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
   data.mKeys.Put(aKey, aValue);
 
-  if (aSource == ContentMutation && Persist(aStorage)) {
+  if (Persist(aStorage)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     if (DOMStringIsNull(aOld)) {
       return sDatabase->AsyncAddItem(this, aKey, aValue);
@@ -438,17 +486,17 @@ StorageCache::SetItem(const Storage* aSt
     return sDatabase->AsyncUpdateItem(this, aKey, aValue);
   }
 
   return NS_OK;
 }
 
 nsresult
 StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
-                         nsString& aOld, const MutationSource aSource)
+                         nsString& aOld)
 {
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
     if (NS_FAILED(mLoadResult)) {
       return mLoadResult;
     }
   }
 
@@ -456,34 +504,34 @@ StorageCache::RemoveItem(const Storage* 
   if (!data.mKeys.Get(aKey, &aOld)) {
     SetDOMStringToNull(aOld);
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
   // Recalculate the cached data size
   const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
                           static_cast<int64_t>(aKey.Length()));
-  Unused << ProcessUsageDelta(aStorage, delta, aSource);
+  Unused << ProcessUsageDelta(aStorage, delta);
   data.mKeys.Remove(aKey);
 
-  if (aSource == ContentMutation && Persist(aStorage)) {
+  if (Persist(aStorage)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     return sDatabase->AsyncRemoveItem(this, aKey);
   }
 
   return NS_OK;
 }
 
 nsresult
-StorageCache::Clear(const Storage* aStorage, const MutationSource aSource)
+StorageCache::Clear(const Storage* aStorage)
 {
   bool refresh = false;
   if (Persist(aStorage)) {
     // We need to preload all data (know the size) before we can proceeed
     // to correctly decrease cached usage number.
     // XXX as in case of unload, this is not technically needed now, but
     // after super-scope quota introduction we have to do this.  Get telemetry
     // right now.
@@ -495,21 +543,21 @@ StorageCache::Clear(const Storage* aStor
       mLoadResult = NS_OK;
     }
   }
 
   Data& data = DataSet(aStorage);
   bool hadData = !!data.mKeys.Count();
 
   if (hadData) {
-    Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource);
+    Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
     data.mKeys.Clear();
   }
 
-  if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) {
+  if (Persist(aStorage) && (refresh || hadData)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     return sDatabase->AsyncClear(this);
   }
@@ -614,16 +662,19 @@ StorageCache::LoadItem(const nsAString& 
   data.mKeys.Put(aKey, aValue);
   data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
   return true;
 }
 
 void
 StorageCache::LoadDone(nsresult aRv)
 {
+  // Keep the preloaded cache alive for a time
+  KeepAlive();
+
   MonitorAutoLock monitor(mMonitor);
   mLoadResult = aRv;
   mLoaded = true;
   monitor.Notify();
 }
 
 void
 StorageCache::LoadWait()
@@ -674,23 +725,22 @@ StorageUsage::LoadUsage(const int64_t aU
   } else {
     // On a child process we get this on the main thread already
     mUsage[kDefaultSet] += aUsage;
   }
 }
 
 bool
 StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
-  const int64_t aDelta, const StorageCache::MutationSource aSource)
+                                         const int64_t aDelta)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
-  if (aSource == StorageCache::ContentMutation &&
-      aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
+  if (aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
     return false;
   }
 
   mUsage[aDataSetIndex] = newUsage;
   return true;
 }
 
 
--- a/dom/storage/StorageCache.h
+++ b/dom/storage/StorageCache.h
@@ -73,34 +73,18 @@ 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 StorageCache : public StorageCacheBridge
 {
 public:
   NS_IMETHOD_(void) Release(void);
 
-  enum MutationSource {
-    // The mutation is a result of an explicit JS mutation in this process.
-    // The mutation should be sent to the sDatabase. Quota will be checked and
-    // QuotaExceededError may be returned without the mutation being applied.
-    ContentMutation,
-    // The mutation initially was triggered in a different process and is being
-    // propagated to this cache via Storage::ApplyEvent.  The mutation should
-    // not be sent to the sDatabase because the originating process is already
-    // doing that.  (In addition to the redundant writes being wasteful, there
-    // is the potential for other processes to see inconsistent state from the
-    // database while preloading.)  Quota will be updated but not checked
-    // because it's assumed it was checked in another process and data-coherency
-    // is more important than slightly exceeding quota.
-    E10sPropagated
-  };
-
-  // Note: We pass aOriginNoSuffix through the ctor here, because
-  // StorageCacheHashKey's ctor is creating this class and
+  // Note: We pass aOriginNoSuffix through the ctor here, because 
+  // StorageCacheHashKey's ctor is creating this class and 
   // accepts reversed-origin-no-suffix as an argument - the hashing key.
   explicit StorageCache(const nsACString* aOriginNoSuffix);
 
 protected:
   virtual ~StorageCache();
 
 public:
   void Init(StorageManagerBase* aManager, bool aPersistent,
@@ -120,23 +104,20 @@ public:
   // We are passing the Storage object just to let the cache
   // read properties like mPrivate, mPrincipal and mSessionOnly.
   // Get* methods return error when load from the database has failed.
   nsresult GetLength(const Storage* aStorage, uint32_t* aRetval);
   nsresult GetKey(const Storage* aStorage, uint32_t index, nsAString& aRetval);
   nsresult GetItem(const Storage* aStorage, const nsAString& aKey,
                    nsAString& aRetval);
   nsresult SetItem(const Storage* aStorage, const nsAString& aKey,
-                   const nsString& aValue, nsString& aOld,
-                   const MutationSource aSource=ContentMutation);
+                   const nsString& aValue, nsString& aOld);
   nsresult RemoveItem(const Storage* aStorage, const nsAString& aKey,
-                      nsString& aOld,
-                      const MutationSource aSource=ContentMutation);
-  nsresult Clear(const Storage* aStorage,
-                 const MutationSource aSource=ContentMutation);
+                      nsString& aOld);
+  nsresult Clear(const Storage* aStorage);
 
   void GetKeys(const Storage* aStorage, nsTArray<nsString>& aKeys);
 
   // Whether the principal equals principal the cache was created for
   bool CheckPrincipal(nsIPrincipal* aPrincipal) const;
   nsIPrincipal* Principal() const { return mPrincipal; }
 
   // Starts the database engine thread or the IPC bridge
@@ -196,53 +177,46 @@ private:
   // Helper to get one of the 3 data sets (regular, private, session)
   Data& DataSet(const Storage* aStorage);
 
   // Whether the storage change is about to persist
   bool Persist(const Storage* aStorage) const;
 
   // Changes the quota usage on the given data set if it fits the quota.
   // If not, then false is returned and no change to the set must be done.
-  // A special case is if aSource==E10sPropagated, then we will return true even
-  // if the change would put us over quota.  This is done to ensure coherency of
-  // caches between processes in the face of races.  It does allow an attacker
-  // to potentially use N multiples of the quota storage limit if they can
-  // arrange for their origin to execute code in N processes.  However, this is
-  // not considered a particularly concerning threat model because it's already
-  // very possible for a rogue page to attempt to intentionally fill up the
-  // user's storage through the use of multiple domains.
-  bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
-                         const MutationSource aSource=ContentMutation);
-  bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta,
-                         const MutationSource aSource=ContentMutation);
+  bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
+  bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta);
 
 private:
   // When a cache is reponsible for its life time (in case of localStorage data
   // cache) we need to refer our manager since removal of the cache from the
   // hash table is handled in the destructor by call to the manager.  Cache
   // could potentially overlive the manager, hence the hard ref.
   RefPtr<StorageManagerBase> mManager;
 
   // Reference to the usage counter object we check on for eTLD+1 quota limit.
   // Obtained from the manager during initialization (Init method).
   RefPtr<StorageUsage> mUsage;
 
+  // Timer that holds this cache alive for a while after it has been preloaded.
+  nsCOMPtr<nsITimer> mKeepAliveTimer;
+
   // Principal the cache has been initially created for, this is used only for
   // sessionStorage access checks since sessionStorage objects are strictly
   // scoped by a principal. localStorage objects on the other hand are scoped
   // by origin only.
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   // The origin this cache belongs to in the "DB format", i.e. reversed
   nsCString mOriginNoSuffix;
 
   // The origin attributes suffix
   nsCString mOriginSuffix;
 
-  // The eTLD+1 scope used to count quota usage.  It is in the reversed format
+  // 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;
@@ -298,18 +272,17 @@ protected:
   virtual ~StorageUsageBridge() {}
 };
 
 class StorageUsage : public StorageUsageBridge
 {
 public:
   explicit StorageUsage(const nsACString& aOriginScope);
 
-  bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta,
-                                  const StorageCache::MutationSource aSource);
+  bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
 
 private:
   virtual const nsCString& OriginScope() { return mOriginScope; }
   virtual void LoadUsage(const int64_t aUsage);
 
   nsCString mOriginScope;
   int64_t mUsage[StorageCache::kDataSetCount];
 };
--- a/dom/storage/StorageIPC.cpp
+++ b/dom/storage/StorageIPC.cpp
@@ -219,22 +219,16 @@ StorageDBChild::RecvObserve(const nsCStr
   StorageObserver::Self()->Notify(
     aTopic.get(), aOriginAttributesPattern, aOriginScope);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 StorageDBChild::RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins)
 {
-  // Force population of mOriginsHavingData even if there are no origins so that
-  // ShouldPreloadOrigin does not generate false positives for all origins.
-  if (!aOrigins.Length()) {
-    Unused << OriginsHavingData();
-  }
-
   for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
     OriginsHavingData().PutEntry(aOrigins[i]);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/dom/storage/StorageManager.cpp
+++ b/dom/storage/StorageManager.cpp
@@ -304,17 +304,17 @@ StorageManagerBase::DropCache(StorageCac
     NS_WARNING("StorageManager::DropCache called on a non-main thread, shutting down?");
   }
 
   CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
   table->RemoveEntry(aCache->OriginNoSuffix());
 }
 
 nsresult
-StorageManagerBase::GetStorageInternal(CreateMode aCreateMode,
+StorageManagerBase::GetStorageInternal(bool aCreate,
                                        mozIDOMWindow* aWindow,
                                        nsIPrincipal* aPrincipal,
                                        const nsAString& aDocumentURI,
                                        bool aPrivate,
                                        nsIDOMStorage** aRetval)
 {
   nsresult rv;
 
@@ -326,22 +326,22 @@ StorageManagerBase::GetStorageInternal(C
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<StorageCache> cache = GetCache(originAttrSuffix, originKey);
 
   // Get or create a cache for the given scope
   if (!cache) {
-    if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
+    if (!aCreate) {
       *aRetval = nullptr;
       return NS_OK;
     }
 
-    if (aCreateMode == CreateMode::CreateIfShouldPreload) {
+    if (!aRetval) {
       // This is a demand to just preload the cache, if the scope has
       // no data stored, bypass creation and preload of the cache.
       StorageDBBridge* db = StorageCache::GetDatabase();
       if (db) {
         if (!db->ShouldPreloadOrigin(StorageManagerBase::CreateOrigin(originAttrSuffix, originKey))) {
           return NS_OK;
         }
       } else {
@@ -367,42 +367,41 @@ StorageManagerBase::GetStorageInternal(C
       inner, this, cache, aDocumentURI, aPrincipal, aPrivate);
     storage.forget(aRetval);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal,
-                                    nsIDOMStorage** aRetval)
+StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal)
 {
-  return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
-                            aPrincipal, EmptyString(), false, aRetval);
+  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
+                            nullptr);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::CreateStorage(mozIDOMWindow* aWindow,
                                   nsIPrincipal* aPrincipal,
                                   const nsAString& aDocumentURI,
                                   bool aPrivate,
                                   nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
-                            aDocumentURI, aPrivate, aRetval);
+  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
+                            aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::GetStorage(mozIDOMWindow* aWindow,
                                nsIPrincipal* aPrincipal,
                                bool aPrivate,
                                nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
-                            aPrincipal, EmptyString(), aPrivate, aRetval);
+  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
+                            aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::CloneStorage(nsIDOMStorage* aStorage)
 {
   if (mType != SessionStorage) {
     // Cloning is supported only for sessionStorage
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/storage/StorageManager.h
+++ b/dom/storage/StorageManager.h
@@ -85,27 +85,18 @@ private:
   };
 
   // Ensures cache for a scope, when it doesn't exist it is created and
   // initalized, this also starts preload of persistent data.
   already_AddRefed<StorageCache> PutCache(const nsACString& aOriginSuffix,
                                           const nsACString& aOriginNoSuffix,
                                           nsIPrincipal* aPrincipal);
 
-  enum class CreateMode {
-    // GetStorage: do not create if it's not already in memory.
-    UseIfExistsNeverCreate,
-    // CreateStorage: Create it if it's not already in memory.
-    CreateAlways,
-    // PrecacheStorage: Create only if the database says we ShouldPreloadOrigin.
-    CreateIfShouldPreload
-  };
-
   // Helper for creation of DOM storage objects
-  nsresult GetStorageInternal(CreateMode aCreate,
+  nsresult GetStorageInternal(bool aCreate,
                               mozIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
                               const nsAString& aDocumentURI,
                               bool aPrivate,
                               nsIDOMStorage** aRetval);
 
   // Suffix->origin->cache map
   typedef nsTHashtable<StorageCacheHashKey> CacheOriginHashtable;
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -1,13 +1,12 @@
 [DEFAULT]
 support-files =
   browser_frame_elements.html
   page_privatestorageevent.html
-  page_localstorage_e10s.html
   position.html
   test-console-api.html
   test_bug1004814.html
   worker_bug1004814.js
   geo_leak_test.html
   dummy.html
   test_largeAllocation.html
   test_largeAllocation.html^headers^
@@ -36,18 +35,16 @@ skip-if = e10s
 [browser_ConsoleStoragePBTest_perwindowpb.js]
 [browser_focus_steal_from_chrome.js]
 [browser_focus_steal_from_chrome_during_mousedown.js]
 [browser_frame_elements.js]
 [browser_largeAllocation_win32.js]
 skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
 [browser_largeAllocation_non_win32.js]
 skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requires e10s
-[browser_localStorage_e10s.js]
-skip-if = !e10s # This is a test of e10s functionality.
 [browser_localStorage_privatestorageevent.js]
 [browser_test__content.js]
 [browser_test_new_window_from_content.js]
 tags = openwindow
 skip-if = toolkit == 'android'  || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
 support-files =
   test_new_window_from_content_child.html
 [browser_test_toolbars_visibility.js]
deleted file mode 100644
--- a/dom/tests/browser/browser_localStorage_e10s.js
+++ /dev/null
@@ -1,330 +0,0 @@
-const HELPER_PAGE_URL =
-  "http://example.com/browser/dom/tests/browser/page_localstorage_e10s.html";
-const HELPER_PAGE_ORIGIN = "http://example.com/";
-
-// Simple tab wrapper abstracting our messaging mechanism;
-class KnownTab {
-  constructor(name, tab) {
-    this.name = name;
-    this.tab = tab;
-  }
-
-  cleanup() {
-    this.tab = null;
-  }
-}
-
-// Simple data structure class to help us track opened tabs and their pids.
-class KnownTabs {
-  constructor() {
-    this.byPid = new Map();
-    this.byName = new Map();
-  }
-
-  cleanup() {
-    this.byPid = null;
-    this.byName = null;
-  }
-}
-
-/**
- * Open our helper page in a tab in its own content process, asserting that it
- * really is in its own process.
- */
-function* openTestTabInOwnProcess(name, knownTabs) {
-  let url = HELPER_PAGE_URL + '?' + encodeURIComponent(name);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
-  let pid = tab.linkedBrowser.frameLoader.tabParent.osPid;
-  ok(!knownTabs.byName.has(name), "tab needs its own name: " + name);
-  ok(!knownTabs.byPid.has(pid), "tab needs to be in its own process: " + pid);
-
-  let knownTab = new KnownTab(name, tab);
-  knownTabs.byPid.set(pid, knownTab);
-  knownTabs.byName.set(name, knownTab);
-  return knownTab;
-}
-
-/**
- * Close all the tabs we opened.
- */
-function* cleanupTabs(knownTabs) {
-  for (let knownTab of knownTabs.byName.values()) {
-    yield BrowserTestUtils.removeTab(knownTab.tab);
-    knownTab.cleanup();
-  }
-  knownTabs.cleanup();
-}
-
-/**
- * Clear the origin's storage so that "OriginsHavingData" will return false for
- * our origin.  Note that this is only the case for AsyncClear() which is
- * explicitly issued against a cache, or AsyncClearAll() which we can trigger
- * by wiping all storage.  However, the more targeted domain clearings that
- * we can trigger via observer, AsyncClearMatchingOrigin and
- * AsyncClearMatchingOriginAttributes will not clear the hashtable entry for
- * the origin.
- *
- * So we explicitly access the cache here in the parent for the origin and issue
- * an explicit clear.  Clearing all storage might be a little easier but seems
- * like asking for intermittent failures.
- */
-function clearOriginStorageEnsuringNoPreload() {
-  let principal =
-    Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
-      HELPER_PAGE_ORIGIN);
-  // We want to use createStorage to force the cache to be created so we can
-  // issue the clear.  It's possible for getStorage to return false but for the
-  // origin preload hash to still have our origin in it.
-  let storage = Services.domStorageManager.createStorage(null, principal, "");
-  storage.clear();
-  // We don't need to wait for anything.  The clear call will have queued the
-  // clear operation on the database thread, and the child process requests
-  // for origins will likewise be answered via the database thread.
-}
-
-function* verifyTabPreload(knownTab, expectStorageExists) {
-  let storageExists = yield ContentTask.spawn(
-    knownTab.tab.linkedBrowser,
-    HELPER_PAGE_ORIGIN,
-    function(origin) {
-      let principal =
-        Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
-          origin);
-      return !!Services.domStorageManager.getStorage(null, principal);
-    });
-  is(storageExists, expectStorageExists, "Storage existence === preload");
-}
-
-/**
- * Instruct the given tab to execute the given series of mutations.  For
- * simplicity, the mutations representation matches the expected events rep.
- */
-function* mutateTabStorage(knownTab, mutations) {
-  yield ContentTask.spawn(
-    knownTab.tab.linkedBrowser,
-    { mutations },
-    function(args) {
-      return content.wrappedJSObject.mutateStorage(args.mutations);
-    });
-}
-
-/**
- * Instruct the given tab to add a "storage" event listener and record all
- * received events.  verifyTabStorageEvents is the corresponding method to
- * check and assert the recorded events.
- */
-function* recordTabStorageEvents(knownTab) {
-  yield ContentTask.spawn(
-    knownTab.tab.linkedBrowser,
-    {},
-    function() {
-      return content.wrappedJSObject.listenForStorageEvents();
-    });
-}
-
-/**
- * Retrieve the current localStorage contents perceived by the tab and assert
- * that they match the provided expected state.
- */
-function* verifyTabStorageState(knownTab, expectedState) {
-  let actualState = yield ContentTask.spawn(
-    knownTab.tab.linkedBrowser,
-    {},
-    function() {
-      return content.wrappedJSObject.getStorageState();
-    });
-
-  for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
-    ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
-    is(actualState[expectedKey], expectedValue, "value correct");
-  }
-  for (let actualKey of Object.keys(actualState)) {
-    if (!expectedState.hasOwnProperty(actualKey)) {
-      ok(false, "actual state has key it shouldn't have: " + actualKey);
-    }
-  }
-}
-
-/**
- * Retrieve and clear the storage events recorded by the tab and assert that
- * they match the provided expected events.  For simplicity, the expected events
- * representation is the same as that used by mutateTabStorage.
- */
-function* verifyTabStorageEvents(knownTab, expectedEvents) {
-  let actualEvents = yield ContentTask.spawn(
-    knownTab.tab.linkedBrowser,
-    {},
-    function() {
-      return content.wrappedJSObject.returnAndClearStorageEvents();
-    });
-
-  is(actualEvents.length, expectedEvents.length, "right number of events");
-  for (let i = 0; i < actualEvents.length; i++) {
-    let [actualKey, actualNewValue, actualOldValue] = actualEvents[i];
-    let [expectedKey, expectedNewValue, expectedOldValue] = expectedEvents[i];
-    is(actualKey, expectedKey, "keys match");
-    is(actualNewValue, expectedNewValue, "new values match");
-    is(actualOldValue, expectedOldValue, "old values match");
-  }
-}
-
-// We spin up a ton of child processes.
-requestLongerTimeout(4);
-
-/**
- * Verify the basics of our multi-e10s localStorage support.  We are focused on
- * whitebox testing two things.  When this is being written, broadcast filtering
- * is not in place, but the test is intended to attempt to verify that its
- * implementation does not break things.
- *
- * 1) That pages see the same localStorage state in a timely fashion when
- *    engaging in non-conflicting operations.  We are not testing races or
- *    conflict resolution; the spec does not cover that.
- *
- * 2) That there are no edge-cases related to when the Storage instance is
- *    created for the page or the StorageCache for the origin.  (StorageCache is
- *    what actually backs the Storage binding exposed to the page.)  This
- *    matters because the following reasons can exist for them to be created:
- *    - Preload, on the basis of knowing the origin uses localStorage.  The
- *      interesting edge case is when we have the same origin open in different
- *      processes and the origin starts using localStorage when it did not
- *      before.  Preload will not have instantiated bindings, which could impact
- *      correctness.
- *    - The page accessing localStorage for read or write purposes.  This is the
- *      obvious, boring one.
- *    - The page adding a "storage" listener.  This is less obvious and
- *      interacts with the preload edge-case mentioned above.  The page needs to
- *      hear "storage" events even if the page has not touched localStorage
- *      itself and its origin had nothing stored in localStorage when the page
- *      was created.
- *
- * We use the same simple child page in all tabs that:
- * - can be instructed to listen for and record "storage" events
- * - can be instructed to issue a series of localStorage writes
- * - can be instructed to return the current entire localStorage contents
- *
- * We open the 5 following tabs:
- * - Open a "writer" tab that does not listen for "storage" events and will
- *   issue only writes.
- * - Open a "listener" tab instructed to listen for "storage" events
- *   immediately.  We expect it to capture all events.
- * - Open an "reader" tab that does not listen for "storage" events and will
- *   only issue reads when instructed.
- * - Open a "lateWriteThenListen" tab that initially does nothing.  We will
- *   later tell it to issue a write and then listen for events to make sure it
- *   captures the later events.
- * - Open "lateOpenSeesPreload" tab after we've done everything and ensure that
- *   it preloads/precaches the data without us having touched localStorage or
- *   added an event listener.
- */
-add_task(function*() {
-  // (There's already one about:blank page open and we open 5 new tabs, so 6
-  // processes.  Actually, 7, just in case.)
-  yield SpecialPowers.pushPrefEnv({
-    set: [
-      ["dom.ipc.processCount", 7]
-    ]
-  });
-
-  // Ensure that there is no localstorage data or potential false positives for
-  // localstorage preloads by forcing the origin to be cleared prior to the
-  // start of our test.
-  clearOriginStorageEnsuringNoPreload();
-
-  // - Open tabs.  Don't configure any of them yet.
-  const knownTabs = new KnownTabs();
-  const writerTab = yield* openTestTabInOwnProcess("writer", knownTabs);
-  const listenerTab = yield* openTestTabInOwnProcess("listener", knownTabs);
-  const readerTab = yield* openTestTabInOwnProcess("reader", knownTabs);
-  const lateWriteThenListenTab = yield* openTestTabInOwnProcess(
-    "lateWriteThenListen", knownTabs);
-
-  // Sanity check that preloading did not occur in the tabs.
-  yield* verifyTabPreload(writerTab, false);
-  yield* verifyTabPreload(listenerTab, false);
-  yield* verifyTabPreload(readerTab, false);
-
-  // - Configure the tabs.
-  yield* recordTabStorageEvents(listenerTab);
-
-  // - Issue the initial batch of writes and verify.
-  const initialWriteMutations = [
-    //[key (null=clear), newValue (null=delete), oldValue (verification)]
-    ["getsCleared", "1", null],
-    ["alsoGetsCleared", "2", null],
-    [null, null, null],
-    ["stays", "3", null],
-    ["clobbered", "pre", null],
-    ["getsDeletedLater", "4", null],
-    ["getsDeletedImmediately", "5", null],
-    ["getsDeletedImmediately", null, "5"],
-    ["alsoStays", "6", null],
-    ["getsDeletedLater", null, "4"],
-    ["clobbered", "post", "pre"]
-  ];
-  const initialWriteState = {
-    stays: "3",
-    clobbered: "post",
-    alsoStays: "6"
-  };
-
-  yield* mutateTabStorage(writerTab, initialWriteMutations);
-
-  yield* verifyTabStorageState(writerTab, initialWriteState);
-  yield* verifyTabStorageEvents(listenerTab, initialWriteMutations);
-  yield* verifyTabStorageState(listenerTab, initialWriteState);
-  yield* verifyTabStorageState(readerTab, initialWriteState);
-
-  // - Issue second set of writes from lateWriteThenListen
-  const lateWriteMutations = [
-    ["lateStays", "10", null],
-    ["lateClobbered", "latePre", null],
-    ["lateDeleted", "11", null],
-    ["lateClobbered", "lastPost", "latePre"],
-    ["lateDeleted", null, "11"]
-  ];
-  const lateWriteState = Object.assign({}, initialWriteState, {
-    lateStays: "10",
-    lateClobbered: "lastPost"
-  });
-
-  yield* mutateTabStorage(lateWriteThenListenTab, lateWriteMutations);
-  yield* recordTabStorageEvents(lateWriteThenListenTab);
-
-  yield* verifyTabStorageState(writerTab, lateWriteState);
-  yield* verifyTabStorageEvents(listenerTab, lateWriteMutations);
-  yield* verifyTabStorageState(listenerTab, lateWriteState);
-  yield* verifyTabStorageState(readerTab, lateWriteState);
-
-  // - Issue last set of writes from writerTab.
-  const lastWriteMutations = [
-    ["lastStays", "20", null],
-    ["lastDeleted", "21", null],
-    ["lastClobbered", "lastPre", null],
-    ["lastClobbered", "lastPost", "lastPre"],
-    ["lastDeleted", null, "21"]
-  ];
-  const lastWriteState = Object.assign({}, lateWriteState, {
-    lastStays: "20",
-    lastClobbered: "lastPost"
-  });
-
-  yield* mutateTabStorage(writerTab, lastWriteMutations);
-
-  yield* verifyTabStorageState(writerTab, lastWriteState);
-  yield* verifyTabStorageEvents(listenerTab, lastWriteMutations);
-  yield* verifyTabStorageState(listenerTab, lastWriteState);
-  yield* verifyTabStorageState(readerTab, lastWriteState);
-  yield* verifyTabStorageEvents(lateWriteThenListenTab, lastWriteMutations);
-  yield* verifyTabStorageState(lateWriteThenListenTab, lastWriteState);
-
-  // - Open a fresh tab and make sure it sees the precache/preload
-  const lateOpenSeesPreload =
-    yield* openTestTabInOwnProcess("lateOpenSeesPreload", knownTabs);
-  yield* verifyTabPreload(lateOpenSeesPreload, true);
-
-  // - Clean up.
-  yield* cleanupTabs(knownTabs);
-
-  clearOriginStorageEnsuringNoPreload();
-});
deleted file mode 100644
--- a/dom/tests/browser/page_localstorage_e10s.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!doctype html>
-<html>
-<head>
-  <meta charset="utf-8">
-<script>
-/**
- * Helper page used by browser_localStorage_e10s.js.
- **/
-var pageName = document.location.search.substring(1);
-window.addEventListener(
-  "load",
-  () => { document.getElementById("pageNameH").textContent = pageName; });
-
-var recordedEvents = null;
-function storageListener(event) {
-  recordedEvents.push([event.key, event.newValue, event.oldValue]);
-}
-
-function listenForStorageEvents() {
-  recordedEvents = [];
-  window.addEventListener("storage", storageListener);
-}
-
-function mutateStorage(mutations) {
-  mutations.forEach(function([key, value]) {
-    if (key !== null) {
-      if (value === null) {
-        localStorage.removeItem(key);
-      } else {
-        localStorage.setItem(key, value);
-      }
-    } else {
-      localStorage.clear();
-    }
-  });
-}
-
-function getStorageState() {
-  let numKeys = localStorage.length;
-  let state = {};
-  for (var iKey = 0; iKey < numKeys; iKey++) {
-    let key = localStorage.key(iKey);
-    state[key] = localStorage.getItem(key);
-  }
-  return state;
-}
-
-function returnAndClearStorageEvents() {
-  let loggedEvents = recordedEvents;
-  recordedEvents = [];
-  return loggedEvents;
-}
-</script>
-</head>
-<body><h2 id="pageNameH"></h2></body>
-</html>
--- a/editor/libeditor/tests/test_bug674770-1.html
+++ b/editor/libeditor/tests/test_bug674770-1.html
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a href="file_bug674770-1.html" id="link2">test</a>
 </div>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
-  SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true]]}, startTests);
+  SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true], ["dom.ipc.processCount", 1]]}, startTests);
 });
 
 function startTests() {
   var tests = [
     { description: "Testing link in <div>: ",
       target: function () { return document.querySelector("#link1"); },
       linkShouldWork: true },
     { description: "Testing link in <div contenteditable>: ",