From b1aadb3572eaf7d2c70e19a2ba5413809d9ac698 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 2 Feb 2017 22:02:24 -0800 Subject: [PATCH] Backed out 5 changesets (bug 1285898) for causing the crashes of bug 1336242 Backed out changeset 15b3b9feb54f (bug 1285898) Backed out changeset 1d0bfb02d6c0 (bug 1285898) Backed out changeset bc99844772ee (bug 1285898) Backed out changeset 8bbe285cf5c0 (bug 1285898) Backed out changeset e56108122b90 (bug 1285898) --- dom/base/nsGlobalWindow.cpp | 100 +++--- dom/events/StorageEvent.h | 15 - .../storage/nsIDOMStorageManager.idl | 9 +- dom/ipc/ContentChild.cpp | 15 - dom/ipc/ContentChild.h | 8 - dom/ipc/ContentParent.cpp | 20 -- dom/ipc/ContentParent.h | 8 - dom/ipc/PContent.ipdl | 13 - dom/storage/Storage.cpp | 87 +---- dom/storage/Storage.h | 23 -- dom/storage/StorageCache.cpp | 102 ++++-- dom/storage/StorageCache.h | 51 +-- dom/storage/StorageIPC.cpp | 6 - dom/storage/StorageManager.cpp | 21 +- dom/storage/StorageManager.h | 11 +- dom/tests/browser/browser.ini | 3 - .../browser/browser_localStorage_e10s.js | 330 ------------------ dom/tests/browser/page_localstorage_e10s.html | 56 --- editor/libeditor/tests/test_bug674770-1.html | 2 +- 19 files changed, 150 insertions(+), 730 deletions(-) delete mode 100644 dom/tests/browser/browser_localStorage_e10s.js delete mode 100644 dom/tests/browser/page_localstorage_e10s.html diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 3023b5eb45ef..d067991cfaaf 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3211,11 +3211,7 @@ nsGlobalWindow::PreloadLocalStorage() // 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 storage; - rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage)); - if (NS_SUCCEEDED(rv)) { - mLocalStorage = static_cast(storage.get()); - } + storageManager->PrecacheStorage(principal); } } @@ -11805,43 +11801,48 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, } 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 event = static_cast(aSubject); if (!event) { return NS_ERROR_FAILURE; } + RefPtr changingStorage = event->GetStorageArea(); + if (!changingStorage) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr istorage = changingStorage.get(); + bool fireMozStorageChanged = false; nsAutoString eventType; eventType.AssignLiteral("storage"); + principal = GetPrincipal(); + if (!principal) { + return NS_OK; + } - if (!NS_strcmp(aData, u"sessionStorage")) { - nsCOMPtr changingStorage = event->GetStorageArea(); - MOZ_ASSERT(changingStorage); + if (changingStorage->IsPrivate() != IsPrivateBrowsing()) { + return NS_OK; + } + switch (changingStorage->GetType()) + { + case Storage::SessionStorage: + { bool check = false; nsCOMPtr 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; } @@ -11862,43 +11863,44 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, if (fireMozStorageChanged) { eventType.AssignLiteral("MozSessionStorageChanged"); } + break; } - else { - MOZ_ASSERT(!NS_strcmp(aData, u"localStorage")); - nsIPrincipal* storagePrincipal = event->GetPrincipal(); - if (!storagePrincipal) { - return NS_OK; - } + 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 clonedEvent = - CloneStorageEvent(eventType, event, error); + RefPtr 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; } @@ -11907,12 +11909,12 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, // 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; } @@ -12003,19 +12005,10 @@ nsGlobalWindow::CloneStorageEvent(const nsAString& aType, aEvent->GetUrl(dict.mUrl); RefPtr storageArea = aEvent->GetStorageArea(); + MOZ_ASSERT(storageArea); RefPtr 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); @@ -12027,7 +12020,7 @@ nsGlobalWindow::CloneStorageEvent(const nsAString& aType, } MOZ_ASSERT(storage); - MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea)); + MOZ_ASSERT(storage->IsForkOf(storageArea)); dict.mStorageArea = storage; @@ -13190,13 +13183,6 @@ nsGlobalWindow::EventListenerAdded(nsIAtom* aType) 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 diff --git a/dom/events/StorageEvent.h b/dom/events/StorageEvent.h index f9639ef58b50..e82baf34131f 100644 --- a/dom/events/StorageEvent.h +++ b/dom/events/StorageEvent.h @@ -13,8 +13,6 @@ #include "mozilla/dom/Event.h" #include "mozilla/dom/StorageEventBinding.h" -class nsIPrincipal; - namespace mozilla { namespace dom { @@ -36,7 +34,6 @@ protected: nsString mNewValue; nsString mUrl; RefPtr mStorageArea; - nsCOMPtr mPrincipal; public: virtual StorageEvent* AsStorageEvent(); @@ -82,18 +79,6 @@ public: { return mStorageArea; } - - // Non WebIDL methods - void SetPrincipal(nsIPrincipal* aPrincipal) - { - MOZ_ASSERT(!mPrincipal); - mPrincipal = aPrincipal; - } - - nsIPrincipal* GetPrincipal() const - { - return mPrincipal; - } }; } // namespace dom diff --git a/dom/interfaces/storage/nsIDOMStorageManager.idl b/dom/interfaces/storage/nsIDOMStorageManager.idl index 45f79f301be0..1008477c099e 100644 --- a/dom/interfaces/storage/nsIDOMStorageManager.idl +++ b/dom/interfaces/storage/nsIDOMStorageManager.idl @@ -20,15 +20,8 @@ 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. diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 6351c45b03c4..aee51eeee77a 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -35,7 +35,6 @@ #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" @@ -3041,20 +3040,6 @@ ContentChild::RecvBlobURLUnregistration(const nsCString& 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() diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 7b28b042815e..c9e4153c2499 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -394,14 +394,6 @@ public: virtual mozilla::ipc::IPCResult RecvInitBlobURLs(nsTArray&& 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, diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 20945753e2a7..030d413fd003 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -51,7 +51,6 @@ #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" @@ -4753,25 +4752,6 @@ ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aUR 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) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index d38837cb308d..f0650a00c601 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -578,14 +578,6 @@ public: 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; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 8ed229029576..eead95c2fa64 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -644,12 +644,6 @@ child: async BlobURLUnregistration(nsCString aURI); - async DispatchLocalStorageChange(nsString documentURI, - nsString key, - nsString oldValue, - nsString newValue, - Principal principal, - bool isPrivate); async GMPsChanged(GMPCapabilityData[] capabilities); @@ -1159,13 +1153,6 @@ parent: 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 */ diff --git a/dom/storage/Storage.cpp b/dom/storage/Storage.cpp index 438acc56285f..71fd932b7dc6 100644 --- a/dom/storage/Storage.cpp +++ b/dom/storage/Storage.cpp @@ -14,9 +14,6 @@ #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" @@ -28,9 +25,6 @@ #include "nsServiceManagerUtils.h" namespace mozilla { - -using namespace ipc; - namespace dom { NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mManager, mPrincipal, mWindow) @@ -64,6 +58,7 @@ Storage::Storage(nsPIDOMWindowInner* aWindow, Storage::~Storage() { + mCache->KeepAlive(); } /* virtual */ JSObject* @@ -217,30 +212,6 @@ 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; @@ -248,67 +219,21 @@ Storage::DispatchStorageEvent(StorageType aStorageType, 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 event = StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict); - event->SetPrincipal(aPrincipal); - RefPtr 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"; diff --git a/dom/storage/Storage.h b/dom/storage/Storage.h index 462c9b9877dd..fb0c93966341 100644 --- a/dom/storage/Storage.h +++ b/dom/storage/Storage.h @@ -24,7 +24,6 @@ namespace dom { class StorageManagerBase; class StorageCache; -class StorageEvent; class Storage final : public nsIDOMStorage @@ -130,28 +129,6 @@ public: 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 diff --git a/dom/storage/StorageCache.cpp b/dom/storage/StorageCache.cpp index 1d89cdfb4978..85da26bb4764 100644 --- a/dom/storage/StorageCache.cpp +++ b/dom/storage/StorageCache.cpp @@ -59,7 +59,7 @@ GetDataSetIndex(const Storage* aStorage) 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) @@ -149,7 +149,7 @@ StorageCache::Init(StorageManagerBase* aManager, // 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); @@ -198,33 +198,28 @@ StorageCache::DataSet(const Storage* aStorage) } 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; } @@ -251,6 +246,60 @@ StorageCache::Preload() 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 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 timer = do_CreateInstance("@mozilla.org/timer;1"); + if (!timer) { + return; + } + + RefPtr 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. @@ -390,8 +439,7 @@ StorageCache::GetItem(const Storage* aStorage, const nsAString& aKey, 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; @@ -414,7 +462,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey, delta += static_cast(aValue.Length()) - static_cast(aOld.Length()); - if (!ProcessUsageDelta(aStorage, delta, aSource)) { + if (!ProcessUsageDelta(aStorage, delta)) { return NS_ERROR_DOM_QUOTA_REACHED; } @@ -424,7 +472,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey, 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!"); @@ -443,7 +491,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey, 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); @@ -461,10 +509,10 @@ StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey, // Recalculate the cached data size const int64_t delta = -(static_cast(aOld.Length()) + static_cast(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!"); @@ -478,7 +526,7 @@ StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey, } nsresult -StorageCache::Clear(const Storage* aStorage, const MutationSource aSource) +StorageCache::Clear(const Storage* aStorage) { bool refresh = false; if (Persist(aStorage)) { @@ -500,11 +548,11 @@ StorageCache::Clear(const Storage* aStorage, const MutationSource aSource) 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!"); @@ -619,6 +667,9 @@ StorageCache::LoadItem(const nsAString& aKey, const nsString& aValue) void StorageCache::LoadDone(nsresult aRv) { + // Keep the preloaded cache alive for a time + KeepAlive(); + MonitorAutoLock monitor(mMonitor); mLoadResult = aRv; mLoaded = true; @@ -679,13 +730,12 @@ StorageUsage::LoadUsage(const int64_t 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; } diff --git a/dom/storage/StorageCache.h b/dom/storage/StorageCache.h index c8549b1e9dfc..dad9daf395f8 100644 --- a/dom/storage/StorageCache.h +++ b/dom/storage/StorageCache.h @@ -78,24 +78,8 @@ 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); @@ -125,13 +109,10 @@ public: 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& aKeys); @@ -201,18 +182,8 @@ private: // 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 @@ -225,6 +196,9 @@ private: // Obtained from the manager during initialization (Init method). RefPtr mUsage; + // Timer that holds this cache alive for a while after it has been preloaded. + nsCOMPtr 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 @@ -237,7 +211,7 @@ private: // 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; @@ -303,8 +277,7 @@ 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; } diff --git a/dom/storage/StorageIPC.cpp b/dom/storage/StorageIPC.cpp index 19abddacb90f..ac9e5f38ec6a 100644 --- a/dom/storage/StorageIPC.cpp +++ b/dom/storage/StorageIPC.cpp @@ -224,12 +224,6 @@ StorageDBChild::RecvObserve(const nsCString& aTopic, mozilla::ipc::IPCResult StorageDBChild::RecvOriginsHavingData(nsTArray&& 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]); } diff --git a/dom/storage/StorageManager.cpp b/dom/storage/StorageManager.cpp index 17f086eeb244..c0301996b79b 100644 --- a/dom/storage/StorageManager.cpp +++ b/dom/storage/StorageManager.cpp @@ -309,7 +309,7 @@ StorageManagerBase::DropCache(StorageCache* aCache) } nsresult -StorageManagerBase::GetStorageInternal(CreateMode aCreateMode, +StorageManagerBase::GetStorageInternal(bool aCreate, mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal, const nsAString& aDocumentURI, @@ -331,12 +331,12 @@ StorageManagerBase::GetStorageInternal(CreateMode aCreateMode, // 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(); @@ -372,11 +372,10 @@ StorageManagerBase::GetStorageInternal(CreateMode aCreateMode, } 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 @@ -386,8 +385,8 @@ StorageManagerBase::CreateStorage(mozIDOMWindow* aWindow, bool aPrivate, nsIDOMStorage** aRetval) { - return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal, - aDocumentURI, aPrivate, aRetval); + return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate, + aRetval); } NS_IMETHODIMP @@ -396,8 +395,8 @@ StorageManagerBase::GetStorage(mozIDOMWindow* aWindow, bool aPrivate, nsIDOMStorage** aRetval) { - return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow, - aPrincipal, EmptyString(), aPrivate, aRetval); + return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate, + aRetval); } NS_IMETHODIMP diff --git a/dom/storage/StorageManager.h b/dom/storage/StorageManager.h index 247c7fc72049..84ec05be7e95 100644 --- a/dom/storage/StorageManager.h +++ b/dom/storage/StorageManager.h @@ -90,17 +90,8 @@ private: 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, diff --git a/dom/tests/browser/browser.ini b/dom/tests/browser/browser.ini index e4f93551eee2..80860cbf969b 100644 --- a/dom/tests/browser/browser.ini +++ b/dom/tests/browser/browser.ini @@ -2,7 +2,6 @@ support-files = browser_frame_elements.html page_privatestorageevent.html - page_localstorage_e10s.html position.html test-console-api.html test_bug1004814.html @@ -41,8 +40,6 @@ skip-if = e10s 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] diff --git a/dom/tests/browser/browser_localStorage_e10s.js b/dom/tests/browser/browser_localStorage_e10s.js deleted file mode 100644 index ff431e27ab5d..000000000000 --- 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(); -}); diff --git a/dom/tests/browser/page_localstorage_e10s.html b/dom/tests/browser/page_localstorage_e10s.html deleted file mode 100644 index 88025dfc931a..000000000000 --- a/dom/tests/browser/page_localstorage_e10s.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - -

- diff --git a/editor/libeditor/tests/test_bug674770-1.html b/editor/libeditor/tests/test_bug674770-1.html index f2e65b2b91cd..4ba65f507f0f 100644 --- a/editor/libeditor/tests/test_bug674770-1.html +++ b/editor/libeditor/tests/test_bug674770-1.html @@ -24,7 +24,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=674770 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() {