From dd51467c228cb5c9ec9d9efbb6e0339037ec7fd5 Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Wed, 26 May 2021 07:14:06 +0000 Subject: [PATCH] Part 7: Bug 1700623 - Make session storage session store work with Fission. r=nika Use the newly added session storage data getter to access the session storage in the parent and store it in session store without a round trip to content processes. Depends on D111433 Differential Revision: https://phabricator.services.mozilla.com/D111434 --- docshell/base/BrowsingContext.cpp | 8 + docshell/base/CanonicalBrowsingContext.cpp | 122 +++++++++++- docshell/base/CanonicalBrowsingContext.h | 13 ++ dom/base/nsFrameLoader.cpp | 54 +++--- dom/chrome-webidl/SessionStoreUtils.webidl | 5 - dom/ipc/BrowserChild.cpp | 10 +- dom/ipc/BrowserParent.cpp | 12 -- dom/ipc/BrowserParent.h | 2 - dom/ipc/PBrowser.ipdl | 2 - dom/ipc/WindowGlobalParent.cpp | 7 +- dom/ipc/WindowGlobalParent.h | 7 +- .../SessionStoreDataCollector.cpp | 3 +- .../sessionstore/SessionStoreFunctions.idl | 4 + .../sessionstore/SessionStoreFunctions.jsm | 74 ++++---- .../sessionstore/SessionStoreListener.cpp | 158 +--------------- .../sessionstore/SessionStoreListener.h | 45 +---- .../sessionstore/SessionStoreUtils.cpp | 178 ++++++++---------- .../sessionstore/SessionStoreUtils.h | 12 +- 18 files changed, 303 insertions(+), 413 deletions(-) diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 41d235b8b9e5..3c8d0184a6b6 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -3025,6 +3025,14 @@ void BrowsingContext::DidSet(FieldIndex) { prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false); } if (mCurrentWindowContext) { + // We set a timer when we set the current inner window. This + // will then flush the session storage to session store to + // make sure that we don't miss to store session storage to + // session store that is a result of navigation. This is due + // to Bug 1700623. We wish to fix this in Bug 1711886, where + // making sure to store everything would make this timer + // unnecessary. + Canonical()->MaybeScheduleSessionStoreUpdate(); mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true); } } diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index a19bc9485230..d196c342f83d 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/BrowsingContextGroup.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/EventTarget.h" +#include "mozilla/dom/PBackgroundSessionStorageCache.h" #include "mozilla/dom/PWindowGlobalParent.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/ContentProcessManager.h" @@ -42,6 +43,9 @@ #include "nsBrowserStatusFilter.h" #include "nsIBrowser.h" #include "nsTHashSet.h" +#include "SessionStoreFunctions.h" +#include "nsIXPConnect.h" +#include "nsImportModule.h" #ifdef NS_PRINTING # include "mozilla/embedding/printingui/PrintingParent.h" @@ -1022,6 +1026,8 @@ void CanonicalBrowsingContext::CanonicalDiscard() { if (IsTop()) { BackgroundSessionStorageManager::RemoveManager(Id()); } + + CancelSessionStoreUpdate(); } void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() { @@ -2026,6 +2032,119 @@ void CanonicalBrowsingContext::RestoreState::Resolve() { mPromise = nullptr; } +nsresult CanonicalBrowsingContext::WriteSessionStorageToSessionStore( + const nsTArray& aSesssionStorage, uint32_t aEpoch) { + RefPtr windowParent = GetCurrentWindowGlobal(); + + if (!windowParent) { + return NS_OK; + } + + Element* frameElement = windowParent->GetRootOwnerElement(); + if (!frameElement) { + return NS_OK; + } + + nsCOMPtr funcs = + do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm"); + if (!funcs) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr wrapped = do_QueryInterface(funcs); + AutoJSAPI jsapi; + if (!jsapi.Init(wrapped->GetJSObjectGlobal())) { + return NS_ERROR_FAILURE; + } + + Record> storage; + JS::RootedValue update(jsapi.cx()); + + if (!aSesssionStorage.IsEmpty()) { + SessionStoreUtils::ConstructSessionStorageValues(this, aSesssionStorage, + storage); + if (!ToJSValue(jsapi.cx(), storage, &update)) { + return NS_ERROR_FAILURE; + } + } else { + update.setNull(); + } + + return funcs->UpdateSessionStoreForStorage(frameElement, this, aEpoch, + update); +} + +void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage( + const std::function& aDone) { + using DataPromise = BackgroundSessionStorageManager::DataPromise; + BackgroundSessionStorageManager::GetData( + this, StaticPrefs::browser_sessionstore_dom_storage_limit(), + /* aCancelSessionStoreTiemr = */ true) + ->Then(GetCurrentSerialEventTarget(), __func__, + [self = RefPtr{this}, aDone, epoch = GetSessionStoreEpoch()]( + const DataPromise::ResolveOrRejectValue& valueList) { + if (valueList.IsResolve()) { + self->WriteSessionStorageToSessionStore( + valueList.ResolveValue(), epoch); + } + aDone(); + }); +} + +/* static */ +void CanonicalBrowsingContext::UpdateSessionStoreForStorage( + uint64_t aBrowsingContextId) { + RefPtr browsingContext = Get(aBrowsingContextId); + + if (!browsingContext) { + return; + } + + browsingContext->UpdateSessionStoreSessionStorage([]() {}); +} + +void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() { + if (!IsTop()) { + Top()->MaybeScheduleSessionStoreUpdate(); + return; + } + + if (IsInBFCache()) { + return; + } + + if (mSessionStoreSessionStorageUpdateTimer) { + return; + } + + if (StaticPrefs::browser_sessionstore_debug_no_auto_updates()) { + UpdateSessionStoreSessionStorage([]() {}); + return; + } + + auto result = NS_NewTimerWithFuncCallback( + [](nsITimer*, void* aClosure) { + auto* context = static_cast(aClosure); + context->UpdateSessionStoreSessionStorage([]() {}); + }, + this, StaticPrefs::browser_sessionstore_interval(), + nsITimer::TYPE_ONE_SHOT, + "CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate"); + + if (result.isErr()) { + return; + } + + mSessionStoreSessionStorageUpdateTimer = result.unwrap(); +} + +void CanonicalBrowsingContext::CancelSessionStoreUpdate() { + if (mSessionStoreSessionStorageUpdateTimer) { + mSessionStoreSessionStorageUpdateTimer->Cancel(); + mSessionStoreSessionStorageUpdateTimer = nullptr; + } +} + void CanonicalBrowsingContext::SetContainerFeaturePolicy( FeaturePolicy* aContainerFeaturePolicy) { mContainerFeaturePolicy = aContainerFeaturePolicy; @@ -2234,7 +2353,8 @@ bool CanonicalBrowsingContext::AllowedInBFCache( NS_IMPL_CYCLE_COLLECTION_INHERITED(CanonicalBrowsingContext, BrowsingContext, mSessionHistory, mContainerFeaturePolicy, - mCurrentBrowserParent) + mCurrentBrowserParent, + mSessionStoreSessionStorageUpdateTimer) NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext, BrowsingContext) NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext, BrowsingContext) diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h index 58d74b9f85aa..58c9a4ea8125 100644 --- a/docshell/base/CanonicalBrowsingContext.h +++ b/docshell/base/CanonicalBrowsingContext.h @@ -30,6 +30,7 @@ class nsSHistory; class nsBrowserStatusFilter; class nsSecureBrowserUI; class CallerWillNotifyHistoryIndexAndLengthChanges; +class nsITimer; namespace mozilla { enum class CallState; @@ -47,6 +48,7 @@ struct LoadURIOptions; class MediaController; struct LoadingSessionHistoryInfo; class SessionHistoryEntry; +class SSCacheCopy; class WindowGlobalParent; // RemotenessChangeOptions is passed through the methods to store the state @@ -292,6 +294,11 @@ class CanonicalBrowsingContext final : public BrowsingContext { void RequestRestoreTabContent(WindowGlobalParent* aWindow); already_AddRefed GetRestorePromise(); + nsresult WriteSessionStorageToSessionStore( + const nsTArray& aSesssionStorage, uint32_t aEpoch); + + void UpdateSessionStoreSessionStorage(const std::function& aDone); + // Called when a BrowserParent for this BrowsingContext has been fully // destroyed (i.e. `ActorDestroy` was called). void BrowserParentDestroyed(BrowserParent* aBrowserParent, @@ -405,6 +412,10 @@ class CanonicalBrowsingContext final : public BrowsingContext { // has become unloaded for one reason or another. void ShowSubframeCrashedUI(BrowserBridgeParent* aBridge); + void MaybeScheduleSessionStoreUpdate(); + + void CancelSessionStoreUpdate(); + // XXX(farre): Store a ContentParent pointer here rather than mProcessId? // Indicates which process owns the docshell. uint64_t mProcessId; @@ -458,6 +469,8 @@ class CanonicalBrowsingContext final : public BrowsingContext { // If this is a top level context, this is true if our browser ID is marked as // active in the process priority manager. bool mPriorityActive = false; + + nsCOMPtr mSessionStoreSessionStorageUpdateTimer; }; } // namespace dom diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 184ae97b5162..7a91e09a8417 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -117,6 +117,12 @@ #include "mozilla/dom/BrowserBridgeHost.h" #include "mozilla/dom/BrowsingContextGroup.h" +#include "mozilla/dom/SessionStorageManager.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/dom/PBackgroundSessionStorageCache.h" +#include "mozilla/ipc/BackgroundUtils.h" + #include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/ContentPrincipal.h" @@ -134,6 +140,7 @@ using namespace mozilla; using namespace mozilla::hal; using namespace mozilla::dom; using namespace mozilla::dom::ipc; +using namespace mozilla::ipc; using namespace mozilla::layers; using namespace mozilla::layout; typedef ScrollableLayerGuid::ViewID ViewID; @@ -3189,42 +3196,28 @@ already_AddRefed nsFrameLoader::RequestTabStateFlush( if (mSessionStoreListener) { context->FlushSessionStore(); mSessionStoreListener->ForceFlushFromParent(false); + context->Canonical()->UpdateSessionStoreSessionStorage( + [promise]() { promise->MaybeResolveWithUndefined(); }); - // No async ipc call is involved in parent only case - promise->MaybeResolveWithUndefined(); return promise.forget(); } - // XXX(farre): We hack around not having fully implemented session - // store session storage collection in the parent. What we need to - // do is to make sure that we always flush the toplevel context - // first. And also to wait for that flush to resolve. This will be - // fixed by moving session storage collection to the parent, which - // will happen in Bug 1700623. - RefPtr contentParent = - context->Canonical()->GetContentParent(); using FlushPromise = ContentParent::FlushTabStatePromise; - contentParent->SendFlushTabState(context)->Then( - GetCurrentSerialEventTarget(), __func__, - [promise, context, - contentParent](const FlushPromise::ResolveOrRejectValue&) { - nsTArray> flushPromises; - context->Group()->EachOtherParent( - contentParent, [&](ContentParent* aParent) { - if (aParent->CanSend()) { - flushPromises.AppendElement( - aParent->SendFlushTabState(context)); - } - }); + nsTArray> flushPromises; + context->Group()->EachParent([&](ContentParent* aParent) { + if (aParent->CanSend()) { + flushPromises.AppendElement(aParent->SendFlushTabState(context)); + } + }); - FlushPromise::All(GetCurrentSerialEventTarget(), flushPromises) - ->Then( - GetCurrentSerialEventTarget(), __func__, - [promise]( - const FlushPromise::AllPromiseType::ResolveOrRejectValue&) { - promise->MaybeResolveWithUndefined(); - }); - }); + RefPtr flushPromise = + FlushPromise::All(GetCurrentSerialEventTarget(), flushPromises); + + context->Canonical()->UpdateSessionStoreSessionStorage([flushPromise, + promise]() { + flushPromise->Then(GetCurrentSerialEventTarget(), __func__, + [promise]() { promise->MaybeResolveWithUndefined(); }); + }); return promise.forget(); } @@ -3241,7 +3234,6 @@ void nsFrameLoader::RequestTabStateFlush() { // No async ipc call is involved in parent only case return; } - context->Group()->EachParent([&](ContentParent* aParent) { if (aParent->CanSend()) { aParent->SendFlushTabState( diff --git a/dom/chrome-webidl/SessionStoreUtils.webidl b/dom/chrome-webidl/SessionStoreUtils.webidl index 19e5f9324471..b7b6452a2bc9 100644 --- a/dom/chrome-webidl/SessionStoreUtils.webidl +++ b/dom/chrome-webidl/SessionStoreUtils.webidl @@ -180,11 +180,6 @@ dictionary InputElementData { dictionary UpdateSessionStoreData { ByteString docShellCaps; boolean isPrivate; - // for sessionStorage - sequence storageOrigins; - sequence storageKeys; - sequence storageValues; - boolean isFullStorage; }; [GenerateConversionToJS] diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 07f578c16517..fde1607e0826 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -3791,15 +3791,7 @@ bool BrowserChild::UpdateSessionStore(bool aIsFinal) { privatedMode.emplace(store->GetPrivateModeEnabled()); } - nsTArray origins; - nsTArray keys, values; - bool isFullStorage = false; - if (store->IsStorageUpdated()) { - isFullStorage = store->GetAndClearStorageChanges(origins, keys, values); - } - - Unused << SendSessionStoreUpdate(docShellCaps, privatedMode, origins, keys, - values, isFullStorage, + Unused << SendSessionStoreUpdate(docShellCaps, privatedMode, store->GetAndClearSHistoryChanged(), aIsFinal, mSessionStoreListener->GetEpoch()); return true; diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 821f35cab164..eafcc9b8268c 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -2960,8 +2960,6 @@ bool BrowserParent::ReconstructWebProgressAndRequest( mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate( const Maybe& aDocShellCaps, const Maybe& aPrivatedMode, - nsTArray&& aOrigins, nsTArray&& aKeys, - nsTArray&& aValues, const bool aIsFullStorage, const bool aNeedCollectSHistory, const bool& aIsFinal, const uint32_t& aEpoch) { UpdateSessionStoreData data; @@ -2971,16 +2969,6 @@ mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate( if (aPrivatedMode.isSome()) { data.mIsPrivate.Construct() = aPrivatedMode.value(); } - // In normal case, we only update the storage when needed. - // However, we need to reset the session storage(aOrigins.Length() will be 0) - // if the usage is over the "browser_sessionstore_dom_storage_limit". - // In this case, aIsFullStorage is true. - if (aOrigins.Length() != 0 || aIsFullStorage) { - data.mStorageOrigins.Construct(std::move(aOrigins)); - data.mStorageKeys.Construct(std::move(aKeys)); - data.mStorageValues.Construct(std::move(aValues)); - data.mIsFullStorage.Construct() = aIsFullStorage; - } nsCOMPtr funcs = do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm"); diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h index 13dfce6bf5e7..eca8931f4434 100644 --- a/dom/ipc/BrowserParent.h +++ b/dom/ipc/BrowserParent.h @@ -314,8 +314,6 @@ class BrowserParent final : public PBrowserParent, mozilla::ipc::IPCResult RecvSessionStoreUpdate( const Maybe& aDocShellCaps, const Maybe& aPrivatedMode, - nsTArray&& aOrigins, nsTArray&& aKeys, - nsTArray&& aValues, const bool aIsFullStorage, const bool aNeedCollectSHistory, const bool& aIsFinal, const uint32_t& aEpoch); diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index b2e911d6d94a..2a89bbf22d74 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -563,8 +563,6 @@ parent: async NavigationFinished(); async SessionStoreUpdate(nsCString? aDocShellCaps, bool? aPrivatedMode, - nsCString[] aOrigins, nsString[] aKeys, - nsString[] aValues, bool aIsFullStorage, bool aNeedCollectSHistory, bool aIsFinal, uint32_t aEpoch); diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index 868edc9d430f..00555e832339 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -67,6 +67,8 @@ #include "nsIXPConnect.h" #include "nsImportModule.h" +#include "mozilla/dom/PBackgroundSessionStorageCache.h" + using namespace mozilla::ipc; using namespace mozilla::dom::ipc; @@ -1244,7 +1246,7 @@ Element* WindowGlobalParent::GetRootOwnerElement() { return nullptr; } -nsresult WindowGlobalParent::UpdateSessionStore( +nsresult WindowGlobalParent::WriteFormDataAndScrollToSessionStore( const Maybe& aFormData, const Maybe& aScrollPosition, uint32_t aEpoch) { if (!aFormData && !aScrollPosition) { @@ -1342,7 +1344,8 @@ nsresult WindowGlobalParent::ResetSessionStore(uint32_t aEpoch) { mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateSessionStore( const Maybe& aFormData, const Maybe& aScrollPosition, uint32_t aEpoch) { - if (NS_FAILED(UpdateSessionStore(aFormData, aScrollPosition, aEpoch))) { + if (NS_FAILED(WriteFormDataAndScrollToSessionStore(aFormData, aScrollPosition, + aEpoch))) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, ("ParentIPC: Failed to update session store entry.")); } diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index 44a3685a9f44..1f758c62aeb4 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -46,6 +46,7 @@ class JSActorMessageMeta; struct PageUseCounters; class WindowSessionStoreState; struct WindowSessionStoreUpdate; +class SSCacheQueryResult; /** * A handle in the parent process to a specific nsGlobalWindowInner object. @@ -213,9 +214,9 @@ class WindowGlobalParent final : public WindowContext, const nsACString& GetRemoteType() override; - nsresult UpdateSessionStore(const Maybe& aFormData, - const Maybe& aScrollPosition, - uint32_t aEpoch); + nsresult WriteFormDataAndScrollToSessionStore( + const Maybe& aFormData, const Maybe& aScrollPosition, + uint32_t aEpoch); Maybe GetSingleChannelId() { return mSingleChannelId; } diff --git a/toolkit/components/sessionstore/SessionStoreDataCollector.cpp b/toolkit/components/sessionstore/SessionStoreDataCollector.cpp index 5ae80b2edaac..04d4419fddb3 100644 --- a/toolkit/components/sessionstore/SessionStoreDataCollector.cpp +++ b/toolkit/components/sessionstore/SessionStoreDataCollector.cpp @@ -145,7 +145,8 @@ void SessionStoreDataCollector::Collect() { if (RefPtr windowParent = mWindowChild->GetParentActor()) { - windowParent->UpdateSessionStore(maybeFormData, maybeScroll, mEpoch); + windowParent->WriteFormDataAndScrollToSessionStore(maybeFormData, + maybeScroll, mEpoch); } else { mWindowChild->SendUpdateSessionStore(maybeFormData, maybeScroll, mEpoch); } diff --git a/toolkit/components/sessionstore/SessionStoreFunctions.idl b/toolkit/components/sessionstore/SessionStoreFunctions.idl index f424e75842fa..48ae2faefadf 100644 --- a/toolkit/components/sessionstore/SessionStoreFunctions.idl +++ b/toolkit/components/sessionstore/SessionStoreFunctions.idl @@ -20,4 +20,8 @@ interface nsISessionStoreFunctions : nsISupports { void UpdateSessionStoreForWindow( in Element aBrowser, in BrowsingContext aBrowsingContext, in uint32_t aEpoch, in jsval aData); + + void UpdateSessionStoreForStorage( + in Element aBrowser, in BrowsingContext aBrowsingContext, + in uint32_t aEpoch, in jsval aData); }; diff --git a/toolkit/components/sessionstore/SessionStoreFunctions.jsm b/toolkit/components/sessionstore/SessionStoreFunctions.jsm index 03f2167a9ea8..1288f0ebe6c1 100644 --- a/toolkit/components/sessionstore/SessionStoreFunctions.jsm +++ b/toolkit/components/sessionstore/SessionStoreFunctions.jsm @@ -40,37 +40,27 @@ function UpdateSessionStoreForWindow( ); } -var EXPORTED_SYMBOLS = ["UpdateSessionStore", "UpdateSessionStoreForWindow"]; +function UpdateSessionStoreForStorage( + aBrowser, + aBrowsingContext, + aEpoch, + aData +) { + return SessionStoreFuncInternal.updateSessionStoreForStorage( + aBrowser, + aBrowsingContext, + aEpoch, + aData + ); +} + +var EXPORTED_SYMBOLS = [ + "UpdateSessionStore", + "UpdateSessionStoreForWindow", + "UpdateSessionStoreForStorage", +]; var SessionStoreFuncInternal = { - updateStorage: function SSF_updateStorage(aOrigins, aKeys, aValues) { - let data = {}; - for (let i = 0; i < aOrigins.length; i++) { - // If the key isn't defined, then .clear() was called, and we send - // up null for this domain to indicate that storage has been cleared - // for it. - if (aKeys[i] == "") { - while (aOrigins[i + 1] == aOrigins[i]) { - i++; - } - data[aOrigins[i]] = null; - } else { - let hostData = {}; - hostData[aKeys[i]] = aValues[i]; - while (aOrigins[i + 1] == aOrigins[i]) { - i++; - hostData[aKeys[i]] = aValues[i]; - } - data[aOrigins[i]] = hostData; - } - } - if (aOrigins.length) { - return data; - } - - return null; - }, - updateSessionStore: function SSF_updateSessionStore( aBrowser, aBrowsingContext, @@ -86,25 +76,13 @@ var SessionStoreFuncInternal = { currentData.isPrivate = aData.isPrivate; } - if (aData.isFullStorage != undefined) { - let storage = this.updateStorage( - aData.storageOrigins, - aData.storageKeys, - aData.storageValues - ); - if (aData.isFullStorage) { - currentData.storage = storage; - } else { - currentData.storagechange = storage; - } - } - SessionStore.updateSessionStoreFromTablistener(aBrowser, aBrowsingContext, { data: currentData, epoch: aEpoch, sHistoryNeeded: aCollectSHistory, }); }, + updateSessionStoreForWindow: function SSF_updateSessionStoreForWindow( aBrowser, aBrowsingContext, @@ -118,4 +96,16 @@ var SessionStoreFuncInternal = { epoch: aEpoch, }); }, + + updateSessionStoreForStorage: function SSF_updateSessionStoreForWindow( + aBrowser, + aBrowsingContext, + aEpoch, + aData + ) { + SessionStore.updateSessionStoreFromTablistener(aBrowser, aBrowsingContext, { + data: { storage: aData }, + epoch: aEpoch, + }); + }, }; diff --git a/toolkit/components/sessionstore/SessionStoreListener.cpp b/toolkit/components/sessionstore/SessionStoreListener.cpp index a84b107b0a84..1bd12b5e3e7a 100644 --- a/toolkit/components/sessionstore/SessionStoreListener.cpp +++ b/toolkit/components/sessionstore/SessionStoreListener.cpp @@ -7,7 +7,6 @@ #include "mozilla/dom/SessionStoreListener.h" #include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtilsBinding.h" -#include "mozilla/dom/StorageEvent.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/StaticPrefs_browser.h" #include "nsGenericHTMLElement.h" @@ -44,7 +43,6 @@ ContentSessionStore::ContentSessionStore(nsIDocShell* aDocShell) : mDocShell(aDocShell), mPrivateChanged(false), mIsPrivate(false), - mStorageStatus(NO_STORAGE), mDocCapChanged(false), mSHistoryChanged(false), mSHistoryChangedFromParent(false) { @@ -104,18 +102,6 @@ bool ContentSessionStore::GetPrivateModeEnabled() { return mIsPrivate; } -void ContentSessionStore::SetFullStorageNeeded() { - // We need the entire session storage, reset the pending individual change - ResetStorageChanges(); - mStorageStatus = FULLSTORAGE; -} - -void ContentSessionStore::ResetStorageChanges() { - mOrigins.Clear(); - mKeys.Clear(); - mValues.Clear(); -} - void ContentSessionStore::SetSHistoryChanged() { mSHistoryChanged = mozilla::SessionHistoryInParent(); } @@ -132,15 +118,12 @@ void ContentSessionStore::OnDocumentStart() { mDocCapChanged = true; } - SetFullStorageNeeded(); - if (mozilla::SessionHistoryInParent()) { mSHistoryChanged = true; } } void ContentSessionStore::OnDocumentEnd() { - SetFullStorageNeeded(); if (mozilla::SessionHistoryInParent()) { mSHistoryChanged = true; } @@ -167,8 +150,6 @@ TabListener::TabListener(nsIDocShell* aDocShell, Element* aElement) mProgressListenerRegistered(false), mEventListenerRegistered(false), mPrefObserverRegistered(false), - mStorageObserverRegistered(false), - mStorageChangeListenerRegistered(false), mUpdatedTimer(nullptr), mTimeoutDisabled(false), mUpdateInterval(15000), @@ -208,13 +189,6 @@ nsresult TabListener::Init() { mPrefObserverRegistered = true; } - nsCOMPtr obs = mozilla::services::GetObserverService(); - NS_WARNING_ASSERTION(obs, "no observer service"); - if (obs) { - obs->AddObserver(this, "browser:purge-sessionStorage", true); - mStorageObserverRegistered = true; - } - AddEventListeners(); return NS_OK; } @@ -225,10 +199,6 @@ void TabListener::AddEventListeners() { eventTarget->AddSystemEventListener(u"DOMTitleChanged"_ns, this, false); } mEventListenerRegistered = true; - - eventTarget->AddSystemEventListener(u"MozSessionStorageChanged"_ns, this, - false); - mStorageChangeListenerRegistered = true; } } @@ -241,11 +211,6 @@ void TabListener::RemoveEventListeners() { } mEventListenerRegistered = false; } - if (mStorageChangeListenerRegistered) { - eventTarget->RemoveSystemEventListener(u"MozSessionStorageChanged"_ns, - this, false); - mStorageChangeListenerRegistered = false; - } } } @@ -324,7 +289,6 @@ NS_IMETHODIMP TabListener::OnStateChange(nsIWebProgress* aWebProgress, if (aStateFlags & (nsIWebProgressListener::STATE_START)) { mSessionStore->OnDocumentStart(); - ResetStorageChangeListener(); } else if (aStateFlags & (nsIWebProgressListener::STATE_STOP)) { mSessionStore->OnDocumentEnd(); } @@ -351,24 +315,7 @@ TabListener::HandleEvent(Event* aEvent) { nsAutoString eventType; aEvent->GetType(eventType); - if (eventType.EqualsLiteral("MozSessionStorageChanged")) { - auto event = static_cast(aEvent); - RefPtr changingStorage = event->GetStorageArea(); - if (!changingStorage) { - return NS_OK; - } - // How much data does DOMSessionStorage contain? - int64_t storageUsage = changingStorage->GetOriginQuotaUsage(); - if (storageUsage > StaticPrefs::browser_sessionstore_dom_storage_limit()) { - RemoveStorageChangeListener(); - mSessionStore->ResetStorageChanges(); - mSessionStore->ResetStorage(); - return NS_OK; - } - if (mSessionStore->AppendSessionStorageChange(event)) { - AddTimerForUpdate(); - } - } else if (eventType.EqualsLiteral("DOMTitleChanged")) { + if (eventType.EqualsLiteral("DOMTitleChanged")) { mSessionStore->SetSHistoryChanged(); AddTimerForUpdate(); } @@ -411,12 +358,6 @@ NS_IMETHODIMP TabListener::OnContentBlockingEvent(nsIWebProgress* aWebProgress, nsresult TabListener::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { - if (!strcmp(aTopic, "browser:purge-sessionStorage")) { - mSessionStore->SetFullStorageNeeded(); - AddTimerForUpdate(); - return NS_OK; - } - if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { nsCOMPtr prefBranch = do_QueryInterface(aSubject); @@ -496,61 +437,6 @@ int CollectPositions(BrowsingContext* aBrowsingContext, return aPositionDescendants[currentIdx] + 1; } -bool ContentSessionStore::AppendSessionStorageChange(StorageEvent* aEvent) { - // We will collect the full SessionStore if mStorageStatus is FULLSTORAGE. - // These partial changes can be skipped in this case. - if (mStorageStatus == FULLSTORAGE) { - return false; - } - - RefPtr changingStorage = aEvent->GetStorageArea(); - if (!changingStorage) { - return false; - } - - nsCOMPtr storagePrincipal = changingStorage->StoragePrincipal(); - if (!storagePrincipal) { - return false; - } - - nsAutoCString origin; - nsresult rv = storagePrincipal->GetOrigin(origin); - if (NS_FAILED(rv)) { - return false; - } - - mOrigins.AppendElement(origin); - aEvent->GetKey(*mKeys.AppendElement()); - aEvent->GetNewValue(*mValues.AppendElement()); - mStorageStatus = STORAGECHANGE; - return true; -} - -bool ContentSessionStore::GetAndClearStorageChanges( - nsTArray& aOrigins, nsTArray& aKeys, - nsTArray& aValues) { - MOZ_ASSERT(IsStorageUpdated()); - bool isFullStorage = false; - - if (mStorageStatus == RESET) { - isFullStorage = true; - } else if (mStorageStatus == FULLSTORAGE) { - MOZ_ASSERT(mDocShell); - SessionStoreUtils::CollectedSessionStorage( - nsDocShell::Cast(mDocShell)->GetBrowsingContext(), aOrigins, aKeys, - aValues); - isFullStorage = true; - } else if (mStorageStatus == STORAGECHANGE) { - aOrigins.SwapElements(mOrigins); - aKeys.SwapElements(mKeys); - aValues.SwapElements(mValues); - } - - ResetStorageChanges(); - mStorageStatus = NO_STORAGE; - return isFullStorage; -} - bool TabListener::ForceFlushFromParent(bool aIsFinal) { if (!XRE_IsParentProcess()) { return false; @@ -610,15 +496,6 @@ bool TabListener::UpdateSessionStore(bool aIsFlush, bool aIsFinal) { if (mSessionStore->IsPrivateChanged()) { data.mIsPrivate.Construct() = mSessionStore->GetPrivateModeEnabled(); } - if (mSessionStore->IsStorageUpdated()) { - nsTArray origins; - nsTArray keys, values; - data.mIsFullStorage.Construct() = - mSessionStore->GetAndClearStorageChanges(origins, keys, values); - data.mStorageOrigins.Construct(std::move(origins)); - data.mStorageKeys.Construct(std::move(keys)); - data.mStorageValues.Construct(std::move(values)); - } nsCOMPtr funcs = do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm"); @@ -638,33 +515,6 @@ bool TabListener::UpdateSessionStore(bool aIsFlush, bool aIsFinal) { return true; } -void TabListener::ResetStorageChangeListener() { - if (mStorageChangeListenerRegistered) { - return; - } - - nsCOMPtr eventTarget = GetEventTarget(); - if (!eventTarget) { - return; - } - eventTarget->AddSystemEventListener(u"MozSessionStorageChanged"_ns, this, - false); - mStorageChangeListenerRegistered = true; -} - -void TabListener::RemoveStorageChangeListener() { - if (!mStorageChangeListenerRegistered) { - return; - } - - nsCOMPtr eventTarget = GetEventTarget(); - if (eventTarget) { - eventTarget->RemoveSystemEventListener(u"MozSessionStorageChanged"_ns, this, - false); - mStorageChangeListenerRegistered = false; - } -} - void TabListener::RemoveListeners() { if (mProgressListenerRegistered) { nsCOMPtr webProgress = do_QueryInterface(mDocShell); @@ -676,7 +526,7 @@ void TabListener::RemoveListeners() { RemoveEventListeners(); - if (mPrefObserverRegistered || mStorageObserverRegistered) { + if (mPrefObserverRegistered) { nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) { return; @@ -686,10 +536,6 @@ void TabListener::RemoveListeners() { obs->RemoveObserver(this, kPrefInterval); mPrefObserverRegistered = false; } - if (mStorageObserverRegistered) { - obs->RemoveObserver(this, "browser:purge-sessionStorage"); - mStorageObserverRegistered = false; - } } } diff --git a/toolkit/components/sessionstore/SessionStoreListener.h b/toolkit/components/sessionstore/SessionStoreListener.h index a5f1e61d6fcc..5611734ae47b 100644 --- a/toolkit/components/sessionstore/SessionStoreListener.h +++ b/toolkit/components/sessionstore/SessionStoreListener.h @@ -19,8 +19,6 @@ class nsITimer; namespace mozilla { namespace dom { -class StorageEvent; - class ContentSessionStore { public: explicit ContentSessionStore(nsIDocShell* aDocShell); @@ -33,31 +31,6 @@ class ContentSessionStore { bool IsPrivateChanged() { return mPrivateChanged; } bool GetPrivateModeEnabled(); - // Use "mStorageStatus" to manage the status of storageChanges - bool IsStorageUpdated() { return mStorageStatus != NO_STORAGE; } - void ResetStorage() { mStorageStatus = RESET; } - /* - There are three situations we need entire session storage: - 1. OnDocumentStart: PageLoad started - 2. OnDocumentEnd: PageLoad completed - 3. receive "browser:purge-sessionStorage" event - Use SetFullStorageNeeded() to set correct "mStorageStatus" and - reset the pending individual change. - */ - void SetFullStorageNeeded(); - void ResetStorageChanges(); - // GetAndClearStorageChanges() is used for getting storageChanges. - // It clears the stored storage changes before returning. - // It will return true if it is a entire session storage. - // Otherwise, it will return false. - bool GetAndClearStorageChanges(nsTArray& aOrigins, - nsTArray& aKeys, - nsTArray& aValues); - // Using AppendSessionStorageChange() to append session storage change when - // receiving "MozSessionStorageChanged". - // Return true if there is a new storage change which is appended. - bool AppendSessionStorageChange(StorageEvent* aEvent); - void SetSHistoryChanged(); // request "collect sessionHistory" which is happened in the parent process void SetSHistoryFromParentChanged(); @@ -71,8 +44,8 @@ class ContentSessionStore { void OnDocumentStart(); void OnDocumentEnd(); bool UpdateNeeded() { - return mPrivateChanged || mDocCapChanged || IsStorageUpdated() || - mSHistoryChanged || mSHistoryChangedFromParent; + return mPrivateChanged || mDocCapChanged || mSHistoryChanged || + mSHistoryChangedFromParent; } private: @@ -82,18 +55,8 @@ class ContentSessionStore { nsCOMPtr mDocShell; bool mPrivateChanged; bool mIsPrivate; - enum { - NO_STORAGE, - RESET, - FULLSTORAGE, - STORAGECHANGE, - } mStorageStatus; bool mDocCapChanged; nsCString mDocCaps; - // mOrigins, mKeys, mValues are for sessionStorage partial changes - nsTArray mOrigins; - nsTArray mKeys; - nsTArray mValues; // mSHistoryChanged means there are history changes which are found // in the child process. The flag is set when // 1. webProgress changes to STATE_START @@ -138,8 +101,6 @@ class TabListener : public nsIDOMEventListener, void AddEventListeners(); void RemoveEventListeners(); bool UpdateSessionStore(bool aIsFlush = false, bool aIsFinal = false); - void ResetStorageChangeListener(); - void RemoveStorageChangeListener(); virtual ~TabListener(); nsCOMPtr mDocShell; @@ -148,8 +109,6 @@ class TabListener : public nsIDOMEventListener, bool mProgressListenerRegistered; bool mEventListenerRegistered; bool mPrefObserverRegistered; - bool mStorageObserverRegistered; - bool mStorageChangeListenerRegistered; // Timer used to update data nsCOMPtr mUpdatedTimer; bool mTimeoutDisabled; diff --git a/toolkit/components/sessionstore/SessionStoreUtils.cpp b/toolkit/components/sessionstore/SessionStoreUtils.cpp index b6f7b1e7e191..ff5e140989ab 100644 --- a/toolkit/components/sessionstore/SessionStoreUtils.cpp +++ b/toolkit/components/sessionstore/SessionStoreUtils.cpp @@ -24,6 +24,8 @@ #include "mozilla/dom/XPathResult.h" #include "mozilla/dom/XPathEvaluator.h" #include "mozilla/dom/XPathExpression.h" +#include "mozilla/dom/PBackgroundSessionStorageCache.h" +#include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ReverseIterator.h" #include "mozilla/UniquePtr.h" #include "nsCharSeparatedTokenizer.h" @@ -1262,105 +1264,6 @@ void SessionStoreUtils::RestoreFormData( } } -/* Read entries in the session storage data contained in a tab's history. */ -static void ReadAllEntriesFromStorage(nsPIDOMWindowOuter* aWindow, - nsTArray& aOrigins, - nsTArray& aKeys, - nsTArray& aValues) { - BrowsingContext* const browsingContext = aWindow->GetBrowsingContext(); - if (!browsingContext) { - return; - } - - Document* doc = aWindow->GetDoc(); - if (!doc) { - return; - } - - nsCOMPtr principal = doc->NodePrincipal(); - if (!principal) { - return; - } - - nsCOMPtr storagePrincipal = doc->EffectiveStoragePrincipal(); - if (!storagePrincipal) { - return; - } - - nsAutoCString origin; - nsresult rv = storagePrincipal->GetOrigin(origin); - if (NS_FAILED(rv) || aOrigins.Contains(origin)) { - // Don't read a host twice. - return; - } - - /* Completed checking for recursion and is about to read storage*/ - const RefPtr storageManager = - browsingContext->GetSessionStorageManager(); - if (!storageManager) { - return; - } - RefPtr storage; - storageManager->GetStorage(aWindow->GetCurrentInnerWindow(), principal, - storagePrincipal, false, getter_AddRefs(storage)); - if (!storage) { - return; - } - mozilla::IgnoredErrorResult result; - uint32_t len = storage->GetLength(*principal, result); - if (result.Failed() || len == 0) { - return; - } - int64_t storageUsage = storage->GetOriginQuotaUsage(); - if (storageUsage > StaticPrefs::browser_sessionstore_dom_storage_limit()) { - return; - } - - for (uint32_t i = 0; i < len; i++) { - nsString key, value; - mozilla::IgnoredErrorResult res; - storage->Key(i, key, *principal, res); - if (res.Failed()) { - continue; - } - - storage->GetItem(key, value, *principal, res); - if (res.Failed()) { - continue; - } - - aKeys.AppendElement(key); - aValues.AppendElement(value); - aOrigins.AppendElement(origin); - } -} - -/* Collect Collect session storage from current frame and all child frame */ -/* static */ -void SessionStoreUtils::CollectedSessionStorage( - BrowsingContext* aBrowsingContext, nsTArray& aOrigins, - nsTArray& aKeys, nsTArray& aValues) { - /* Collect session store from current frame */ - nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow(); - if (!window) { - return; - } - ReadAllEntriesFromStorage(window, aOrigins, aKeys, aValues); - - /* Collect session storage from all child frame */ - if (!window->GetDocShell()) { - return; - } - - // This is not going to work for fission. Bug 1572084 for tracking it. - for (BrowsingContext* child : aBrowsingContext->Children()) { - if (!child->CreatedDynamically()) { - SessionStoreUtils::CollectedSessionStorage(child, aOrigins, aKeys, - aValues); - } - } -} - /* static */ void SessionStoreUtils::RestoreSessionStorage( const GlobalObject& aGlobal, nsIDocShell* aDocShell, @@ -1804,6 +1707,83 @@ nsresult SessionStoreUtils::ConstructFormDataValues( return NS_OK; } +static nsresult ConstructSessionStorageValue( + const nsTArray& aValues, + Record& aRecord) { + auto& entries = aRecord.Entries(); + for (const auto& value : aValues) { + auto entry = entries.AppendElement(); + entry->mKey = value.key(); + entry->mValue = value.value(); + } + + return NS_OK; +} + +/* static */ +nsresult SessionStoreUtils::ConstructSessionStorageValues( + CanonicalBrowsingContext* aBrowsingContext, + const nsTArray& aValues, + Record>& aRecord) { + if (!aRecord.Entries().SetCapacity(aValues.Length(), fallible)) { + return NS_ERROR_FAILURE; + } + + // We wish to remove this step of mapping originAttributes+originKey + // to a storage principal in Bug 1711886 by consolidating the + // storage format in SessionStorageManagerBase and Session Store. + nsTHashMap storagePrincipalList; + aBrowsingContext->PreOrderWalk([&storagePrincipalList]( + BrowsingContext* aContext) { + WindowGlobalParent* windowParent = + aContext->Canonical()->GetCurrentWindowGlobal(); + if (!windowParent) { + return; + } + + nsIPrincipal* storagePrincipal = windowParent->DocumentStoragePrincipal(); + if (!storagePrincipal) { + return; + } + + const OriginAttributes& originAttributes = + storagePrincipal->OriginAttributesRef(); + nsAutoCString originAttributesSuffix; + originAttributes.CreateSuffix(originAttributesSuffix); + + nsAutoCString originKey; + storagePrincipal->GetStorageOriginKey(originKey); + + storagePrincipalList.InsertOrUpdate(originAttributesSuffix + originKey, + storagePrincipal); + }); + + for (const auto& value : aValues) { + nsIPrincipal* storagePrincipal = + storagePrincipalList.Get(value.originAttributes() + value.originKey()); + if (!storagePrincipal) { + continue; + } + + auto entry = aRecord.Entries().AppendElement(); + + if (!entry->mValue.Entries().SetCapacity( + value.defaultData().Length() + value.sessionData().Length(), + fallible)) { + return NS_ERROR_FAILURE; + } + + if (NS_FAILED(storagePrincipal->GetOrigin(entry->mKey))) { + return NS_ERROR_FAILURE; + } + + ConstructSessionStorageValue(value.defaultData(), entry->mValue); + ConstructSessionStorageValue(value.sessionData(), entry->mValue); + } + + return NS_OK; +} + /* static */ void SessionStoreUtils::ResetSessionStore( BrowsingContext* aContext) { MOZ_RELEASE_ASSERT(NATIVE_LISTENER); diff --git a/toolkit/components/sessionstore/SessionStoreUtils.h b/toolkit/components/sessionstore/SessionStoreUtils.h index 3390e0df7a12..4ba52e390197 100644 --- a/toolkit/components/sessionstore/SessionStoreUtils.h +++ b/toolkit/components/sessionstore/SessionStoreUtils.h @@ -25,6 +25,8 @@ namespace dom { class CanonicalBrowsingContext; class GlobalObject; struct SSScrollPositionDict; +class SSCacheCopy; +class SSSetItemInfo; namespace sessionstore { class DocShellRestoreState; @@ -104,11 +106,6 @@ class SessionStoreUtils { Document& aDocument, const nsString& aInnerHTML, const nsTArray& aEntries); - static void CollectedSessionStorage(BrowsingContext* aBrowsingContext, - nsTArray& aOrigins, - nsTArray& aKeys, - nsTArray& aValues); - static void RestoreSessionStorage( const GlobalObject& aGlobal, nsIDocShell* aDocShell, const Record>& aData); @@ -138,6 +135,11 @@ class SessionStoreUtils { aEntries, bool aParseSessionData = false); + static nsresult ConstructSessionStorageValues( + CanonicalBrowsingContext* aBrowsingContext, + const nsTArray& aValues, + Record>& aStorage); + static void ResetSessionStore(BrowsingContext* aContext); #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_THUNDERBIRD) || \