Bug 1576188 - Handle save-as for cross process iframes. r=peterv

Depends on D70388

Differential Revision: https://phabricator.services.mozilla.com/D70389
This commit is contained in:
Andreas Farre 2020-04-27 05:41:40 +00:00
parent a5c1034d17
commit 6adf2b375d
28 changed files with 419 additions and 48 deletions

View File

@ -567,6 +567,7 @@ class ContextMenuChild extends JSWindowActorChild {
} = doc;
docLocation = docLocation && docLocation.spec;
let frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView);
let frameBrowsingContextID = doc.defaultView.docShell.browsingContext.id;
let loginFillInfo = LoginManagerChild.forWindow(
doc.defaultView
).getFieldContext(aEvent.composedTarget);
@ -683,6 +684,7 @@ class ContextMenuChild extends JSWindowActorChild {
customMenuItems,
contentDisposition,
frameOuterWindowID,
frameBrowsingContextID,
disableSetDesktopBackground,
parentAllowsMixedContent,
};
@ -902,6 +904,9 @@ class ContextMenuChild extends JSWindowActorChild {
context.target.ownerGlobal
);
context.frameBrowsingContextID =
context.target.ownerGlobal.docShell.browsingContext.id;
// Check if we are in the PDF Viewer.
context.inPDFViewer =
context.target.ownerDocument.nodePrincipal.origin == "resource://pdf.js";

View File

