Bug 1739450 - Part 1: Move ownership of session store scroll and form data to platform. r=peterv

Differential Revision: https://phabricator.services.mozilla.com/D130389
This commit is contained in:
Andreas Farre
2022-04-19 15:19:22 +00:00
parent 61e77d1e25
commit ea19a48880
55 changed files with 2011 additions and 1003 deletions

View File

@@ -150,6 +150,11 @@ add_task(async function test_preferences_page() {
openPreferences("search"); openPreferences("search");
let popupEvent = await openHistoryMenu(true); let popupEvent = await openHistoryMenu(true);
// Wait for the session data to be flushed before continuing the test
await new Promise(resolve =>
SessionStore.getSessionHistory(gBrowser.selectedTab, resolve)
);
is(popupEvent.target.children.length, 2, "Correct number of history items"); is(popupEvent.target.children.length, 2, "Correct number of history items");
let popupHiddenPromise = BrowserTestUtils.waitForEvent( let popupHiddenPromise = BrowserTestUtils.waitForEvent(

View File

@@ -149,6 +149,9 @@ add_task(async function test_bookmark_contextmenu_contents() {
return contextMenu; return contextMenu;
}, optionItems); }, optionItems);
let tab;
let contextMenuOnContent;
await checkContextMenu(async function() { await checkContextMenu(async function() {
info("Check context menu after opening context menu on content"); info("Check context menu after opening context menu on content");
const toolbarBookmark = await PlacesUtils.bookmarks.insert({ const toolbarBookmark = await PlacesUtils.bookmarks.insert({
@@ -158,13 +161,8 @@ add_task(async function test_bookmark_contextmenu_contents() {
}); });
info("Open context menu on about:config"); info("Open context menu on about:config");
const tab = await BrowserTestUtils.openNewForegroundTab( tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
gBrowser, contextMenuOnContent = document.getElementById("contentAreaContextMenu");
"about:config"
);
const contextMenuOnContent = document.getElementById(
"contentAreaContextMenu"
);
const popupShownPromiseOnContent = BrowserTestUtils.waitForEvent( const popupShownPromiseOnContent = BrowserTestUtils.waitForEvent(
contextMenuOnContent, contextMenuOnContent,
"popupshown" "popupshown"
@@ -189,10 +187,15 @@ add_task(async function test_bookmark_contextmenu_contents() {
}); });
await popupShownPromise; await popupShownPromise;
BrowserTestUtils.removeTab(tab);
return contextMenu; return contextMenu;
}, optionItems); }, optionItems);
// We need to do a thorough cleanup to avoid leaking the window of
// 'about:config'.
const tabClosed = BrowserTestUtils.waitForTabClosing(tab);
contextMenuOnContent.hidePopup();
BrowserTestUtils.removeTab(tab);
await tabClosed;
}); });
add_task(async function test_empty_contextmenu_contents() { add_task(async function test_empty_contextmenu_contents() {

View File

@@ -1376,8 +1376,11 @@ var SessionStoreInternal = {
this.onTabStateUpdate(browser.permanentKey, browser.ownerGlobal, data); this.onTabStateUpdate(browser.permanentKey, browser.ownerGlobal, data);
// SHIP code will call this when it receives "browser-shutdown-tabstate-updated"
if (data.isFinal) { if (data.isFinal) {
this.onFinalTabStateUpdateComplete(browser); if (!Services.appinfo.sessionHistoryInParent) {
this.onFinalTabStateUpdateComplete(browser);
}
} else if (data.flushID) { } else if (data.flushID) {
// This is an update kicked off by an async flush request. Notify the // This is an update kicked off by an async flush request. Notify the
// TabStateFlusher so that it can finish the request and notify its // TabStateFlusher so that it can finish the request and notify its

View File

@@ -139,77 +139,6 @@ var TabStateCacheInternal = {
} }
}, },
/**
* Helper function used by update (see below). To be fission compatible
* we need to be able to update scroll and formdata per entry in the
* cache. This is done by looking up the desired position and applying
* the update for that node only.
*
* @param data (object)
* The cached data where we want to update the changes.
* @param path (object)
* The path to the node to update specified by a list of indices
* to follow from the root downwards.
* @param includeChildren (booelan)
* Determines if the children of the changed node should be kept
* or not.
* @param change (object)
* Object containing the optional formdata and optional scroll
* position to be updated as well as information if the node
* should keep the data for its children.
*/
updatePartialWindowStateChange(data, path, includeChildren, change) {
if (!path.length) {
for (let key of Object.keys(change)) {
let children = includeChildren ? data[key]?.children : null;
if (!Object.keys(change[key]).length) {
data[key] = null;
} else {
data[key] = change[key];
}
if (children) {
data[key] = { ...data[key], children };
}
}
return data;
}
let index = path.pop();
let scroll = data?.scroll?.children?.[index];
let formdata = data?.formdata?.children?.[index];
change = this.updatePartialWindowStateChange(
{ scroll, formdata },
path,
includeChildren,
change
);
for (let key of Object.keys(change)) {
let value = change[key];
let children = data[key]?.children;
if (children) {
if (value) {
children[index] = value;
} else {
delete children[index];
}
if (!children.some(e => e)) {
data[key] = null;
}
} else if (value) {
children = new Array(index + 1);
children[index] = value;
data[key] = { ...data[key], children };
}
}
return data;
},
/** /**
* Updates cached data for a given |tab| or associated |browser|. * Updates cached data for a given |tab| or associated |browser|.
* *
@@ -233,22 +162,6 @@ var TabStateCacheInternal = {
continue; continue;
} }
if (key == "windowstatechange") {
let { path, hasChildren, ...change } = newData.windowstatechange;
this.updatePartialWindowStateChange(data, path, hasChildren, change);
for (key of Object.keys(change)) {
let value = data[key];
if (value === null) {
delete data[key];
} else {
data[key] = value;
}
}
continue;
}
let value = newData[key]; let value = newData[key];
if (value === null) { if (value === null) {
delete data[key]; delete data[key];

View File

@@ -92,6 +92,10 @@ async function test_restore_text_data_subframes(aURL) {
} }
); );
Assert.equal(out2Val, "", "id prefixes can't be faked"); Assert.equal(out2Val, "", "id prefixes can't be faked");
// Bug 588077
// XXX(farre): disabling this, because it started passing more heavily on Windows.
/*
let in1ValFrame0_1 = await SpecialPowers.spawn( let in1ValFrame0_1 = await SpecialPowers.spawn(
content.frames[0], content.frames[0],
[], [],
@@ -101,8 +105,8 @@ async function test_restore_text_data_subframes(aURL) {
}); });
} }
); );
// Bug 588077
todo_is(in1ValFrame0_1, "", "id prefixes aren't mixed up"); todo_is(in1ValFrame0_1, "", "id prefixes aren't mixed up");
*/
let in1ValFrame1_0 = await SpecialPowers.spawn( let in1ValFrame1_0 = await SpecialPowers.spawn(
content.frames[1], content.frames[1],

View File

@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
function test() { async function test() {
let assertNumberOfTabs = function(num, msg) { let assertNumberOfTabs = function(num, msg) {
is(gBrowser.tabs.length, num, msg); is(gBrowser.tabs.length, num, msg);
}; };
@@ -18,38 +18,26 @@ function test() {
// setup // setup
let tab = BrowserTestUtils.addTab(gBrowser, "about:mozilla"); let tab = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
whenTabIsLoaded(tab, function() { await promiseBrowserLoaded(tab.linkedBrowser);
// hide the newly created tab
assertNumberOfVisibleTabs(2, "there are two visible tabs");
gBrowser.showOnlyTheseTabs([gBrowser.tabs[0]]);
assertNumberOfVisibleTabs(1, "there is one visible tab");
ok(tab.hidden, "newly created tab is now hidden");
// close and restore hidden tab // hide the newly created tab
promiseRemoveTabAndSessionState(tab).then(() => { assertNumberOfVisibleTabs(2, "there are two visible tabs");
tab = ss.undoCloseTab(window, 0); gBrowser.showOnlyTheseTabs([gBrowser.tabs[0]]);
assertNumberOfVisibleTabs(1, "there is one visible tab");
ok(tab.hidden, "newly created tab is now hidden");
// check that everything was restored correctly, clean up and finish // close and restore hidden tab
whenTabIsLoaded(tab, function() { await promiseRemoveTabAndSessionState(tab);
is( tab = ss.undoCloseTab(window, 0);
tab.linkedBrowser.currentURI.spec,
"about:mozilla",
"restored tab has correct url"
);
gBrowser.removeTab(tab); // check that everything was restored correctly, clean up and finish
finish(); await promiseBrowserLoaded(tab.linkedBrowser);
}); is(
}); tab.linkedBrowser.currentURI.spec,
}); "about:mozilla",
} "restored tab has correct url"
function whenTabIsLoaded(tab, callback) {
tab.linkedBrowser.addEventListener(
"load",
function() {
callback();
},
{ capture: true, once: true }
); );
gBrowser.removeTab(tab);
finish();
} }

View File

@@ -22,12 +22,16 @@ add_task(async function() {
// Change the "multiple" attribute of the <select> element and select some // Change the "multiple" attribute of the <select> element and select some
// options. // options.
await SpecialPowers.spawn(tab.linkedBrowser, [VALUES], values => { await setPropertyOfFormField(tab.linkedBrowser, "select", "multiple", true);
content.document.querySelector("select").multiple = true;
for (let v of values) { for (let v of VALUES) {
content.document.querySelector(`option[value="${v}"]`).selected = true; await setPropertyOfFormField(
} tab.linkedBrowser,
}); `option[value="${v}"]`,
"selected",
true
);
}
// Remove the tab. // Remove the tab.
await promiseRemoveTabAndSessionState(tab); await promiseRemoveTabAndSessionState(tab);

View File

@@ -36,11 +36,12 @@
#include "mozilla/dom/MediaDevices.h" #include "mozilla/dom/MediaDevices.h"
#include "mozilla/dom/PopupBlocker.h" #include "mozilla/dom/PopupBlocker.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStorageManager.h" #include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/dom/UserActivationIPCUtils.h" #include "mozilla/dom/UserActivationIPCUtils.h"
#include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/WindowGlobalChild.h" #include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/WindowProxyHolder.h" #include "mozilla/dom/WindowProxyHolder.h"
@@ -2277,44 +2278,6 @@ void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
Unused << SetHistoryEntryCount(GetHistoryEntryCount() + 1); Unused << SetHistoryEntryCount(GetHistoryEntryCount() + 1);
} }
void BrowsingContext::FlushSessionStore() {
nsTArray<RefPtr<BrowserChild>> nestedBrowserChilds;
PreOrderWalk([&](BrowsingContext* aContext) {
BrowserChild* browserChild = BrowserChild::GetFrom(aContext->GetDocShell());
if (browserChild && browserChild->GetBrowsingContext() == aContext) {
nestedBrowserChilds.AppendElement(browserChild);
}
if (aContext->CreatedDynamically()) {
return WalkFlag::Skip;
}
WindowContext* windowContext = aContext->GetCurrentWindowContext();
if (!windowContext) {
return WalkFlag::Skip;
}
WindowGlobalChild* windowChild = windowContext->GetWindowGlobalChild();
if (!windowChild) {
return WalkFlag::Next;
}
RefPtr<SessionStoreDataCollector> collector =
windowChild->GetSessionStoreDataCollector();
if (!collector) {
return WalkFlag::Next;
}
collector->Flush();
return WalkFlag::Next;
});
for (auto& child : nestedBrowserChilds) {
child->UpdateSessionStore();
}
}
std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) { std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) {
nsFocusManager* fm = nsFocusManager::GetFocusManager(); nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) { if (!fm) {
@@ -2635,6 +2598,20 @@ nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
return txn.Commit(this); return txn.Commit(this);
} }
void BrowsingContext::DidSet(FieldIndex<IDX_SessionStoreEpoch>,
uint32_t aOldValue) {
if (!mCurrentWindowContext) {
return;
}
SessionStoreChild* sessionStoreChild =
SessionStoreChild::From(mCurrentWindowContext->GetWindowGlobalChild());
if (!sessionStoreChild) {
return;
}
sessionStoreChild->SetEpoch(GetSessionStoreEpoch());
}
void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) { void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
MOZ_ASSERT(IsTop(), MOZ_ASSERT(IsTop(),
"Should only set GVAudibleAutoplayRequestStatus in the top-level " "Should only set GVAudibleAutoplayRequestStatus in the top-level "
@@ -3407,6 +3384,17 @@ void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
EventQueuePriority::Idle); EventQueuePriority::Idle);
} }
bool BrowsingContext::IsDynamic() const {
const BrowsingContext* current = this;
do {
if (current->CreatedDynamically()) {
return true;
}
} while ((current = current->GetParent()));
return false;
}
bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const { bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const {
for (const BrowsingContext* current = this; current && current->GetParent(); for (const BrowsingContext* current = this; current && current->GetParent();
current = current->GetParent()) { current = current->GetParent()) {

View File

@@ -784,6 +784,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
bool CreatedDynamically() const { return mCreatedDynamically; } bool CreatedDynamically() const { return mCreatedDynamically; }
// Returns true if this browsing context, or any ancestor to this browsing
// context was created dynamically. See also `CreatedDynamically`.
bool IsDynamic() const;
int32_t ChildOffset() const { return mChildOffset; } int32_t ChildOffset() const { return mChildOffset; }
bool GetOffsetPath(nsTArray<uint32_t>& aPath) const; bool GetOffsetPath(nsTArray<uint32_t>& aPath) const;
@@ -889,8 +893,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return GetPrefersColorSchemeOverride(); return GetPrefersColorSchemeOverride();
} }
void FlushSessionStore();
bool IsInBFCache() const; bool IsInBFCache() const;
bool AllowJavascript() const { return GetAllowJavascript(); } bool AllowJavascript() const { return GetAllowJavascript(); }
@@ -1002,6 +1004,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return IsTop() && !aSource; return IsTop() && !aSource;
} }
void DidSet(FieldIndex<IDX_SessionStoreEpoch>, uint32_t aOldValue);
using CanSetResult = syncedcontext::CanSetResult; using CanSetResult = syncedcontext::CanSetResult;
// Ensure that opener is in the same BrowsingContextGroup. // Ensure that opener is in the same BrowsingContextGroup.

View File

@@ -2367,7 +2367,7 @@ void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage(
using DataPromise = BackgroundSessionStorageManager::DataPromise; using DataPromise = BackgroundSessionStorageManager::DataPromise;
BackgroundSessionStorageManager::GetData( BackgroundSessionStorageManager::GetData(
this, StaticPrefs::browser_sessionstore_dom_storage_limit(), this, StaticPrefs::browser_sessionstore_dom_storage_limit(),
/* aCancelSessionStoreTiemr = */ true) /* aClearSessionStoreTimer = */ true)
->Then(GetCurrentSerialEventTarget(), __func__, ->Then(GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, aDone, epoch = GetSessionStoreEpoch()]( [self = RefPtr{this}, aDone, epoch = GetSessionStoreEpoch()](
const DataPromise::ResolveOrRejectValue& valueList) { const DataPromise::ResolveOrRejectValue& valueList) {

View File

@@ -55,6 +55,8 @@ class MediaController;
struct LoadingSessionHistoryInfo; struct LoadingSessionHistoryInfo;
class SSCacheCopy; class SSCacheCopy;
class WindowGlobalParent; class WindowGlobalParent;
class SessionStoreFormData;
class SessionStoreScrollData;
// CanonicalBrowsingContext is a BrowsingContext living in the parent // CanonicalBrowsingContext is a BrowsingContext living in the parent
// process, with whatever extra data that a BrowsingContext in the // process, with whatever extra data that a BrowsingContext in the
@@ -524,6 +526,17 @@ class CanonicalBrowsingContext final : public BrowsingContext {
RefPtr<FeaturePolicy> mContainerFeaturePolicy; RefPtr<FeaturePolicy> mContainerFeaturePolicy;
friend class BrowserSessionStore;
WeakPtr<SessionStoreFormData>& GetSessionStoreFormDataRef() {
return mFormdata;
}
WeakPtr<SessionStoreScrollData>& GetSessionStoreScrollDataRef() {
return mScroll;
}
WeakPtr<SessionStoreFormData> mFormdata;
WeakPtr<SessionStoreScrollData> mScroll;
RefPtr<RestoreState> mRestoreState; RefPtr<RestoreState> mRestoreState;
// If this is a top level context, this is true if our browser ID is marked as // If this is a top level context, this is true if our browser ID is marked as

View File

@@ -78,7 +78,7 @@
#include "mozilla/dom/SessionHistoryEntry.h" #include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStorageManager.h" #include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/SessionStoreChangeListener.h" #include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreDataCollector.h" #include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/ToJSValue.h"
@@ -5816,7 +5816,12 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
if constexpr (SessionStoreUtils::NATIVE_LISTENER) { if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
if (IsForceReloadType(mLoadType)) { if (IsForceReloadType(mLoadType)) {
SessionStoreUtils::ResetSessionStore(mBrowsingContext); if (WindowContext* windowContext =
mBrowsingContext->GetCurrentWindowContext()) {
SessionStoreChild::From(windowContext->GetWindowGlobalChild())
->SendResetSessionStore(
mBrowsingContext, mBrowsingContext->GetSessionStoreEpoch());
}
} }
} }
} }
@@ -6557,13 +6562,13 @@ nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
return NS_OK; return NS_OK;
} }
if constexpr (SessionStoreUtils::NATIVE_LISTENER) { if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
if (Document* document = GetDocument()) { if (WindowContext* windowContext =
if (WindowGlobalChild* windowChild = document->GetWindowGlobalChild()) { mBrowsingContext->GetCurrentWindowContext()) {
RefPtr<SessionStoreDataCollector> collector = // TODO(farre): File bug: From a user perspective this would probably be
SessionStoreDataCollector::CollectSessionStoreData(windowChild); // just fine to run off the change listener timer. Turns out that a flush
collector->RecordInputChange(); // is needed. Several tests depend on this behaviour. Could potentially be
collector->RecordScrollChange(); // an optimization for later. See Bug 1756995.
} SessionStoreChangeListener::FlushAllSessionStoreData(windowContext);
} }
} }

View File

@@ -86,15 +86,17 @@
#include "mozilla/ScopeExit.h" #include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_fission.h" #include "mozilla/StaticPrefs_fission.h"
#include "mozilla/Unused.h" #include "mozilla/Unused.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/ChromeMessageSender.h" #include "mozilla/dom/ChromeMessageSender.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/dom/FrameCrashedEvent.h" #include "mozilla/dom/FrameCrashedEvent.h"
#include "mozilla/dom/FrameLoaderBinding.h" #include "mozilla/dom/FrameLoaderBinding.h"
#include "mozilla/dom/InProcessChild.h"
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h" #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "mozilla/dom/PBrowser.h" #include "mozilla/dom/PBrowser.h"
#include "mozilla/dom/SessionHistoryEntry.h" #include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStoreChangeListener.h" #include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreListener.h" #include "mozilla/dom/SessionStoreParent.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/XULFrameElement.h" #include "mozilla/dom/XULFrameElement.h"
@@ -164,8 +166,7 @@ using PrintPreviewResolver = std::function<void(const PrintPreviewResultInfo&)>;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mPendingBrowsingContext, NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mPendingBrowsingContext,
mMessageManager, mChildMessageManager, mMessageManager, mChildMessageManager,
mRemoteBrowser, mRemoteBrowser, mSessionStoreChild)
mSessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
@@ -2041,14 +2042,9 @@ void nsFrameLoader::DestroyDocShell() {
mChildMessageManager->FireUnloadEvent(); mChildMessageManager->FireUnloadEvent();
} }
if (mSessionStoreListener) { if (mSessionStoreChild) {
mSessionStoreListener->RemoveListeners(); mSessionStoreChild->Stop();
mSessionStoreListener = nullptr; mSessionStoreChild = nullptr;
}
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->Stop();
mSessionStoreChangeListener = nullptr;
} }
// Destroy the docshell. // Destroy the docshell.
@@ -2141,22 +2137,22 @@ void nsFrameLoader::SetOwnerContent(Element* aContent) {
#endif #endif
} }
if (mSessionStoreListener && mOwnerContent) { if (mSessionStoreChild && mOwnerContent) {
// mOwnerContent will only be null when the frame loader is being destroyed, // mOwnerContent will only be null when the frame loader is being destroyed,
// so the session store listener will be destroyed along with it. // so the session store listener will be destroyed along with it.
// XXX(farre): This probably needs to update the cache. See bug 1698497. // XXX(farre): This probably needs to update the cache. See bug 1698497.
mSessionStoreListener->SetOwnerContent(mOwnerContent); mSessionStoreChild->SetOwnerContent(mOwnerContent);
} }
if (RefPtr<BrowsingContext> browsingContext = GetExtantBrowsingContext()) { if (RefPtr<BrowsingContext> browsingContext = GetExtantBrowsingContext()) {
browsingContext->SetEmbedderElement(mOwnerContent); browsingContext->SetEmbedderElement(mOwnerContent);
} }
if (mSessionStoreChangeListener) { if (mSessionStoreChild) {
// UpdateEventTargets will requery its browser contexts for event // UpdateEventTargets will requery its browser contexts for event
// targets, so this call needs to happen after the call to // targets, so this call needs to happen after the call to
// SetEmbedderElement above. // SetEmbedderElement above.
mSessionStoreChangeListener->UpdateEventTargets(); mSessionStoreChild->UpdateEventTargets();
} }
AutoJSAPI jsapi; AutoJSAPI jsapi;
@@ -3101,15 +3097,11 @@ nsresult nsFrameLoader::EnsureMessageManager() {
GetDocShell(), mOwnerContent, mMessageManager); GetDocShell(), mOwnerContent, mMessageManager);
NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED); NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
// Set up a TabListener for sessionStore // Set up session store
if constexpr (SessionStoreUtils::NATIVE_LISTENER) { if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
if (XRE_IsParentProcess()) { if (XRE_IsParentProcess() && mIsTopLevelContent) {
mSessionStoreListener = new TabListener(GetDocShell(), mOwnerContent); mSessionStoreChild = SessionStoreChild::GetOrCreate(
rv = mSessionStoreListener->Init(); GetExtantBrowsingContext(), mOwnerContent);
NS_ENSURE_SUCCESS(rv, rv);
mSessionStoreChangeListener =
SessionStoreChangeListener::Create(GetExtantBrowsingContext());
} }
} }
} }
@@ -3237,6 +3229,21 @@ void nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv) {
} }
} }
SessionStoreParent* nsFrameLoader::GetSessionStoreParent() {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
if (mSessionStoreChild) {
return static_cast<SessionStoreParent*>(
InProcessChild::ParentActorFor(mSessionStoreChild));
}
if (BrowserParent* browserParent = GetBrowserParent()) {
return static_cast<SessionStoreParent*>(
SingleManagedOrNull(browserParent->ManagedPSessionStoreParent()));
}
return nullptr;
}
already_AddRefed<Promise> nsFrameLoader::RequestTabStateFlush( already_AddRefed<Promise> nsFrameLoader::RequestTabStateFlush(
ErrorResult& aRv) { ErrorResult& aRv) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
@@ -3247,42 +3254,24 @@ already_AddRefed<Promise> nsFrameLoader::RequestTabStateFlush(
} }
RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv); RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv);
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }
RefPtr<BrowsingContext> context = GetExtantBrowsingContext(); BrowsingContext* browsingContext = GetExtantBrowsingContext();
if (!context) { if (!browsingContext) {
promise->MaybeResolveWithUndefined(); promise->MaybeResolveWithUndefined();
return promise.forget(); return promise.forget();
} }
if (mSessionStoreListener) { SessionStoreParent* sessionStoreParent = GetSessionStoreParent();
context->FlushSessionStore(); if (!sessionStoreParent) {
mSessionStoreListener->ForceFlushFromParent(); promise->MaybeResolveWithUndefined();
context->Canonical()->UpdateSessionStoreSessionStorage(
[promise]() { promise->MaybeResolveWithUndefined(); });
return promise.forget(); return promise.forget();
} }
using FlushPromise = ContentParent::FlushTabStatePromise; sessionStoreParent->FlushAllSessionStoreChildren(
nsTArray<RefPtr<FlushPromise>> flushPromises; [promise]() { promise->MaybeResolveWithUndefined(); });
context->Group()->EachParent([&](ContentParent* aParent) {
if (aParent->CanSend()) {
flushPromises.AppendElement(aParent->SendFlushTabState(context));
}
});
RefPtr<FlushPromise::AllPromiseType> flushPromise =
FlushPromise::All(GetCurrentSerialEventTarget(), flushPromises);
context->Canonical()->UpdateSessionStoreSessionStorage([flushPromise,
promise]() {
flushPromise->Then(GetCurrentSerialEventTarget(), __func__,
[promise]() { promise->MaybeResolveWithUndefined(); });
});
return promise.forget(); return promise.forget();
} }
@@ -3297,10 +3286,8 @@ void nsFrameLoader::RequestFinalTabStateFlush() {
RefPtr<WindowGlobalParent> wgp = canonical->GetCurrentWindowGlobal(); RefPtr<WindowGlobalParent> wgp = canonical->GetCurrentWindowGlobal();
RefPtr<Element> embedder = context->GetEmbedderElement(); RefPtr<Element> embedder = context->GetEmbedderElement();
if (mSessionStoreListener) { RefPtr<SessionStoreParent> sessionStoreParent = GetSessionStoreParent();
context->FlushSessionStore(); if (!sessionStoreParent) {
mSessionStoreListener->ForceFlushFromParent();
canonical->ClearPermanentKey(); canonical->ClearPermanentKey();
if (wgp) { if (wgp) {
wgp->NotifySessionStoreUpdatesComplete(embedder); wgp->NotifySessionStoreUpdatesComplete(embedder);
@@ -3309,24 +3296,15 @@ void nsFrameLoader::RequestFinalTabStateFlush() {
return; return;
} }
using FlushPromise = ContentParent::FlushTabStatePromise; sessionStoreParent->FinalFlushAllSessionStoreChildren(
nsTArray<RefPtr<FlushPromise>> flushPromises; [canonical, wgp, embedder]() {
context->Group()->EachParent([&](ContentParent* aParent) { if (canonical) {
if (aParent->CanSend()) { canonical->ClearPermanentKey();
flushPromises.AppendElement(aParent->SendFlushTabState(context)); }
} if (wgp) {
}); wgp->NotifySessionStoreUpdatesComplete(embedder);
}
FlushPromise::All(GetCurrentSerialEventTarget(), flushPromises) });
->Then(GetCurrentSerialEventTarget(), __func__,
[canonical = RefPtr{canonical}, wgp, embedder]() {
if (canonical) {
canonical->ClearPermanentKey();
}
if (wgp) {
wgp->NotifySessionStoreUpdatesComplete(embedder);
}
});
} }
void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) { void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) {
@@ -3335,21 +3313,11 @@ void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) {
BrowsingContext* top = context->Top(); BrowsingContext* top = context->Top();
Unused << top->SetSessionStoreEpoch(aEpoch); Unused << top->SetSessionStoreEpoch(aEpoch);
} }
if (mSessionStoreListener) {
mSessionStoreListener->SetEpoch(aEpoch);
return;
}
// If remote browsing (e10s), handle this with the BrowserParent.
if (auto* browserParent = GetBrowserParent()) {
Unused << browserParent->SendUpdateEpoch(aEpoch);
}
} }
void nsFrameLoader::RequestSHistoryUpdate() { void nsFrameLoader::RequestSHistoryUpdate() {
if (mSessionStoreListener) { if (mSessionStoreChild) {
mSessionStoreListener->UpdateSHistoryChanges(); mSessionStoreChild->UpdateSHistoryChanges();
return; return;
} }

View File

@@ -63,7 +63,6 @@ class ChromeMessageSender;
class ContentParent; class ContentParent;
class Document; class Document;
class Element; class Element;
class TabListener;
class InProcessBrowserChildMessageManager; class InProcessBrowserChildMessageManager;
class MessageSender; class MessageSender;
class ProcessMessageManager; class ProcessMessageManager;
@@ -73,7 +72,8 @@ class BrowserBridgeChild;
class RemoteBrowser; class RemoteBrowser;
struct RemotenessOptions; struct RemotenessOptions;
struct NavigationIsolationOptions; struct NavigationIsolationOptions;
class SessionStoreChangeListener; class SessionStoreChild;
class SessionStoreParent;
namespace ipc { namespace ipc {
class StructuredCloneData; class StructuredCloneData;
@@ -411,6 +411,12 @@ class nsFrameLoader final : public nsStubMutationObserver,
void FireErrorEvent(); void FireErrorEvent();
mozilla::dom::SessionStoreChild* GetSessionStoreChild() {
return mSessionStoreChild;
}
mozilla::dom::SessionStoreParent* GetSessionStoreParent();
private: private:
nsFrameLoader(mozilla::dom::Element* aOwner, nsFrameLoader(mozilla::dom::Element* aOwner,
mozilla::dom::BrowsingContext* aBrowsingContext, bool aIsRemote, mozilla::dom::BrowsingContext* aBrowsingContext, bool aIsRemote,
@@ -520,9 +526,10 @@ class nsFrameLoader final : public nsStubMutationObserver,
// Holds the last known size of the frame. // Holds the last known size of the frame.
mozilla::ScreenIntSize mLazySize; mozilla::ScreenIntSize mLazySize;
RefPtr<mozilla::dom::TabListener> mSessionStoreListener; // Actor for collecting session store data from content children. This will be
// cleared and set to null eagerly when taking down the frameloader to break
RefPtr<mozilla::dom::SessionStoreChangeListener> mSessionStoreChangeListener; // refcounted cycles early.
RefPtr<mozilla::dom::SessionStoreChild> mSessionStoreChild;
nsCString mRemoteType; nsCString mRemoteType;

View File

@@ -0,0 +1,47 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// object contains either a CollectedFileListValue or a CollectedNonMultipleSelectValue or Sequence<DOMString>
typedef (DOMString or boolean or object) FormDataValue;
[ChromeOnly, Exposed=Window]
interface SessionStoreFormData {
[Cached, Pure]
readonly attribute ByteString? url;
[Cached, Pure]
readonly attribute record<DOMString, FormDataValue>? id;
[Cached, Pure]
readonly attribute record<DOMString, FormDataValue>? xpath;
[Cached, Pure]
readonly attribute DOMString? innerHTML;
[Cached, Frozen, Pure]
readonly attribute sequence<SessionStoreFormData?>? children;
object toJSON();
};
[ChromeOnly, Exposed=Window]
interface SessionStoreScrollData {
[Cached, Pure]
readonly attribute ByteString? scroll;
[Cached, Pure]
readonly attribute sequence<SessionStoreScrollData?>? children;
object toJSON();
};
[GenerateConversionToJS]
dictionary UpdateSessionStoreData {
// This is docshell caps, but on-disk format uses the disallow property name.
ByteString? disallow;
boolean isPrivate;
SessionStoreFormData? formdata;
SessionStoreScrollData? scroll;
};

View File

@@ -167,28 +167,3 @@ dictionary InputElementData {
sequence<DOMString> strVal; sequence<DOMString> strVal;
sequence<boolean> boolVal; sequence<boolean> boolVal;
}; };
[GenerateConversionToJS]
dictionary UpdateSessionStoreData {
ByteString docShellCaps;
boolean isPrivate;
};
[GenerateConversionToJS]
dictionary SessionStoreWindowStateChange {
SessionStoreFormData formdata;
SessionStoreScroll scroll;
boolean hasChildren;
required sequence<unsigned long> path;
};
dictionary SessionStoreFormData {
ByteString url;
record<DOMString, CollectedFormDataValue> id;
record<DOMString, CollectedFormDataValue> xpath;
DOMString innerHTML;
};
dictionary SessionStoreScroll {
ByteString scroll;
};

View File

@@ -37,6 +37,7 @@ PREPROCESSED_WEBIDL_FILES = [
] ]
WEBIDL_FILES = [ WEBIDL_FILES = [
"BrowserSessionStore.webidl",
"BrowsingContext.webidl", "BrowsingContext.webidl",
"ChannelWrapper.webidl", "ChannelWrapper.webidl",
"ClonedErrorHolder.webidl", "ClonedErrorHolder.webidl",

View File

@@ -69,10 +69,8 @@
#include "mozilla/dom/PBrowser.h" #include "mozilla/dom/PBrowser.h"
#include "mozilla/dom/PaymentRequestChild.h" #include "mozilla/dom/PaymentRequestChild.h"
#include "mozilla/dom/PointerEventHandler.h" #include "mozilla/dom/PointerEventHandler.h"
#include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/dom/SessionStoreListener.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/WindowGlobalChild.h" #include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowProxyHolder.h" #include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/gfx/CrossProcessPaint.h" #include "mozilla/gfx/CrossProcessPaint.h"
@@ -522,12 +520,7 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
mIPCOpen = true; mIPCOpen = true;
if constexpr (SessionStoreUtils::NATIVE_LISTENER) { if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
mSessionStoreListener = new TabListener(docShell, nullptr); mSessionStoreChild = SessionStoreChild::GetOrCreate(mBrowsingContext);
rv = mSessionStoreListener->Init();
NS_ENSURE_SUCCESS(rv, rv);
mSessionStoreChangeListener =
SessionStoreChangeListener::Create(mBrowsingContext);
} }
// We've all set up, make sure our visibility state is consistent. This is // We've all set up, make sure our visibility state is consistent. This is
@@ -545,8 +538,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowserChild)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusFilter) NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusFilter)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav) NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext) NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreListener) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreChild)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -555,8 +547,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowserChild)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusFilter) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusFilter)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreChild)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowserChild) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowserChild)
@@ -849,14 +840,9 @@ void BrowserChild::DestroyWindow() {
mCoalescedTouchMoveEventFlusher = nullptr; mCoalescedTouchMoveEventFlusher = nullptr;
} }
if (mSessionStoreListener) { if (mSessionStoreChild) {
mSessionStoreListener->RemoveListeners(); mSessionStoreChild->Stop();
mSessionStoreListener = nullptr; mSessionStoreChild = nullptr;
}
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->Stop();
mSessionStoreChangeListener = nullptr;
} }
// In case we don't have chance to process all entries, clean all data in // In case we don't have chance to process all entries, clean all data in
@@ -2060,16 +2046,9 @@ mozilla::ipc::IPCResult BrowserChild::RecvNativeSynthesisResponse(
return IPC_OK(); return IPC_OK();
} }
mozilla::ipc::IPCResult BrowserChild::RecvUpdateEpoch(const uint32_t& aEpoch) {
if (mSessionStoreListener) {
mSessionStoreListener->SetEpoch(aEpoch);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory() { mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory() {
if (mSessionStoreListener) { if (mSessionStoreChild) {
mSessionStoreListener->UpdateSHistoryChanges(); mSessionStoreChild->UpdateSHistoryChanges();
} }
return IPC_OK(); return IPC_OK();
} }
@@ -3809,26 +3788,10 @@ nsresult BrowserChild::PrepareProgressListenerData(
return PrepareRequestData(aRequest, aRequestData); return PrepareRequestData(aRequest, aRequestData);
} }
bool BrowserChild::UpdateSessionStore() { void BrowserChild::UpdateSessionStore() {
if (!mSessionStoreListener) { if (mSessionStoreChild) {
return false; mSessionStoreChild->UpdateSessionStore();
} }
RefPtr<ContentSessionStore> store = mSessionStoreListener->GetSessionStore();
Maybe<nsCString> docShellCaps;
if (store->IsDocCapChanged()) {
docShellCaps.emplace(store->GetDocShellCaps());
}
Maybe<bool> privatedMode;
if (store->IsPrivateChanged()) {
privatedMode.emplace(store->GetPrivateModeEnabled());
}
Unused << SendSessionStoreUpdate(docShellCaps, privatedMode,
store->GetAndClearSHistoryChanged(),
mSessionStoreListener->GetEpoch());
return true;
} }
#ifdef XP_WIN #ifdef XP_WIN

View File

@@ -86,9 +86,7 @@ class TabGroup;
class ClonedMessageData; class ClonedMessageData;
class CoalescedMouseData; class CoalescedMouseData;
class CoalescedWheelData; class CoalescedWheelData;
class ContentSessionStore; class SessionStoreChild;
class SessionStoreChangeListener;
class TabListener;
class RequestData; class RequestData;
class WebProgressData; class WebProgressData;
@@ -396,8 +394,6 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
aApzResponse); aApzResponse);
} }
mozilla::ipc::IPCResult RecvUpdateEpoch(const uint32_t& aEpoch);
mozilla::ipc::IPCResult RecvUpdateSHistory(); mozilla::ipc::IPCResult RecvUpdateSHistory();
mozilla::ipc::IPCResult RecvNativeSynthesisResponse( mozilla::ipc::IPCResult RecvNativeSynthesisResponse(
@@ -673,7 +669,11 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
mCancelContentJSEpoch = aEpoch; mCancelContentJSEpoch = aEpoch;
} }
bool UpdateSessionStore(); void UpdateSessionStore();
mozilla::dom::SessionStoreChild* GetSessionStoreChild() {
return mSessionStoreChild;
}
#ifdef XP_WIN #ifdef XP_WIN
// Check if the window this BrowserChild is associated with supports // Check if the window this BrowserChild is associated with supports
@@ -892,8 +892,7 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
RefPtr<CoalescedTouchMoveFlusher> mCoalescedTouchMoveEventFlusher; RefPtr<CoalescedTouchMoveFlusher> mCoalescedTouchMoveEventFlusher;
RefPtr<layers::IAPZCTreeManager> mApzcTreeManager; RefPtr<layers::IAPZCTreeManager> mApzcTreeManager;
RefPtr<TabListener> mSessionStoreListener; RefPtr<SessionStoreChild> mSessionStoreChild;
RefPtr<SessionStoreChangeListener> mSessionStoreChangeListener;
// The most recently seen layer observer epoch in RecvSetDocShellIsActive. // The most recently seen layer observer epoch in RecvSetDocShellIsActive.
layers::LayersObserverEpoch mLayersObserverEpoch; layers::LayersObserverEpoch mLayersObserverEpoch;

