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 1fdc18969d
commit e4c062012a
55 changed files with 2011 additions and 1003 deletions

View File

@ -150,6 +150,11 @@ add_task(async function test_preferences_page() {
openPreferences("search");
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");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(

View File

@ -149,6 +149,9 @@ add_task(async function test_bookmark_contextmenu_contents() {
return contextMenu;
}, optionItems);
let tab;
let contextMenuOnContent;
await checkContextMenu(async function() {
info("Check context menu after opening context menu on content");
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");
const tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:config"
);
const contextMenuOnContent = document.getElementById(
"contentAreaContextMenu"
);
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
contextMenuOnContent = document.getElementById("contentAreaContextMenu");
const popupShownPromiseOnContent = BrowserTestUtils.waitForEvent(
contextMenuOnContent,
"popupshown"
@ -189,10 +187,15 @@ add_task(async function test_bookmark_contextmenu_contents() {
});
await popupShownPromise;
BrowserTestUtils.removeTab(tab);
return contextMenu;
}, 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() {

View File

@ -1376,8 +1376,11 @@ var SessionStoreInternal = {
this.onTabStateUpdate(browser.permanentKey, browser.ownerGlobal, data);
// SHIP code will call this when it receives "browser-shutdown-tabstate-updated"
if (data.isFinal) {
this.onFinalTabStateUpdateComplete(browser);
if (!Services.appinfo.sessionHistoryInParent) {
this.onFinalTabStateUpdateComplete(browser);
}
} else if (data.flushID) {
// This is an update kicked off by an async flush request. Notify the
// 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|.
*
@ -233,22 +162,6 @@ var TabStateCacheInternal = {
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];
if (value === null) {
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");
// Bug 588077
// XXX(farre): disabling this, because it started passing more heavily on Windows.
/*
let in1ValFrame0_1 = await SpecialPowers.spawn(
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");
*/
let in1ValFrame1_0 = await SpecialPowers.spawn(
content.frames[1],

View File

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
async function test() {
let assertNumberOfTabs = function(num, msg) {
is(gBrowser.tabs.length, num, msg);
};
@ -18,38 +18,26 @@ function test() {
// setup
let tab = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
whenTabIsLoaded(tab, function() {
// 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");
await promiseBrowserLoaded(tab.linkedBrowser);
// close and restore hidden tab
promiseRemoveTabAndSessionState(tab).then(() => {
tab = ss.undoCloseTab(window, 0);
// 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");
// check that everything was restored correctly, clean up and finish
whenTabIsLoaded(tab, function() {
is(
tab.linkedBrowser.currentURI.spec,
"about:mozilla",
"restored tab has correct url"
);
// close and restore hidden tab
await promiseRemoveTabAndSessionState(tab);
tab = ss.undoCloseTab(window, 0);
gBrowser.removeTab(tab);
finish();
});
});
});
}
function whenTabIsLoaded(tab, callback) {
tab.linkedBrowser.addEventListener(
"load",
function() {
callback();
},
{ capture: true, once: true }
// check that everything was restored correctly, clean up and finish
await promiseBrowserLoaded(tab.linkedBrowser);
is(
tab.linkedBrowser.currentURI.spec,
"about:mozilla",
"restored tab has correct url"
);
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
// options.
await SpecialPowers.spawn(tab.linkedBrowser, [VALUES], values => {
content.document.querySelector("select").multiple = true;
for (let v of values) {
content.document.querySelector(`option[value="${v}"]`).selected = true;
}
});
await setPropertyOfFormField(tab.linkedBrowser, "select", "multiple", true);
for (let v of VALUES) {
await setPropertyOfFormField(
tab.linkedBrowser,
`option[value="${v}"]`,
"selected",
true
);
}
// Remove the tab.
await promiseRemoveTabAndSessionState(tab);

View File

@ -36,11 +36,12 @@
#include "mozilla/dom/MediaDevices.h"
#include "mozilla/dom/PopupBlocker.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/dom/UserActivationIPCUtils.h"
#include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/WindowProxyHolder.h"
@ -2277,44 +2278,6 @@ void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
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) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) {
@ -2635,6 +2598,20 @@ nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
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>) {
MOZ_ASSERT(IsTop(),
"Should only set GVAudibleAutoplayRequestStatus in the top-level "
@ -3407,6 +3384,17 @@ void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
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 {
for (const BrowsingContext* current = this; current && current->GetParent();
current = current->GetParent()) {

View File

@ -784,6 +784,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
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; }
bool GetOffsetPath(nsTArray<uint32_t>& aPath) const;
@ -889,8 +893,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return GetPrefersColorSchemeOverride();
}
void FlushSessionStore();
bool IsInBFCache() const;
bool AllowJavascript() const { return GetAllowJavascript(); }
@ -1002,6 +1004,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return IsTop() && !aSource;
}
void DidSet(FieldIndex<IDX_SessionStoreEpoch>, uint32_t aOldValue);
using CanSetResult = syncedcontext::CanSetResult;
// Ensure that opener is in the same BrowsingContextGroup.

View File

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

View File

@ -55,6 +55,8 @@ class MediaController;
struct LoadingSessionHistoryInfo;
class SSCacheCopy;
class WindowGlobalParent;
class SessionStoreFormData;
class SessionStoreScrollData;
// CanonicalBrowsingContext is a BrowsingContext living in the parent
// process, with whatever extra data that a BrowsingContext in the
@ -524,6 +526,17 @@ class CanonicalBrowsingContext final : public BrowsingContext {
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;
// 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/SessionStorageManager.h"
#include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/ToJSValue.h"
@ -5816,7 +5816,12 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
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;
}
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
if (Document* document = GetDocument()) {
if (WindowGlobalChild* windowChild = document->GetWindowGlobalChild()) {
RefPtr<SessionStoreDataCollector> collector =
SessionStoreDataCollector::CollectSessionStoreData(windowChild);
collector->RecordInputChange();
collector->RecordScrollChange();
}
if (WindowContext* windowContext =
mBrowsingContext->GetCurrentWindowContext()) {
// TODO(farre): File bug: From a user perspective this would probably be
// just fine to run off the change listener timer. Turns out that a flush
// is needed. Several tests depend on this behaviour. Could potentially be
// an optimization for later. See Bug 1756995.
SessionStoreChangeListener::FlushAllSessionStoreData(windowContext);
}
}

View File

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

View File

@ -63,7 +63,6 @@ class ChromeMessageSender;
class ContentParent;
class Document;
class Element;
class TabListener;
class InProcessBrowserChildMessageManager;
class MessageSender;
class ProcessMessageManager;
@ -73,7 +72,8 @@ class BrowserBridgeChild;
class RemoteBrowser;
struct RemotenessOptions;
struct NavigationIsolationOptions;
class SessionStoreChangeListener;
class SessionStoreChild;
class SessionStoreParent;
namespace ipc {
class StructuredCloneData;
@ -411,6 +411,12 @@ class nsFrameLoader final : public nsStubMutationObserver,
void FireErrorEvent();
mozilla::dom::SessionStoreChild* GetSessionStoreChild() {
return mSessionStoreChild;
}
mozilla::dom::SessionStoreParent* GetSessionStoreParent();
private:
nsFrameLoader(mozilla::dom::Element* aOwner,
mozilla::dom::BrowsingContext* aBrowsingContext, bool aIsRemote,
@ -520,9 +526,10 @@ class nsFrameLoader final : public nsStubMutationObserver,
// Holds the last known size of the frame.
mozilla::ScreenIntSize mLazySize;
RefPtr<mozilla::dom::TabListener> mSessionStoreListener;
RefPtr<mozilla::dom::SessionStoreChangeListener> mSessionStoreChangeListener;
// 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
// refcounted cycles early.
RefPtr<mozilla::dom::SessionStoreChild> mSessionStoreChild;
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<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 = [
"BrowserSessionStore.webidl",
"BrowsingContext.webidl",
"ChannelWrapper.webidl",
"ClonedErrorHolder.webidl",

View File

@ -69,10 +69,8 @@
#include "mozilla/dom/PBrowser.h"
#include "mozilla/dom/PaymentRequestChild.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/SessionStoreChild.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/gfx/CrossProcessPaint.h"
@ -522,12 +520,7 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
mIPCOpen = true;
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
mSessionStoreListener = new TabListener(docShell, nullptr);
rv = mSessionStoreListener->Init();
NS_ENSURE_SUCCESS(rv, rv);
mSessionStoreChangeListener =
SessionStoreChangeListener::Create(mBrowsingContext);
mSessionStoreChild = SessionStoreChild::GetOrCreate(mBrowsingContext);
}
// 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(mWebNav)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreChild)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
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(mWebNav)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreChangeListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreChild)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowserChild)
@ -849,14 +840,9 @@ void BrowserChild::DestroyWindow() {
mCoalescedTouchMoveEventFlusher = nullptr;
}
if (mSessionStoreListener) {
mSessionStoreListener->RemoveListeners();
mSessionStoreListener = nullptr;
}
if (mSessionStoreChangeListener) {
mSessionStoreChangeListener->Stop();
mSessionStoreChangeListener = nullptr;
if (mSessionStoreChild) {
mSessionStoreChild->Stop();
mSessionStoreChild = nullptr;
}
// 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();
}
mozilla::ipc::IPCResult BrowserChild::RecvUpdateEpoch(const uint32_t& aEpoch) {
if (mSessionStoreListener) {
mSessionStoreListener->SetEpoch(aEpoch);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory() {
if (mSessionStoreListener) {
mSessionStoreListener->UpdateSHistoryChanges();
if (mSessionStoreChild) {
mSessionStoreChild->UpdateSHistoryChanges();
}
return IPC_OK();
}
@ -3809,26 +3788,10 @@ nsresult BrowserChild::PrepareProgressListenerData(
return PrepareRequestData(aRequest, aRequestData);
}
bool BrowserChild::UpdateSessionStore() {
if (!mSessionStoreListener) {
return false;
void BrowserChild::UpdateSessionStore() {
if (mSessionStoreChild) {
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

View File

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

View File

@ -16,6 +16,7 @@
#endif
#include "mozilla/Components.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/BrowserSessionStore.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CancelContentJSOptionsBinding.h"
#include "mozilla/dom/ChromeMessageSender.h"
@ -32,8 +33,7 @@
#include "mozilla/dom/RemoteDragStartData.h"
#include "mozilla/dom/RemoteWebProgressRequest.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h"
#include "mozilla/dom/SessionStoreParent.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/gfx/2D.h"
@ -53,6 +53,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/TextEventDispatcher.h"
@ -128,7 +129,6 @@
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/ProfilerLabels.h"
#include "MMPrinter.h"
#include "SessionStoreFunctions.h"
#include "mozilla/dom/CrashReport.h"
#include "nsISecureBrowserUI.h"
#include "nsIXULRuntime.h"
@ -1372,6 +1372,17 @@ IPCResult BrowserParent::RecvIndexedDBPermissionRequest(
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(
ManagedEndpoint<PWindowGlobalParent>&& aEndpoint,
const WindowGlobalInit& aInit) {
@ -3008,41 +3019,6 @@ BrowserParent::BrowsingContextForWebProgress(
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(
const Maybe<IntrinsicSize>& aIntrinsicSize,
const Maybe<AspectRatio>& aIntrinsicRatio) {

View File

@ -317,10 +317,6 @@ class BrowserParent final : public PBrowserParent,
already_AddRefed<CanonicalBrowsingContext> BrowsingContextForWebProgress(
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(
const Maybe<IntrinsicSize>& aIntrinsicSize,
const Maybe<AspectRatio>& aIntrinsicRatio);
@ -445,6 +441,8 @@ class BrowserParent final : public PBrowserParent,
const IAccessibleHolder& aDocCOMProxy) override;
#endif
already_AddRefed<PSessionStoreParent> AllocPSessionStoreParent();
mozilla::ipc::IPCResult RecvNewWindowGlobal(
ManagedEndpoint<PWindowGlobalParent>&& aEndpoint,
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(
const MaybeDiscarded<BrowsingContext>& aContext,
const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,

View File

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

View File

@ -17,6 +17,7 @@ include protocol PParentToChildStream;
include protocol PFileDescriptorSet;
include protocol PRemoteLazyInputStream;
include protocol PPaymentRequest;
include protocol PSessionStore;
include protocol PWindowGlobal;
include protocol PBrowserBridge;
include protocol PVsync;
@ -190,6 +191,7 @@ struct PrintPreviewResultInfo
manages PFilePicker;
manages PPaymentRequest;
manages PSessionStore;
manages PWindowGlobal;
manages PBrowserBridge;
manages PVsync;
@ -575,9 +577,6 @@ parent:
async NavigationFinished();
async SessionStoreUpdate(nsCString? aDocShellCaps, bool? aPrivatedMode,
bool aNeedCollectSHistory, uint32_t aEpoch);
async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
AspectRatio? aIntrinsicRatio);
@ -595,7 +594,6 @@ parent:
child:
async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
async UpdateEpoch(uint32_t aEpoch);
async UpdateSHistory();
async CloneDocumentTreeIntoSelf(MaybeDiscardedBrowsingContext aBc, PrintData aPrintData) returns(bool aSuccess);
async UpdateRemotePrintSettings(PrintData aPrintData);
@ -1016,6 +1014,9 @@ parent:
/** Fetches the visited status for an array of URIs (Android-only). */
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
*/

View File

@ -1027,9 +1027,6 @@ child:
// Update the cached list of codec supported in the given process.
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
async DecoderSupportedMimeTypes(nsCString[] supportedTypes);

View File

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

View File

@ -180,11 +180,6 @@ parent:
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
// flags in aOffFlags from the current BFCache status. See the BFCacheStatus
// enum for the valid flags.

View File

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

View File

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

View File

@ -26,9 +26,6 @@
#include "mozilla/dom/ChromeUtils.h"
#include "mozilla/dom/ipc/IdType.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/ScopeExit.h"
#include "mozilla/ServoCSSParser.h"
@ -535,16 +532,6 @@ const nsACString& WindowGlobalParent::GetRemoteType() {
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(
uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
const nsACString& aTrackingOrigin,
@ -1166,44 +1153,6 @@ void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
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() {
WindowGlobalParent* top = TopWindowContext();
if (!top) {
@ -1221,104 +1170,6 @@ Element* WindowGlobalParent::GetRootOwnerElement() {
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) {
if (!aEmbedder) {
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() {
CanonicalBrowsingContext* bc = BrowsingContext();
if (bc && bc->AncestorsAreCurrent()) {

View File

@ -80,7 +80,7 @@ class WindowGlobalParent final : public WindowContext,
WindowGlobalParent* TopWindowContext() {
return static_cast<WindowGlobalParent*>(WindowContext::TopWindowContext());
}
CanonicalBrowsingContext* GetBrowsingContext() {
CanonicalBrowsingContext* GetBrowsingContext() const {
return CanonicalBrowsingContext::Cast(WindowContext::GetBrowsingContext());
}
@ -214,10 +214,6 @@ class WindowGlobalParent final : public WindowContext,
const nsACString& GetRemoteType() override;
nsresult WriteFormDataAndScrollToSessionStore(
const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition,
uint32_t aEpoch);
void NotifySessionStoreUpdatesComplete(Element* aEmbedder);
Maybe<uint64_t> GetSingleChannelId() { return mSingleChannelId; }
@ -288,12 +284,6 @@ class WindowGlobalParent final : public WindowContext,
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,
const uint32_t& aOffFlags);
@ -320,8 +310,6 @@ class WindowGlobalParent final : public WindowContext,
bool ShouldTrackSiteOriginTelemetry();
void FinishAccumulatingPageUseCounters();
nsresult ResetSessionStore(uint32_t aEpoch);
// Returns failure if the new storage principal cannot be validated
// against the current document principle.
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/Event.h"
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/SessionStoreDataCollector.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_browser.h"
#include "nsBaseHashtable.h"
#include "nsDocShell.h"
#include "nsGenericHTMLElement.h"
#include "nsIXULRuntime.h"
#include "nsPIDOMWindow.h"
#include "nsTHashMap.h"
#include "nsTHashtable.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace {
constexpr auto kInput = u"input"_ns;
@ -28,13 +38,28 @@ static constexpr char kInterval[] = "browser.sessionstore.interval";
static const char* kObservedPrefs[] = {kNoAutoUpdates, kInterval, nullptr};
} // 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,
mCurrentEventTarget)
mCurrentEventTarget, mSessionStoreChild,
mSessionStoreChanges)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStoreChangeListener)
NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_END
@ -42,10 +67,22 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(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
SessionStoreChangeListener::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
Flush();
FlushSessionStore();
return NS_OK;
}
@ -62,45 +99,33 @@ SessionStoreChangeListener::HandleEvent(dom::Event* aEvent) {
return NS_OK;
}
WindowGlobalChild* windowChild = inner->GetWindowGlobalChild();
if (!windowChild) {
WindowContext* windowContext = inner->GetWindowContext();
if (!windowContext) {
return NS_OK;
}
RefPtr<BrowsingContext> browsingContext = windowChild->BrowsingContext();
RefPtr<BrowsingContext> browsingContext = windowContext->GetBrowsingContext();
if (!browsingContext) {
return NS_OK;
}
bool dynamic = false;
BrowsingContext* current = browsingContext;
while (current) {
if ((dynamic = current->CreatedDynamically())) {
break;
}
current = current->GetParent();
}
if (dynamic) {
if (browsingContext->IsDynamic()) {
return NS_OK;
}
nsAutoString eventType;
aEvent->GetType(eventType);
RefPtr<SessionStoreDataCollector> collector =
SessionStoreDataCollector::CollectSessionStoreData(windowChild);
if (!collector) {
return NS_OK;
}
Change change = Change::None;
if (eventType == kInput) {
collector->RecordInputChange();
change = Change::Input;
} else if (eventType == kScroll) {
collector->RecordScrollChange();
change = Change::Scroll;
}
RecordChange(windowContext, EnumSet(change));
return NS_OK;
}
@ -128,13 +153,134 @@ void SessionStoreChangeListener::UpdateEventTargets() {
AddEventListeners();
}
void SessionStoreChangeListener::Flush() {
mBrowsingContext->FlushSessionStore();
static void CollectFormData(Document* aDocument,
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(
BrowsingContext* aBrowsingContext)
: mBrowsingContext(aBrowsingContext) {}
: mBrowsingContext(aBrowsingContext),
mEpoch(aBrowsingContext->GetSessionStoreEpoch()) {}
void SessionStoreChangeListener::Init() {
AddEventListeners();
@ -167,4 +313,19 @@ void SessionStoreChangeListener::RemoveEventListeners() {
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 "PLDHashTable.h"
#include "mozilla/EnumSet.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include "nsIDOMEventListener.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/Result.h"
#include "mozilla/dom/WindowGlobalChild.h"
namespace mozilla::dom {
class BrowsingContext;
class Element;
class EventTarget;
class SessionStoreDataCollector;
class BrowsingContext;
class SessionStoreChild;
class WindowContext;
class SessionStoreChangeListener final : public nsIObserver,
class SessionStoreChangeListener final : public nsINamed,
public nsIObserver,
public nsITimerCallback,
public nsIDOMEventListener {
public:
NS_DECL_NSINAMED
NS_DECL_NSIOBSERVER
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SessionStoreChangeListener,
@ -40,7 +54,29 @@ class SessionStoreChangeListener final : public nsIObserver,
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:
explicit SessionStoreChangeListener(BrowsingContext* aBrowsingContext);
@ -53,8 +89,15 @@ class SessionStoreChangeListener final : public nsIObserver,
void AddEventListeners();
void RemoveEventListeners();
void EnsureTimer();
RefPtr<BrowsingContext> mBrowsingContext;
RefPtr<EventTarget> mCurrentEventTarget;
uint32_t mEpoch;
nsCOMPtr<nsITimer> mTimer;
RefPtr<SessionStoreChild> mSessionStoreChild;
SessionStoreChangeTable mSessionStoreChanges;
};
} // 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 aData);
void UpdateSessionStoreForWindow(
in Element aBrowser, in BrowsingContext aBrowsingContext,
in jsval aPermanentKey, in uint32_t aEpoch, in jsval aData);
void UpdateSessionStoreForStorage(
in Element aBrowser, in BrowsingContext aBrowsingContext,
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(
aBrowser,
aBrowsingContext,
@ -60,11 +44,7 @@ function UpdateSessionStoreForStorage(
);
}
var EXPORTED_SYMBOLS = [
"UpdateSessionStore",
"UpdateSessionStoreForWindow",
"UpdateSessionStoreForStorage",
];
var EXPORTED_SYMBOLS = ["UpdateSessionStore", "UpdateSessionStoreForStorage"];
var SessionStoreFuncInternal = {
updateSessionStore: function SSF_updateSessionStore(
@ -75,12 +55,14 @@ var SessionStoreFuncInternal = {
aCollectSHistory,
aData
) {
let currentData = {};
if (aData.docShellCaps != undefined) {
currentData.disallow = aData.docShellCaps ? aData.docShellCaps : null;
let { formdata, scroll } = aData;
if (formdata) {
aData.formdata = formdata.toJSON();
}
if (aData.isPrivate != undefined) {
currentData.isPrivate = aData.isPrivate;
if (scroll) {
aData.scroll = scroll.toJSON();
}
SessionStore.updateSessionStoreFromTablistener(
@ -88,28 +70,13 @@ var SessionStoreFuncInternal = {
aBrowsingContext,
aPermanentKey,
{
data: currentData,
data: aData,
epoch: aEpoch,
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(
aBrowser,
aBrowsingContext,

View File

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

View File

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

View File

@ -6,6 +6,7 @@
#define mozilla_dom_SessionStoreMessageUtils_h
#include "ipc/IPCMessageUtils.h"
#include "mozilla/ipc/IPDLParamTraits.h"
#include "SessionStoreData.h"
#include "SessionStoreUtils.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[] xpath;
nsString innerHTML;
nsCString uri;
};
struct DocShellRestoreState {

View File

@ -7,6 +7,7 @@
#include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject
#include "js/JSON.h"
#include "js/PropertyAndElement.h" // JS_GetElement
#include "js/TypeDecls.h"
#include "jsapi.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/AutocompleteInfoBinding.h"
@ -19,6 +20,8 @@
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/PBackgroundSessionStorageCache.h"
#include "mozilla/dom/SessionStoreChangeListener.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/txIXPathContext.h"
#include "mozilla/dom/WindowGlobalParent.h"
@ -300,6 +303,10 @@ void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal,
/* static */
void SessionStoreUtils::RestoreScrollPosition(
nsGlobalWindowInner& aWindow, const nsCString& aScrollPosition) {
using Change = mozilla::dom::SessionStoreChangeListener::Change;
SessionStoreChangeListener::CollectSessionStoreData(
aWindow.GetWindowContext(), EnumSet<Change>(Change::Scroll));
nsCCharSeparatedTokenizer tokenizer(aScrollPosition, ',');
nsAutoCString token(tokenizer.nextToken());
int pos_X = atoi(token.get());
@ -1203,6 +1210,7 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
if (!aData.mUrl.WasPassed()) {
return true;
}
// Don't restore any data for the given frame if the URL
// stored in the form data doesn't match its current URL.
nsAutoCString url;
@ -1210,6 +1218,11 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
if (!aData.mUrl.Value().Equals(url)) {
return false;
}
using Change = SessionStoreChangeListener::Change;
SessionStoreChangeListener::CollectSessionStoreData(
aDocument.GetWindowContext(), EnumSet<Change>(Change::Input));
if (aData.mInnerHTML.WasPassed()) {
SetInnerHTML(aDocument, aData.mInnerHTML.Value());
}
@ -1311,6 +1324,10 @@ void RestoreFormEntry(Element* aNode, const FormEntryValue& aValue) {
void SessionStoreUtils::RestoreFormData(
Document& aDocument, const nsString& aInnerHTML,
const nsTArray<SessionStoreRestoreData::Entry>& aEntries) {
using Change = SessionStoreChangeListener::Change;
SessionStoreChangeListener::CollectSessionStoreData(
aDocument.GetWindowContext(), EnumSet<Change>(Change::Input));
if (!aInnerHTML.IsEmpty()) {
SetInnerHTML(aDocument, aInnerHTML);
}
@ -1698,20 +1715,31 @@ nsresult SessionStoreUtils::ConstructSessionStorageValues(
return NS_OK;
}
/* static */ void SessionStoreUtils::ResetSessionStore(
BrowsingContext* aContext) {
MOZ_RELEASE_ASSERT(NATIVE_LISTENER);
WindowContext* windowContext = aContext->GetCurrentWindowContext();
if (!windowContext) {
return;
/* static */
bool SessionStoreUtils::CopyProperty(JSContext* aCx, JS::HandleObject aDst,
JS::HandleObject aSrc,
const nsAString& aName) {
JS::RootedId name(aCx);
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();
if (!windowChild || !windowChild->CanSend()) {
return;
bool found = false;
if (!JS_HasPropertyById(aCx, aSrc, name, &found) || !found) {
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
#include "mozilla/Attributes.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h"
@ -139,14 +140,44 @@ class SessionStoreUtils {
const nsTArray<SSCacheCopy>& aValues,
Record<nsCString, Record<nsString, nsString>>& aStorage);
static void ResetSessionStore(BrowsingContext* aContext);
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_THUNDERBIRD) || \
defined(MOZ_SUITE)
static constexpr bool NATIVE_LISTENER = false;
#else
static constexpr bool NATIVE_LISTENER = true;
#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

View File

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