Bug 1597499 - Make Session Restore work in Fission, r=nika

Differential Revision: https://phabricator.services.mozilla.com/D107883
This commit is contained in:
Kashav Madan 2021-03-17 16:43:05 +00:00
Родитель 4dd9e85c76
Коммит 83da101af6
25 изменённых файлов: 786 добавлений и 111 удалений

Просмотреть файл

@ -56,7 +56,6 @@ function ContentRestore(chromeGlobal) {
"restoreTabContent", "restoreTabContent",
"restoreDocument", "restoreDocument",
"resetRestore", "resetRestore",
"setRestoringDocument",
]; ];
for (let method of EXPORTED_METHODS) { for (let method of EXPORTED_METHODS) {
@ -101,13 +100,6 @@ ContentRestoreInternal.prototype = {
return this.chromeGlobal.docShell; return this.chromeGlobal.docShell;
}, },
setRestoringDocument(data) {
if (!Services.appinfo.sessionHistoryInParent) {
throw new Error("This function should only be used with SHIP");
}
this._restoringDocument = data;
},
/** /**
* Starts the process of restoring a tab. The tabData to be restored is passed * Starts the process of restoring a tab. The tabData to be restored is passed
* in here and used throughout the restoration. The epoch (which must be * in here and used throughout the restoration. The epoch (which must be
@ -224,9 +216,7 @@ ContentRestoreInternal.prototype = {
webNavigation.loadURI(tabData.userTypedValue, loadURIOptions); webNavigation.loadURI(tabData.userTypedValue, loadURIOptions);
} else if (tabData.entries.length) { } else if (tabData.entries.length) {
// Stash away the data we need for restoreDocument. // Stash away the data we need for restoreDocument.
let activeIndex = tabData.index - 1;
this._restoringDocument = { this._restoringDocument = {
entry: tabData.entries[activeIndex] || {},
formdata: tabData.formdata || {}, formdata: tabData.formdata || {},
scrollPositions: tabData.scroll || {}, scrollPositions: tabData.scroll || {},
}; };
@ -294,7 +284,7 @@ ContentRestoreInternal.prototype = {
*/ */
restoreDocument() { restoreDocument() {
if (!this._restoringDocument) { if (!this._restoringDocument) {
return false; return;
} }
let { formdata, scrollPositions } = this._restoringDocument; let { formdata, scrollPositions } = this._restoringDocument;
@ -316,7 +306,6 @@ ContentRestoreInternal.prototype = {
SessionStoreUtils.restoreScrollPosition(frame, data); SessionStoreUtils.restoreScrollPosition(frame, data);
} }
}); });
return true;
}, },
/** /**

Просмотреть файл

@ -99,16 +99,8 @@ class EventListener extends Handler {
} }
if (this.contentRestoreInitialized) { if (this.contentRestoreInitialized) {
// Restore the form data and scroll position (if we're restoring a // Restore the form data and scroll position.
// document). this.contentRestore.restoreDocument();
if (
this.contentRestore.restoreDocument() &&
Services.appinfo.sessionHistoryInParent
) {
this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
epoch: this.store.epoch,
});
}
} }
} }
} }
@ -518,7 +510,6 @@ const MESSAGES = [
"SessionStore:flush", "SessionStore:flush",
"SessionStore:becomeActiveProcess", "SessionStore:becomeActiveProcess",
"SessionStore:prepareForProcessChange", "SessionStore:prepareForProcessChange",
"SessionStore:setRestoringDocument",
]; ];
class ContentSessionStore { class ContentSessionStore {
@ -530,16 +521,17 @@ class ContentSessionStore {
this.contentRestoreInitialized = false; this.contentRestoreInitialized = false;
XPCOMUtils.defineLazyGetter(this, "contentRestore", () => { this.handlers = [this.messageQueue];
this.contentRestoreInitialized = true;
return new ContentRestore(mm);
});
this.handlers = [new EventListener(this), this.messageQueue];
if (Services.appinfo.sessionHistoryInParent) { if (Services.appinfo.sessionHistoryInParent) {
this.mm.sendAsyncMessage("SessionStore:addSHistoryListener"); this.mm.sendAsyncMessage("SessionStore:addSHistoryListener");
} else { } else {
this.handlers.push(new EventListener(this));
this.handlers.push(new SessionHistoryListener(this)); this.handlers.push(new SessionHistoryListener(this));
XPCOMUtils.defineLazyGetter(this, "contentRestore", () => {
this.contentRestoreInitialized = true;
return new ContentRestore(mm);
});
} }
MESSAGES.forEach(m => mm.addMessageListener(m, this)); MESSAGES.forEach(m => mm.addMessageListener(m, this));
@ -595,9 +587,6 @@ class ContentSessionStore {
// parent process. // parent process.
this.mm.docShell.persistLayoutHistoryState(); this.mm.docShell.persistLayoutHistoryState();
break; break;
case "SessionStore:setRestoringDocument":
this.contentRestore.setRestoringDocument(data);
break;
default: default:
debug("received unknown message '" + name + "'"); debug("received unknown message '" + name + "'");
break; break;

Просмотреть файл

@ -527,6 +527,10 @@ var SessionStore = {
finishTabRemotenessChange(aTab, aSwitchId) { finishTabRemotenessChange(aTab, aSwitchId) {
SessionStoreInternal.finishTabRemotenessChange(aTab, aSwitchId); SessionStoreInternal.finishTabRemotenessChange(aTab, aSwitchId);
}, },
restoreTabContentComplete(aBrowser, aData) {
SessionStoreInternal._restoreTabContentComplete(aBrowser, aData);
},
}; };
// Freeze the SessionStore object. We don't want anyone to modify it. // Freeze the SessionStore object. We don't want anyone to modify it.
@ -1195,26 +1199,12 @@ var SessionStoreInternal = {
} }
onStateChange(webProgress, request, stateFlags, status) { onStateChange(webProgress, request, stateFlags, status) {
if ( if (
!webProgress.isTopLevel || webProgress.isTopLevel &&
!(stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
callbacks.onStopRequest
) { ) {
return; callbacks.onStopRequest(request, this);
}
if (
callbacks.onStartRequest &&
stateFlags & Ci.nsIWebProgressListener.STATE_START
) {
callbacks.onStartRequest();
this.uninstall();
}
if (
callbacks.onStopRequest &&
stateFlags & Ci.nsIWebProgressListener.STATE_STOP
) {
callbacks.onStopRequest();
this.uninstall();
} }
} }
} }
@ -1411,6 +1401,7 @@ var SessionStoreInternal = {
} }
} }
} }
if (aMessage.data.isFinal) { if (aMessage.data.isFinal) {
// If this the final message we need to resolve all pending flush // If this the final message we need to resolve all pending flush
// requests for the given browser as they might have been sent too // requests for the given browser as they might have been sent too
@ -1421,7 +1412,6 @@ var SessionStoreInternal = {
for (let wm of [ for (let wm of [
this._browserSHistoryListener, this._browserSHistoryListener,
this._browserSHistoryListenerForRestore, this._browserSHistoryListenerForRestore,
this._browserProgressListenerForRestore,
]) { ]) {
let listener = wm.get(browser.permanentKey); let listener = wm.get(browser.permanentKey);
if (listener) { if (listener) {
@ -5641,6 +5631,15 @@ var SessionStoreInternal = {
_resetLocalTabRestoringState(aTab) { _resetLocalTabRestoringState(aTab) {
let browser = aTab.linkedBrowser; let browser = aTab.linkedBrowser;
if (Services.appinfo.sessionHistoryInParent) {
if (this._browserProgressListenerForRestore.has(browser.permanentKey)) {
this._browserProgressListenerForRestore
.get(browser.permanentKey)
.uninstall();
}
SessionStoreUtils.setRestoreData(browser.browsingContext, {});
}
// Keep the tab's previous state for later in this method // Keep the tab's previous state for later in this method
let previousState = TAB_STATE_FOR_BROWSER.get(browser); let previousState = TAB_STATE_FOR_BROWSER.get(browser);
@ -5674,7 +5673,9 @@ var SessionStoreInternal = {
return; return;
} }
browser.messageManager.sendAsyncMessage("SessionStore:resetRestore", {}); if (!Services.appinfo.sessionHistoryInParent) {
browser.messageManager.sendAsyncMessage("SessionStore:resetRestore", {});
}
this._resetLocalTabRestoringState(tab); this._resetLocalTabRestoringState(tab);
}, },
@ -5759,6 +5760,91 @@ var SessionStoreInternal = {
return deferred; return deferred;
}, },
/**
* Builds a single nsISessionStoreRestoreData tree for the provided |formdata|
* and |scroll| trees.
*/
buildRestoreData(formdata, scroll) {
function addFormEntries(root, fields, isXpath) {
for (let [key, value] of Object.entries(fields)) {
switch (typeof value) {
case "string":
root.addTextField(isXpath, key, value);
break;
case "boolean":
root.addCheckbox(isXpath, key, value);
break;
case "object": {
if (value === null) {
break;
}
if (
value.hasOwnProperty("type") &&
value.hasOwnProperty("fileList")
) {
root.addFileList(isXpath, key, value.type, value.fileList);
break;
}
if (
value.hasOwnProperty("selectedIndex") &&
value.hasOwnProperty("value")
) {
root.addSingleSelect(
isXpath,
key,
value.selectedIndex,
value.value
);
break;
}
if (
key === "sessionData" &&
["about:sessionrestore", "about:welcomeback"].includes(
formdata.url
)
) {
root.addTextField(isXpath, key, JSON.stringify(value));
break;
}
if (Array.isArray(value)) {
root.addMultipleSelect(isXpath, key, value);
break;
}
}
}
}
}
let root = SessionStoreUtils.constructSessionStoreRestoreData();
if (scroll?.hasOwnProperty("scroll")) {
root.scroll = scroll.scroll;
}
if (formdata?.hasOwnProperty("url")) {
root.url = formdata.url;
if (formdata.hasOwnProperty("innerHTML")) {
// eslint-disable-next-line no-unsanitized/property
root.innerHTML = formdata.innerHTML;
}
if (formdata.hasOwnProperty("xpath")) {
addFormEntries(root, formdata.xpath, /* isXpath */ true);
}
if (formdata.hasOwnProperty("id")) {
addFormEntries(root, formdata.id, /* isXpath */ false);
}
}
let childrenLength = Math.max(
scroll?.children?.length || 0,
formdata?.children?.length || 0
);
for (let i = 0; i < childrenLength; i++) {
root.addChild(
this.buildRestoreData(formdata?.children?.[i], scroll?.children?.[i]),
i
);
}
return root;
},
/** /**
* This mirrors ContentRestore.restoreHistory() for parent process session * This mirrors ContentRestore.restoreHistory() for parent process session
* history restores, but we're not actually restoring history here. * history restores, but we're not actually restoring history here.
@ -5823,18 +5909,14 @@ var SessionStoreInternal = {
throw new Error("This function should only be used with SHIP"); throw new Error("This function should only be used with SHIP");
} }
let listener = this._browserProgressListenerForRestore.get( for (let map of [
browser.permanentKey this._browserProgressListenerForRestore,
); this._browserSHistoryListenerForRestore,
if (listener) { ]) {
listener.uninstall(); let listener = map.get(browser.permanentKey);
} if (listener) {
listener.uninstall();
listener = this._browserSHistoryListenerForRestore.get( }
browser.permanentKey
);
if (listener) {
listener.uninstall();
} }
let restoreData = { let restoreData = {
@ -5846,43 +5928,56 @@ var SessionStoreInternal = {
this._restoreTabContentStarted(browser, restoreData); this._restoreTabContentStarted(browser, restoreData);
let { tabData } = restoreData; let { tabData } = restoreData;
let uri = null;
let uri = "about:blank"; let loadFlags = null;
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY;
if (tabData?.userTypedValue && tabData?.userTypedClear) { if (tabData?.userTypedValue && tabData?.userTypedClear) {
uri = tabData.userTypedValue; uri = tabData.userTypedValue;
loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
} else if (tabData?.entries.length) { } else if (tabData?.entries.length) {
uri = loadFlags = null; uri = tabData.entries[tabData.index - 1].url;
browser.messageManager.sendAsyncMessage( let willRestoreContent = SessionStoreUtils.setRestoreData(
"SessionStore:setRestoringDocument", browser.browsingContext,
{ this.buildRestoreData(tabData.formdata, tabData.scroll)
entry: tabData.entries[tabData.index - 1] || {},
formdata: tabData.formdata || {},
scrollPositions: tabData.scroll || {},
}
); );
browser.browsingContext.sessionHistory.reloadCurrentEntry(); // We'll manually call RestoreTabContentComplete when the restore is done,
// so we only want to create the listener below if we're not restoring tab
// content.
if (willRestoreContent) {
return;
}
} else {
uri = "about:blank";
loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY;
} }
if (uri && loadFlags) { if (uri) {
// We only create this listener if we're *not* expecting a reply from
// content. OTherwise we'll call _restoreTabContentComplete too early, and
// fire events before the restore has actually completed.
//
// XXX: If this causes problems, we may be able to just update tests that
// rely on the existing timing to wait for the load event instead.
this.addProgressListenerForRestore(browser, { this.addProgressListenerForRestore(browser, {
onStopRequest: () => { onStopRequest: (request, listener) => {
this._restoreTabContentComplete(browser, restoreData); let requestURI = request.QueryInterface(Ci.nsIChannel)?.originalURI;
// FIXME: We sometimes see spurious STATE_STOP events for about:blank
// URIs, so we have to manually drop those here (unless we're actually
// expecting an about:blank load).
//
// In the case where we're firing _restoreTabContentComplete due to
// a normal load (i.e. !willRestoreContent), we could perhaps just not
// wait for the load here, and instead fix tests that depend on this
// behavior.
if (requestURI?.spec !== "about:blank" || uri === "about:blank") {
listener.uninstall();
this._restoreTabContentComplete(browser, restoreData);
}
}, },
}); });
browser.browsingContext.loadURI(uri, { if (loadFlags) {
loadFlags, browser.browsingContext.loadURI(uri, {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), loadFlags,
}); triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
} else {
browser.browsingContext.sessionHistory.reloadCurrentEntry();
}
} }
}, },

Просмотреть файл

@ -2683,8 +2683,8 @@ bool BrowsingContext::LegacyCheckOnlyOwningProcessCanSet(
return true; return true;
} }
auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(ContentParent* aSource) auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
-> CanSetResult { ContentParent* aSource) -> CanSetResult {
if (aSource) { if (aSource) {
MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(XRE_IsParentProcess());
@ -3168,6 +3168,11 @@ bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
return IsTop() && GetPendingInitialization() && !aNewValue; return IsTop() && GetPendingInitialization() && !aNewValue;
} }
bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
ContentParent* aSource) {
return IsTop();
}
bool BrowsingContext::IsPopupAllowed() { bool BrowsingContext::IsPopupAllowed() {
for (auto* context = GetCurrentWindowContext(); context; for (auto* context = GetCurrentWindowContext(); context;
context = context->GetParentWindowContext()) { context = context->GetParentWindowContext()) {

Просмотреть файл

@ -198,7 +198,8 @@ enum class ExplicitActiveStatus : uint8_t {
/* The number of entries added to the session history because of this \ /* The number of entries added to the session history because of this \
* browsing context. */ \ * browsing context. */ \
FIELD(HistoryEntryCount, uint32_t) \ FIELD(HistoryEntryCount, uint32_t) \
FIELD(IsInBFCache, bool) FIELD(IsInBFCache, bool) \
FIELD(HasRestoreData, bool)
// BrowsingContext, in this context, is the cross process replicated // BrowsingContext, in this context, is the cross process replicated
// environment in which information about documents is stored. In // environment in which information about documents is stored. In
@ -1027,6 +1028,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
ContentParent* aSource); ContentParent* aSource);
void DidSet(FieldIndex<IDX_HasMainMediaController>, bool aOldValue); void DidSet(FieldIndex<IDX_HasMainMediaController>, bool aOldValue);
bool CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
ContentParent* aSource);
template <size_t I, typename T> template <size_t I, typename T>
bool CanSet(FieldIndex<I>, const T&, ContentParent*) { bool CanSet(FieldIndex<I>, const T&, ContentParent*) {
return true; return true;

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/dom/BrowsingContextGroup.h" #include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/EventTarget.h" #include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/PWindowGlobalParent.h"
#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ContentProcessManager.h" #include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/MediaController.h" #include "mozilla/dom/MediaController.h"
@ -173,6 +174,14 @@ void CanonicalBrowsingContext::ReplacedBy(
} }
aNewContext->mWebProgress = std::move(mWebProgress); aNewContext->mWebProgress = std::move(mWebProgress);
mRequestedContentRestores = 0;
mCompletedContentRestores = 0;
SetRestoreData(nullptr);
aNewContext->mRestoreData = nullptr;
aNewContext->mRequestedContentRestores = 0;
aNewContext->mCompletedContentRestores = 0;
// Use the Transaction for the fields which need to be updated whether or not // Use the Transaction for the fields which need to be updated whether or not
// the new context has been attached before. // the new context has been attached before.
// SetWithoutSyncing can be used if context hasn't been attached. // SetWithoutSyncing can be used if context hasn't been attached.
@ -180,6 +189,7 @@ void CanonicalBrowsingContext::ReplacedBy(
txn.SetBrowserId(GetBrowserId()); txn.SetBrowserId(GetBrowserId());
txn.SetHistoryID(GetHistoryID()); txn.SetHistoryID(GetHistoryID());
txn.SetExplicitActive(GetExplicitActive()); txn.SetExplicitActive(GetExplicitActive());
txn.SetHasRestoreData(false);
if (aNewContext->EverAttached()) { if (aNewContext->EverAttached()) {
MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext)); MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext));
} else { } else {
@ -1721,6 +1731,56 @@ void CanonicalBrowsingContext::ResetScalingZoom() {
} }
} }
void CanonicalBrowsingContext::SetRestoreData(SessionStoreRestoreData* aData) {
MOZ_DIAGNOSTIC_ASSERT(!mRestoreData || !aData,
"must either be clearing or initializing");
MOZ_DIAGNOSTIC_ASSERT(
!aData || mCompletedContentRestores == mRequestedContentRestores,
"must not start restore in an unstable state");
mRestoreData = aData;
MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(!!mRestoreData));
}
void CanonicalBrowsingContext::RequestRestoreTabContent(
WindowGlobalParent* aWindow) {
if (!mRestoreData || mRestoreData->IsEmpty()) {
return;
}
CanonicalBrowsingContext* context = aWindow->GetBrowsingContext();
RefPtr<SessionStoreRestoreData> data = mRestoreData->FindChild(context);
// We'll only arrive here for a toplevel context after we've already sent
// down data for any out-of-process descendants, so it's fine to clear our
// data now.
if (context->IsTop()) {
MOZ_DIAGNOSTIC_ASSERT(context == this);
SetRestoreData(nullptr);
}
if (data && !data->IsEmpty()) {
auto onTabRestoreComplete = [self = RefPtr{this}](auto) {
self->mCompletedContentRestores++;
if (!self->mRestoreData &&
self->mCompletedContentRestores == self->mRequestedContentRestores) {
if (Element* browser = self->GetEmbedderElement()) {
SessionStoreUtils::CallRestoreTabContentComplete(browser);
}
}
};
mRequestedContentRestores++;
if (aWindow->IsInProcess()) {
data->RestoreInto(context);
onTabRestoreComplete(true);
} else {
aWindow->SendRestoreTabContent(data, onTabRestoreComplete,
onTabRestoreComplete);
}
}
}
void CanonicalBrowsingContext::SetContainerFeaturePolicy( void CanonicalBrowsingContext::SetContainerFeaturePolicy(
FeaturePolicy* aContainerFeaturePolicy) { FeaturePolicy* aContainerFeaturePolicy) {
mContainerFeaturePolicy = aContainerFeaturePolicy; mContainerFeaturePolicy = aContainerFeaturePolicy;

Просмотреть файл

@ -10,6 +10,8 @@
#include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/MediaControlKeySource.h" #include "mozilla/dom/MediaControlKeySource.h"
#include "mozilla/dom/BrowsingContextWebProgress.h" #include "mozilla/dom/BrowsingContextWebProgress.h"
#include "mozilla/dom/SessionStoreRestoreData.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/ipc/IdType.h" #include "mozilla/dom/ipc/IdType.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/MozPromise.h" #include "mozilla/MozPromise.h"
@ -282,6 +284,9 @@ class CanonicalBrowsingContext final : public BrowsingContext {
return mContainerFeaturePolicy; return mContainerFeaturePolicy;
} }
void SetRestoreData(SessionStoreRestoreData* aData);
void RequestRestoreTabContent(WindowGlobalParent* aWindow);
protected: protected:
// Called when the browsing context is being discarded. // Called when the browsing context is being discarded.
void CanonicalDiscard(); void CanonicalDiscard();
@ -392,6 +397,10 @@ class CanonicalBrowsingContext final : public BrowsingContext {
RefPtr<nsBrowserStatusFilter> mStatusFilter; RefPtr<nsBrowserStatusFilter> mStatusFilter;
RefPtr<FeaturePolicy> mContainerFeaturePolicy; RefPtr<FeaturePolicy> mContainerFeaturePolicy;
RefPtr<SessionStoreRestoreData> mRestoreData;
uint32_t mRequestedContentRestores = 0;
uint32_t mCompletedContentRestores = 0;
}; };
} // namespace dom } // namespace dom