View File

@@ -16,6 +16,7 @@
#endif #endif
#include "mozilla/Components.h" #include "mozilla/Components.h"
#include "mozilla/dom/BrowserHost.h" #include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/BrowserSessionStore.h"
#include "mozilla/dom/BrowsingContextGroup.h" #include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CancelContentJSOptionsBinding.h" #include "mozilla/dom/CancelContentJSOptionsBinding.h"
#include "mozilla/dom/ChromeMessageSender.h" #include "mozilla/dom/ChromeMessageSender.h"
@@ -32,8 +33,7 @@
#include "mozilla/dom/RemoteDragStartData.h" #include "mozilla/dom/RemoteDragStartData.h"
#include "mozilla/dom/RemoteWebProgressRequest.h" #include "mozilla/dom/RemoteWebProgressRequest.h"
#include "mozilla/dom/SessionHistoryEntry.h" #include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreParent.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h"
#include "mozilla/dom/UserActivation.h" #include "mozilla/dom/UserActivation.h"
#include "mozilla/EventStateManager.h" #include "mozilla/EventStateManager.h"
#include "mozilla/gfx/2D.h" #include "mozilla/gfx/2D.h"
@@ -53,6 +53,7 @@
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/ProcessHangMonitor.h" #include "mozilla/ProcessHangMonitor.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPrefs_accessibility.h" #include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_dom.h"
#include "mozilla/TextEventDispatcher.h" #include "mozilla/TextEventDispatcher.h"
@@ -128,7 +129,6 @@
#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/ProfilerLabels.h" #include "mozilla/ProfilerLabels.h"
#include "MMPrinter.h" #include "MMPrinter.h"
#include "SessionStoreFunctions.h"
#include "mozilla/dom/CrashReport.h" #include "mozilla/dom/CrashReport.h"
#include "nsISecureBrowserUI.h" #include "nsISecureBrowserUI.h"
#include "nsIXULRuntime.h" #include "nsIXULRuntime.h"
@@ -1372,6 +1372,17 @@ IPCResult BrowserParent::RecvIndexedDBPermissionRequest(
return IPC_OK(); return IPC_OK();
} }
already_AddRefed<PSessionStoreParent>
BrowserParent::AllocPSessionStoreParent() {
RefPtr<BrowserSessionStore> sessionStore =
BrowserSessionStore::GetOrCreate(mBrowsingContext->Top());
if (!sessionStore) {
return nullptr;
}
return do_AddRef(new SessionStoreParent(mBrowsingContext, sessionStore));
}
IPCResult BrowserParent::RecvNewWindowGlobal( IPCResult BrowserParent::RecvNewWindowGlobal(
ManagedEndpoint<PWindowGlobalParent>&& aEndpoint, ManagedEndpoint<PWindowGlobalParent>&& aEndpoint,
const WindowGlobalInit& aInit) { const WindowGlobalInit& aInit) {
@@ -3008,41 +3019,6 @@ BrowserParent::BrowsingContextForWebProgress(
return browsingContext.forget(); return browsingContext.forget();
} }
mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate(
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
const bool aNeedCollectSHistory, const uint32_t& aEpoch) {
UpdateSessionStoreData data;
if (aDocShellCaps.isSome()) {
data.mDocShellCaps.Construct() = aDocShellCaps.value();
}
if (aPrivatedMode.isSome()) {
data.mIsPrivate.Construct() = aPrivatedMode.value();
}
nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule(
"resource://gre/modules/SessionStoreFunctions.jsm", fallible);
nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
NS_ENSURE_TRUE(wrapped, IPC_OK());
AutoJSAPI jsapi;
if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
return IPC_OK();
}
JS::Rooted<JS::Value> update(jsapi.cx());
if (!ToJSValue(jsapi.cx(), data, &update)) {
return IPC_OK();
}
JS::RootedValue key(jsapi.cx(),
mBrowsingContext->Canonical()->Top()->PermanentKey());
Unused << funcs->UpdateSessionStore(mFrameElement, mBrowsingContext, key,
aEpoch, aNeedCollectSHistory, update);
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserParent::RecvIntrinsicSizeOrRatioChanged( mozilla::ipc::IPCResult BrowserParent::RecvIntrinsicSizeOrRatioChanged(
const Maybe<IntrinsicSize>& aIntrinsicSize, const Maybe<IntrinsicSize>& aIntrinsicSize,
const Maybe<AspectRatio>& aIntrinsicRatio) { const Maybe<AspectRatio>& aIntrinsicRatio) {

View File

@@ -317,10 +317,6 @@ class BrowserParent final : public PBrowserParent,
already_AddRefed<CanonicalBrowsingContext> BrowsingContextForWebProgress( already_AddRefed<CanonicalBrowsingContext> BrowsingContextForWebProgress(
const WebProgressData& aWebProgressData); const WebProgressData& aWebProgressData);
mozilla::ipc::IPCResult RecvSessionStoreUpdate(
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
const bool aNeedCollectSHistory, const uint32_t& aEpoch);
mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged( mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged(
const Maybe<IntrinsicSize>& aIntrinsicSize, const Maybe<IntrinsicSize>& aIntrinsicSize,
const Maybe<AspectRatio>& aIntrinsicRatio); const Maybe<AspectRatio>& aIntrinsicRatio);
@@ -445,6 +441,8 @@ class BrowserParent final : public PBrowserParent,
const IAccessibleHolder& aDocCOMProxy) override; const IAccessibleHolder& aDocCOMProxy) override;
#endif #endif
already_AddRefed<PSessionStoreParent> AllocPSessionStoreParent();
mozilla::ipc::IPCResult RecvNewWindowGlobal( mozilla::ipc::IPCResult RecvNewWindowGlobal(
ManagedEndpoint<PWindowGlobalParent>&& aEndpoint, ManagedEndpoint<PWindowGlobalParent>&& aEndpoint,
const WindowGlobalInit& aInit); const WindowGlobalInit& aInit);

View File

@@ -4524,25 +4524,6 @@ mozilla::ipc::IPCResult ContentChild::RecvInitNextGenLocalStorageEnabled(
} }
} }
mozilla::ipc::IPCResult ContentChild::RecvFlushTabState(
const MaybeDiscarded<BrowsingContext>& aContext,
FlushTabStateResolver&& aResolver) {
if (aContext.IsNullOrDiscarded()) {
aResolver(false);
return IPC_OK();
}
if (auto* docShell = nsDocShell::Cast(aContext->GetDocShell())) {
docShell->CollectWireframe();
}
aContext->FlushSessionStore();
aResolver(true);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvGoBack( mozilla::ipc::IPCResult ContentChild::RecvGoBack(
const MaybeDiscarded<BrowsingContext>& aContext, const MaybeDiscarded<BrowsingContext>& aContext,
const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction, const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,

View File

@@ -812,10 +812,6 @@ class ContentChild final : public PContentChild,
const MaybeDiscarded<BrowsingContext>& aStartingAt, const MaybeDiscarded<BrowsingContext>& aStartingAt,
DispatchBeforeUnloadToSubtreeResolver&& aResolver); DispatchBeforeUnloadToSubtreeResolver&& aResolver);
mozilla::ipc::IPCResult RecvFlushTabState(
const MaybeDiscarded<BrowsingContext>& aContext,
FlushTabStateResolver&& aResolver);
mozilla::ipc::IPCResult RecvDecoderSupportedMimeTypes( mozilla::ipc::IPCResult RecvDecoderSupportedMimeTypes(
nsTArray<nsCString>&& aSupportedTypes); nsTArray<nsCString>&& aSupportedTypes);

View File

@@ -17,6 +17,7 @@ include protocol PParentToChildStream;
include protocol PFileDescriptorSet; include protocol PFileDescriptorSet;
include protocol PRemoteLazyInputStream; include protocol PRemoteLazyInputStream;
include protocol PPaymentRequest; include protocol PPaymentRequest;
include protocol PSessionStore;
include protocol PWindowGlobal; include protocol PWindowGlobal;
include protocol PBrowserBridge; include protocol PBrowserBridge;
include protocol PVsync; include protocol PVsync;
@@ -190,6 +191,7 @@ struct PrintPreviewResultInfo
manages PFilePicker; manages PFilePicker;
manages PPaymentRequest; manages PPaymentRequest;
manages PSessionStore;
manages PWindowGlobal; manages PWindowGlobal;
manages PBrowserBridge; manages PBrowserBridge;
manages PVsync; manages PVsync;
@@ -575,9 +577,6 @@ parent:
async NavigationFinished(); async NavigationFinished();
async SessionStoreUpdate(nsCString? aDocShellCaps, bool? aPrivatedMode,
bool aNeedCollectSHistory, uint32_t aEpoch);
async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize, async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
AspectRatio? aIntrinsicRatio); AspectRatio? aIntrinsicRatio);
@@ -595,7 +594,6 @@ parent:
child: child:
async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse); async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
async UpdateEpoch(uint32_t aEpoch);
async UpdateSHistory(); async UpdateSHistory();
async CloneDocumentTreeIntoSelf(MaybeDiscardedBrowsingContext aBc, PrintData aPrintData) returns(bool aSuccess); async CloneDocumentTreeIntoSelf(MaybeDiscardedBrowsingContext aBc, PrintData aPrintData) returns(bool aSuccess);
async UpdateRemotePrintSettings(PrintData aPrintData); async UpdateRemotePrintSettings(PrintData aPrintData);
@@ -1016,6 +1014,9 @@ parent:
/** Fetches the visited status for an array of URIs (Android-only). */ /** Fetches the visited status for an array of URIs (Android-only). */
async QueryVisitedState(nsIURI[] aURIs); async QueryVisitedState(nsIURI[] aURIs);
/** Create a session store for a browser child. */
async PSessionStore();
/** /**
* Construct a new WindowGlobal for an existing global in the content process * Construct a new WindowGlobal for an existing global in the content process
*/ */