@ -52,6 +52,7 @@ function openContextMenu(aMessage, aBrowser, aActor) {
contentType: data.contentType,
contentDisposition: data.contentDisposition,
frameOuterWindowID: data.frameOuterWindowID,
frameBrowsingContext: BrowsingContext.get(data.frameBrowsingContextID),
selectionInfo: data.selectionInfo,
disableSetDesktopBackground: data.disableSetDesktopBackground,
loginFillInfo: data.loginFillInfo,
@ -231,6 +232,9 @@ class nsContextMenu {
this.principal = context.principal;
this.storagePrincipal = context.storagePrincipal;
this.frameOuterWindowID = context.frameOuterWindowID;
this.frameBrowsingContext = BrowsingContext.get(
context.frameBrowsingContextID
);
this.inSyntheticDoc = context.inSyntheticDoc;
this.inAboutDevtoolsToolbox = context.inAboutDevtoolsToolbox;
@ -1395,7 +1399,7 @@ class nsContextMenu {
// Save URL of clicked-on frame.
saveFrame() {
saveBrowser(this.browser, false, this.frameOuterWindowID);
saveBrowser(this.browser, false, this.frameBrowsingContext);
}
// Helper function to wait for appropriate MIME-type headers and

View File

@ -1320,7 +1320,7 @@ const JsonView = {
// principal is from the child. Null principals don't survive crossing
// over IPC, so there's no other principal that'll work.
const persistable = browser.frameLoader;
persistable.startPersistence(0, {
persistable.startPersistence(null, {
onDocumentReady(doc) {
const uri = chrome.makeURI(doc.documentURI, doc.characterSet);
const filename = chrome.getDefaultFileName(undefined, uri, doc, null);

View File

@ -98,6 +98,16 @@ BrowsingContext* BrowsingContext::GetParent() const {
return mParentWindow ? mParentWindow->GetBrowsingContext() : nullptr;
}
bool BrowsingContext::IsInSubtreeOf(BrowsingContext* aContext) {
BrowsingContext* bc = this;
do {
if (bc == aContext) {
return true;
}
} while ((bc = bc->GetParent()));
return false;
}
BrowsingContext* BrowsingContext::Top() {
BrowsingContext* bc = this;
while (bc->mParentWindow) {

View File

@ -216,6 +216,12 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void SetDocShell(nsIDocShell* aDocShell);
void ClearDocShell() { mDocShell = nullptr; }
// Get the Document for this BrowsingContext if it is in-process, or
// null if it's not.
Document* GetDocument() const {
return mDocShell ? mDocShell->GetDocument() : nullptr;
}
// This cleans up remote outer window proxies that might have been left behind
// when the browsing context went from being remote to local. It does this by
// turning them into cross-compartment wrappers to aOuter. If there is already
@ -281,6 +287,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
bool IsTopContent() const { return IsContent() && !GetParent(); }
bool IsInSubtreeOf(BrowsingContext* aContext);
bool IsContentSubframe() const { return IsContent() && GetParent(); }
uint64_t Id() const { return mBrowsingContextId; }

View File

@ -3203,25 +3203,27 @@ void nsFrameLoader::DestroyBrowserFrameScripts() {
}
void nsFrameLoader::StartPersistence(
uint64_t aOuterWindowID, nsIWebBrowserPersistDocumentReceiver* aRecv,
BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv,
ErrorResult& aRv) {
MOZ_ASSERT(aRecv);
RefPtr<BrowsingContext> context = aContext ? aContext : GetBrowsingContext();
if (auto* browserParent = GetBrowserParent()) {
browserParent->StartPersistence(aOuterWindowID, aRecv, aRv);
if (!context || !context->IsInSubtreeOf(GetBrowsingContext())) {
aRecv->OnError(NS_ERROR_NO_CONTENT);
return;
}
nsCOMPtr<Document> rootDoc =
GetDocShell() ? GetDocShell()->GetDocument() : nullptr;
nsCOMPtr<Document> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc,
aOuterWindowID);
} else {
foundDoc = rootDoc;
if (!context->GetDocShell() && XRE_IsParentProcess()) {
CanonicalBrowsingContext* canonical =
CanonicalBrowsingContext::Cast(context);
RefPtr<BrowserParent> browserParent =
canonical->GetCurrentWindowGlobal()->GetBrowserParent();
browserParent->StartPersistence(canonical, aRecv, aRv);
return;
}
nsCOMPtr<Document> foundDoc = context->GetDocument();
if (!foundDoc) {
aRecv->OnError(NS_ERROR_NO_CONTENT);
} else {

View File

@ -221,7 +221,7 @@ class nsFrameLoader final : public nsStubMutationObserver,
nsIWebProgressListener* aProgressListener,
mozilla::ErrorResult& aRv);
void StartPersistence(uint64_t aOuterWindowID,
void StartPersistence(BrowsingContext* aContext,
nsIWebBrowserPersistDocumentReceiver* aRecv,
mozilla::ErrorResult& aRv);

View File

@ -0,0 +1,210 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
interface LoadContext;
interface RemoteTab;
interface URI;
interface nsIDocShell;
interface nsIPrintSettings;
interface nsIWebBrowserPersistDocumentReceiver;
interface nsIWebProgressListener;
[ChromeOnly,
Exposed=Window]
interface FrameLoader {
/**
* Get the docshell from the frame loader.
*/
[GetterThrows]
readonly attribute nsIDocShell? docShell;
/**
* Get this frame loader's RemoteTab, if it has a remote frame. Otherwise,
* returns null.
*/
readonly attribute RemoteTab? remoteTab;
/**
* Get an nsILoadContext for the top-level docshell. For remote
* frames, a shim is returned that contains private browsing and app
* information.
*/
readonly attribute LoadContext loadContext;
/**
* Get the root BrowsingContext within the frame.
* This may be null immediately after creating a remote frame.
*/
readonly attribute BrowsingContext? browsingContext;
/**
* Find out whether the loader's frame is at too great a depth in
* the frame tree. This can be used to decide what operations may
* or may not be allowed on the loader's docshell.
*/
[Pure]
readonly attribute boolean depthTooGreat;
/**
* Find out whether the loader's frame is a remote frame.
*/
readonly attribute boolean isRemoteFrame;
/**
* Activate remote frame.
* Throws an exception with non-remote frames.
*/
[Throws]
void activateRemoteFrame();
/**
* Deactivate remote frame.
* Throws an exception with non-remote frames.
*/
[Throws]
void deactivateRemoteFrame();
/**
* @see nsIDOMWindowUtils sendMouseEvent.
*/
[Throws]
void sendCrossProcessMouseEvent(DOMString aType,
float aX,
float aY,
long aButton,
long aClickCount,
long aModifiers,
optional boolean aIgnoreRootScrollFrame = false);
/**
* Activate event forwarding from client (remote frame) to parent.
*/
[Throws]
void activateFrameEvent(DOMString aType, boolean capture);
// Note, when frameloaders are swapped, also messageManagers are swapped.
readonly attribute MessageSender? messageManager;
/**
* Request that the next time a remote layer transaction has been
* received by the Compositor, a MozAfterRemoteFrame event be sent
* to the window.
*/
void requestNotifyAfterRemotePaint();
/**
* Force a remote browser to recompute its dimension and screen position.
*/
[Throws]
void requestUpdatePosition();
/**
* Force a TabStateFlush from native sessionStoreListeners.
* Return true if the flush requires async ipc call.
*/
boolean requestTabStateFlush(unsigned long aFlushId);
/**
* Force Epoch update in native sessionStoreListeners.
*/
void requestEpochUpdate(unsigned long aEpoch);
/**
* Request a session history update in native sessionStoreListeners.
*/
void requestSHistoryUpdate(boolean aImmediately);
/**
* Print the current document.
*
* @param aOuterWindowID the ID of the outer window to print
* @param aPrintSettings optional print settings to use; printSilent can be
* set to prevent prompting.
* @param aProgressListener optional print progress listener.
*/
[Throws]
void print(unsigned long long aOuterWindowID,
nsIPrintSettings aPrintSettings,
optional nsIWebProgressListener? aProgressListener = null);
/**
* The element which owns this frame loader.
*
* For example, if this is a frame loader for an <iframe>, this attribute
* returns the iframe element.
*/
[Pure]
readonly attribute Element? ownerElement;
/**
* Cached childID of the ContentParent owning the RemoteTab in this frame
* loader. This can be used to obtain the childID after the RemoteTab died.
*/
[Pure]
readonly attribute unsigned long long childID;
/**
* Find out whether the owner content really is a mozbrowser. <xul:browser>
* is not considered to be a mozbrowser frame.
*/
[Pure]
readonly attribute boolean ownerIsMozBrowserFrame;
/**
* The last known width of the frame. Reading this property will not trigger
* a reflow, and therefore may not reflect the current state of things. It
* should only be used in asynchronous APIs where values are not guaranteed
* to be up-to-date when received.
*/
[Pure]
readonly attribute unsigned long lazyWidth;
/**
* The last known height of the frame. Reading this property will not trigger
* a reflow, and therefore may not reflect the current state of things. It
* should only be used in asynchronous APIs where values are not guaranteed
* to be up-to-date when received.
*/
[Pure]
readonly attribute unsigned long lazyHeight;
/**
* Is `true` if the frameloader is dead (destroy has been called on it)
*/
[Pure]
readonly attribute boolean isDead;
};
/**
* Interface for objects which represent a document that can be
* serialized with nsIWebBrowserPersist. This interface is
* asynchronous because the actual document can be in another process
* (e.g., if this object is a FrameLoader for an out-of-process
* frame).
*
* @see nsIWebBrowserPersistDocumentReceiver
* @see nsIWebBrowserPersistDocument
* @see nsIWebBrowserPersist
*
* @param aContext
* The browsing context of the subframe we'd like to persist.
* If set to nullptr, WebBrowserPersistable will attempt to persist
* the top-level document. If the browsing context is for a subframe
* that is not held beneath the WebBrowserPersistable, aRecv's onError
* method will be called with NS_ERROR_NO_CONTENT.
* @param aRecv
* The nsIWebBrowserPersistDocumentReceiver is a callback that
* will be fired once the document is ready for persisting.
*/
interface mixin WebBrowserPersistable
{
[Throws]
void startPersistence(BrowsingContext? aContext,
nsIWebBrowserPersistDocumentReceiver aRecv);
};
FrameLoader includes WebBrowserPersistable;

View File

@ -45,6 +45,7 @@ WEBIDL_FILES = [
'DOMLocalization.webidl',
'Flex.webidl',
'Fluent.webidl',
'FrameLoader.webidl',
'HeapSnapshot.webidl',
'InspectorUtils.webidl',
'IteratorResult.webidl',

View File

@ -3895,12 +3895,12 @@ bool BrowserParent::AsyncPanZoomEnabled() const {
}
void BrowserParent::StartPersistence(
uint64_t aOuterWindowID, nsIWebBrowserPersistDocumentReceiver* aRecv,
ErrorResult& aRv) {
CanonicalBrowsingContext* aContext,
nsIWebBrowserPersistDocumentReceiver* aRecv, ErrorResult& aRv) {
auto* actor = new WebBrowserPersistDocumentParent();
actor->SetOnReady(aRecv);
bool ok = Manager()->SendPWebBrowserPersistDocumentConstructor(
actor, this, aOuterWindowID);
bool ok = Manager()->SendPWebBrowserPersistDocumentConstructor(actor, this,
aContext);
if (!ok) {
aRv.Throw(NS_ERROR_FAILURE);
}

View File

@ -612,7 +612,7 @@ class BrowserParent final : public PBrowserParent,
bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
void StartPersistence(uint64_t aOuterWindowID,
void StartPersistence(CanonicalBrowsingContext* aContext,
nsIWebBrowserPersistDocumentReceiver* aRecv,
ErrorResult& aRv);

View File

@ -2947,26 +2947,24 @@ bool ContentChild::DeallocPContentPermissionRequestChild(
PWebBrowserPersistDocumentChild*
ContentChild::AllocPWebBrowserPersistDocumentChild(
PBrowserChild* aBrowser, const uint64_t& aOuterWindowID) {
PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
return new WebBrowserPersistDocumentChild();
}
mozilla::ipc::IPCResult ContentChild::RecvPWebBrowserPersistDocumentConstructor(
PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
const uint64_t& aOuterWindowID) {
const MaybeDiscarded<BrowsingContext>& aContext) {
if (NS_WARN_IF(!aBrowser)) {
return IPC_FAIL_NO_REASON(this);
}
nsCOMPtr<Document> rootDoc =
static_cast<BrowserChild*>(aBrowser)->GetTopLevelDocument();
nsCOMPtr<Document> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc,
aOuterWindowID);
} else {
foundDoc = rootDoc;
if (aContext.IsNullOrDiscarded()) {
aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
return IPC_OK();
}
nsCOMPtr<Document> foundDoc = aContext.get()->GetDocument();
if (!foundDoc) {
aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
} else {

View File

@ -216,11 +216,11 @@ class ContentChild final
const FileDescriptor& aGCLog, const FileDescriptor& aCCLog) override;
PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild(
PBrowserChild* aBrowser, const uint64_t& aOuterWindowID);
PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext);
virtual mozilla::ipc::IPCResult RecvPWebBrowserPersistDocumentConstructor(
PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
const uint64_t& aOuterWindowID) override;
const MaybeDiscarded<BrowsingContext>& aContext) override;
bool DeallocPWebBrowserPersistDocumentChild(
PWebBrowserPersistDocumentChild* aActor);

View File

@ -4539,7 +4539,7 @@ bool ContentParent::DeallocPContentPermissionRequestParent(
PWebBrowserPersistDocumentParent*
ContentParent::AllocPWebBrowserPersistDocumentParent(
PBrowserParent* aBrowser, const uint64_t& aOuterWindowID) {
PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
return new WebBrowserPersistDocumentParent();
}

View File

@ -966,7 +966,8 @@ class ContentParent final
#endif
PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent(
PBrowserParent* aBrowser, const uint64_t& aOuterWindowID);
PBrowserParent* aBrowser,
const MaybeDiscarded<BrowsingContext>& aContext);
bool DeallocPWebBrowserPersistDocumentParent(
PWebBrowserPersistDocumentParent* aActor);

View File

@ -435,12 +435,12 @@ child:
both:
async PFileDescriptorSet(FileDescriptor fd);
// For parent->child, aBrowser must be non-null; aOuterWindowID can
// be 0 to indicate the browser's current root document, or nonzero
// For parent->child, aBrowser must be non-null; aContext can
// be null to indicate the browser's current root document, or non-null
// to persist a subdocument. For child->parent, arguments are
// ignored and should be null/zero.
// ignored and should be null.
async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
uint64_t aOuterWindowID);
MaybeDiscardedBrowsingContext aContext);
child:
async InitGMPService(Endpoint<PGMPServiceChild> service);

View File

@ -5,6 +5,8 @@
include protocol PWebBrowserPersistDocument;
using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
namespace mozilla {
// == nsIWebBrowserPersistResourceVisitor
@ -19,6 +21,8 @@ parent:
// before exposing it with a visitDocument call.
async VisitDocument(PWebBrowserPersistDocument aSubDocument);
async VisitBrowsingContext(MaybeDiscardedBrowsingContext aContext);
// This reflects the endVisit method.
async __delete__(nsresult aStatus);
};

View File

@ -7,6 +7,7 @@
#include "WebBrowserPersistDocumentParent.h"
#include "mozilla/dom/Attr.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/Comment.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLAnchorElement.h"
@ -287,12 +288,17 @@ nsresult ResourceReader::OnWalkSubframe(nsINode* aNode) {
RefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
NS_ENSURE_STATE(loader);
RefPtr<dom::BrowsingContext> context = loader->GetBrowsingContext();
NS_ENSURE_STATE(context);
if (loader->IsRemoteFrame()) {
mVisitor->VisitBrowsingContext(mParent, context);
return NS_OK;
}
++mOutstandingDocuments;
// Pass in 0 as the outer window ID so that we start
// persisting the root of this subframe, and not some other
// subframe child of this subframe.
ErrorResult err;
loader->StartPersistence(0, this, err);
loader->StartPersistence(context, this, err);
nsresult rv = err.StealNSResult();
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_NO_CONTENT) {

View File

@ -38,7 +38,7 @@ WebBrowserPersistResourcesChild::VisitDocument(
// persistence started does not necessarily exist at this point;
// see bug 1203602.
if (!Manager()->Manager()->SendPWebBrowserPersistDocumentConstructor(
subActor, nullptr, 0)) {
subActor, nullptr, nullptr)) {
// NOTE: subActor is freed at this point.
return NS_ERROR_FAILURE;
}
@ -57,6 +57,14 @@ WebBrowserPersistResourcesChild::VisitDocument(
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistResourcesChild::VisitBrowsingContext(
nsIWebBrowserPersistDocument* aDocument,
dom::BrowsingContext* aBrowsingContext) {
SendVisitBrowsingContext(aBrowsingContext);
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistResourcesChild::EndVisit(
nsIWebBrowserPersistDocument* aDocument, nsresult aStatus) {

View File

@ -8,6 +8,11 @@
#include "nsThreadUtils.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/WindowGlobalParent.h"
namespace mozilla {
NS_IMPL_ISUPPORTS(WebBrowserPersistResourcesParent,
@ -58,6 +63,18 @@ mozilla::ipc::IPCResult WebBrowserPersistResourcesParent::RecvVisitDocument(
return IPC_OK();
}
mozilla::ipc::IPCResult
WebBrowserPersistResourcesParent::RecvVisitBrowsingContext(
const dom::MaybeDiscarded<dom::BrowsingContext>& aContext) {
if (aContext.IsNullOrDiscarded()) {
// Nothing useful to do but ignore the discarded context.
return IPC_OK();
}
mVisitor->VisitBrowsingContext(mDocument, aContext.get());
return IPC_OK();
}
NS_IMETHODIMP
WebBrowserPersistResourcesParent::OnDocumentReady(
nsIWebBrowserPersistDocument* aSubDocument) {

View File

@ -30,6 +30,9 @@ class WebBrowserPersistResourcesParent final
virtual mozilla::ipc::IPCResult RecvVisitDocument(
PWebBrowserPersistDocumentParent* aSubDocument) override;
virtual mozilla::ipc::IPCResult RecvVisitBrowsingContext(
const dom::MaybeDiscarded<dom::BrowsingContext>& aContext) override;
virtual mozilla::ipc::IPCResult Recv__delete__(
const nsresult& aStatus) override;

View File

@ -16,6 +16,8 @@ interface nsIWebBrowserPersistWriteCompletion;
interface nsIReferrerInfo;
interface nsISHEntry;
webidl BrowsingContext;
native SHEntryRef(already_AddRefed<nsISHEntry>);
/**
@ -147,6 +149,7 @@ interface nsIWebBrowserPersistResourceVisitor : nsISupports
void visitResource(in nsIWebBrowserPersistDocument aDocument,
in AUTF8String aURI,
in nsContentPolicyType aContentPolicyType);
/**
* Indicates a subdocument resource; e.g., a frame or iframe.
*
@ -156,6 +159,16 @@ interface nsIWebBrowserPersistResourceVisitor : nsISupports
void visitDocument(in nsIWebBrowserPersistDocument aDocument,
in nsIWebBrowserPersistDocument aSubDocument);
/**
* Indicates a cross origin subdocument resource; e.g., a frame
* or iframe loaded in another process.
*
* @param aDocument The document containing the reference.
* @param aContext The referenced document's browsing context.
*/
void visitBrowsingContext(in nsIWebBrowserPersistDocument aDocument,
in BrowsingContext aContext);
/**
* Indicates that the document traversal is complete.
*

View File

@ -139,7 +139,11 @@ class nsWebBrowserPersist::OnWalk final
: public nsIWebBrowserPersistResourceVisitor {
public:
OnWalk(nsWebBrowserPersist* aParent, nsIURI* aFile, nsIFile* aDataPath)
: mParent(aParent), mFile(aFile), mDataPath(aDataPath) {}
: mParent(aParent),
mFile(aFile),
mDataPath(aDataPath),
mPendingDocuments(1),
mStatus(NS_OK) {}
NS_DECL_NSIWEBBROWSERPERSISTRESOURCEVISITOR
NS_DECL_ISUPPORTS
@ -148,12 +152,34 @@ class nsWebBrowserPersist::OnWalk final
nsCOMPtr<nsIURI> mFile;
nsCOMPtr<nsIFile> mDataPath;
uint32_t mPendingDocuments;
nsresult mStatus;
virtual ~OnWalk() = default;
};
NS_IMPL_ISUPPORTS(nsWebBrowserPersist::OnWalk,
nsIWebBrowserPersistResourceVisitor)
class nsWebBrowserPersist::OnRemoteWalk final
: public nsIWebBrowserPersistDocumentReceiver {
public:
OnRemoteWalk(nsIWebBrowserPersistResourceVisitor* aVisitor,
nsIWebBrowserPersistDocument* aDocument)
: mVisitor(aVisitor), mDocument(aDocument) {}
NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER
NS_DECL_ISUPPORTS
private:
nsCOMPtr<nsIWebBrowserPersistResourceVisitor> mVisitor;
nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
virtual ~OnRemoteWalk() = default;
};
NS_IMPL_ISUPPORTS(nsWebBrowserPersist::OnRemoteWalk,
nsIWebBrowserPersistDocumentReceiver)
class nsWebBrowserPersist::OnWrite final
: public nsIWebBrowserPersistWriteCompletion {
public:
@ -1568,18 +1594,73 @@ nsWebBrowserPersist::OnWalk::VisitDocument(
return mParent->SaveSubframeContent(aSubDoc, aDoc, uriSpec, data);
}
NS_IMETHODIMP
nsWebBrowserPersist::OnWalk::VisitBrowsingContext(
nsIWebBrowserPersistDocument* aDoc, BrowsingContext* aContext) {
RefPtr<dom::CanonicalBrowsingContext> context = aContext->Canonical();
UniquePtr<WebBrowserPersistDocumentParent> actor(
new WebBrowserPersistDocumentParent());
nsCOMPtr<nsIWebBrowserPersistDocumentReceiver> receiver =
new OnRemoteWalk(this, aDoc);
actor->SetOnReady(receiver);
RefPtr<dom::BrowserParent> browserParent =
context->GetCurrentWindowGlobal()->GetBrowserParent();
bool ok =
context->GetContentParent()->SendPWebBrowserPersistDocumentConstructor(
actor.release(), browserParent, context);
if (NS_WARN_IF(!ok)) {
// (The actor will be destroyed on constructor failure.)
EndVisit(nullptr, NS_ERROR_FAILURE);
return NS_ERROR_FAILURE;
}
++mPendingDocuments;
return NS_OK;
}
NS_IMETHODIMP
nsWebBrowserPersist::OnWalk::EndVisit(nsIWebBrowserPersistDocument* aDoc,
nsresult aStatus) {
if (NS_FAILED(mStatus)) {
return mStatus;
}
if (NS_FAILED(aStatus)) {
mStatus = aStatus;
mParent->SendErrorStatusChange(true, aStatus, nullptr, mFile);
mParent->EndDownload(aStatus);
return aStatus;
}
if (--mPendingDocuments) {
// We're not done yet, wait for more.
return NS_OK;
}
mParent->FinishSaveDocumentInternal(mFile, mDataPath);
return NS_OK;
}
NS_IMETHODIMP
nsWebBrowserPersist::OnRemoteWalk::OnDocumentReady(
nsIWebBrowserPersistDocument* aSubDocument) {
mVisitor->VisitDocument(mDocument, aSubDocument);
mVisitor->EndVisit(mDocument, NS_OK);
return NS_OK;
}
NS_IMETHODIMP
nsWebBrowserPersist::OnRemoteWalk::OnError(nsresult aFailure) {
mVisitor->EndVisit(nullptr, aFailure);
return NS_OK;
}
void nsWebBrowserPersist::FinishSaveDocumentInternal(nsIURI* aFile,
nsIFile* aDataPath) {
// If there are things to persist, create a directory to hold them

View File

@ -74,6 +74,7 @@ class nsWebBrowserPersist final : public nsIInterfaceRequestor,
struct WalkData;
class OnWalk;
class OnRemoteWalk;
class OnWrite;
class FlatURIMap;
friend class OnWalk;

View File

@ -542,7 +542,6 @@ WEBIDL_FILES = [
'FontFaceSet.webidl',
'FontFaceSource.webidl',
'FormData.webidl',
'FrameLoader.webidl',
'Function.webidl',
'GainNode.webidl',
'Gamepad.webidl',

View File

@ -13,7 +13,7 @@ function one_test(delay, continuation) {
BrowserTestUtils.openNewForegroundTab(gBrowser, testPageURL).then(tab => {
browser = tab.linkedBrowser;
let persistable = browser.frameLoader;
persistable.startPersistence(/* outer window ID: */ 0, {
persistable.startPersistence(null, {
onDocumentReady,
onError(status) {
ok(false, new Components.Exception("startPersistence failed", status));

View File

@ -22,7 +22,7 @@ add_task(async function checkFormStateSaved() {
await SpecialPowers.spawn(browser, [{ textareas, textboxes }], fillform);
let fileURISpec = await new Promise((resolve, reject) => {
let stack = Components.stack.caller;
browser.frameLoader.startPersistence(0, {
browser.frameLoader.startPersistence(null, {
onDocumentReady(document) {
// Note that 'document' here is going to be an nsIWebBrowserPersistDocument,
// not a regular DOM document.

View File

@ -84,7 +84,7 @@ function saveURL(
// Save the current document inside any browser/frame-like element,
// whether in-process or out-of-process.
function saveBrowser(aBrowser, aSkipPrompt, aOuterWindowID = 0) {
function saveBrowser(aBrowser, aSkipPrompt, aBrowsingContext = null) {
if (!aBrowser) {
throw new Error("Must have a browser when calling saveBrowser");
}
@ -119,7 +119,7 @@ function saveBrowser(aBrowser, aSkipPrompt, aOuterWindowID = 0) {
return;
}
let stack = Components.stack.caller;
persistable.startPersistence(aOuterWindowID, {
persistable.startPersistence(aBrowsingContext, {
onDocumentReady(document) {
if (!document || !(document instanceof Ci.nsIWebBrowserPersistDocument)) {
throw new Error("Must have an nsIWebBrowserPersistDocument!");