Просмотреть файл

@ -5882,6 +5882,7 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
} }
} }
} }
if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) { if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
nsCOMPtr<nsIWebProgress> webProgress = nsCOMPtr<nsIWebProgress> webProgress =
do_QueryInterface(GetAsSupports(this)); do_QueryInterface(GetAsSupports(this));
@ -6528,6 +6529,12 @@ nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
// //
nsCOMPtr<nsIDocShell> kungFuDeathGrip(this); nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
// We'll never need to restore data into an about:blank document, so we can
// ignore those here.
if (!NS_IsAboutBlank(url)) {
MaybeRestoreTabContent();
}
// Notify the ContentViewer that the Document has finished loading. This // Notify the ContentViewer that the Document has finished loading. This
// will cause any OnLoad(...) and PopState(...) handlers to fire. // will cause any OnLoad(...) and PopState(...) handlers to fire.
if (!mEODForCurrentDocument && mContentViewer) { if (!mEODForCurrentDocument && mContentViewer) {
@ -13271,6 +13278,23 @@ void nsDocShell::NotifyJSRunToCompletionStop() {
} }
} }
void nsDocShell::MaybeRestoreTabContent() {
BrowsingContext* bc = mBrowsingContext;
if (bc && bc->Top()->GetHasRestoreData()) {
if (XRE_IsParentProcess()) {
if (WindowGlobalParent* wgp = bc->Canonical()->GetCurrentWindowGlobal()) {
bc->Canonical()->RequestRestoreTabContent(wgp);
}
} else {
if (WindowContext* windowContext = bc->GetCurrentWindowContext()) {
if (WindowGlobalChild* wgc = windowContext->GetWindowGlobalChild()) {
wgc->SendRequestRestoreTabContent();
}
}
}
}
}
/* static */ /* static */
void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider, void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
const nsString& aKeyword) { const nsString& aKeyword) {

Просмотреть файл

@ -398,6 +398,8 @@ class nsDocShell final : public nsDocLoader,
void StoreWindowNameToSHEntries(); void StoreWindowNameToSHEntries();
void MaybeRestoreTabContent();
void SetWillChangeProcess() { mWillChangeProcess = true; } void SetWillChangeProcess() { mWillChangeProcess = true; }
bool WillChangeProcess() { return mWillChangeProcess; } bool WillChangeProcess() { return mWillChangeProcess; }

Просмотреть файл

@ -1969,3 +1969,6 @@ addExternalIface('nsICookieJarSettings', nativeType='nsICookieJarSettings',
notflattened=True) notflattened=True)
addExternalIface('nsIGleanPing', headerFile='mozilla/glean/bindings/Ping.h', addExternalIface('nsIGleanPing', headerFile='mozilla/glean/bindings/Ping.h',
nativeType='nsIGleanPing', notflattened=True) nativeType='nsIGleanPing', notflattened=True)
addExternalIface('nsISessionStoreRestoreData',
nativeType='nsISessionStoreRestoreData',
headerFile='nsISessionStoreRestoreData.h', notflattened=True)