View File

@@ -1027,9 +1027,6 @@ child:
// Update the cached list of codec supported in the given process. // Update the cached list of codec supported in the given process.
async UpdateMediaCodecsSupported(RemoteDecodeIn aLocation, MediaCodecsSupported aSupported); async UpdateMediaCodecsSupported(RemoteDecodeIn aLocation, MediaCodecsSupported aSupported);
async FlushTabState(MaybeDiscardedBrowsingContext aBrowsingContext)
returns(bool aHadContext);
// Send the list of the supported mimetypes in the given process. GeckoView-specific // Send the list of the supported mimetypes in the given process. GeckoView-specific
async DecoderSupportedMimeTypes(nsCString[] supportedTypes); async DecoderSupportedMimeTypes(nsCString[] supportedTypes);

View File

@@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PExtensions; include protocol PExtensions;
include protocol PSessionStore;
include protocol PWindowGlobal; include protocol PWindowGlobal;
include DOMTypes; include DOMTypes;
@@ -23,6 +24,7 @@ namespace dom {
async protocol PInProcess async protocol PInProcess
{ {
manages PExtensions; manages PExtensions;
manages PSessionStore;
manages PWindowGlobal; manages PWindowGlobal;
}; };

View File

@@ -180,11 +180,6 @@ parent:
async RequestRestoreTabContent(); async RequestRestoreTabContent();
async UpdateSessionStore(FormData? aFormData, nsPoint? aScrollPosition,
uint32_t aEpoch);
async ResetSessionStore(uint32_t aEpoch);
// Add the flags in aOnFlags to the current BFCache status and remove the // Add the flags in aOnFlags to the current BFCache status and remove the
// flags in aOffFlags from the current BFCache status. See the BFCacheStatus // flags in aOffFlags from the current BFCache status. See the BFCacheStatus
// enum for the valid flags. // enum for the valid flags.

View File

@@ -18,7 +18,6 @@
#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/SecurityPolicyViolationEvent.h" #include "mozilla/dom/SecurityPolicyViolationEvent.h"
#include "mozilla/dom/SessionStoreRestoreData.h" #include "mozilla/dom/SessionStoreRestoreData.h"
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/dom/WindowGlobalActorsBinding.h" #include "mozilla/dom/WindowGlobalActorsBinding.h"
#include "mozilla/dom/WindowContext.h" #include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/InProcessChild.h" #include "mozilla/dom/InProcessChild.h"
@@ -327,11 +326,6 @@ void WindowGlobalChild::Destroy() {
if (!browserChild || !browserChild->IsDestroyed()) { if (!browserChild || !browserChild->IsDestroyed()) {
SendDestroy(); SendDestroy();
} }
if (mSessionStoreDataCollector) {
mSessionStoreDataCollector->Cancel();
mSessionStoreDataCollector = nullptr;
}
} }
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal( mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
@@ -703,20 +697,9 @@ nsISupports* WindowGlobalChild::GetParentObject() {
return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
} }
void WindowGlobalChild::SetSessionStoreDataCollector(
SessionStoreDataCollector* aCollector) {
mSessionStoreDataCollector = aCollector;
}
SessionStoreDataCollector* WindowGlobalChild::GetSessionStoreDataCollector()
const {
return mSessionStoreDataCollector;
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(WindowGlobalChild, mWindowGlobal, NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(WindowGlobalChild, mWindowGlobal,
mContainerFeaturePolicy, mContainerFeaturePolicy,
mWindowContext, mWindowContext)
mSessionStoreDataCollector)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY

View File

@@ -28,7 +28,6 @@ class WindowGlobalParent;
class JSWindowActorChild; class JSWindowActorChild;
class JSActorMessageMeta; class JSActorMessageMeta;
class BrowserChild; class BrowserChild;
class SessionStoreDataCollector;
/** /**
* Actor for a single nsGlobalWindowInner. This actor is used to communicate * Actor for a single nsGlobalWindowInner. This actor is used to communicate
@@ -133,9 +132,6 @@ class WindowGlobalChild final : public WindowGlobalActor,
return mContainerFeaturePolicy; return mContainerFeaturePolicy;
} }
void SetSessionStoreDataCollector(SessionStoreDataCollector* aCollector);
SessionStoreDataCollector* GetSessionStoreDataCollector() const;
void UnblockBFCacheFor(BFCacheStatus aStatus); void UnblockBFCacheFor(BFCacheStatus aStatus);
void BlockBFCacheFor(BFCacheStatus aStatus); void BlockBFCacheFor(BFCacheStatus aStatus);
@@ -205,7 +201,6 @@ class WindowGlobalChild final : public WindowGlobalActor,
nsCOMPtr<nsIPrincipal> mDocumentPrincipal; nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
RefPtr<dom::FeaturePolicy> mContainerFeaturePolicy; RefPtr<dom::FeaturePolicy> mContainerFeaturePolicy;
nsCOMPtr<nsIURI> mDocumentURI; nsCOMPtr<nsIURI> mDocumentURI;
RefPtr<SessionStoreDataCollector> mSessionStoreDataCollector;
int64_t mBeforeUnloadListeners = 0; int64_t mBeforeUnloadListeners = 0;
}; };

View File

@@ -26,9 +26,6 @@
#include "mozilla/dom/ChromeUtils.h" #include "mozilla/dom/ChromeUtils.h"
#include "mozilla/dom/ipc/IdType.h" #include "mozilla/dom/ipc/IdType.h"
#include "mozilla/dom/ipc/StructuredCloneData.h" #include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/dom/sessionstore/SessionStoreTypes.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h"
#include "mozilla/Components.h" #include "mozilla/Components.h"
#include "mozilla/ScopeExit.h" #include "mozilla/ScopeExit.h"
#include "mozilla/ServoCSSParser.h" #include "mozilla/ServoCSSParser.h"
@@ -535,16 +532,6 @@ const nsACString& WindowGlobalParent::GetRemoteType() {
return NOT_REMOTE_TYPE; return NOT_REMOTE_TYPE;
} }
static nsCString PointToString(const nsPoint& aPoint) {
int scrollX = nsPresContext::AppUnitsToIntCSSPixels(aPoint.x);
int scrollY = nsPresContext::AppUnitsToIntCSSPixels(aPoint.y);
if ((scrollX != 0) || (scrollY != 0)) {
return nsPrintfCString("%d,%d", scrollX, scrollY);
}
return ""_ns;
}
void WindowGlobalParent::NotifyContentBlockingEvent( void WindowGlobalParent::NotifyContentBlockingEvent(
uint32_t aEvent, nsIRequest* aRequest, bool aBlocked, uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
const nsACString& aTrackingOrigin, const nsACString& aTrackingOrigin,
@@ -1166,44 +1153,6 @@ void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
mPageUseCounters = nullptr; mPageUseCounters = nullptr;
} }
static void GetFormData(JSContext* aCx, const sessionstore::FormData& aFormData,
nsIURI* aDocumentURI, SessionStoreFormData& aUpdate) {
if (!aFormData.hasData()) {
return;
}
bool parseSessionData = false;
if (aDocumentURI) {
nsCString& url = aUpdate.mUrl.Construct();
aDocumentURI->GetSpecIgnoringRef(url);
// We want to avoid saving data for about:sessionrestore as a string.
// Since it's stored in the form as stringified JSON, stringifying
// further causes an explosion of escape characters. cf. bug 467409
parseSessionData =
url == "about:sessionrestore"_ns || url == "about:welcomeback"_ns;
}
if (!aFormData.innerHTML().IsEmpty()) {
aUpdate.mInnerHTML.Construct(aFormData.innerHTML());
}
if (!aFormData.id().IsEmpty()) {
auto& id = aUpdate.mId.Construct();
if (NS_FAILED(SessionStoreUtils::ConstructFormDataValues(
aCx, aFormData.id(), id.Entries(), parseSessionData))) {
return;
}
}
if (!aFormData.xpath().IsEmpty()) {
auto& xpath = aUpdate.mXpath.Construct();
if (NS_FAILED(SessionStoreUtils::ConstructFormDataValues(
aCx, aFormData.xpath(), xpath.Entries()))) {
return;
}
}
}
Element* WindowGlobalParent::GetRootOwnerElement() { Element* WindowGlobalParent::GetRootOwnerElement() {
WindowGlobalParent* top = TopWindowContext(); WindowGlobalParent* top = TopWindowContext();
if (!top) { if (!top) {
@@ -1221,104 +1170,6 @@ Element* WindowGlobalParent::GetRootOwnerElement() {
return nullptr; return nullptr;
} }
nsresult WindowGlobalParent::WriteFormDataAndScrollToSessionStore(
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch) {
if (!aFormData && !aScrollPosition) {
return NS_OK;
}
RefPtr<CanonicalBrowsingContext> context = BrowsingContext();
if (!context) {
return NS_OK;
}
nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule(
"resource://gre/modules/SessionStoreFunctions.jsm", fallible);
if (!funcs) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
AutoJSAPI jsapi;
if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
return NS_ERROR_FAILURE;
}
RootedDictionary<SessionStoreWindowStateChange> windowState(jsapi.cx());
if (aFormData) {
GetFormData(jsapi.cx(), *aFormData, mDocumentURI,
windowState.mFormdata.Construct());
}
if (aScrollPosition) {
auto& update = windowState.mScroll.Construct();
if (*aScrollPosition != nsPoint(0, 0)) {
update.mScroll.Construct() = PointToString(*aScrollPosition);
}
}
nsTArray<uint32_t> path;
if (!context->GetOffsetPath(path)) {
return NS_OK;
}
windowState.mPath = std::move(path);
windowState.mHasChildren.Construct() = !context->Children().IsEmpty();
JS::RootedValue update(jsapi.cx());
if (!ToJSValue(jsapi.cx(), windowState, &update)) {
return NS_ERROR_FAILURE;
}
JS::RootedValue key(jsapi.cx(), context->Top()->PermanentKey());
return funcs->UpdateSessionStoreForWindow(GetRootOwnerElement(), context, key,
aEpoch, update);
}
nsresult WindowGlobalParent::ResetSessionStore(uint32_t aEpoch) {
RefPtr<CanonicalBrowsingContext> context = BrowsingContext();
if (!context) {
return NS_OK;
}
nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule(
"resource://gre/modules/SessionStoreFunctions.jsm", fallible);
if (!funcs) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
AutoJSAPI jsapi;
if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
return NS_ERROR_FAILURE;
}
RootedDictionary<SessionStoreWindowStateChange> windowState(jsapi.cx());
nsTArray<uint32_t> path;
if (!context->GetOffsetPath(path)) {
return NS_OK;
}
windowState.mPath = std::move(path);
windowState.mHasChildren.Construct() = false;
windowState.mFormdata.Construct();
windowState.mScroll.Construct();
JS::RootedValue update(jsapi.cx());
if (!ToJSValue(jsapi.cx(), windowState, &update)) {
return NS_ERROR_FAILURE;
}
JS::RootedValue key(jsapi.cx(), context->Top()->PermanentKey());
return funcs->UpdateSessionStoreForWindow(GetRootOwnerElement(), context, key,
aEpoch, update);
}
void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) { void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
if (!aEmbedder) { if (!aEmbedder) {
aEmbedder = GetRootOwnerElement(); aEmbedder = GetRootOwnerElement();
@@ -1331,27 +1182,6 @@ void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
} }
} }
mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateSessionStore(
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch) {
if (NS_FAILED(WriteFormDataAndScrollToSessionStore(aFormData, aScrollPosition,
aEpoch))) {
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
("ParentIPC: Failed to update session store entry."));
}
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvResetSessionStore(
uint32_t aEpoch) {
if (NS_FAILED(ResetSessionStore(aEpoch))) {
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
("ParentIPC: Failed to reset session store entry."));
}
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() { mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
CanonicalBrowsingContext* bc = BrowsingContext(); CanonicalBrowsingContext* bc = BrowsingContext();
if (bc && bc->AncestorsAreCurrent()) { if (bc && bc->AncestorsAreCurrent()) {

View File

@@ -80,7 +80,7 @@ class WindowGlobalParent final : public WindowContext,
WindowGlobalParent* TopWindowContext() { WindowGlobalParent* TopWindowContext() {
return static_cast<WindowGlobalParent*>(WindowContext::TopWindowContext()); return static_cast<WindowGlobalParent*>(WindowContext::TopWindowContext());
} }
CanonicalBrowsingContext* GetBrowsingContext() { CanonicalBrowsingContext* GetBrowsingContext() const {
return CanonicalBrowsingContext::Cast(WindowContext::GetBrowsingContext()); return CanonicalBrowsingContext::Cast(WindowContext::GetBrowsingContext());
} }
@@ -214,10 +214,6 @@ class WindowGlobalParent final : public WindowContext,
const nsACString& GetRemoteType() override; const nsACString& GetRemoteType() override;
nsresult WriteFormDataAndScrollToSessionStore(
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch);
void NotifySessionStoreUpdatesComplete(Element* aEmbedder); void NotifySessionStoreUpdatesComplete(Element* aEmbedder);
Maybe<uint64_t> GetSingleChannelId() { return mSingleChannelId; } Maybe<uint64_t> GetSingleChannelId() { return mSingleChannelId; }
@@ -288,12 +284,6 @@ class WindowGlobalParent final : public WindowContext,
mozilla::ipc::IPCResult RecvRequestRestoreTabContent(); mozilla::ipc::IPCResult RecvRequestRestoreTabContent();
mozilla::ipc::IPCResult RecvUpdateSessionStore(
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch);
mozilla::ipc::IPCResult RecvResetSessionStore(uint32_t aEpoch);
mozilla::ipc::IPCResult RecvUpdateBFCacheStatus(const uint32_t& aOnFlags, mozilla::ipc::IPCResult RecvUpdateBFCacheStatus(const uint32_t& aOnFlags,
const uint32_t& aOffFlags); const uint32_t& aOffFlags);
@@ -320,8 +310,6 @@ class WindowGlobalParent final : public WindowContext,
bool ShouldTrackSiteOriginTelemetry(); bool ShouldTrackSiteOriginTelemetry();
void FinishAccumulatingPageUseCounters(); void FinishAccumulatingPageUseCounters();
nsresult ResetSessionStore(uint32_t aEpoch);
// Returns failure if the new storage principal cannot be validated // Returns failure if the new storage principal cannot be validated
// against the current document principle. // against the current document principle.
nsresult SetDocumentStoragePrincipal( nsresult SetDocumentStoragePrincipal(

View File

@@ -0,0 +1,293 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/BrowserSessionStore.h"
#include <algorithm>
#include <cstdint>
#include <functional>
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/BrowserSessionStoreBinding.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/SessionStoreFormData.h"
#include "mozilla/dom/SessionStoreScrollData.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "nsTHashMap.h"
#include "nsHashtablesFwd.h"
#include "js/RootingAPI.h"
using namespace mozilla;
using namespace mozilla::dom;
static StaticAutoPtr<nsTHashMap<nsUint64HashKey, BrowserSessionStore*>>
sSessionStore;
NS_IMPL_CYCLE_COLLECTION(BrowserSessionStore, mBrowsingContext, mFormData,
mScrollData)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowserSessionStore, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowserSessionStore, Release)
/* static */
already_AddRefed<BrowserSessionStore> BrowserSessionStore::GetOrCreate(
CanonicalBrowsingContext* aBrowsingContext) {
if (!aBrowsingContext->IsTop()) {
return nullptr;
}
if (!sSessionStore) {
sSessionStore = new nsTHashMap<nsUint64HashKey, BrowserSessionStore*>();
ClearOnShutdown(&sSessionStore);
}
return do_AddRef(sSessionStore->LookupOrInsertWith(
aBrowsingContext->Id(),
[&] { return new BrowserSessionStore(aBrowsingContext); }));
}
BrowserSessionStore::BrowserSessionStore(
CanonicalBrowsingContext* aBrowsingContext)
: mBrowsingContext(aBrowsingContext) {}
SessionStoreFormData* BrowserSessionStore::GetFormdata() { return mFormData; }
SessionStoreScrollData* BrowserSessionStore::GetScroll() { return mScrollData; }
static bool ShouldUpdateSessionStore(CanonicalBrowsingContext* aBrowsingContext,
uint32_t aEpoch) {
if (!aBrowsingContext) {
return false;
}
if (aBrowsingContext->Top()->GetSessionStoreEpoch() != aEpoch) {
return false;
}
if (aBrowsingContext->IsReplaced()) {
return false;
}
if (aBrowsingContext->IsDynamic()) {
return false;
}
return true;
}
// With GetOrCreate we can create either of the weak fields:
// WeakPtr<SessionStoreFormData> mFormdata;
// WeakPtr<SessionStoreScrollData> mScroll;
// in CanonicalBrowsingContext. If one already exists, then we return that.
template <typename T, WeakPtr<T>& (CanonicalBrowsingContext::*GetWeakRef)()>
static already_AddRefed<T> GetOrCreateEntry(
CanonicalBrowsingContext* aBrowsingContext) {
typename T::LocationType& location = (aBrowsingContext->*GetWeakRef)();
RefPtr<T> entry = location.get();
if (!entry) {
entry = MakeRefPtr<T>();
location = entry;
}
return entry.forget();
}
// With InsertEntry we can insert an entry in the session store data tree in
// either of the weak fields:
// WeakPtr<SessionStoreFormData> mFormdata;
// WeakPtr<SessionStoreScrollData> mScroll;
// in CanonicalBrowsingContext. If an entry is inserted where there is no parent
// entry, a spine of entries will be created until one is found, or we reach the
// top browsing context.
template <typename T>
void InsertEntry(BrowsingContext* aBrowsingContext, T* aParent, T* aUpdate) {
int32_t offset = aBrowsingContext->ChildOffset();
if (offset < 0) {
return;
}
aParent->ClearCachedChildren();
auto& children = aParent->Children();
children.EnsureLengthAtLeast(offset + 1);
if (children[offset] && !aBrowsingContext->Children().IsEmpty()) {
children[offset]->ClearCachedChildren();
aUpdate->ClearCachedChildren();
}
children[offset] = aUpdate;
}
// With RemoveEntry we can remove an entry in the session store data tree in
// either of the weak fields:
// WeakPtr<SessionStoreFormData> mFormdata;
// WeakPtr<SessionStoreScrollData> mScroll;
// in CanonicalBrowsingContext. If an entry is removed, where its parent doesn't
// contain data, we'll remove the parent and repeat until we either find an
// entry with data or reach the top browsing context.
template <typename T>
void RemoveEntry(BrowsingContext* aBrowsingContext, T* aParent) {
int32_t offset = aBrowsingContext->ChildOffset();
if (offset < 0) {
return;
}
if (!aParent) {
return;
}
aParent->ClearCachedChildren();
auto& children = aParent->Children();
size_t length = children.Length();
if (children.Length() <= static_cast<size_t>(offset)) {
// The children array doesn't extend to offset.
return;
}
if (static_cast<size_t>(offset) < length - 1) {
// offset is before the last item in the children array.
children[offset] = nullptr;
return;
}
// offset is the last item, find the first non-null item before it
// and remove anything after that item.
while (offset > 0 && !children[offset - 1]) {
--offset;
}
children.TruncateLength(offset);
}
// With UpdateSessionStoreField we can update an entry in the session store
// data tree in either of the weak fields:
// WeakPtr<SessionStoreFormData> mFormdata;
// WeakPtr<SessionStoreScrollData> mScroll;
// in CanonicalBrowsingContext. UpdateSessionStoreField uses the above
// functions, `GetOrCreateEntry`, `InsertEntry` and `RemoveEntry` to operate on
// the weak fields. We return the top-level entry attached to the top browsing
// context through `aEntry`. If the entire browsing context tree contains no
// session store data this will be set to nullptr.
template <typename T, WeakPtr<T>& (CanonicalBrowsingContext::*GetWeakRef)()>
void UpdateSessionStoreField(CanonicalBrowsingContext* aBrowsingContext,
const typename T::CollectedType& aUpdate,
T** aEntry) {
RefPtr<T> currentEntry;
if (T::HasData(aUpdate)) {
currentEntry = GetOrCreateEntry<T, GetWeakRef>(aBrowsingContext);
currentEntry->Update(aUpdate);
CanonicalBrowsingContext* currentBrowsingContext = aBrowsingContext;
while (CanonicalBrowsingContext* parent =
currentBrowsingContext->GetParent()) {
WeakPtr<T>& parentEntry = (parent->*GetWeakRef)();
if (parentEntry) {
InsertEntry(aBrowsingContext, parentEntry.get(), currentEntry.get());
break;
}
RefPtr<T> entry = GetOrCreateEntry<T, GetWeakRef>(parent);
InsertEntry(currentBrowsingContext, entry.get(), currentEntry.get());
currentEntry = entry;
currentBrowsingContext = parent;
}
currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get();
} else if ((currentEntry = (aBrowsingContext->*GetWeakRef)())) {
currentEntry->Update(aUpdate);
CanonicalBrowsingContext* currentBrowsingContext = aBrowsingContext;
while (CanonicalBrowsingContext* parent =
currentBrowsingContext->GetParent()) {
if (!currentEntry || !currentEntry->IsEmpty()) {
break;
}
T* parentEntry = (parent->*GetWeakRef)().get();
RemoveEntry(currentBrowsingContext, parentEntry);
currentEntry = parentEntry;
currentBrowsingContext = parent;
}
if (currentEntry && currentEntry->IsEmpty()) {
currentEntry = nullptr;
} else {
currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get();
}
}
*aEntry = currentEntry.forget().take();
}
void BrowserSessionStore::UpdateSessionStore(
CanonicalBrowsingContext* aBrowsingContext,
const Maybe<sessionstore::FormData>& aFormData,
const Maybe<nsPoint>& aScrollPosition, uint32_t aEpoch) {
if (!aFormData && !aScrollPosition) {
return;
}
if (!ShouldUpdateSessionStore(aBrowsingContext, aEpoch)) {
return;
}
if (aFormData) {
UpdateSessionStoreField<
SessionStoreFormData,
&CanonicalBrowsingContext::GetSessionStoreFormDataRef>(
aBrowsingContext, *aFormData, getter_AddRefs(mFormData));
}
if (aScrollPosition) {
UpdateSessionStoreField<
SessionStoreScrollData,
&CanonicalBrowsingContext::GetSessionStoreScrollDataRef>(
aBrowsingContext, *aScrollPosition, getter_AddRefs(mScrollData));
}
}
void BrowserSessionStore::RemoveSessionStore(
CanonicalBrowsingContext* aBrowsingContext) {
if (!aBrowsingContext) {
return;
}
CanonicalBrowsingContext* parentContext = aBrowsingContext->GetParent();
if (parentContext) {
RemoveEntry(aBrowsingContext,
parentContext->GetSessionStoreFormDataRef().get());
RemoveEntry(aBrowsingContext,
parentContext->GetSessionStoreScrollDataRef().get());
return;
}
if (aBrowsingContext->IsTop()) {
mFormData = nullptr;
mScrollData = nullptr;
}
}
BrowserSessionStore::~BrowserSessionStore() {
if (sSessionStore) {
sSessionStore->Remove(mBrowsingContext->Id());
}
}

View File

@@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SessionStore_h
#define mozilla_dom_SessionStore_h
#include "nsWrapperCache.h"
#include "mozilla/Maybe.h"
struct nsPoint;
namespace mozilla::dom {
class CanonicalBrowsingContext;
class GlobalObject;
class SessionStoreFormData;
class SessionStoreScrollData;
class WindowGlobalParent;
namespace sessionstore {
class FormData;
}
class BrowserSessionStore final {
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowserSessionStore)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(BrowserSessionStore)
static already_AddRefed<BrowserSessionStore> GetOrCreate(
CanonicalBrowsingContext* aBrowsingContext);
SessionStoreFormData* GetFormdata();
SessionStoreScrollData* GetScroll();
void UpdateSessionStore(CanonicalBrowsingContext* aBrowsingContext,
const Maybe<sessionstore::FormData>& aFormData,
const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch);
void RemoveSessionStore(CanonicalBrowsingContext* aBrowsingContext);
private:
explicit BrowserSessionStore(CanonicalBrowsingContext* aBrowsingContext);
virtual ~BrowserSessionStore();
RefPtr<CanonicalBrowsingContext> mBrowsingContext;
RefPtr<SessionStoreFormData> mFormData;
RefPtr<SessionStoreScrollData> mScrollData;
};
} // namespace mozilla::dom
#endif // mozilla_dom_SessionStore_h

