зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1597499 - Make Session Restore work in Fission, r=nika
Differential Revision: https://phabricator.services.mozilla.com/D107883
This commit is contained in:
Родитель
4dd9e85c76
Коммит
83da101af6
|
@ -56,7 +56,6 @@ function ContentRestore(chromeGlobal) {
|
|||
"restoreTabContent",
|
||||
"restoreDocument",
|
||||
"resetRestore",
|
||||
"setRestoringDocument",
|
||||
];
|
||||
|
||||
for (let method of EXPORTED_METHODS) {
|
||||
|
@ -101,13 +100,6 @@ ContentRestoreInternal.prototype = {
|
|||
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
|
||||
* in here and used throughout the restoration. The epoch (which must be
|
||||
|
@ -224,9 +216,7 @@ ContentRestoreInternal.prototype = {
|
|||
webNavigation.loadURI(tabData.userTypedValue, loadURIOptions);
|
||||
} else if (tabData.entries.length) {
|
||||
// Stash away the data we need for restoreDocument.
|
||||
let activeIndex = tabData.index - 1;
|
||||
this._restoringDocument = {
|
||||
entry: tabData.entries[activeIndex] || {},
|
||||
formdata: tabData.formdata || {},
|
||||
scrollPositions: tabData.scroll || {},
|
||||
};
|
||||
|
@ -294,7 +284,7 @@ ContentRestoreInternal.prototype = {
|
|||
*/
|
||||
restoreDocument() {
|
||||
if (!this._restoringDocument) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
let { formdata, scrollPositions } = this._restoringDocument;
|
||||
|
@ -316,7 +306,6 @@ ContentRestoreInternal.prototype = {
|
|||
SessionStoreUtils.restoreScrollPosition(frame, data);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -99,16 +99,8 @@ class EventListener extends Handler {
|
|||
}
|
||||
|
||||
if (this.contentRestoreInitialized) {
|
||||
// Restore the form data and scroll position (if we're restoring a
|
||||
// document).
|
||||
if (
|
||||
this.contentRestore.restoreDocument() &&
|
||||
Services.appinfo.sessionHistoryInParent
|
||||
) {
|
||||
this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
|
||||
epoch: this.store.epoch,
|
||||
});
|
||||
}
|
||||
// Restore the form data and scroll position.
|
||||
this.contentRestore.restoreDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -518,7 +510,6 @@ const MESSAGES = [
|
|||
"SessionStore:flush",
|
||||
"SessionStore:becomeActiveProcess",
|
||||
"SessionStore:prepareForProcessChange",
|
||||
"SessionStore:setRestoringDocument",
|
||||
];
|
||||
|
||||
class ContentSessionStore {
|
||||
|
@ -530,16 +521,17 @@ class ContentSessionStore {
|
|||
|
||||
this.contentRestoreInitialized = false;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "contentRestore", () => {
|
||||
this.contentRestoreInitialized = true;
|
||||
return new ContentRestore(mm);
|
||||
});
|
||||
|
||||
this.handlers = [new EventListener(this), this.messageQueue];
|
||||
this.handlers = [this.messageQueue];
|
||||
if (Services.appinfo.sessionHistoryInParent) {
|
||||
this.mm.sendAsyncMessage("SessionStore:addSHistoryListener");
|
||||
} else {
|
||||
this.handlers.push(new EventListener(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));
|
||||
|
@ -595,9 +587,6 @@ class ContentSessionStore {
|
|||
// parent process.
|
||||
this.mm.docShell.persistLayoutHistoryState();
|
||||
break;
|
||||
case "SessionStore:setRestoringDocument":
|
||||
this.contentRestore.setRestoringDocument(data);
|
||||
break;
|
||||
default:
|
||||
debug("received unknown message '" + name + "'");
|
||||
break;
|
||||
|
|
|
@ -527,6 +527,10 @@ var SessionStore = {
|
|||
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.
|
||||
|
@ -1195,26 +1199,12 @@ var SessionStoreInternal = {
|
|||
}
|
||||
onStateChange(webProgress, request, stateFlags, status) {
|
||||
if (
|
||||
!webProgress.isTopLevel ||
|
||||
!(stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
|
||||
webProgress.isTopLevel &&
|
||||
stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
|
||||
stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
callbacks.onStopRequest
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
callbacks.onStartRequest &&
|
||||
stateFlags & Ci.nsIWebProgressListener.STATE_START
|
||||
) {
|
||||
callbacks.onStartRequest();
|
||||
this.uninstall();
|
||||
}
|
||||
|
||||
if (
|
||||
callbacks.onStopRequest &&
|
||||
stateFlags & Ci.nsIWebProgressListener.STATE_STOP
|
||||
) {
|
||||
callbacks.onStopRequest();
|
||||
this.uninstall();
|
||||
callbacks.onStopRequest(request, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1411,6 +1401,7 @@ var SessionStoreInternal = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aMessage.data.isFinal) {
|
||||
// If this the final message we need to resolve all pending flush
|
||||
// requests for the given browser as they might have been sent too
|
||||
|
@ -1421,7 +1412,6 @@ var SessionStoreInternal = {
|
|||
for (let wm of [
|
||||
this._browserSHistoryListener,
|
||||
this._browserSHistoryListenerForRestore,
|
||||
this._browserProgressListenerForRestore,
|
||||
]) {
|
||||
let listener = wm.get(browser.permanentKey);
|
||||
if (listener) {
|
||||
|
@ -5641,6 +5631,15 @@ var SessionStoreInternal = {
|
|||
_resetLocalTabRestoringState(aTab) {
|
||||
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
|
||||
let previousState = TAB_STATE_FOR_BROWSER.get(browser);
|
||||
|
||||
|
@ -5674,7 +5673,9 @@ var SessionStoreInternal = {
|
|||
return;
|
||||
}
|
||||
|
||||
browser.messageManager.sendAsyncMessage("SessionStore:resetRestore", {});
|
||||
if (!Services.appinfo.sessionHistoryInParent) {
|
||||
browser.messageManager.sendAsyncMessage("SessionStore:resetRestore", {});
|
||||
}
|
||||
this._resetLocalTabRestoringState(tab);
|
||||
},
|
||||
|
||||
|
@ -5759,6 +5760,91 @@ var SessionStoreInternal = {
|
|||
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
|
||||
* 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");
|
||||
}
|
||||
|
||||
let listener = this._browserProgressListenerForRestore.get(
|
||||
browser.permanentKey
|
||||
);
|
||||
if (listener) {
|
||||
listener.uninstall();
|
||||
}
|
||||
|
||||
listener = this._browserSHistoryListenerForRestore.get(
|
||||
browser.permanentKey
|
||||
);
|
||||
if (listener) {
|
||||
listener.uninstall();
|
||||
for (let map of [
|
||||
this._browserProgressListenerForRestore,
|
||||
this._browserSHistoryListenerForRestore,
|
||||
]) {
|
||||
let listener = map.get(browser.permanentKey);
|
||||
if (listener) {
|
||||
listener.uninstall();
|
||||
}
|
||||
}
|
||||
|
||||
let restoreData = {
|
||||
|
@ -5846,43 +5928,56 @@ var SessionStoreInternal = {
|
|||
this._restoreTabContentStarted(browser, restoreData);
|
||||
|
||||
let { tabData } = restoreData;
|
||||
|
||||
let uri = "about:blank";
|
||||
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY;
|
||||
let uri = null;
|
||||
let loadFlags = null;
|
||||
|
||||
if (tabData?.userTypedValue && tabData?.userTypedClear) {
|
||||
uri = tabData.userTypedValue;
|
||||
loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
|
||||
} else if (tabData?.entries.length) {
|
||||
uri = loadFlags = null;
|
||||
browser.messageManager.sendAsyncMessage(
|
||||
"SessionStore:setRestoringDocument",
|
||||
{
|
||||
entry: tabData.entries[tabData.index - 1] || {},
|
||||
formdata: tabData.formdata || {},
|
||||
scrollPositions: tabData.scroll || {},
|
||||
}
|
||||
uri = tabData.entries[tabData.index - 1].url;
|
||||
let willRestoreContent = SessionStoreUtils.setRestoreData(
|
||||
browser.browsingContext,
|
||||
this.buildRestoreData(tabData.formdata, 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) {
|
||||
// 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.
|
||||
if (uri) {
|
||||
this.addProgressListenerForRestore(browser, {
|
||||
onStopRequest: () => {
|
||||
this._restoreTabContentComplete(browser, restoreData);
|
||||
onStopRequest: (request, listener) => {
|
||||
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, {
|
||||
loadFlags,
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
});
|
||||
if (loadFlags) {
|
||||
browser.browsingContext.loadURI(uri, {
|
||||
loadFlags,
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
});
|
||||
} else {
|
||||
browser.browsingContext.sessionHistory.reloadCurrentEntry();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -2683,8 +2683,8 @@ bool BrowsingContext::LegacyCheckOnlyOwningProcessCanSet(
|
|||
return true;
|
||||
}
|
||||
|
||||
auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(ContentParent* aSource)
|
||||
-> CanSetResult {
|
||||
auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
|
||||
ContentParent* aSource) -> CanSetResult {
|
||||
if (aSource) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
|
@ -3168,6 +3168,11 @@ bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
|
|||
return IsTop() && GetPendingInitialization() && !aNewValue;
|
||||
}
|
||||
|
||||
bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
|
||||
ContentParent* aSource) {
|
||||
return IsTop();
|
||||
}
|
||||
|
||||
bool BrowsingContext::IsPopupAllowed() {
|
||||
for (auto* context = GetCurrentWindowContext(); context;
|
||||
context = context->GetParentWindowContext()) {
|
||||
|
|
|
@ -198,7 +198,8 @@ enum class ExplicitActiveStatus : uint8_t {
|
|||
/* The number of entries added to the session history because of this \
|
||||
* browsing context. */ \
|
||||
FIELD(HistoryEntryCount, uint32_t) \
|
||||
FIELD(IsInBFCache, bool)
|
||||
FIELD(IsInBFCache, bool) \
|
||||
FIELD(HasRestoreData, bool)
|
||||
|
||||
// BrowsingContext, in this context, is the cross process replicated
|
||||
// environment in which information about documents is stored. In
|
||||
|
@ -1027,6 +1028,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
|||
ContentParent* aSource);
|
||||
void DidSet(FieldIndex<IDX_HasMainMediaController>, bool aOldValue);
|
||||
|
||||
bool CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
|
||||
ContentParent* aSource);
|
||||
|
||||
template <size_t I, typename T>
|
||||
bool CanSet(FieldIndex<I>, const T&, ContentParent*) {
|
||||
return true;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/dom/BrowsingContextGroup.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/EventTarget.h"
|
||||
#include "mozilla/dom/PWindowGlobalParent.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/dom/ContentProcessManager.h"
|
||||
#include "mozilla/dom/MediaController.h"
|
||||
|
@ -173,6 +174,14 @@ void CanonicalBrowsingContext::ReplacedBy(
|
|||
}
|
||||
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
|
||||
// the new context has been attached before.
|
||||
// SetWithoutSyncing can be used if context hasn't been attached.
|
||||
|
@ -180,6 +189,7 @@ void CanonicalBrowsingContext::ReplacedBy(
|
|||
txn.SetBrowserId(GetBrowserId());
|
||||
txn.SetHistoryID(GetHistoryID());
|
||||
txn.SetExplicitActive(GetExplicitActive());
|
||||
txn.SetHasRestoreData(false);
|
||||
if (aNewContext->EverAttached()) {
|
||||
MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext));
|
||||
} 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(
|
||||
FeaturePolicy* aContainerFeaturePolicy) {
|
||||
mContainerFeaturePolicy = aContainerFeaturePolicy;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/MediaControlKeySource.h"
|
||||
#include "mozilla/dom/BrowsingContextWebProgress.h"
|
||||
#include "mozilla/dom/SessionStoreRestoreData.h"
|
||||
#include "mozilla/dom/SessionStoreUtils.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
@ -282,6 +284,9 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
return mContainerFeaturePolicy;
|
||||
}
|
||||
|
||||
void SetRestoreData(SessionStoreRestoreData* aData);
|
||||
void RequestRestoreTabContent(WindowGlobalParent* aWindow);
|
||||
|
||||
protected:
|
||||
// Called when the browsing context is being discarded.
|
||||
void CanonicalDiscard();
|
||||
|
@ -392,6 +397,10 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
RefPtr<nsBrowserStatusFilter> mStatusFilter;
|
||||
|
||||
RefPtr<FeaturePolicy> mContainerFeaturePolicy;
|
||||
|
||||
RefPtr<SessionStoreRestoreData> mRestoreData;
|
||||
uint32_t mRequestedContentRestores = 0;
|
||||
uint32_t mCompletedContentRestores = 0;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -5882,6 +5882,7 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
|
||||
nsCOMPtr<nsIWebProgress> webProgress =
|
||||
do_QueryInterface(GetAsSupports(this));
|
||||
|
@ -6528,6 +6529,12 @@ nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
|
|||
//
|
||||
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
|
||||
// will cause any OnLoad(...) and PopState(...) handlers to fire.
|
||||
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 */
|
||||
void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
|
||||
const nsString& aKeyword) {
|
||||
|
|
|
@ -398,6 +398,8 @@ class nsDocShell final : public nsDocLoader,
|
|||
|
||||
void StoreWindowNameToSHEntries();
|
||||
|
||||
void MaybeRestoreTabContent();
|
||||
|
||||
void SetWillChangeProcess() { mWillChangeProcess = true; }
|
||||
bool WillChangeProcess() { return mWillChangeProcess; }
|
||||
|
||||
|
|
|
@ -1969,3 +1969,6 @@ addExternalIface('nsICookieJarSettings', nativeType='nsICookieJarSettings',
|
|||
notflattened=True)
|
||||
addExternalIface('nsIGleanPing', headerFile='mozilla/glean/bindings/Ping.h',
|
||||
nativeType='nsIGleanPing', notflattened=True)
|
||||
addExternalIface('nsISessionStoreRestoreData',
|
||||
nativeType='nsISessionStoreRestoreData',
|
||||
headerFile='nsISessionStoreRestoreData.h', notflattened=True)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
interface nsIDocShell;
|
||||
interface nsISupports;
|
||||
interface nsISessionStoreRestoreData;
|
||||
|
||||
/**
|
||||
* A callback passed to SessionStoreUtils.forEachNonDynamicChildFrame().
|
||||
|
@ -122,6 +123,11 @@ namespace SessionStoreUtils {
|
|||
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
|
||||
*/
|
||||
void restoreSessionStorage(nsIDocShell docShell, record<DOMString, record<DOMString, DOMString>> data);
|
||||
|
||||
nsISessionStoreRestoreData constructSessionStoreRestoreData();
|
||||
|
||||
boolean setRestoreData(CanonicalBrowsingContext browsingContext,
|
||||
nsISessionStoreRestoreData? data);
|
||||
};
|
||||
|
||||
[GenerateConversionToJS, GenerateInit]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
include "mozilla/dom/DocShellMessageUtils.h";
|
||||
include "mozilla/dom/FeaturePolicyUtils.h";
|
||||
include "mozilla/dom/PermissionMessageUtils.h";
|
||||
include "mozilla/dom/SessionStoreMessageUtils.h";
|
||||
include "mozilla/ipc/TransportSecurityInfoUtils.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::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
|
||||
[RefCounted] using mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h";
|
||||
[RefCounted] using mozilla::dom::SessionStoreRestoreData from "mozilla/dom/SessionStoreRestoreData.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -90,6 +92,8 @@ child:
|
|||
|
||||
async SetContainerFeaturePolicy(FeaturePolicy aContainerFeaturePolicy);
|
||||
|
||||
async RestoreTabContent(SessionStoreRestoreData aData) returns (bool success);
|
||||
|
||||
both:
|
||||
async RawMessage(JSActorMessageMeta aMetadata, ClonedMessageData? aData,
|
||||
ClonedMessageData? aStack);
|
||||
|
@ -169,6 +173,8 @@ parent:
|
|||
*/
|
||||
async AccumulatePageUseCounters(UseCounters aUseCounters);
|
||||
|
||||
async RequestRestoreTabContent();
|
||||
|
||||
async Destroy();
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "mozilla/dom/BrowserBridgeChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/SecurityPolicyViolationEvent.h"
|
||||
#include "mozilla/dom/SessionStoreRestoreData.h"
|
||||
#include "mozilla/dom/WindowGlobalActorsBinding.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/dom/WindowContext.h"
|
||||
|
@ -565,6 +566,13 @@ mozilla::ipc::IPCResult WindowGlobalChild::RecvSetContainerFeaturePolicy(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
|
||||
dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
|
||||
aData->RestoreInto(BrowsingContext());
|
||||
aResolve(true);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
IPCResult WindowGlobalChild::RecvRawMessage(
|
||||
const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
|
||||
const Maybe<ClonedMessageData>& aStack) {
|
||||
|
|
|
@ -170,6 +170,10 @@ class WindowGlobalChild final : public WindowGlobalActor,
|
|||
mozilla::ipc::IPCResult RecvSetContainerFeaturePolicy(
|
||||
dom::FeaturePolicy* aContainerFeaturePolicy);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRestoreTabContent(
|
||||
dom::SessionStoreRestoreData* aData,
|
||||
RestoreTabContentResolver&& aResolve);
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -1086,6 +1086,11 @@ void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
|
|||
mPageUseCounters = nullptr;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
|
||||
BrowsingContext()->Top()->RequestRestoreTabContent(this);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
if (mPageUseCountersWindow) {
|
||||
mPageUseCountersWindow->FinishAccumulatingPageUseCounters();
|
||||
|
|
|
@ -261,6 +261,8 @@ class WindowGlobalParent final : public WindowContext,
|
|||
mozilla::ipc::IPCResult RecvAccumulatePageUseCounters(
|
||||
const UseCounters& aUseCounters);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestRestoreTabContent();
|
||||
|
||||
private:
|
||||
WindowGlobalParent(CanonicalBrowsingContext* aBrowsingContext,
|
||||
uint64_t aInnerWindowId, uint64_t aOuterWindowId,
|
||||
|
|
|
@ -16,4 +16,6 @@ interface nsISessionStoreFunctions : nsISupports {
|
|||
in Element aBrowser, in BrowsingContext aBrowsingContext,
|
||||
in uint32_t aFlushId, in boolean aIsFinal, in uint32_t aEpoch,
|
||||
in jsval aData, in boolean aCollectSHistory);
|
||||
|
||||
void RestoreTabContentComplete(in Element aBrowser);
|
||||
};
|
||||
|
|
|
@ -10,6 +10,10 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
|
||||
});
|
||||
|
||||
function RestoreTabContentComplete(aBrowser) {
|
||||
SessionStore.restoreTabContentComplete(aBrowser, {});
|
||||
}
|
||||
|
||||
function UpdateSessionStore(
|
||||
aBrowser,
|
||||
aBrowsingContext,
|
||||
|
@ -30,7 +34,7 @@ function UpdateSessionStore(
|
|||
);
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ["UpdateSessionStore"];
|
||||
var EXPORTED_SYMBOLS = ["RestoreTabContentComplete", "UpdateSessionStore"];
|
||||
|
||||
var SessionStoreFuncInternal = {
|
||||
// form data which is waiting to be updated
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "SessionStoreData.h"
|
||||
#include "SessionStoreUtils.h"
|
||||
#include "SessionStoreRestoreData.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -68,4 +70,68 @@ struct ParamTraits<InputFormData> {
|
|||
|
||||
} // 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
|
||||
|
|
|
@ -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 "nsIFormControl.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsISHistory.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::sessionstore;
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -274,11 +276,15 @@ static void CollectCurrentScrollPosition(JSContext* aCx, Document& aDocument,
|
|||
void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal,
|
||||
nsGlobalWindowInner& aWindow,
|
||||
const CollectedData& aData) {
|
||||
if (!aData.mScroll.WasPassed()) {
|
||||
return;
|
||||
if (aData.mScroll.WasPassed()) {
|
||||
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());
|
||||
int pos_X = atoi(token.get());
|
||||
token = tokenizer.nextToken();
|
||||
|
@ -802,7 +808,6 @@ static void SetElementAsBool(Element* aElement, bool aValue) {
|
|||
MOZ_CAN_RUN_SCRIPT
|
||||
static void SetElementAsFiles(HTMLInputElement* aElement,
|
||||
const CollectedFileListValue& aValue) {
|
||||
nsTArray<nsString> fileList;
|
||||
IgnoredErrorResult rv;
|
||||
aElement->MozSetFileNameArray(aValue.mFileList, rv);
|
||||
if (rv.Failed()) {
|
||||
|
@ -927,7 +932,7 @@ static void SetElementAsObject(JSContext* aCx, Element* aElement,
|
|||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
static void SetRestoreData(JSContext* aCx, Element* aElement,
|
||||
static void SetSessionData(JSContext* aCx, Element* aElement,
|
||||
JS::MutableHandle<JS::Value> aObject) {
|
||||
nsAutoString data;
|
||||
if (nsContentUtils::StringifyJSON(aCx, aObject, data)) {
|
||||
|
@ -938,12 +943,11 @@ static void SetRestoreData(JSContext* aCx, Element* aElement,
|
|||
}
|
||||
|
||||
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();
|
||||
if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
|
||||
IgnoredErrorResult rv;
|
||||
bodyElement->SetInnerHTML(aData.mInnerHTML.Value(),
|
||||
aDocument.NodePrincipal(), rv);
|
||||
bodyElement->SetInnerHTML(aInnerHTML, aDocument.NodePrincipal(), rv);
|
||||
if (!rv.Failed()) {
|
||||
nsContentUtils::DispatchInputEvent(bodyElement);
|
||||
}
|
||||
|
@ -978,7 +982,7 @@ class FormDataParseContext : public txIParseContext {
|
|||
bool mIsCaseInsensitive;
|
||||
};
|
||||
|
||||
static Element* FindNodeByXPath(JSContext* aCx, Document& aDocument,
|
||||
static Element* FindNodeByXPath(Document& aDocument,
|
||||
const nsAString& aExpression) {
|
||||
FormDataParseContext parsingContext(aDocument.IsHTMLDocument());
|
||||
IgnoredErrorResult rv;
|
||||
|
@ -989,7 +993,7 @@ static Element* FindNodeByXPath(JSContext* aCx, Document& aDocument,
|
|||
return nullptr;
|
||||
}
|
||||
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()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1012,7 +1016,7 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
|
|||
return false;
|
||||
}
|
||||
if (aData.mInnerHTML.WasPassed()) {
|
||||
SetInnerHTML(aDocument, aData);
|
||||
SetInnerHTML(aDocument, aData.mInnerHTML.Value());
|
||||
}
|
||||
if (aData.mId.WasPassed()) {
|
||||
for (auto& entry : aData.mId.Value().Entries()) {
|
||||
|
@ -1030,13 +1034,11 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
|
|||
// cf. bug 467409
|
||||
JSContext* cx = aGlobal.Context();
|
||||
if (entry.mKey.EqualsLiteral("sessionData")) {
|
||||
nsAutoCString url;
|
||||
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
|
||||
if (url.EqualsLiteral("about:sessionrestore") ||
|
||||
url.EqualsLiteral("about:welcomeback")) {
|
||||
JS::Rooted<JS::Value> object(
|
||||
cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
|
||||
SetRestoreData(cx, node, &object);
|
||||
SetSessionData(cx, node, &object);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1048,8 +1050,7 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
|
|||
}
|
||||
if (aData.mXpath.WasPassed()) {
|
||||
for (auto& entry : aData.mXpath.Value().Entries()) {
|
||||
RefPtr<Element> node =
|
||||
FindNodeByXPath(aGlobal.Context(), aDocument, entry.mKey);
|
||||
RefPtr<Element> node = FindNodeByXPath(aDocument, entry.mKey);
|
||||
if (node == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1067,6 +1068,74 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
|
|||
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. */
|
||||
static void ReadAllEntriesFromStorage(nsPIDOMWindowOuter* aWindow,
|
||||
nsTArray<nsCString>& aOrigins,
|
||||
|
@ -1356,3 +1425,43 @@ static void CollectFrameTreeData(JSContext* aCx,
|
|||
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/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||
#include "mozilla/dom/SessionStoreUtilsBinding.h"
|
||||
#include "SessionStoreData.h"
|
||||
#include "SessionStoreRestoreData.h"
|
||||
|
||||
class nsIDocument;
|
||||
class nsGlobalWindowInner;
|
||||
|
@ -55,6 +57,8 @@ class SessionStoreUtils {
|
|||
static void RestoreScrollPosition(const GlobalObject& aGlobal,
|
||||
nsGlobalWindowInner& aWindow,
|
||||
const CollectedData& data);
|
||||
static void RestoreScrollPosition(nsGlobalWindowInner& aWindow,
|
||||
const nsCString& aScrollPosition);
|
||||
|
||||
/*
|
||||
@param aDocument: DOMDocument instance to obtain form data for.
|
||||
|
@ -81,6 +85,10 @@ class SessionStoreUtils {
|
|||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
static bool RestoreFormData(const GlobalObject& aGlobal, Document& aDocument,
|
||||
const CollectedData& aData);
|
||||
static bool RestoreFormData(
|
||||
Document& aDocument, const nsCString& aFormUrl,
|
||||
const nsString& aInnerHTML,
|
||||
const nsTArray<SessionStoreRestoreData::Entry>& aEntries);
|
||||
|
||||
static void CollectedSessionStorage(BrowsingContext* aBrowsingContext,
|
||||
nsTArray<nsCString>& aOrigins,
|
||||
|
@ -93,6 +101,13 @@ class SessionStoreUtils {
|
|||
|
||||
static void ComposeInputData(const nsTArray<CollectedInputDataValue>& aData,
|
||||
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
|
||||
|
|
|
@ -8,11 +8,13 @@ EXPORTS.mozilla.dom += [
|
|||
"SessionStoreData.h",
|
||||
"SessionStoreListener.h",
|
||||
"SessionStoreMessageUtils.h",
|
||||
"SessionStoreRestoreData.h",
|
||||
"SessionStoreUtils.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"SessionStoreListener.cpp",
|
||||
"SessionStoreRestoreData.cpp",
|
||||
"SessionStoreUtils.cpp",
|
||||
]
|
||||
|
||||
|
@ -20,9 +22,10 @@ EXTRA_JS_MODULES += [
|
|||
"SessionStoreFunctions.jsm",
|
||||
]
|
||||
|
||||
XPIDL_MODULE = "sessionStore_funcs"
|
||||
XPIDL_MODULE = "sessionstore"
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsISessionStoreRestoreData.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);
|
||||
};
|
Загрузка…
Ссылка в новой задаче