Просмотреть файл

@ -4,6 +4,7 @@
interface nsIDocShell; interface nsIDocShell;
interface nsISupports; interface nsISupports;
interface nsISessionStoreRestoreData;
/** /**
* A callback passed to SessionStoreUtils.forEachNonDynamicChildFrame(). * A callback passed to SessionStoreUtils.forEachNonDynamicChildFrame().
@ -122,6 +123,11 @@ namespace SessionStoreUtils {
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}} * {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/ */
void restoreSessionStorage(nsIDocShell docShell, record<DOMString, record<DOMString, DOMString>> data); void restoreSessionStorage(nsIDocShell docShell, record<DOMString, record<DOMString, DOMString>> data);
nsISessionStoreRestoreData constructSessionStoreRestoreData();
boolean setRestoreData(CanonicalBrowsingContext browsingContext,
nsISessionStoreRestoreData? data);
}; };
[GenerateConversionToJS, GenerateInit] [GenerateConversionToJS, GenerateInit]

Просмотреть файл

@ -7,6 +7,7 @@
include "mozilla/dom/DocShellMessageUtils.h"; include "mozilla/dom/DocShellMessageUtils.h";
include "mozilla/dom/FeaturePolicyUtils.h"; include "mozilla/dom/FeaturePolicyUtils.h";
include "mozilla/dom/PermissionMessageUtils.h"; include "mozilla/dom/PermissionMessageUtils.h";
include "mozilla/dom/SessionStoreMessageUtils.h";
include "mozilla/ipc/TransportSecurityInfoUtils.h"; include "mozilla/ipc/TransportSecurityInfoUtils.h";
include "mozilla/ipc/URIUtils.h"; include "mozilla/ipc/URIUtils.h";
@ -32,6 +33,7 @@ using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
using mozilla::UseCounters from "mozilla/UseCounter.h"; using mozilla::UseCounters from "mozilla/UseCounter.h";
using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h"; using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
[RefCounted] using mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h"; [RefCounted] using mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h";
[RefCounted] using mozilla::dom::SessionStoreRestoreData from "mozilla/dom/SessionStoreRestoreData.h";
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -90,6 +92,8 @@ child:
async SetContainerFeaturePolicy(FeaturePolicy aContainerFeaturePolicy); async SetContainerFeaturePolicy(FeaturePolicy aContainerFeaturePolicy);
async RestoreTabContent(SessionStoreRestoreData aData) returns (bool success);
both: both:
async RawMessage(JSActorMessageMeta aMetadata, ClonedMessageData? aData, async RawMessage(JSActorMessageMeta aMetadata, ClonedMessageData? aData,
ClonedMessageData? aStack); ClonedMessageData? aStack);
@ -169,6 +173,8 @@ parent:
*/ */
async AccumulatePageUseCounters(UseCounters aUseCounters); async AccumulatePageUseCounters(UseCounters aUseCounters);
async RequestRestoreTabContent();
async Destroy(); async Destroy();
}; };