View File

@@ -0,0 +1,60 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBrowser;
include protocol PInProcess;
include SessionStoreTypes;
using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
namespace mozilla {
namespace dom {
/**
* The PSessionStore actor handles collection of session store data from content
* type documents. It can be used both in a content process and in the parent
* process. In particular it solves the problem of handling incremental updates
* to the session store, since we're collecting from potentially several content
* processes.
*/
async protocol PSessionStore
{
manager PBrowser or PInProcess;
parent:
/**
* Sends data to be stored and instructions to the session store to
* potentially collect data in the parent. This is data that is not
* collected incrementally.
*/
async SessionStoreUpdate(
nsCString? aDocShellCaps, bool? aPrivateMode, bool aNeedCollectSHistory,
uint32_t aEpoch);
/**
* Sends data to be stored to the session store. The collected data
* is all the collected changed data from all the in-process documents
* in the process in which the SessionStoreChild actor lives.
*/
async IncrementalSessionStoreUpdate(
MaybeDiscardedBrowsingContext aBrowsingContext, FormData? aFormData,
nsPoint? aScrollPosition, uint32_t aEpoch);
/**
* Drop all the collected data associated with the provided browsing
* context.
*/
async ResetSessionStore(
MaybeDiscardedBrowsingContext aBrowsingContext, uint32_t aEpoch);
child:
async FlushTabState() returns(bool aHadContext);
async __delete__();
};
} // namespace dom
} // namespace mozilla

View File

@@ -10,13 +10,23 @@
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h" #include "mozilla/dom/Event.h"
#include "mozilla/dom/EventTarget.h" #include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/SessionStoreDataCollector.h" #include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/WindowGlobalChild.h" #include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_browser.h"
#include "nsBaseHashtable.h"
#include "nsDocShell.h" #include "nsDocShell.h"
#include "nsGenericHTMLElement.h"
#include "nsIXULRuntime.h"
#include "nsPIDOMWindow.h" #include "nsPIDOMWindow.h"
#include "nsTHashMap.h"
#include "nsTHashtable.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace { namespace {
constexpr auto kInput = u"input"_ns; constexpr auto kInput = u"input"_ns;
@@ -28,13 +38,28 @@ static constexpr char kInterval[] = "browser.sessionstore.interval";
static const char* kObservedPrefs[] = {kNoAutoUpdates, kInterval, nullptr}; static const char* kObservedPrefs[] = {kNoAutoUpdates, kInterval, nullptr};
} // namespace } // namespace
namespace mozilla::dom { inline void ImplCycleCollectionUnlink(
SessionStoreChangeListener::SessionStoreChangeTable& aField) {
aField.Clear();
}
inline void ImplCycleCollectionTraverse(
nsCycleCollectionTraversalCallback& aCallback,
const SessionStoreChangeListener::SessionStoreChangeTable& aField,
const char* aName, uint32_t aFlags = 0) {
for (auto iter = aField.ConstIter(); !iter.Done(); iter.Next()) {
CycleCollectionNoteChild(aCallback, iter.Key(), aName, aFlags);
}
}
NS_IMPL_CYCLE_COLLECTION(SessionStoreChangeListener, mBrowsingContext, NS_IMPL_CYCLE_COLLECTION(SessionStoreChangeListener, mBrowsingContext,
mCurrentEventTarget) mCurrentEventTarget, mSessionStoreChild,
mSessionStoreChanges)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStoreChangeListener) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStoreChangeListener)
NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END
@@ -42,10 +67,22 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStoreChangeListener) NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStoreChangeListener) NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStoreChangeListener)
NS_IMETHODIMP
SessionStoreChangeListener::GetName(nsACString& aName) {
aName.AssignLiteral("SessionStoreChangeListener");
return NS_OK;
}
NS_IMETHODIMP
SessionStoreChangeListener::Notify(nsITimer* aTimer) {
FlushSessionStore();
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
SessionStoreChangeListener::Observe(nsISupports* aSubject, const char* aTopic, SessionStoreChangeListener::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) { const char16_t* aData) {
Flush(); FlushSessionStore();
return NS_OK; return NS_OK;
} }
@@ -62,45 +99,33 @@ SessionStoreChangeListener::HandleEvent(dom::Event* aEvent) {
return NS_OK; return NS_OK;
} }
WindowGlobalChild* windowChild = inner->GetWindowGlobalChild(); WindowContext* windowContext = inner->GetWindowContext();
if (!windowChild) { if (!windowContext) {
return NS_OK; return NS_OK;
} }
RefPtr<BrowsingContext> browsingContext = windowChild->BrowsingContext(); RefPtr<BrowsingContext> browsingContext = windowContext->GetBrowsingContext();
if (!browsingContext) { if (!browsingContext) {
return NS_OK; return NS_OK;
} }
bool dynamic = false; if (browsingContext->IsDynamic()) {
BrowsingContext* current = browsingContext;
while (current) {
if ((dynamic = current->CreatedDynamically())) {
break;
}
current = current->GetParent();
}
if (dynamic) {
return NS_OK; return NS_OK;
} }
nsAutoString eventType; nsAutoString eventType;
aEvent->GetType(eventType); aEvent->GetType(eventType);
RefPtr<SessionStoreDataCollector> collector = Change change = Change::None;
SessionStoreDataCollector::CollectSessionStoreData(windowChild);
if (!collector) {
return NS_OK;
}
if (eventType == kInput) { if (eventType == kInput) {
collector->RecordInputChange(); change = Change::Input;
} else if (eventType == kScroll) { } else if (eventType == kScroll) {
collector->RecordScrollChange(); change = Change::Scroll;
} }
RecordChange(windowContext, EnumSet(change));
return NS_OK; return NS_OK;
} }
@@ -128,13 +153,134 @@ void SessionStoreChangeListener::UpdateEventTargets() {
AddEventListeners(); AddEventListeners();
} }
void SessionStoreChangeListener::Flush() { static void CollectFormData(Document* aDocument,
mBrowsingContext->FlushSessionStore(); Maybe<sessionstore::FormData>& aFormData) {
aFormData.emplace();
auto& formData = aFormData.ref();
uint32_t size = SessionStoreUtils::CollectFormData(aDocument, formData);
Element* body = aDocument->GetBody();
if (aDocument->HasFlag(NODE_IS_EDITABLE) && body) {
IgnoredErrorResult result;
body->GetInnerHTML(formData.innerHTML(), result);
size += formData.innerHTML().Length();
if (!result.Failed()) {
formData.hasData() = true;
}
}
if (!formData.hasData()) {
return;
}
nsIURI* documentURI = aDocument->GetDocumentURI();
if (!documentURI) {
return;
}
documentURI->GetSpecIgnoringRef(formData.uri());
if (size > StaticPrefs::browser_sessionstore_dom_form_max_limit()) {
aFormData = Nothing();
}
}
void SessionStoreChangeListener::FlushSessionStore() {
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
for (auto& iter : mSessionStoreChanges) {
WindowContext* windowContext = iter.GetKey();
if (!windowContext) {
continue;
}
RefPtr<Document> document = windowContext->GetDocument();
if (!document) {
continue;
}
EnumSet<Change> changes = iter.GetData();
Maybe<sessionstore::FormData> maybeFormData;
if (changes.contains(Change::Input)) {
CollectFormData(document, maybeFormData);
}
Maybe<nsPoint> maybeScroll;
PresShell* presShell = document->GetPresShell();
if (presShell && changes.contains(Change::Scroll)) {
maybeScroll = Some(presShell->GetVisualViewportOffset());
}
mSessionStoreChild->SendIncrementalSessionStoreUpdate(
windowContext->GetBrowsingContext(), maybeFormData, maybeScroll,
mEpoch);
}
mSessionStoreChanges.Clear();
mSessionStoreChild->UpdateSessionStore();
}
/* static */
SessionStoreChangeListener* SessionStoreChangeListener::CollectSessionStoreData(
WindowContext* aWindowContext, const EnumSet<Change>& aChanges) {
SessionStoreChild* sessionStoreChild =
SessionStoreChild::From(aWindowContext->GetWindowGlobalChild());
if (!sessionStoreChild) {
return nullptr;
}
SessionStoreChangeListener* sessionStoreChangeListener =
sessionStoreChild->GetSessionStoreChangeListener();
if (!sessionStoreChangeListener) {
return nullptr;
}
sessionStoreChangeListener->RecordChange(aWindowContext, aChanges);
return sessionStoreChangeListener;
}
/* static */
void SessionStoreChangeListener::FlushAllSessionStoreData(
WindowContext* aWindowContext) {
EnumSet<Change> allChanges(Change::Input, Change::Scroll);
SessionStoreChangeListener* listener =
CollectSessionStoreData(aWindowContext, allChanges);
if (listener) {
listener->FlushSessionStore();
}
}
void SessionStoreChangeListener::SetActor(
SessionStoreChild* aSessionStoreChild) {
mSessionStoreChild = aSessionStoreChild;
}
void SessionStoreChangeListener::RecordChange(WindowContext* aWindowContext,
EnumSet<Change> aChange) {
EnsureTimer();
Unused << mSessionStoreChanges.WithEntryHandle(
aWindowContext, [&](auto entryHandle) -> EnumSet<Change>& {
if (entryHandle) {
*entryHandle += aChange;
return *entryHandle;
}
return entryHandle.Insert(aChange);
});
} }
SessionStoreChangeListener::SessionStoreChangeListener( SessionStoreChangeListener::SessionStoreChangeListener(
BrowsingContext* aBrowsingContext) BrowsingContext* aBrowsingContext)
: mBrowsingContext(aBrowsingContext) {} : mBrowsingContext(aBrowsingContext),
mEpoch(aBrowsingContext->GetSessionStoreEpoch()) {}
void SessionStoreChangeListener::Init() { void SessionStoreChangeListener::Init() {
AddEventListeners(); AddEventListeners();
@@ -167,4 +313,19 @@ void SessionStoreChangeListener::RemoveEventListeners() {
mCurrentEventTarget = nullptr; mCurrentEventTarget = nullptr;
} }
} // namespace mozilla::dom void SessionStoreChangeListener::EnsureTimer() {
if (mTimer) {
return;
}
if (!StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
auto result = NS_NewTimerWithCallback(
this, StaticPrefs::browser_sessionstore_interval(),
nsITimer::TYPE_ONE_SHOT);
if (result.isErr()) {
return;
}
mTimer = result.unwrap();
}
}

View File