Просмотреть файл

@ -17,6 +17,7 @@
#include "mozilla/dom/BrowserBridgeChild.h" #include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/SecurityPolicyViolationEvent.h" #include "mozilla/dom/SecurityPolicyViolationEvent.h"
#include "mozilla/dom/SessionStoreRestoreData.h"
#include "mozilla/dom/WindowGlobalActorsBinding.h" #include "mozilla/dom/WindowGlobalActorsBinding.h"
#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/WindowContext.h" #include "mozilla/dom/WindowContext.h"
@ -565,6 +566,13 @@ mozilla::ipc::IPCResult WindowGlobalChild::RecvSetContainerFeaturePolicy(
return IPC_OK(); return IPC_OK();
} }
mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
aData->RestoreInto(BrowsingContext());
aResolve(true);
return IPC_OK();
}
IPCResult WindowGlobalChild::RecvRawMessage( IPCResult WindowGlobalChild::RecvRawMessage(
const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData, const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
const Maybe<ClonedMessageData>& aStack) { const Maybe<ClonedMessageData>& aStack) {

Просмотреть файл

@ -170,6 +170,10 @@ class WindowGlobalChild final : public WindowGlobalActor,
mozilla::ipc::IPCResult RecvSetContainerFeaturePolicy( mozilla::ipc::IPCResult RecvSetContainerFeaturePolicy(
dom::FeaturePolicy* aContainerFeaturePolicy); dom::FeaturePolicy* aContainerFeaturePolicy);
mozilla::ipc::IPCResult RecvRestoreTabContent(
dom::SessionStoreRestoreData* aData,
RestoreTabContentResolver&& aResolve);
virtual void ActorDestroy(ActorDestroyReason aWhy) override; virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private: private:

Просмотреть файл

@ -1086,6 +1086,11 @@ void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
mPageUseCounters = nullptr; mPageUseCounters = nullptr;
} }
mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
BrowsingContext()->Top()->RequestRestoreTabContent(this);
return IPC_OK();
}
void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
if (mPageUseCountersWindow) { if (mPageUseCountersWindow) {
mPageUseCountersWindow->FinishAccumulatingPageUseCounters(); mPageUseCountersWindow->FinishAccumulatingPageUseCounters();

Просмотреть файл

@ -261,6 +261,8 @@ class WindowGlobalParent final : public WindowContext,
mozilla::ipc::IPCResult RecvAccumulatePageUseCounters( mozilla::ipc::IPCResult RecvAccumulatePageUseCounters(
const UseCounters& aUseCounters); const UseCounters& aUseCounters);
mozilla::ipc::IPCResult RecvRequestRestoreTabContent();
private: private:
WindowGlobalParent(CanonicalBrowsingContext* aBrowsingContext, WindowGlobalParent(CanonicalBrowsingContext* aBrowsingContext,
uint64_t aInnerWindowId, uint64_t aOuterWindowId, uint64_t aInnerWindowId, uint64_t aOuterWindowId,

Просмотреть файл

@ -16,4 +16,6 @@ interface nsISessionStoreFunctions : nsISupports {
in Element aBrowser, in BrowsingContext aBrowsingContext, in Element aBrowser, in BrowsingContext aBrowsingContext,
in uint32_t aFlushId, in boolean aIsFinal, in uint32_t aEpoch, in uint32_t aFlushId, in boolean aIsFinal, in uint32_t aEpoch,
in jsval aData, in boolean aCollectSHistory); in jsval aData, in boolean aCollectSHistory);
void RestoreTabContentComplete(in Element aBrowser);
}; };

Просмотреть файл

@ -10,6 +10,10 @@ XPCOMUtils.defineLazyModuleGetters(this, {
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm", SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
}); });
function RestoreTabContentComplete(aBrowser) {
SessionStore.restoreTabContentComplete(aBrowser, {});
}
function UpdateSessionStore( function UpdateSessionStore(
aBrowser, aBrowser,
aBrowsingContext, aBrowsingContext,
@ -30,7 +34,7 @@ function UpdateSessionStore(
); );
} }
var EXPORTED_SYMBOLS = ["UpdateSessionStore"]; var EXPORTED_SYMBOLS = ["RestoreTabContentComplete", "UpdateSessionStore"];
var SessionStoreFuncInternal = { var SessionStoreFuncInternal = {
// form data which is waiting to be updated // form data which is waiting to be updated

Просмотреть файл

@ -7,6 +7,8 @@
#include "ipc/IPCMessageUtils.h" #include "ipc/IPCMessageUtils.h"
#include "SessionStoreData.h" #include "SessionStoreData.h"
#include "SessionStoreUtils.h"
#include "SessionStoreRestoreData.h"
namespace IPC { namespace IPC {
@ -68,4 +70,68 @@ struct ParamTraits<InputFormData> {
} // namespace IPC } // namespace IPC
namespace mozilla {
namespace ipc {
template <>
struct IPDLParamTraits<mozilla::dom::SessionStoreRestoreData*> {
// Note that we intentionally don't de/serialize mChildren here. The receiver
// won't be doing anything with the children lists, and it avoids sending form
// data for subframes to the content processes of their embedders.
static void Write(IPC::Message* aMsg, IProtocol* aActor,
mozilla::dom::SessionStoreRestoreData* aParam) {
bool isNull = !aParam;
WriteIPDLParam(aMsg, aActor, isNull);
if (isNull) {
return;
}
WriteIPDLParam(aMsg, aActor, aParam->mUrl);
WriteIPDLParam(aMsg, aActor, aParam->mInnerHTML);
WriteIPDLParam(aMsg, aActor, aParam->mScroll);
WriteIPDLParam(aMsg, aActor, aParam->mEntries);
}
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
IProtocol* aActor,
RefPtr<mozilla::dom::SessionStoreRestoreData>* aResult) {
*aResult = nullptr;
bool isNull;
if (!ReadIPDLParam(aMsg, aIter, aActor, &isNull)) {
return false;
}
if (isNull) {
return true;
}
auto data = MakeRefPtr<mozilla::dom::SessionStoreRestoreData>();
if (!ReadIPDLParam(aMsg, aIter, aActor, &data->mUrl) ||
!ReadIPDLParam(aMsg, aIter, aActor, &data->mInnerHTML) ||
!ReadIPDLParam(aMsg, aIter, aActor, &data->mScroll) ||
!ReadIPDLParam(aMsg, aIter, aActor, &data->mEntries)) {
return false;
}
*aResult = std::move(data);
return true;
}
};
template <>
struct IPDLParamTraits<mozilla::dom::SessionStoreRestoreData::Entry> {
static void Write(IPC::Message* aMsg, IProtocol* aActor,
mozilla::dom::SessionStoreRestoreData::Entry aParam) {
WriteIPDLParam(aMsg, aActor, aParam.mData);
WriteIPDLParam(aMsg, aActor, aParam.mIsXPath);
}
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
IProtocol* aActor,
mozilla::dom::SessionStoreRestoreData::Entry* aResult) {
return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mData) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mIsXPath);
}
};
} // namespace ipc
} // namespace mozilla
#endif // mozilla_dom_SessionStoreMessageUtils_h #endif // mozilla_dom_SessionStoreMessageUtils_h