@@ -9,25 +9,39 @@
#include "ErrorList.h" #include "ErrorList.h"
#include "PLDHashTable.h"
#include "mozilla/EnumSet.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include "nsIDOMEventListener.h" #include "nsIDOMEventListener.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsISupports.h"
#include "nsITimer.h"
#include "nsINamed.h"
#include "nsHashtablesFwd.h"
#include "nsCycleCollectionParticipant.h" #include "mozilla/EnumSet.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/Result.h" #include "mozilla/Result.h"
#include "mozilla/dom/WindowGlobalChild.h"
namespace mozilla::dom { namespace mozilla::dom {
class BrowsingContext;
class Element; class Element;
class EventTarget; class EventTarget;
class SessionStoreDataCollector; class SessionStoreChild;
class BrowsingContext; class WindowContext;
class SessionStoreChangeListener final : public nsIObserver, class SessionStoreChangeListener final : public nsINamed,
public nsIObserver,
public nsITimerCallback,
public nsIDOMEventListener { public nsIDOMEventListener {
public: public:
NS_DECL_NSINAMED
NS_DECL_NSIOBSERVER NS_DECL_NSIOBSERVER
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIDOMEVENTLISTENER NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SessionStoreChangeListener, NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SessionStoreChangeListener,
@@ -40,7 +54,29 @@ class SessionStoreChangeListener final : public nsIObserver,
void UpdateEventTargets(); void UpdateEventTargets();
void Flush(); void FlushSessionStore();
enum class Change { None, Input, Scroll };
static SessionStoreChangeListener* CollectSessionStoreData(
WindowContext* aWindowContext, const EnumSet<Change>& aChanges);
static void FlushAllSessionStoreData(WindowContext* aWindowContext);
void SetActor(SessionStoreChild* aSessionStoreChild);
void SetEpoch(uint32_t aEpoch) { mEpoch = aEpoch; }
uint32_t GetEpoch() const { return mEpoch; }
BrowsingContext* GetBrowsingContext() const { return mBrowsingContext; }
private:
void RecordChange(WindowContext* aWindowContext, EnumSet<Change> aChanges);
public:
using SessionStoreChangeTable =
nsTHashMap<RefPtr<WindowContext>, EnumSet<Change>>;
private: private:
explicit SessionStoreChangeListener(BrowsingContext* aBrowsingContext); explicit SessionStoreChangeListener(BrowsingContext* aBrowsingContext);
@@ -53,8 +89,15 @@ class SessionStoreChangeListener final : public nsIObserver,
void AddEventListeners(); void AddEventListeners();
void RemoveEventListeners(); void RemoveEventListeners();
void EnsureTimer();
RefPtr<BrowsingContext> mBrowsingContext; RefPtr<BrowsingContext> mBrowsingContext;
RefPtr<EventTarget> mCurrentEventTarget; RefPtr<EventTarget> mCurrentEventTarget;
uint32_t mEpoch;
nsCOMPtr<nsITimer> mTimer;
RefPtr<SessionStoreChild> mSessionStoreChild;
SessionStoreChangeTable mSessionStoreChanges;
}; };
} // namespace mozilla::dom } // namespace mozilla::dom

View File

@@ -0,0 +1,226 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/InProcessChild.h"
#include "mozilla/dom/InProcessParent.h"
#include "mozilla/dom/BrowserSessionStore.h"
#include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreParent.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/ipc/Endpoint.h"
#include "nsCOMPtr.h"
#include "nsFrameLoader.h"
using namespace mozilla;
using namespace mozilla::dom;
class nsIDocShell;
static already_AddRefed<TabListener> CreateTabListener(nsIDocShell* aDocShell) {
RefPtr<TabListener> tabListener =
mozilla::MakeRefPtr<TabListener>(aDocShell, nullptr);
nsresult rv = tabListener->Init();
if (NS_FAILED(rv)) {
return nullptr;
}
return tabListener.forget();
}
already_AddRefed<SessionStoreChild> SessionStoreChild::GetOrCreate(
BrowsingContext* aBrowsingContext, Element* aOwnerElement) {
RefPtr<TabListener> tabListener =
CreateTabListener(aBrowsingContext->GetDocShell());
if (!tabListener) {
return nullptr;
}
RefPtr<SessionStoreChangeListener> sessionStoreChangeListener =
SessionStoreChangeListener::Create(aBrowsingContext);
if (!sessionStoreChangeListener) {
return nullptr;
}
RefPtr<SessionStoreChild> sessionStoreChild =
new SessionStoreChild(tabListener, sessionStoreChangeListener);
sessionStoreChangeListener->SetActor(sessionStoreChild);
if (XRE_IsParentProcess()) {
MOZ_DIAGNOSTIC_ASSERT(aOwnerElement);
InProcessChild* inProcessChild = InProcessChild::Singleton();
InProcessParent* inProcessParent = InProcessParent::Singleton();
if (!inProcessChild || !inProcessParent) {
return nullptr;
}
RefPtr<BrowserSessionStore> sessionStore =
BrowserSessionStore::GetOrCreate(aBrowsingContext->Canonical()->Top());
if (!sessionStore) {
return nullptr;
}
CanonicalBrowsingContext* browsingContext = aBrowsingContext->Canonical();
RefPtr<SessionStoreParent> sessionStoreParent =
new SessionStoreParent(browsingContext, sessionStore);
ManagedEndpoint<PSessionStoreParent> endpoint =
inProcessChild->OpenPSessionStoreEndpoint(sessionStoreChild);
inProcessParent->BindPSessionStoreEndpoint(std::move(endpoint),
sessionStoreParent);
} else {
MOZ_DIAGNOSTIC_ASSERT(!aOwnerElement);
RefPtr<BrowserChild> browserChild =
BrowserChild::GetFrom(aBrowsingContext->GetDOMWindow());
MOZ_DIAGNOSTIC_ASSERT(browserChild);
MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext->IsInProcess());
sessionStoreChild = static_cast<SessionStoreChild*>(
browserChild->SendPSessionStoreConstructor(sessionStoreChild));
}
return sessionStoreChild.forget();
}
/* static */
SessionStoreChild* SessionStoreChild::From(WindowGlobalChild* aWindowChild) {
if (!aWindowChild) {
return nullptr;
}
// If `aWindowChild` is inprocess
if (RefPtr<BrowserChild> browserChild = aWindowChild->GetBrowserChild()) {
return browserChild->GetSessionStoreChild();
}
if (XRE_IsContentProcess()) {
return nullptr;
}
WindowGlobalParent* windowParent = aWindowChild->WindowContext()->Canonical();
if (!windowParent) {
return nullptr;
}
RefPtr<nsFrameLoader> frameLoader = windowParent->GetRootFrameLoader();
if (!frameLoader) {
return nullptr;
}
return frameLoader->GetSessionStoreChild();
}
SessionStoreChild::SessionStoreChild(
TabListener* aSessionStoreListener,
SessionStoreChangeListener* aSessionStoreChangeListener)
: mSessionStoreListener(aSessionStoreListener),
mSessionStoreChangeListener(aSessionStoreChangeListener) {}
void SessionStoreChild::SetEpoch(uint32_t aEpoch) {
if (mSessionStoreListener) {
mSessionStoreListener->SetEpoch(aEpoch);
}
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->SetEpoch(aEpoch);
}
}
void SessionStoreChild::SetOwnerContent(Element* aElement) {
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->FlushSessionStore();
}
if (!aElement) {
return;
}
if (mSessionStoreListener) {
mSessionStoreListener->SetOwnerContent(aElement);
}
}
void SessionStoreChild::Stop() {
if (mSessionStoreListener) {
mSessionStoreListener->RemoveListeners();
mSessionStoreListener = nullptr;
}
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->Stop();
}
}
void SessionStoreChild::UpdateEventTargets() {
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->UpdateEventTargets();
}
}
void SessionStoreChild::UpdateSessionStore() {
if (!mSessionStoreListener) {
// This is the case when we're shutting down, and expect a final update.
Unused << SendSessionStoreUpdate(Nothing(), Nothing(), false, 0);
return;
}
RefPtr<ContentSessionStore> store = mSessionStoreListener->GetSessionStore();
Maybe<nsCString> docShellCaps;
if (store->IsDocCapChanged()) {
docShellCaps.emplace(store->GetDocShellCaps());
}
Maybe<bool> privatedMode;
if (store->IsPrivateChanged()) {
privatedMode.emplace(store->GetPrivateModeEnabled());
}
Unused << SendSessionStoreUpdate(docShellCaps, privatedMode,
store->GetAndClearSHistoryChanged(),
mSessionStoreListener->GetEpoch());
}
void SessionStoreChild::FlushSessionStore() {
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->FlushSessionStore();
}
}
void SessionStoreChild::UpdateSHistoryChanges() {
if (mSessionStoreListener) {
mSessionStoreListener->UpdateSHistoryChanges();
}
}
mozilla::ipc::IPCResult SessionStoreChild::RecvFlushTabState(
FlushTabStateResolver&& aResolver) {
if (mSessionStoreChangeListener) {
if (BrowsingContext* context =
mSessionStoreChangeListener->GetBrowsingContext()) {
if (auto* docShell = nsDocShell::Cast(context->GetDocShell())) {
docShell->CollectWireframe();
}
}
mSessionStoreChangeListener->FlushSessionStore();
}
aResolver(true);
return IPC_OK();
}
NS_IMPL_CYCLE_COLLECTION(SessionStoreChild, mSessionStoreListener,
mSessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SessionStoreChild, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SessionStoreChild, Release)

View File

@@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SessionStoreChild_h
#define mozilla_dom_SessionStoreChild_h
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/dom/PSessionStoreChild.h"
#include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreListener.h"
#include "mozilla/RefPtr.h"
#include "nsCycleCollectionParticipant.h"
namespace mozilla::dom {
class BrowsingContext;
class SessionStoreChangeListener;
class TabListener;
class SessionStoreChild final : public PSessionStoreChild {
public:
static already_AddRefed<SessionStoreChild> GetOrCreate(
BrowsingContext* aBrowsingContext, Element* aOwnerElement = nullptr);
static SessionStoreChild* From(WindowGlobalChild* aWindowChild);
void SetEpoch(uint32_t aEpoch);
void SetOwnerContent(Element* aElement);
void Stop();
void UpdateEventTargets();
void UpdateSessionStore();
void FlushSessionStore();
void UpdateSHistoryChanges();
SessionStoreChangeListener* GetSessionStoreChangeListener() const {
return mSessionStoreChangeListener;
}
mozilla::ipc::IPCResult RecvFlushTabState(FlushTabStateResolver&& aResolver);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SessionStoreChild)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(SessionStoreChild)
private:
SessionStoreChild(TabListener* aSessionStoreListener,
SessionStoreChangeListener* aSessionStoreChangeListener);
~SessionStoreChild() = default;
RefPtr<TabListener> mSessionStoreListener;
RefPtr<SessionStoreChangeListener> mSessionStoreChangeListener;
};
} // namespace mozilla::dom
#endif // mozilla_dom_SessionStoreChild_h

View File

@@ -1,180 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/Assertions.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/sessionstore/SessionStoreTypes.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "nsGenericHTMLElement.h"
#include "nsIContentInlines.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION(SessionStoreDataCollector, mWindowChild, mTimer)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStoreDataCollector)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStoreDataCollector)
NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStoreDataCollector)
NS_IMETHODIMP
SessionStoreDataCollector::GetName(nsACString& aName) {
aName.AssignLiteral("SessionStoreDataCollector");
return NS_OK;
}
NS_IMETHODIMP
SessionStoreDataCollector::Notify(nsITimer* aTimer) {
Collect();
return NS_OK;
}
/* static */ already_AddRefed<SessionStoreDataCollector>
SessionStoreDataCollector::CollectSessionStoreData(
WindowGlobalChild* aWindowChild) {
MOZ_RELEASE_ASSERT(SessionStoreUtils::NATIVE_LISTENER);
MOZ_DIAGNOSTIC_ASSERT(aWindowChild);
RefPtr<SessionStoreDataCollector> listener =
aWindowChild->GetSessionStoreDataCollector();
uint32_t epoch =
aWindowChild->BrowsingContext()->Top()->GetSessionStoreEpoch();
if (listener) {
MOZ_DIAGNOSTIC_ASSERT_IF(
!StaticPrefs::browser_sessionstore_debug_no_auto_updates(),
listener->mTimer);
if (listener->mEpoch == epoch) {
return listener.forget();
}
if (listener->mTimer) {
listener->mTimer->Cancel();
}
}
listener = new SessionStoreDataCollector(aWindowChild, epoch);
if (!StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
auto result = NS_NewTimerWithCallback(
listener, StaticPrefs::browser_sessionstore_interval(),
nsITimer::TYPE_ONE_SHOT);
if (result.isErr()) {
return nullptr;
}
listener->mTimer = result.unwrap();
}
aWindowChild->SetSessionStoreDataCollector(listener);
return listener.forget();
}
void SessionStoreDataCollector::RecordInputChange() { mInputChanged = true; }
void SessionStoreDataCollector::RecordScrollChange() { mScrollChanged = true; }
void SessionStoreDataCollector::Flush() { Collect(); }
void SessionStoreDataCollector::Cancel() {
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
mWindowChild->SetSessionStoreDataCollector(nullptr);
}
void SessionStoreDataCollector::Collect() {
if (this != mWindowChild->GetSessionStoreDataCollector()) {
return;
}
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
nsGlobalWindowInner* inner = mWindowChild->GetWindowGlobal();
if (!inner) {
return;
}
Document* document = inner->GetDocument();
if (!document) {
return;
}
Maybe<sessionstore::FormData> maybeFormData;
if (mInputChanged) {
maybeFormData.emplace();
auto& formData = maybeFormData.ref();
uint32_t size = SessionStoreUtils::CollectFormData(document, formData);
Element* body = document->GetBody();
if (body && body->IsInDesignMode()) {
IgnoredErrorResult result;
body->GetInnerHTML(formData.innerHTML(), result);
size += formData.innerHTML().Length();
if (!result.Failed()) {
formData.hasData() = true;
}
}
if (size > StaticPrefs::browser_sessionstore_dom_form_max_limit()) {
maybeFormData = Nothing();
}
}
PresShell* presShell = document->GetPresShell();
Maybe<nsPoint> maybeScroll;
if (mScrollChanged && presShell) {
maybeScroll = Some(presShell->GetVisualViewportOffset());
}
if (!mWindowChild->CanSend()) {
return;
}
if (RefPtr<WindowGlobalParent> windowParent =
mWindowChild->GetParentActor()) {
windowParent->WriteFormDataAndScrollToSessionStore(maybeFormData,
maybeScroll, mEpoch);
} else {
mWindowChild->SendUpdateSessionStore(maybeFormData, maybeScroll, mEpoch);
}
mWindowChild->SetSessionStoreDataCollector(nullptr);
}
SessionStoreDataCollector::SessionStoreDataCollector(
WindowGlobalChild* aWindowChild, uint32_t aEpoch)
: mWindowChild(aWindowChild),
mTimer(nullptr),
mEpoch(aEpoch),
mInputChanged(false),
mScrollChanged(false) {}
SessionStoreDataCollector::~SessionStoreDataCollector() {
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
}
} // namespace mozilla::dom

View File

@@ -1,68 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SessionStoreDataCollector_h
#define mozilla_dom_SessionStoreDataCollector_h
#include "ErrorList.h"
#include "nsITimer.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/RefPtr.h"
namespace mozilla::dom {
class BrowserChild;
class EventTarget;
class WindowGlobalChild;
namespace sessionstore {
class FormData;
}
class SessionStoreDataCollector final : public nsITimerCallback,
public nsINamed {
public:
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SessionStoreDataCollector,
nsITimerCallback)
enum class Change { Input, Scroll };
static already_AddRefed<SessionStoreDataCollector> CollectSessionStoreData(
WindowGlobalChild* aWindowChild);
void RecordInputChange();
void RecordScrollChange();
void Flush();
void Cancel();
private:
void Collect();
nsresult Apply(Maybe<sessionstore::FormData>&& aFormData,
Maybe<nsPoint>&& aScroll);
SessionStoreDataCollector(WindowGlobalChild* aWindowChild, uint32_t aEpoch);
~SessionStoreDataCollector();
RefPtr<WindowGlobalChild> mWindowChild;
nsCOMPtr<nsITimer> mTimer;
uint32_t mEpoch;
bool mInputChanged : 1;
bool mScrollChanged : 1;
};
} // namespace mozilla::dom
#endif // mozilla_dom_SessionStoreDataCollector_h

View File

@@ -0,0 +1,172 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/SessionStoreFormData.h"
#include "mozilla/Assertions.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/BrowserSessionStoreBinding.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/sessionstore/SessionStoreTypes.h"
#include "nsContentUtils.h"
#include "js/JSON.h"
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStoreFormData)
NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStoreFormData)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(SessionStoreFormData, mChildren)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStoreFormData)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
nsISupports* SessionStoreFormData::GetParentObject() const {
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}
JSObject* SessionStoreFormData::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return SessionStoreFormData_Binding::Wrap(aCx, this, aGivenProto);
}
void SessionStoreFormData::GetUrl(nsACString& aUrl) const {
if (mUrl.IsEmpty()) {
aUrl.SetIsVoid(true);
} else {
aUrl = mUrl;
}
}
void SessionStoreFormData::GetId(
JSContext* aCx,
Nullable<Record<nsString, OwningStringOrBooleanOrObject>>& aId) {
if (mId.IsEmpty() ||
NS_FAILED(SessionStoreUtils::ConstructFormDataValues(
aCx, mId, aId.SetValue().Entries(), mParseSessionData))) {
aId.SetNull();
}
// SessionStoreFormData.id is now stored in a slot, so we can free our
// internal state.
mId.Clear();
}
void SessionStoreFormData::GetXpath(
JSContext* aCx,
Nullable<Record<nsString, OwningStringOrBooleanOrObject>>& aXpath) {
if (mXpath.IsEmpty() ||
NS_FAILED(SessionStoreUtils::ConstructFormDataValues(
aCx, mXpath, aXpath.SetValue().Entries(), mParseSessionData))) {
aXpath.SetNull();
}
// SessionStoreFormData.xpath is now stored in a slot, so we can free our
// internal state.
mXpath.Clear();
}
void SessionStoreFormData::GetInnerHTML(nsAString& aInnerHTML) {
if (mInnerHTML.IsEmpty()) {
SetDOMStringToNull(aInnerHTML);
} else {
aInnerHTML = mInnerHTML;
}
// SessionStoreFormData.innerHTML is now stored in a slot, so we can free our
// internal state.
mInnerHTML.SetIsVoid(true);
}
SessionStoreFormData::ChildrenArray& SessionStoreFormData::Children() {
return mChildren;
}
void SessionStoreFormData::GetChildren(
Nullable<ChildrenArray>& aChildren) const {
if (!mChildren.IsEmpty()) {
aChildren.SetValue() = mChildren.Clone();
} else {
aChildren.SetNull();
}
}
void SessionStoreFormData::ToJSON(JSContext* aCx,
JS::MutableHandleObject aRetval) {
JS::RootedObject self(aCx);
{
JS::RootedValue value(aCx);
if (!GetOrCreateDOMReflector(aCx, this, &value)) {
return;
}
self.set(value.toObjectOrNull());
}
JS::Rooted<JSObject*> result(aCx, JS_NewPlainObject(aCx));
if (!IsEmpty()) {
for (const auto& name :
{u"url"_ns, u"id"_ns, u"xpath"_ns, u"innerHTML"_ns}) {
if (!SessionStoreUtils::CopyProperty(aCx, result, self, name)) {
return;
}
}
SessionStoreUtils::CopyChildren(aCx, result, mChildren);
}
aRetval.set(result);
}
void SessionStoreFormData::Update(const CollectedType& aFormData) {
SessionStoreFormData_Binding::ClearCachedUrlValue(this);
SessionStoreFormData_Binding::ClearCachedIdValue(this);
SessionStoreFormData_Binding::ClearCachedXpathValue(this);
SessionStoreFormData_Binding::ClearCachedInnerHTMLValue(this);
if (!aFormData.hasData()) {
mParseSessionData = false;
mHasData = false;
mUrl = ""_ns;
mId.Clear();
mXpath.Clear();
mInnerHTML = u""_ns;
return;
}
mHasData = true;
mUrl = aFormData.uri();
// We want to avoid saving data for about:sessionrestore as a string.
// Since it's stored in the form as stringified JSON, stringifying
// further causes an explosion of escape characters. cf. bug 467409
mParseSessionData =
mUrl == "about:sessionrestore"_ns || mUrl == "about:welcomeback"_ns;
mInnerHTML = aFormData.innerHTML();
mId.Assign(aFormData.id());
mXpath.Assign(aFormData.xpath());
}
void SessionStoreFormData::ClearCachedChildren() {
SessionStoreFormData_Binding::ClearCachedChildrenValue(this);
}
/* static */ bool SessionStoreFormData::HasData(
const CollectedType& aFormData) {
return aFormData.hasData();
}
bool SessionStoreFormData::IsEmpty() const {
return !mHasData && mChildren.IsEmpty();
}

View File

@@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SessionStoreFormData_h
#define mozilla_dom_SessionStoreFormData_h
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "nsTArrayForwardDeclare.h"
#include "nsWrapperCache.h"
class nsIGlobalObject;
namespace mozilla::dom {
namespace sessionstore {
class FormData;
}
class BrowsingContext;
class OwningByteStringOrObjectOrNull;
class OwningStringOrObjectOrNull;
class WindowGlobalParent;
class SessionStoreFormData final : public nsISupports,
public nsWrapperCache,
public SupportsWeakPtr {
public:
using CollectedType = sessionstore::FormData;
using LocationType = WeakPtr<SessionStoreFormData>;
using ChildrenArray = nsTArray<RefPtr<SessionStoreFormData>>;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SessionStoreFormData)
nsISupports* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetUrl(nsACString& aUrl) const;
void GetId(JSContext* aCx,
Nullable<Record<nsString, OwningStringOrBooleanOrObject>>& aId);
void GetXpath(
JSContext* aCx,
Nullable<Record<nsString, OwningStringOrBooleanOrObject>>& aXpath);
void GetInnerHTML(nsAString& aInnerHTML);
ChildrenArray& Children();
void GetChildren(Nullable<ChildrenArray>& aChildren) const;
void ToJSON(JSContext* aCx, JS::MutableHandleObject aRetval);
void Update(const CollectedType& aFormData);
void ClearCachedChildren();
static bool HasData(const CollectedType& aFormData);
bool IsEmpty() const;
private:
~SessionStoreFormData() = default;
bool mParseSessionData = false;
bool mHasData = false;
nsCString mUrl;
nsTArray<sessionstore::FormEntry> mId;
nsTArray<sessionstore::FormEntry> mXpath;
nsString mInnerHTML;
ChildrenArray mChildren;
};
} // namespace mozilla::dom
#endif // mozilla_dom_SessionStoreFormData_h

View File

@@ -17,10 +17,6 @@ interface nsISessionStoreFunctions : nsISupports {
in jsval aPermanentKey, in uint32_t aEpoch, in boolean aCollectSHistory, in jsval aPermanentKey, in uint32_t aEpoch, in boolean aCollectSHistory,
in jsval aData); in jsval aData);
void UpdateSessionStoreForWindow(
in Element aBrowser, in BrowsingContext aBrowsingContext,
in jsval aPermanentKey, in uint32_t aEpoch, in jsval aData);
void UpdateSessionStoreForStorage( void UpdateSessionStoreForStorage(
in Element aBrowser, in BrowsingContext aBrowsingContext, in Element aBrowser, in BrowsingContext aBrowsingContext,
in jsval aPermanentKey, in uint32_t aEpoch, in jsval aData); in jsval aPermanentKey, in uint32_t aEpoch, in jsval aData);

View File

@@ -28,22 +28,6 @@ function UpdateSessionStore(
); );
} }
function UpdateSessionStoreForWindow(
aBrowser,
aBrowsingContext,
aPermanentKey,
aEpoch,
aData
) {
return SessionStoreFuncInternal.updateSessionStoreForWindow(
aBrowser,
aBrowsingContext,
aPermanentKey,
aEpoch,
aData
);
}
function UpdateSessionStoreForStorage( function UpdateSessionStoreForStorage(
aBrowser, aBrowser,
aBrowsingContext, aBrowsingContext,
@@ -60,11 +44,7 @@ function UpdateSessionStoreForStorage(
); );
} }
var EXPORTED_SYMBOLS = [ var EXPORTED_SYMBOLS = ["UpdateSessionStore", "UpdateSessionStoreForStorage"];
"UpdateSessionStore",
"UpdateSessionStoreForWindow",
"UpdateSessionStoreForStorage",
];
var SessionStoreFuncInternal = { var SessionStoreFuncInternal = {
updateSessionStore: function SSF_updateSessionStore( updateSessionStore: function SSF_updateSessionStore(
@@ -75,12 +55,14 @@ var SessionStoreFuncInternal = {
aCollectSHistory, aCollectSHistory,
aData aData
) { ) {
let currentData = {}; let { formdata, scroll } = aData;
if (aData.docShellCaps != undefined) {
currentData.disallow = aData.docShellCaps ? aData.docShellCaps : null; if (formdata) {
aData.formdata = formdata.toJSON();
} }
if (aData.isPrivate != undefined) {
currentData.isPrivate = aData.isPrivate; if (scroll) {
aData.scroll = scroll.toJSON();
} }
SessionStore.updateSessionStoreFromTablistener( SessionStore.updateSessionStoreFromTablistener(
@@ -88,28 +70,13 @@ var SessionStoreFuncInternal = {
aBrowsingContext, aBrowsingContext,
aPermanentKey, aPermanentKey,
{ {
data: currentData, data: aData,
epoch: aEpoch, epoch: aEpoch,
sHistoryNeeded: aCollectSHistory, sHistoryNeeded: aCollectSHistory,
} }
); );
}, },
updateSessionStoreForWindow: function SSF_updateSessionStoreForWindow(
aBrowser,
aBrowsingContext,
aPermanentKey,
aEpoch,
aData
) {
SessionStore.updateSessionStoreFromTablistener(
aBrowser,
aBrowsingContext,
aPermanentKey,
{ data: { windowstatechange: aData }, epoch: aEpoch }
);
},
updateSessionStoreForStorage: function SSF_updateSessionStoreForWindow( updateSessionStoreForStorage: function SSF_updateSessionStoreForWindow(
aBrowser, aBrowser,
aBrowsingContext, aBrowsingContext,

View File

@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/dom/BrowserSessionStoreBinding.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/dom/SessionStoreListener.h" #include "mozilla/dom/SessionStoreListener.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtils.h"
@@ -384,20 +385,21 @@ nsresult TabListener::Observe(nsISupports* aSubject, const char* aTopic,
return NS_ERROR_UNEXPECTED; return NS_ERROR_UNEXPECTED;
} }
bool TabListener::ForceFlushFromParent() { void TabListener::ForceFlushFromParent() {
if (!XRE_IsParentProcess()) { if (!XRE_IsParentProcess()) {
return false; return;
} }
if (!mSessionStore) { if (!mSessionStore) {
return false; return;
} }
return UpdateSessionStore(true);
UpdateSessionStore(true);
} }
bool TabListener::UpdateSessionStore(bool aIsFlush) { void TabListener::UpdateSessionStore(bool aIsFlush) {
if (!aIsFlush) { if (!aIsFlush) {
if (!mSessionStore || !mSessionStore->UpdateNeeded()) { if (!mSessionStore || !mSessionStore->UpdateNeeded()) {
return false; return;
} }
} }
@@ -405,33 +407,33 @@ bool TabListener::UpdateSessionStore(bool aIsFlush) {
BrowserChild* browserChild = BrowserChild::GetFrom(mDocShell); BrowserChild* browserChild = BrowserChild::GetFrom(mDocShell);
if (browserChild) { if (browserChild) {
StopTimerForUpdate(); StopTimerForUpdate();
return browserChild->UpdateSessionStore(); browserChild->UpdateSessionStore();
} }
return false; return;
} }
BrowsingContext* context = mDocShell->GetBrowsingContext(); BrowsingContext* context = mDocShell->GetBrowsingContext();
if (!context) { if (!context) {
return false; return;
} }
uint32_t chromeFlags = 0; uint32_t chromeFlags = 0;
nsCOMPtr<nsIDocShellTreeOwner> treeOwner; nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
if (!treeOwner) { if (!treeOwner) {
return false; return;
} }
nsCOMPtr<nsIAppWindow> window(do_GetInterface(treeOwner)); nsCOMPtr<nsIAppWindow> window(do_GetInterface(treeOwner));
if (!window) { if (!window) {
return false; return;
} }
if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) { if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
return false; return;
} }
UpdateSessionStoreData data; UpdateSessionStoreData data;
if (mSessionStore->IsDocCapChanged()) { if (mSessionStore->IsDocCapChanged()) {
data.mDocShellCaps.Construct() = mSessionStore->GetDocShellCaps(); data.mDisallow.Construct() = mSessionStore->GetDocShellCaps();
} }
if (mSessionStore->IsPrivateChanged()) { if (mSessionStore->IsPrivateChanged()) {
data.mIsPrivate.Construct() = mSessionStore->GetPrivateModeEnabled(); data.mIsPrivate.Construct() = mSessionStore->GetPrivateModeEnabled();
@@ -440,16 +442,18 @@ bool TabListener::UpdateSessionStore(bool aIsFlush) {
nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule( nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule(
"resource://gre/modules/SessionStoreFunctions.jsm", fallible); "resource://gre/modules/SessionStoreFunctions.jsm", fallible);
nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs); nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
NS_ENSURE_TRUE(wrapped, false); if (!wrapped) {
return;
}
AutoJSAPI jsapi; AutoJSAPI jsapi;
if (!jsapi.Init(wrapped->GetJSObjectGlobal())) { if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
return false; return;
} }
JS::Rooted<JS::Value> update(jsapi.cx()); JS::Rooted<JS::Value> update(jsapi.cx());
if (!ToJSValue(jsapi.cx(), data, &update)) { if (!ToJSValue(jsapi.cx(), data, &update)) {
return false; return;
} }
JS::RootedValue key(jsapi.cx(), context->Canonical()->Top()->PermanentKey()); JS::RootedValue key(jsapi.cx(), context->Canonical()->Top()->PermanentKey());
@@ -458,11 +462,10 @@ bool TabListener::UpdateSessionStore(bool aIsFlush) {
mOwnerContent, context, key, mEpoch, mOwnerContent, context, key, mEpoch,
mSessionStore->GetAndClearSHistoryChanged(), update); mSessionStore->GetAndClearSHistoryChanged(), update);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return false; return;
} }
StopTimerForUpdate(); StopTimerForUpdate();
return true;
} }
void TabListener::RemoveListeners() { void TabListener::RemoveListeners() {

View File

@@ -73,7 +73,7 @@ class TabListener : public nsIDOMEventListener,
nsresult Init(); nsresult Init();
ContentSessionStore* GetSessionStore() { return mSessionStore; } ContentSessionStore* GetSessionStore() { return mSessionStore; }
// the function is called only when TabListener is in parent process // the function is called only when TabListener is in parent process
bool ForceFlushFromParent(); void ForceFlushFromParent();
void RemoveListeners(); void RemoveListeners();
void SetEpoch(uint32_t aEpoch) { mEpoch = aEpoch; } void SetEpoch(uint32_t aEpoch) { mEpoch = aEpoch; }
uint32_t GetEpoch() { return mEpoch; } uint32_t GetEpoch() { return mEpoch; }
@@ -94,7 +94,7 @@ class TabListener : public nsIDOMEventListener,
void StopTimerForUpdate(); void StopTimerForUpdate();
void AddEventListeners(); void AddEventListeners();
void RemoveEventListeners(); void RemoveEventListeners();
bool UpdateSessionStore(bool aIsFlush = false); void UpdateSessionStore(bool aIsFlush = false);
virtual ~TabListener(); virtual ~TabListener();
nsCOMPtr<nsIDocShell> mDocShell; nsCOMPtr<nsIDocShell> mDocShell;

View File

@@ -6,6 +6,7 @@
#define mozilla_dom_SessionStoreMessageUtils_h #define mozilla_dom_SessionStoreMessageUtils_h
#include "ipc/IPCMessageUtils.h" #include "ipc/IPCMessageUtils.h"
#include "mozilla/ipc/IPDLParamTraits.h"
#include "SessionStoreData.h" #include "SessionStoreData.h"
#include "SessionStoreUtils.h" #include "SessionStoreUtils.h"
#include "SessionStoreRestoreData.h" #include "SessionStoreRestoreData.h"

View File

@@ -0,0 +1,194 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/SessionStoreParent.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/BrowserSessionStore.h"
#include "mozilla/dom/BrowserSessionStoreBinding.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/InProcessChild.h"
#include "mozilla/dom/InProcessParent.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h"
#include "SessionStoreFunctions.h"
#include "nsISupports.h"
#include "nsIXULRuntime.h"
#include "nsImportModule.h"
#include "nsIXPConnect.h"
using namespace mozilla;
using namespace mozilla::dom;
SessionStoreParent::SessionStoreParent(
CanonicalBrowsingContext* aBrowsingContext,
BrowserSessionStore* aSessionStore)
: mBrowsingContext(aBrowsingContext), mSessionStore(aSessionStore) {}
static void SessionStoreUpdate(CanonicalBrowsingContext* aBrowsingContext,
const Maybe<nsCString>& aDocShellCaps,
const Maybe<bool>& aPrivatedMode,
bool aNeedCollectSHistory, uint32_t aEpoch) {
UpdateSessionStoreData data;
if (aDocShellCaps.isSome()) {
auto& disallow = data.mDisallow.Construct();
if (!aDocShellCaps->IsEmpty()) {
disallow = aDocShellCaps.value();
} else {
disallow.SetIsVoid(true);
}
}
if (aPrivatedMode.isSome()) {
data.mIsPrivate.Construct() = aPrivatedMode.value();
}
RefPtr<BrowserSessionStore> sessionStore =
BrowserSessionStore::GetOrCreate(aBrowsingContext->Top());
SessionStoreFormData* formData = sessionStore->GetFormdata();
data.mFormdata.Construct(formData);
SessionStoreScrollData* scroll = sessionStore->GetScroll();
data.mScroll.Construct(scroll);
nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule(
"resource://gre/modules/SessionStoreFunctions.jsm", fallible);
nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
if (!wrapped) {
return;
}
AutoJSAPI jsapi;
if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
return;
}
JS::Rooted<JS::Value> update(jsapi.cx());
if (!ToJSValue(jsapi.cx(), data, &update)) {
return;
}
JS::RootedValue key(jsapi.cx(), aBrowsingContext->Top()->PermanentKey());
Unused << funcs->UpdateSessionStore(nullptr, aBrowsingContext, key, aEpoch,
aNeedCollectSHistory, update);
}
void SessionStoreParent::FlushAllSessionStoreChildren(
const std::function<void()>& aDone) {
if (!mBrowsingContext) {
aDone();
return;
}
nsTArray<RefPtr<SessionStoreParent::FlushTabStatePromise>> flushPromises;
// We're special casing this for when the SessionStore{Child, Parent} have
// been created in the same process. This is only ever true for the parent
// process session store actor, and is needed because
// nsFrameLoader::RequestTabStateFlush expect flushes to happen faster than we
// can manage by using the common path of sending a message the
// SessionStoreChild. Ideally we should be able to do just that, but not
// without more work.
if (InProcessParent::ChildActorFor(this)) {
// Here we assume that the session store data collection only collect for in
// (parent-)process content type browsing contexts, which we only flush one
// session store actor.
flushPromises.AppendElement(FlushSessionStore());
} else {
// While here we flush all participating actors.
BrowserParent* browserParent = static_cast<BrowserParent*>(Manager());
browserParent->VisitAll([&flushPromises](BrowserParent* aBrowser) {
if (PSessionStoreParent* sessionStoreParent =
SingleManagedOrNull(aBrowser->ManagedPSessionStoreParent())) {
flushPromises.AppendElement(
static_cast<SessionStoreParent*>(sessionStoreParent)
->FlushSessionStore());
}
});
}
RefPtr<SessionStoreParent::FlushTabStatePromise::AllPromiseType>
flushPromise = SessionStoreParent::FlushTabStatePromise::All(
GetMainThreadSerialEventTarget(), flushPromises);
mBrowsingContext->UpdateSessionStoreSessionStorage([aDone, flushPromise]() {
flushPromise->Then(GetCurrentSerialEventTarget(), __func__,
[aDone]() { aDone(); });
});
}
already_AddRefed<SessionStoreParent::FlushTabStatePromise>
SessionStoreParent::FlushSessionStore() {
if (!mBrowsingContext) {
return nullptr;
}
RefPtr<SessionStoreParent::FlushTabStatePromise> promise =
SendFlushTabState();
return promise.forget();
}
void SessionStoreParent::FinalFlushAllSessionStoreChildren(
const std::function<void()>& aDone) {
if (!mBrowsingContext) {
aDone();
return;
}
SessionStoreChild* sessionStoreChild =
static_cast<SessionStoreChild*>(InProcessParent::ChildActorFor(this));
if (!sessionStoreChild || mozilla::SessionHistoryInParent()) {
return FlushAllSessionStoreChildren(aDone);
}
sessionStoreChild->FlushSessionStore();
mBrowsingContext->UpdateSessionStoreSessionStorage(aDone);
}
mozilla::ipc::IPCResult SessionStoreParent::RecvSessionStoreUpdate(
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
const bool aNeedCollectSHistory, const uint32_t& aEpoch) {
if (!mBrowsingContext) {
return IPC_OK();
}
SessionStoreUpdate(mBrowsingContext, aDocShellCaps, aPrivatedMode,
aNeedCollectSHistory, aEpoch);
return IPC_OK();
}
mozilla::ipc::IPCResult SessionStoreParent::RecvIncrementalSessionStoreUpdate(
const MaybeDiscarded<BrowsingContext>& aBrowsingContext,
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch) {
if (!aBrowsingContext.IsNull()) {
mSessionStore->UpdateSessionStore(
aBrowsingContext.GetMaybeDiscarded()->Canonical(), aFormData,
aScrollPosition, aEpoch);
}
return IPC_OK();
}
mozilla::ipc::IPCResult SessionStoreParent::RecvResetSessionStore(
const MaybeDiscarded<BrowsingContext>& aBrowsingContext, uint32_t aEpoch) {
if (!aBrowsingContext.IsNull()) {
mSessionStore->RemoveSessionStore(
aBrowsingContext.GetMaybeDiscarded()->Canonical());
}
return IPC_OK();
}
NS_IMPL_CYCLE_COLLECTION(SessionStoreParent, mBrowsingContext, mSessionStore)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SessionStoreParent, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SessionStoreParent, Release)