Просмотреть файл

@ -0,0 +1,171 @@
/* -*- 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/. */
namespace mozilla {
namespace dom {
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/sessionstore/SessionStoreTypes.h"
#include "nsISessionStoreRestoreData.h"
bool SessionStoreRestoreData::IsEmpty() {
return (mUrl.IsEmpty() && mScroll.IsEmpty() && mInnerHTML.IsEmpty() &&
mEntries.IsEmpty() && mChildren.IsEmpty());
}
SessionStoreRestoreData* SessionStoreRestoreData::FindChild(
BrowsingContext* aContext) {
nsTArray<uint32_t> offsets;
for (const BrowsingContext* current = aContext;
current && current->GetParent(); current = current->GetParent()) {
// Don't bother continuing if any frame in our chain was created
// dynamically.
if (current->ChildOffset() < 0) {
return nullptr;
}
offsets.AppendElement(current->ChildOffset());
}
SessionStoreRestoreData* data = this;
for (uint32_t offset : Reversed(offsets)) {
if (!data || data->mChildren.Length() <= offset ||
!data->mChildren[offset] || data->mChildren[offset]->IsEmpty()) {
return nullptr;
}
data = data->mChildren[offset];
}
return data;
}
MOZ_CAN_RUN_SCRIPT
bool SessionStoreRestoreData::RestoreInto(RefPtr<BrowsingContext> aContext) {
if (!aContext->IsInProcess()) {
return false;
}
if (WindowContext* window = aContext->GetCurrentWindowContext()) {
if (!mScroll.IsEmpty()) {
if (nsGlobalWindowInner* inner = window->GetInnerWindow()) {
SessionStoreUtils::RestoreScrollPosition(*inner, mScroll);
}
}
if (!mUrl.IsEmpty()) {
if (nsCOMPtr<Document> doc = window->GetExtantDoc()) {
if (!SessionStoreUtils::RestoreFormData(*doc, mUrl, mInnerHTML,
mEntries)) {
return false;
}
}
}
}
return true;
}
void SessionStoreRestoreData::AddFormEntry(
bool aIsXPath, const nsAString& aIdOrXPath,
sessionstore::FormEntryValue aValue) {
mEntries.AppendElement(
Entry{sessionstore::FormEntry{nsString(aIdOrXPath), aValue}, aIsXPath});
}
NS_IMPL_ISUPPORTS(SessionStoreRestoreData, nsISessionStoreRestoreData,
SessionStoreRestoreData)
NS_IMETHODIMP
SessionStoreRestoreData::GetUrl(nsACString& aUrl) {
aUrl = mUrl;
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::SetUrl(const nsACString& aUrl) {
mUrl = aUrl;
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::GetInnerHTML(nsAString& aInnerHTML) {
aInnerHTML = mInnerHTML;
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::SetInnerHTML(const nsAString& aInnerHTML) {
mInnerHTML = aInnerHTML;
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::GetScroll(nsACString& aScroll) {
aScroll = mScroll;
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::SetScroll(const nsACString& aScroll) {
mScroll = aScroll;
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::AddTextField(bool aIsXPath,
const nsAString& aIdOrXPath,
const nsAString& aValue) {
AddFormEntry(aIsXPath, aIdOrXPath, sessionstore::TextField{nsString(aValue)});
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::AddCheckbox(bool aIsXPath, const nsAString& aIdOrXPath,
const bool aValue) {
AddFormEntry(aIsXPath, aIdOrXPath, sessionstore::Checkbox{aValue});
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::AddFileList(bool aIsXPath, const nsAString& aIdOrXPath,
const nsAString& aType,
const nsTArray<nsString>& aFileList) {
AddFormEntry(aIsXPath, aIdOrXPath, sessionstore::FileList{aFileList});
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::AddSingleSelect(bool aIsXPath,
const nsAString& aIdOrXPath,
uint32_t aSelectedIndex,
const nsAString& aValue) {
AddFormEntry(aIsXPath, aIdOrXPath,
sessionstore::SingleSelect{aSelectedIndex, nsString(aValue)});
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::AddMultipleSelect(bool aIsXPath,
const nsAString& aIdOrXPath,
const nsTArray<nsString>& aValues) {
AddFormEntry(aIsXPath, aIdOrXPath, sessionstore::MultipleSelect{aValues});
return NS_OK;
}
NS_IMETHODIMP
SessionStoreRestoreData::AddChild(nsISessionStoreRestoreData* aChild,
uint32_t aIndex) {
if (nsCOMPtr<SessionStoreRestoreData> child = do_QueryInterface(aChild)) {
if (aIndex > mChildren.Length()) {
mChildren.SetLength(aIndex);
}
mChildren.InsertElementAt(aIndex, child);
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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_SessionStoreRestoreData_h
#define mozilla_dom_SessionStoreRestoreData_h
#include "mozilla/Tuple.h"
#include "mozilla/dom/sessionstore/SessionStoreTypes.h"
#include "mozilla/dom/SessionStoreData.h"
#include "nsISessionStoreRestoreData.h"
namespace mozilla {
namespace dom {
#define NS_SESSIONSTORERESTOREDATA_IID \
{ \
0x88800c5b, 0xe142, 0x4ac6, { \
0x91, 0x52, 0xca, 0xbd, 0x3b, 0x74, 0xb9, 0xf8 \
} \
}
class SessionStoreRestoreData final : public nsISessionStoreRestoreData {
public:
SessionStoreRestoreData() = default;
bool IsEmpty();
SessionStoreRestoreData* FindChild(BrowsingContext* aContext);
bool RestoreInto(RefPtr<BrowsingContext> aContext);
NS_DECL_ISUPPORTS
NS_DECL_NSISESSIONSTORERESTOREDATA
NS_DECLARE_STATIC_IID_ACCESSOR(NS_SESSIONSTORERESTOREDATA_IID)
struct Entry {
sessionstore::FormEntry mData;
bool mIsXPath;
};
private:
virtual ~SessionStoreRestoreData() = default;
void AddFormEntry(bool aIsXPath, const nsAString& aIdOrXPath,
sessionstore::FormEntryValue aValue);
nsCString mScroll;
nsCString mUrl;
nsString mInnerHTML;
nsTArray<Entry> mEntries;
nsTArray<RefPtr<SessionStoreRestoreData>> mChildren;
friend struct mozilla::ipc::IPDLParamTraits<SessionStoreRestoreData*>;
};
NS_DEFINE_STATIC_IID_ACCESSOR(SessionStoreRestoreData,
NS_SESSIONSTORERESTOREDATA_IID)
} // namespace dom
} // namespace mozilla
#endif

Просмотреть файл

@ -29,11 +29,13 @@
#include "nsIDocShell.h" #include "nsIDocShell.h"
#include "nsIFormControl.h" #include "nsIFormControl.h"
#include "nsIScrollableFrame.h" #include "nsIScrollableFrame.h"
#include "nsISHistory.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
using namespace mozilla::dom::sessionstore;
namespace { namespace {
@ -274,11 +276,15 @@ static void CollectCurrentScrollPosition(JSContext* aCx, Document& aDocument,
void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal, void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal,
nsGlobalWindowInner& aWindow, nsGlobalWindowInner& aWindow,
const CollectedData& aData) { const CollectedData& aData) {
if (!aData.mScroll.WasPassed()) { if (aData.mScroll.WasPassed()) {
return; RestoreScrollPosition(aWindow, aData.mScroll.Value());
} }
}
nsCCharSeparatedTokenizer tokenizer(aData.mScroll.Value(), ','); /* static */
void SessionStoreUtils::RestoreScrollPosition(
nsGlobalWindowInner& aWindow, const nsCString& aScrollPosition) {
nsCCharSeparatedTokenizer tokenizer(aScrollPosition, ',');
nsAutoCString token(tokenizer.nextToken()); nsAutoCString token(tokenizer.nextToken());
int pos_X = atoi(token.get()); int pos_X = atoi(token.get());
token = tokenizer.nextToken(); token = tokenizer.nextToken();
@ -802,7 +808,6 @@ static void SetElementAsBool(Element* aElement, bool aValue) {
MOZ_CAN_RUN_SCRIPT MOZ_CAN_RUN_SCRIPT
static void SetElementAsFiles(HTMLInputElement* aElement, static void SetElementAsFiles(HTMLInputElement* aElement,
const CollectedFileListValue& aValue) { const CollectedFileListValue& aValue) {
nsTArray<nsString> fileList;
IgnoredErrorResult rv; IgnoredErrorResult rv;
aElement->MozSetFileNameArray(aValue.mFileList, rv); aElement->MozSetFileNameArray(aValue.mFileList, rv);
if (rv.Failed()) { if (rv.Failed()) {
@ -927,7 +932,7 @@ static void SetElementAsObject(JSContext* aCx, Element* aElement,
} }
MOZ_CAN_RUN_SCRIPT MOZ_CAN_RUN_SCRIPT
static void SetRestoreData(JSContext* aCx, Element* aElement, static void SetSessionData(JSContext* aCx, Element* aElement,
JS::MutableHandle<JS::Value> aObject) { JS::MutableHandle<JS::Value> aObject) {
nsAutoString data; nsAutoString data;
if (nsContentUtils::StringifyJSON(aCx, aObject, data)) { if (nsContentUtils::StringifyJSON(aCx, aObject, data)) {
@ -938,12 +943,11 @@ static void SetRestoreData(JSContext* aCx, Element* aElement,
} }
MOZ_CAN_RUN_SCRIPT MOZ_CAN_RUN_SCRIPT
static void SetInnerHTML(Document& aDocument, const CollectedData& aData) { static void SetInnerHTML(Document& aDocument, const nsString& aInnerHTML) {
RefPtr<Element> bodyElement = aDocument.GetBody(); RefPtr<Element> bodyElement = aDocument.GetBody();
if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) { if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
IgnoredErrorResult rv; IgnoredErrorResult rv;
bodyElement->SetInnerHTML(aData.mInnerHTML.Value(), bodyElement->SetInnerHTML(aInnerHTML, aDocument.NodePrincipal(), rv);
aDocument.NodePrincipal(), rv);
if (!rv.Failed()) { if (!rv.Failed()) {
nsContentUtils::DispatchInputEvent(bodyElement); nsContentUtils::DispatchInputEvent(bodyElement);
} }
@ -978,7 +982,7 @@ class FormDataParseContext : public txIParseContext {
bool mIsCaseInsensitive; bool mIsCaseInsensitive;
}; };
static Element* FindNodeByXPath(JSContext* aCx, Document& aDocument, static Element* FindNodeByXPath(Document& aDocument,
const nsAString& aExpression) { const nsAString& aExpression) {
FormDataParseContext parsingContext(aDocument.IsHTMLDocument()); FormDataParseContext parsingContext(aDocument.IsHTMLDocument());
IgnoredErrorResult rv; IgnoredErrorResult rv;
@ -989,7 +993,7 @@ static Element* FindNodeByXPath(JSContext* aCx, Document& aDocument,
return nullptr; return nullptr;
} }
RefPtr<XPathResult> result = expression->Evaluate( RefPtr<XPathResult> result = expression->Evaluate(
aCx, aDocument, XPathResult::FIRST_ORDERED_NODE_TYPE, nullptr, rv); aDocument, XPathResult::FIRST_ORDERED_NODE_TYPE, nullptr, rv);
if (rv.Failed()) { if (rv.Failed()) {
return nullptr; return nullptr;
} }
@ -1012,7 +1016,7 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
return false; return false;
} }
if (aData.mInnerHTML.WasPassed()) { if (aData.mInnerHTML.WasPassed()) {
SetInnerHTML(aDocument, aData); SetInnerHTML(aDocument, aData.mInnerHTML.Value());
} }
if (aData.mId.WasPassed()) { if (aData.mId.WasPassed()) {
for (auto& entry : aData.mId.Value().Entries()) { for (auto& entry : aData.mId.Value().Entries()) {
@ -1030,13 +1034,11 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
// cf. bug 467409 // cf. bug 467409
JSContext* cx = aGlobal.Context(); JSContext* cx = aGlobal.Context();
if (entry.mKey.EqualsLiteral("sessionData")) { if (entry.mKey.EqualsLiteral("sessionData")) {
nsAutoCString url;
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
if (url.EqualsLiteral("about:sessionrestore") || if (url.EqualsLiteral("about:sessionrestore") ||
url.EqualsLiteral("about:welcomeback")) { url.EqualsLiteral("about:welcomeback")) {
JS::Rooted<JS::Value> object( JS::Rooted<JS::Value> object(
cx, JS::ObjectValue(*entry.mValue.GetAsObject())); cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
SetRestoreData(cx, node, &object); SetSessionData(cx, node, &object);
continue; continue;
} }
} }
@ -1048,8 +1050,7 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
} }
if (aData.mXpath.WasPassed()) { if (aData.mXpath.WasPassed()) {
for (auto& entry : aData.mXpath.Value().Entries()) { for (auto& entry : aData.mXpath.Value().Entries()) {
RefPtr<Element> node = RefPtr<Element> node = FindNodeByXPath(aDocument, entry.mKey);
FindNodeByXPath(aGlobal.Context(), aDocument, entry.mKey);
if (node == nullptr) { if (node == nullptr) {
continue; continue;
} }
@ -1067,6 +1068,74 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
return true; return true;
} }
MOZ_CAN_RUN_SCRIPT
void RestoreFormEntry(Element* aNode, FormEntryValue aValue) {
using Type = sessionstore::FormEntryValue::Type;
switch (aValue.type()) {
case Type::TCheckbox:
SetElementAsBool(aNode, aValue.get_Checkbox().value());
break;
case Type::TTextField:
SetElementAsString(aNode, aValue.get_TextField().value());
break;
case Type::TFileList: {
if (RefPtr<HTMLInputElement> input = HTMLInputElement::FromNode(aNode);
input && input->ControlType() == NS_FORM_INPUT_FILE) {
CollectedFileListValue value;
value.mFileList = std::move(aValue.get_FileList().valueList());
SetElementAsFiles(input, value);
}
break;
}
case Type::TSingleSelect:
case Type::TMultipleSelect: {
if (RefPtr<HTMLSelectElement> select =
HTMLSelectElement::FromNode(aNode)) {
if (!select->Multiple()) {
CollectedNonMultipleSelectValue value;
value.mSelectedIndex = aValue.get_SingleSelect().index();
value.mValue = aValue.get_SingleSelect().value();
SetElementAsSelect(select, value);
} else {
SetElementAsMultiSelect(select,
aValue.get_MultipleSelect().valueList());
}
}
break;
}
default:
MOZ_ASSERT_UNREACHABLE();
}
}
MOZ_CAN_RUN_SCRIPT
/* static */
bool SessionStoreUtils::RestoreFormData(
Document& aDocument, const nsCString& aFormUrl, const nsString& aInnerHTML,
const nsTArray<SessionStoreRestoreData::Entry>& aEntries) {
// FIXME: Bug 1698878 - We should do this check in the parent instead.
nsAutoCString url;
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
if (!aFormUrl.Equals(url)) {
return false;
}
if (!aInnerHTML.IsEmpty()) {
SetInnerHTML(aDocument, aInnerHTML);
}
for (const auto& entry : aEntries) {
RefPtr<Element> node = entry.mIsXPath
? FindNodeByXPath(aDocument, entry.mData.id())
: aDocument.GetElementById(entry.mData.id());
if (node) {
RestoreFormEntry(node, entry.mData.value());
}
}
return true;
}
/* Read entries in the session storage data contained in a tab's history. */ /* Read entries in the session storage data contained in a tab's history. */
static void ReadAllEntriesFromStorage(nsPIDOMWindowOuter* aWindow, static void ReadAllEntriesFromStorage(nsPIDOMWindowOuter* aWindow,
nsTArray<nsCString>& aOrigins, nsTArray<nsCString>& aOrigins,
@ -1356,3 +1425,43 @@ static void CollectFrameTreeData(JSContext* aCx,
ret.mBoolVal.Construct(std::move(boolVal)); ret.mBoolVal.Construct(std::move(boolVal));
} }
} }
/* static */
MOZ_CAN_RUN_SCRIPT
already_AddRefed<nsISessionStoreRestoreData>
SessionStoreUtils::ConstructSessionStoreRestoreData(
const GlobalObject& aGlobal) {
nsCOMPtr<nsISessionStoreRestoreData> data = new SessionStoreRestoreData();
return data.forget();
}
/* static */
MOZ_CAN_RUN_SCRIPT
bool SessionStoreUtils::SetRestoreData(const GlobalObject& aGlobal,
CanonicalBrowsingContext& aContext,
nsISessionStoreRestoreData* aData) {
if (!mozilla::SessionHistoryInParent()) {
MOZ_CRASH("why were we called?");
}
MOZ_DIAGNOSTIC_ASSERT(aContext.IsTop());
if (nsCOMPtr<SessionStoreRestoreData> data = do_QueryInterface(aData);
data && !data->IsEmpty()) {
aContext.SetRestoreData(data);
if (nsISHistory* shistory = aContext.GetSessionHistory()) {
shistory->ReloadCurrentEntry();
return true;
}
}
return false;
}
/* static */
nsresult SessionStoreUtils::CallRestoreTabContentComplete(Element* aBrowser) {
nsCOMPtr<nsISessionStoreFunctions> funcs =
do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm");
NS_ENSURE_TRUE(funcs, NS_ERROR_FAILURE);
return funcs->RestoreTabContentComplete(aBrowser);
}

Просмотреть файл

@ -9,8 +9,10 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h" #include "mozilla/dom/SessionStoreUtilsBinding.h"
#include "SessionStoreData.h" #include "SessionStoreData.h"
#include "SessionStoreRestoreData.h"
class nsIDocument; class nsIDocument;
class nsGlobalWindowInner; class nsGlobalWindowInner;
@ -55,6 +57,8 @@ class SessionStoreUtils {
static void RestoreScrollPosition(const GlobalObject& aGlobal, static void RestoreScrollPosition(const GlobalObject& aGlobal,
nsGlobalWindowInner& aWindow, nsGlobalWindowInner& aWindow,
const CollectedData& data); const CollectedData& data);
static void RestoreScrollPosition(nsGlobalWindowInner& aWindow,
const nsCString& aScrollPosition);
/* /*
@param aDocument: DOMDocument instance to obtain form data for. @param aDocument: DOMDocument instance to obtain form data for.
@ -81,6 +85,10 @@ class SessionStoreUtils {
MOZ_CAN_RUN_SCRIPT_BOUNDARY MOZ_CAN_RUN_SCRIPT_BOUNDARY
static bool RestoreFormData(const GlobalObject& aGlobal, Document& aDocument, static bool RestoreFormData(const GlobalObject& aGlobal, Document& aDocument,
const CollectedData& aData); const CollectedData& aData);
static bool RestoreFormData(
Document& aDocument, const nsCString& aFormUrl,
const nsString& aInnerHTML,
const nsTArray<SessionStoreRestoreData::Entry>& aEntries);
static void CollectedSessionStorage(BrowsingContext* aBrowsingContext, static void CollectedSessionStorage(BrowsingContext* aBrowsingContext,
nsTArray<nsCString>& aOrigins, nsTArray<nsCString>& aOrigins,
@ -93,6 +101,13 @@ class SessionStoreUtils {
static void ComposeInputData(const nsTArray<CollectedInputDataValue>& aData, static void ComposeInputData(const nsTArray<CollectedInputDataValue>& aData,
InputElementData& ret); InputElementData& ret);
static already_AddRefed<nsISessionStoreRestoreData>
ConstructSessionStoreRestoreData(const GlobalObject& aGlobal);
static bool SetRestoreData(const GlobalObject& aGlobal,
CanonicalBrowsingContext& aContext,
nsISessionStoreRestoreData* aData);
static nsresult CallRestoreTabContentComplete(Element* aBrowser);
}; };
} // namespace dom } // namespace dom

Просмотреть файл

@ -8,11 +8,13 @@ EXPORTS.mozilla.dom += [
"SessionStoreData.h", "SessionStoreData.h",
"SessionStoreListener.h", "SessionStoreListener.h",
"SessionStoreMessageUtils.h", "SessionStoreMessageUtils.h",
"SessionStoreRestoreData.h",
"SessionStoreUtils.h", "SessionStoreUtils.h",
] ]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
"SessionStoreListener.cpp", "SessionStoreListener.cpp",
"SessionStoreRestoreData.cpp",
"SessionStoreUtils.cpp", "SessionStoreUtils.cpp",
] ]
@ -20,9 +22,10 @@ EXTRA_JS_MODULES += [
"SessionStoreFunctions.jsm", "SessionStoreFunctions.jsm",
] ]
XPIDL_MODULE = "sessionStore_funcs" XPIDL_MODULE = "sessionstore"
XPIDL_SOURCES += [ XPIDL_SOURCES += [
"nsISessionStoreRestoreData.idl",
"SessionStoreFunctions.idl", "SessionStoreFunctions.idl",
] ]

Просмотреть файл

@ -0,0 +1,33 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "nsISupports.idl"
[scriptable, uuid(cd9f33c5-460d-4bbf-a459-f375ca9566d8)]
interface nsISessionStoreRestoreData : nsISupports {
// Setters for form data.
attribute AUTF8String url;
attribute AString innerHTML;
// Setters for scroll data.
attribute ACString scroll;
// Methods for adding individual form fields which are called as the JS code
// finds them.
void addTextField(in boolean aIsXPath, in AString aIdOrXPath,
in AString aValue);
void addCheckbox(in boolean aIsXPath, in AString aIdOrXPath,
in boolean aValue);
void addFileList(in boolean aIsXPath, in AString aIdOrXPath, in AString aType,
in Array<AString> aFileList);
void addSingleSelect(in boolean aIsXPath, in AString aIdOrXPath,
in unsigned long aSelectedIndex, in AString aValue);
void addMultipleSelect(in boolean aIsXPath, in AString aIdOrXPath,
in Array<AString> aValues);
// Add a child data object to our children list.
void addChild(in nsISessionStoreRestoreData aChild, in unsigned long aIndex);
};