View File

@@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SessionStoreParent_h
#define mozilla_dom_SessionStoreParent_h
#include "mozilla/dom/BrowserSessionStore.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/PSessionStoreParent.h"
#include "mozilla/dom/WindowGlobalParent.h"
namespace mozilla::dom {
class BrowserParent;
class SessionStoreParent final : public PSessionStoreParent {
public:
SessionStoreParent(CanonicalBrowsingContext* aBrowsingContext,
BrowserSessionStore* aSessionStore);
void FlushAllSessionStoreChildren(const std::function<void()>& aDone);
void FinalFlushAllSessionStoreChildren(const std::function<void()>& aDone);
/**
* Sends data to be stored and instructions to the session store to
* potentially collect data in the parent.
*/
mozilla::ipc::IPCResult RecvSessionStoreUpdate(
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
const bool aNeedCollectSHistory, const uint32_t& aEpoch);
mozilla::ipc::IPCResult RecvIncrementalSessionStoreUpdate(
const MaybeDiscarded<BrowsingContext>& aBrowsingContext,
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch);
mozilla::ipc::IPCResult RecvResetSessionStore(
const MaybeDiscarded<BrowsingContext>& aBrowsingContext, uint32_t aEpoch);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SessionStoreParent)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(SessionStoreParent)
private:
~SessionStoreParent() = default;
already_AddRefed<SessionStoreParent::FlushTabStatePromise>
FlushSessionStore();
RefPtr<CanonicalBrowsingContext> mBrowsingContext;
RefPtr<BrowserSessionStore> mSessionStore;
};
} // namespace mozilla::dom
#endif // mozilla_dom_SessionStoreParent_h

View File

@@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/SessionStoreScrollData.h"
#include <utility>
#include "js/PropertyAndElement.h"
#include "js/TypeDecls.h"
#include "mozilla/Assertions.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/BrowserSessionStoreBinding.h"
#include "mozilla/dom/BrowsingContext.h"
#include "nsPresContext.h"
#include "mozilla/dom/WebIDLGlobalNameHash.h"
#include "js/PropertyDescriptor.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/GeneratedAtomList.h"
#include "js/StructuredClone.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "nsContentUtils.h"
#include "js/Array.h"
#include "js/JSON.h"
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStoreScrollData)
NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStoreScrollData)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(SessionStoreScrollData,
mChildren)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStoreScrollData)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
nsISupports* SessionStoreScrollData::GetParentObject() const {
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
;
}
JSObject* SessionStoreScrollData::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return SessionStoreScrollData_Binding::Wrap(aCx, this, aGivenProto);
}
void SessionStoreScrollData::GetScroll(nsACString& aScroll) const {
int scrollX = nsPresContext::AppUnitsToIntCSSPixels(mScroll.x);
int scrollY = nsPresContext::AppUnitsToIntCSSPixels(mScroll.y);
if ((scrollX != 0) || (scrollY != 0)) {
aScroll = nsPrintfCString("%d,%d", scrollX, scrollY);
} else {
aScroll.SetIsVoid(true);
}
}
SessionStoreScrollData::ChildrenArray& SessionStoreScrollData::Children() {
return mChildren;
}
void SessionStoreScrollData::GetChildren(
Nullable<ChildrenArray>& aChildren) const {
if (!mChildren.IsEmpty()) {
aChildren.SetValue() = mChildren.Clone();
} else {
aChildren.SetNull();
}
}
void SessionStoreScrollData::ToJSON(JSContext* aCx,
JS::MutableHandleObject aRetval) {
JS::RootedObject self(aCx);
{
JS::RootedValue value(aCx);
if (!GetOrCreateDOMReflector(aCx, this, &value)) {
return;
}
self.set(value.toObjectOrNull());
}
JS::RootedObject result(aCx, JS_NewPlainObject(aCx));
if (!IsEmpty()) {
if (HasData(mScroll)) {
if (!SessionStoreUtils::CopyProperty(aCx, result, self, u"scroll"_ns)) {
return;
}
}
SessionStoreUtils::CopyChildren(aCx, result, mChildren);
}
aRetval.set(result);
}
void SessionStoreScrollData::Update(const CollectedType& aUpdate) {
SessionStoreScrollData_Binding::ClearCachedScrollValue(this);
mScroll = aUpdate;
}
void SessionStoreScrollData::ClearCachedChildren() {
SessionStoreScrollData_Binding::ClearCachedChildrenValue(this);
}
/* static */
bool SessionStoreScrollData::HasData(const CollectedType& aPoint) {
int scrollX = nsPresContext::AppUnitsToIntCSSPixels(aPoint.x);
int scrollY = nsPresContext::AppUnitsToIntCSSPixels(aPoint.y);
return scrollX != 0 || scrollY != 0;
}
bool SessionStoreScrollData::IsEmpty() const {
return !HasData(mScroll) && mChildren.IsEmpty();
}

View File

@@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SessionStoreScrollData_h
#define mozilla_dom_SessionStoreScrollData_h
#include "mozilla/WeakPtr.h"
#include "nsPoint.h"
#include "nsTArray.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/WindowGlobalParent.h"
namespace mozilla::dom {
class BrowsingContext;
class WindowGlobalParent;
class OwningByteStringOrObjectOrNull;
class SessionStoreScrollData final : public nsISupports,
public nsWrapperCache,
public SupportsWeakPtr {
public:
using CollectedType = nsPoint;
using LocationType = WeakPtr<SessionStoreScrollData>;
using ChildrenArray = nsTArray<RefPtr<SessionStoreScrollData>>;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SessionStoreScrollData)
nsISupports* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetScroll(nsACString& aScroll) const;
ChildrenArray& Children();
void GetChildren(Nullable<ChildrenArray>& aChildren) const;
void ToJSON(JSContext* aCx, JS::MutableHandleObject aRetval);
void Update(const CollectedType& aUpdate);
void ClearCachedChildren();
static bool HasData(const CollectedType& aPoint);
bool IsEmpty() const;
private:
~SessionStoreScrollData() = default;
nsPoint mScroll;
nsTArray<RefPtr<SessionStoreScrollData>> mChildren;
};
} // namespace mozilla::dom
#endif // mozilla_dom_SessionStoreScrollData_h

View File

@@ -52,6 +52,7 @@ struct FormData {
FormEntry[] id; FormEntry[] id;
FormEntry[] xpath; FormEntry[] xpath;
nsString innerHTML; nsString innerHTML;
nsCString uri;
}; };
struct DocShellRestoreState { struct DocShellRestoreState {

View File

@@ -7,6 +7,7 @@
#include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject
#include "js/JSON.h" #include "js/JSON.h"
#include "js/PropertyAndElement.h" // JS_GetElement #include "js/PropertyAndElement.h" // JS_GetElement
#include "js/TypeDecls.h"
#include "jsapi.h" #include "jsapi.h"
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/dom/AutocompleteInfoBinding.h" #include "mozilla/dom/AutocompleteInfoBinding.h"
@@ -19,6 +20,8 @@
#include "mozilla/dom/RootedDictionary.h" #include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/SessionStorageManager.h" #include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/PBackgroundSessionStorageCache.h" #include "mozilla/dom/PBackgroundSessionStorageCache.h"
#include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtils.h" #include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/txIXPathContext.h" #include "mozilla/dom/txIXPathContext.h"
#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WindowGlobalParent.h"
@@ -300,6 +303,10 @@ void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal,
/* static */ /* static */
void SessionStoreUtils::RestoreScrollPosition( void SessionStoreUtils::RestoreScrollPosition(
nsGlobalWindowInner& aWindow, const nsCString& aScrollPosition) { nsGlobalWindowInner& aWindow, const nsCString& aScrollPosition) {
using Change = mozilla::dom::SessionStoreChangeListener::Change;
SessionStoreChangeListener::CollectSessionStoreData(
aWindow.GetWindowContext(), EnumSet<Change>(Change::Scroll));
nsCCharSeparatedTokenizer tokenizer(aScrollPosition, ','); nsCCharSeparatedTokenizer tokenizer(aScrollPosition, ',');
nsAutoCString token(tokenizer.nextToken()); nsAutoCString token(tokenizer.nextToken());
int pos_X = atoi(token.get()); int pos_X = atoi(token.get());
@@ -1203,6 +1210,7 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
if (!aData.mUrl.WasPassed()) { if (!aData.mUrl.WasPassed()) {
return true; return true;
} }
// Don't restore any data for the given frame if the URL // Don't restore any data for the given frame if the URL
// stored in the form data doesn't match its current URL. // stored in the form data doesn't match its current URL.
nsAutoCString url; nsAutoCString url;
@@ -1210,6 +1218,11 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
if (!aData.mUrl.Value().Equals(url)) { if (!aData.mUrl.Value().Equals(url)) {
return false; return false;
} }
using Change = SessionStoreChangeListener::Change;
SessionStoreChangeListener::CollectSessionStoreData(
aDocument.GetWindowContext(), EnumSet<Change>(Change::Input));
if (aData.mInnerHTML.WasPassed()) { if (aData.mInnerHTML.WasPassed()) {
SetInnerHTML(aDocument, aData.mInnerHTML.Value()); SetInnerHTML(aDocument, aData.mInnerHTML.Value());
} }
@@ -1311,6 +1324,10 @@ void RestoreFormEntry(Element* aNode, const FormEntryValue& aValue) {
void SessionStoreUtils::RestoreFormData( void SessionStoreUtils::RestoreFormData(
Document& aDocument, const nsString& aInnerHTML, Document& aDocument, const nsString& aInnerHTML,
const nsTArray<SessionStoreRestoreData::Entry>& aEntries) { const nsTArray<SessionStoreRestoreData::Entry>& aEntries) {
using Change = SessionStoreChangeListener::Change;
SessionStoreChangeListener::CollectSessionStoreData(
aDocument.GetWindowContext(), EnumSet<Change>(Change::Input));
if (!aInnerHTML.IsEmpty()) { if (!aInnerHTML.IsEmpty()) {
SetInnerHTML(aDocument, aInnerHTML); SetInnerHTML(aDocument, aInnerHTML);
} }
@@ -1698,20 +1715,31 @@ nsresult SessionStoreUtils::ConstructSessionStorageValues(
return NS_OK; return NS_OK;
} }
/* static */ void SessionStoreUtils::ResetSessionStore( /* static */
BrowsingContext* aContext) { bool SessionStoreUtils::CopyProperty(JSContext* aCx, JS::HandleObject aDst,
MOZ_RELEASE_ASSERT(NATIVE_LISTENER); JS::HandleObject aSrc,
WindowContext* windowContext = aContext->GetCurrentWindowContext(); const nsAString& aName) {
if (!windowContext) { JS::RootedId name(aCx);
return; const char16_t* data;
size_t length = aName.GetData(&data);
if (!JS_CharsToId(aCx, JS::TwoByteChars(data, length), &name)) {
return false;
} }
WindowGlobalChild* windowChild = windowContext->GetWindowGlobalChild(); bool found = false;
if (!windowChild || !windowChild->CanSend()) { if (!JS_HasPropertyById(aCx, aSrc, name, &found) || !found) {
return; return true;
} }
uint32_t epoch = aContext->GetSessionStoreEpoch(); JS::RootedValue value(aCx);
if (!JS_GetPropertyById(aCx, aSrc, name, &value)) {
return false;
}
Unused << windowChild->SendResetSessionStore(epoch); if (value.isNullOrUndefined()) {
return true;
}
return JS_DefinePropertyById(aCx, aDst, name, value, JSPROP_ENUMERATE);
} }

View File

@@ -8,6 +8,7 @@
#define mozilla_dom_SessionStoreUtils_h #define mozilla_dom_SessionStoreUtils_h
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h" #include "mozilla/dom/SessionStoreUtilsBinding.h"
@@ -139,14 +140,44 @@ class SessionStoreUtils {
const nsTArray<SSCacheCopy>& aValues, const nsTArray<SSCacheCopy>& aValues,
Record<nsCString, Record<nsString, nsString>>& aStorage); Record<nsCString, Record<nsString, nsString>>& aStorage);
static void ResetSessionStore(BrowsingContext* aContext);
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_THUNDERBIRD) || \ #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_THUNDERBIRD) || \
defined(MOZ_SUITE) defined(MOZ_SUITE)
static constexpr bool NATIVE_LISTENER = false; static constexpr bool NATIVE_LISTENER = false;
#else #else
static constexpr bool NATIVE_LISTENER = true; static constexpr bool NATIVE_LISTENER = true;
#endif #endif
static bool CopyProperty(JSContext* aCx, JS::HandleObject aDst,
JS::HandleObject aSrc, const nsAString& aName);
template <typename T>
static bool CopyChildren(JSContext* aCx, JS::HandleObject aDst,
const nsTArray<RefPtr<T>>& aChildren) {
if (!aChildren.IsEmpty()) {
JS::RootedObject children(aCx,
JS::NewArrayObject(aCx, aChildren.Length()));
for (const auto index : IntegerRange(aChildren.Length())) {
if (!aChildren[index]) {
continue;
}
JS::RootedObject object(aCx);
aChildren[index]->ToJSON(aCx, &object);
if (!JS_DefineElement(aCx, children, index, object, JSPROP_ENUMERATE)) {
return false;
}
}
if (!JS_DefineProperty(aCx, aDst, "children", children,
JSPROP_ENUMERATE)) {
return false;
}
}
return true;
}
}; };
} // namespace dom } // namespace dom

View File

@@ -5,21 +5,29 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [ EXPORTS.mozilla.dom += [
"BrowserSessionStore.h",
"SessionStoreChangeListener.h", "SessionStoreChangeListener.h",
"SessionStoreChild.h",
"SessionStoreData.h", "SessionStoreData.h",
"SessionStoreDataCollector.h", "SessionStoreFormData.h",
"SessionStoreListener.h", "SessionStoreListener.h",
"SessionStoreMessageUtils.h", "SessionStoreMessageUtils.h",
"SessionStoreParent.h",
"SessionStoreRestoreData.h", "SessionStoreRestoreData.h",
"SessionStoreScrollData.h",
"SessionStoreUtils.h", "SessionStoreUtils.h",
] ]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
"BrowserSessionStore.cpp",
"RestoreTabContentObserver.cpp", "RestoreTabContentObserver.cpp",
"SessionStoreChangeListener.cpp", "SessionStoreChangeListener.cpp",
"SessionStoreDataCollector.cpp", "SessionStoreChild.cpp",
"SessionStoreFormData.cpp",
"SessionStoreListener.cpp", "SessionStoreListener.cpp",
"SessionStoreParent.cpp",
"SessionStoreRestoreData.cpp", "SessionStoreRestoreData.cpp",
"SessionStoreScrollData.cpp",
"SessionStoreUtils.cpp", "SessionStoreUtils.cpp",
] ]
@@ -35,6 +43,7 @@ XPIDL_SOURCES += [
] ]
IPDL_SOURCES += [ IPDL_SOURCES += [
"PSessionStore.ipdl",
"SessionStoreTypes.ipdlh", "SessionStoreTypes.ipdlh",
] ]