Bug 1557645 - Clone OOP iframes in the right process. r=nika

This works, though probably we want to do some follow-up tweaks, like
the adding of the onload blocker and so on, so that we can avoid the
UpdateDimensions hack.

We may also want a PrintObject in the nsPrintJob tree, perhaps...

Differential Revision: https://phabricator.services.mozilla.com/D90310
This commit is contained in:
Emilio Cobos Álvarez 2020-09-22 09:57:27 +00:00
parent 3c91b4555b
commit a4e300194a
14 changed files with 255 additions and 64 deletions

View File

@ -26,6 +26,7 @@
#include "mozilla/MozPromiseInlines.h"
#include "nsDocShell.h"
#include "nsFrameLoader.h"
#include "nsFrameLoaderOwner.h"
#include "nsGlobalWindowOuter.h"
#include "nsIWebBrowserChrome.h"
#include "nsNetUtil.h"
@ -1078,7 +1079,9 @@ void CanonicalBrowsingContext::PendingRemotenessChange::Finish() {
}
// Resume the pending load in our new process.
newBrowser->ResumeLoad(mPendingSwitchId);
if (mPendingSwitchId) {
newBrowser->ResumeLoad(mPendingSwitchId);
}
// We did it! The process switch is complete.
mPromise->Resolve(newBrowser, __func__);
@ -1148,6 +1151,9 @@ CanonicalBrowsingContext::ChangeRemoteness(const nsACString& aRemoteType,
"Cannot replace BrowsingContext for subframes");
MOZ_DIAGNOSTIC_ASSERT(aSpecificGroupId == 0 || aReplaceBrowsingContext,
"Cannot specify group ID unless replacing BC");
MOZ_DIAGNOSTIC_ASSERT(aPendingSwitchId || !IsTop(),
"Should always have aPendingSwitchId for top-level "
"frames");
if (!AncestorsAreCurrent()) {
NS_WARNING("An ancestor context is no longer current");
@ -1182,6 +1188,11 @@ CanonicalBrowsingContext::ChangeRemoteness(const nsACString& aRemoteType,
// Switching to local. No new process, so perform switch sync.
if (embedderBrowser &&
aRemoteType.Equals(embedderBrowser->Manager()->GetRemoteType())) {
MOZ_DIAGNOSTIC_ASSERT(
aPendingSwitchId,
"We always have a PendingSwitchId, except for print-preview loads, "
"which will never perform a process-switch to being in-process with "
"their embedder");
if (GetCurrentWindowGlobal()) {
MOZ_DIAGNOSTIC_ASSERT(GetCurrentWindowGlobal()->IsProcessRoot());
RefPtr<BrowserParent> oldBrowser =

View File

@ -0,0 +1,52 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_AutoPrintEventDispatcher_h
#define mozilla_dom_AutoPrintEventDispatcher_h
#include "mozilla/dom/Document.h"
#include "nsContentUtils.h"
namespace mozilla::dom {
class AutoPrintEventDispatcher {
// NOTE(emilio): For fission iframes, we dispatch this event in
// RecvCloneDocumentTreeIntoSelf.
static void CollectInProcessSubdocuments(
Document& aDoc, nsTArray<nsCOMPtr<Document>>& aDocs) {
aDocs.AppendElement(&aDoc);
auto recurse = [&aDocs](Document& aSubDoc) {
CollectInProcessSubdocuments(aSubDoc, aDocs);
return CallState::Continue;
};
aDoc.EnumerateSubDocuments(recurse);
}
void DispatchEvent(bool aBefore) {
for (nsCOMPtr<Document>& doc : mDocuments) {
nsContentUtils::DispatchTrustedEvent(
doc, doc->GetWindow(), aBefore ? u"beforeprint"_ns : u"afterprint"_ns,
CanBubble::eNo, Cancelable::eNo, nullptr);
}
}
public:
explicit AutoPrintEventDispatcher(Document& aDoc) {
if (!aDoc.IsStaticDocument()) {
CollectInProcessSubdocuments(aDoc, mDocuments);
}
DispatchEvent(true);
}
~AutoPrintEventDispatcher() { DispatchEvent(false); }
AutoTArray<nsCOMPtr<Document>, 8> mDocuments;
};
} // namespace mozilla::dom
#endif

View File

@ -141,6 +141,7 @@ EXPORTS.mozilla.dom += [
'AncestorIterator.h',
'AnonymousContent.h',
'Attr.h',
'AutoPrintEventDispatcher.h',
'BarProps.h',
'BindContext.h',
'BodyConsumer.h',

View File

@ -2754,6 +2754,23 @@ void nsFrameLoader::ActivateFrameEvent(const nsAString& aType, bool aCapture,
}
}
nsresult nsFrameLoader::DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf) {
MOZ_ASSERT(aStaticCloneOf->IsRemoteFrame());
MOZ_DIAGNOSTIC_ASSERT(GetBrowsingContext());
auto* cc = ContentChild::GetSingleton();
if (!cc) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
// TODO: Could possibly be implemented without too much effort.
return NS_ERROR_NOT_IMPLEMENTED;
}
BrowsingContext* bcToClone = aStaticCloneOf->GetBrowsingContext();
if (NS_WARN_IF(!bcToClone)) {
return NS_ERROR_UNEXPECTED;
}
cc->SendCloneDocumentTreeInto(bcToClone, GetBrowsingContext());
return NS_OK;
}
nsresult nsFrameLoader::FinishStaticClone(
nsFrameLoader* aStaticCloneOf, bool* aOutHasInProcessPrintCallbacks) {
MOZ_DIAGNOSTIC_ASSERT(
@ -2772,10 +2789,6 @@ nsresult nsFrameLoader::FinishStaticClone(
return NS_ERROR_UNEXPECTED;
}
if (NS_WARN_IF(aStaticCloneOf->IsRemoteFrame())) {
return NS_ERROR_NOT_IMPLEMENTED;
}
MaybeCreateDocShell();
RefPtr<nsDocShell> docShell = GetDocShell();
NS_ENSURE_STATE(docShell);
@ -2783,6 +2796,10 @@ nsresult nsFrameLoader::FinishStaticClone(
nsCOMPtr<Document> kungFuDeathGrip = docShell->GetDocument();
Unused << kungFuDeathGrip;
if (aStaticCloneOf->IsRemoteFrame()) {
return DoRemoteStaticClone(aStaticCloneOf);
}
nsCOMPtr<nsIContentViewer> viewer;
docShell->GetContentViewer(getter_AddRefs(viewer));
NS_ENSURE_STATE(viewer);

View File

@ -149,6 +149,8 @@ class nsFrameLoader final : public nsStubMutationObserver,
nsresult FinishStaticClone(nsFrameLoader* aStaticCloneOf,
bool* aOutHasInProcessPrintCallbacks);
nsresult DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf);
// WebIDL methods
nsDocShell* GetDocShell(mozilla::ErrorResult& aRv);

View File

@ -23,6 +23,7 @@
#include "nsIWebProgressListener.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/ContentBlocking.h"
#include "mozilla/dom/AutoPrintEventDispatcher.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowsingContextBinding.h"
@ -5207,31 +5208,6 @@ void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) {
}
}
static CallState CollectDocuments(Document& aDoc,
nsTArray<nsCOMPtr<Document>>& aDocs) {
aDocs.AppendElement(&aDoc);
auto recurse = [&aDocs](Document& aSubDoc) {
return CollectDocuments(aSubDoc, aDocs);
};
aDoc.EnumerateSubDocuments(recurse);
return CallState::Continue;
}
static void DispatchPrintEventToWindowTree(Document& aDoc,
const nsAString& aEvent) {
if (aDoc.IsStaticDocument()) {
return;
}
nsTArray<nsCOMPtr<Document>> targets;
CollectDocuments(aDoc, targets);
for (nsCOMPtr<Document>& doc : targets) {
nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent,
CanBubble::eNo, Cancelable::eNo,
nullptr);
}
}
#ifdef NS_PRINTING
static void SetIsPrintingInDocShellTree(nsIDocShellTreeItem* aParentNode,
bool aIsPrintingOrPP,
@ -5442,9 +5418,7 @@ Nullable<WindowProxyHolder> nsGlobalWindowOuter::Print(
}
// TODO(emilio): Should dispatch this to OOP iframes too.
DispatchPrintEventToWindowTree(*docToPrint, u"beforeprint"_ns);
auto dispatchAfterPrint = MakeScopeExit(
[&] { DispatchPrintEventToWindowTree(*docToPrint, u"afterprint"_ns); });
AutoPrintEventDispatcher dispatcher(*docToPrint);
nsAutoScriptBlocker blockScripts;
RefPtr<Document> clone =

View File

@ -52,6 +52,7 @@
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/AutoPrintEventDispatcher.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DocGroup.h"
@ -1012,6 +1013,52 @@ mozilla::ipc::IPCResult BrowserChild::RecvResumeLoad(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvCloneDocumentTreeIntoSelf(
const MaybeDiscarded<BrowsingContext>& aSourceBC) {
if (NS_WARN_IF(aSourceBC.IsNullOrDiscarded())) {
return IPC_OK();
}
nsCOMPtr<Document> sourceDocument = aSourceBC.get()->GetDocument();
if (NS_WARN_IF(!sourceDocument)) {
return IPC_OK();
}
nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
if (NS_WARN_IF(!ourDocShell)) {
return IPC_OK();
}
nsCOMPtr<nsIContentViewer> cv;
ourDocShell->GetContentViewer(getter_AddRefs(cv));
if (NS_WARN_IF(!cv)) {
return IPC_OK();
}
RefPtr<Document> clone;
{
AutoPrintEventDispatcher dispatcher(*sourceDocument);
nsAutoScriptBlocker scriptBlocker;
bool hasInProcessCallbacks = false;
clone = sourceDocument->CreateStaticClone(ourDocShell, cv,
&hasInProcessCallbacks);
if (NS_WARN_IF(!clone)) {
return IPC_OK();
}
}
// Since the clone document is not parsed-created, we need to initialize
// layout manually. This is usually done in ReflowPrintObject for non-remote
// documents.
if (RefPtr<PresShell> ps = clone->GetPresShell()) {
if (!ps->DidInitialize()) {
nsresult rv = ps->Initialize();
Unused << NS_WARN_IF(NS_FAILED(rv));
}
}
return IPC_OK();
}
void BrowserChild::DoFakeShow(const ParentShowInfo& aParentShowInfo) {
OwnerShowInfo ownerInfo{ScreenIntSize(), ScrollbarPreference::Auto,
mParentIsActive, nsSizeMode_Normal};

View File

@ -260,6 +260,9 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
mozilla::ipc::IPCResult RecvResumeLoad(const uint64_t& aPendingSwitchID,
const ParentShowInfo&);
mozilla::ipc::IPCResult RecvCloneDocumentTreeIntoSelf(
const MaybeDiscarded<BrowsingContext>& aSourceBC);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
mozilla::ipc::IPCResult RecvShow(const ParentShowInfo&, const OwnerShowInfo&);

View File

@ -3522,6 +3522,72 @@ bool ContentParent::CanOpenBrowser(const IPCTabContext& aContext) {
return true;
}
static bool CloneIsLegal(ContentParent* aCp, CanonicalBrowsingContext& aSource,
CanonicalBrowsingContext& aTarget) {
// Source and target must be in the same BCG
if (NS_WARN_IF(aSource.Group() != aTarget.Group())) {
return false;
}
// The source and target must be in different toplevel <browser>s
if (NS_WARN_IF(aSource.Top() == aTarget.Top())) {
return false;
}
// Neither source nor target must be toplevel.
if (NS_WARN_IF(aSource.IsTop()) || NS_WARN_IF(aTarget.IsTop())) {
return false;
}
// Both should be embedded by the same process.
auto* sourceEmbedder = aSource.GetParentWindowContext();
if (NS_WARN_IF(!sourceEmbedder) ||
NS_WARN_IF(sourceEmbedder->GetContentParent() != aCp)) {
return false;
}
auto* targetEmbedder = aSource.GetParentWindowContext();
if (NS_WARN_IF(!targetEmbedder) ||
NS_WARN_IF(targetEmbedder->GetContentParent() != aCp)) {
return false;
}
// All seems sane.
return true;
}
mozilla::ipc::IPCResult ContentParent::RecvCloneDocumentTreeInto(
const MaybeDiscarded<BrowsingContext>& aSource,
const MaybeDiscarded<BrowsingContext>& aTarget) {
if (aSource.IsNullOrDiscarded() || aTarget.IsNullOrDiscarded()) {
return IPC_OK();
}
auto* source = aSource.get_canonical();
auto* target = aTarget.get_canonical();
if (!CloneIsLegal(this, *source, *target)) {
return IPC_FAIL(this, "Illegal subframe clone");
}
ContentParent* cp = source->GetContentParent();
if (NS_WARN_IF(!cp)) {
return IPC_OK();
}
target
->ChangeRemoteness(cp->GetRemoteType(), /* aLoadID = */ 0,
/* aReplaceBC = */ false, /* aSpecificGroupId = */ 0)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[source = RefPtr{source}](BrowserParent* aBp) {
Unused << aBp->SendCloneDocumentTreeIntoSelf(source);
},
[](nsresult aRv) {
NS_WARNING(nsPrintfCString("Remote clone failed: %x\n", aRv).get());
});
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvConstructPopupBrowser(
ManagedEndpoint<PBrowserParent>&& aBrowserEp,
ManagedEndpoint<PWindowGlobalParent>&& aWindowEp, const TabId& aTabId,

View File

@ -902,6 +902,10 @@ class ContentParent final
bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*);
mozilla::ipc::IPCResult RecvCloneDocumentTreeInto(
const MaybeDiscarded<BrowsingContext>& aSource,
const MaybeDiscarded<BrowsingContext>& aTarget);
mozilla::ipc::IPCResult RecvConstructPopupBrowser(
ManagedEndpoint<PBrowserParent>&& actor,
ManagedEndpoint<PWindowGlobalParent>&& windowEp, const TabId& tabId,

View File

@ -614,6 +614,7 @@ child:
async FlushTabState(uint32_t aFlushId, bool aIsFinal);
async UpdateEpoch(uint32_t aEpoch);
async UpdateSHistory(bool aImmediately);
async CloneDocumentTreeIntoSelf(MaybeDiscardedBrowsingContext aBc);
parent:

View File

@ -453,6 +453,12 @@ parent:
WindowGlobalInit windowInit,
uint32_t chromeFlags);
// TODO: Do I need to make this return something to watch for completion?
// Guess we'll see how we end up triggering the actual print, for preview
// this should be enough...
async CloneDocumentTreeInto(MaybeDiscardedBrowsingContext aSourceBc,
MaybeDiscardedBrowsingContext aTargetBc);
child:
async ConstructBrowser(ManagedEndpoint<PBrowserChild> browserEp,
ManagedEndpoint<PWindowGlobalChild> windowEp,

View File

@ -180,31 +180,33 @@ void nsSubDocumentFrame::ShowViewer() {
return;
}
if (!PresContext()->IsDynamic()) {
// We let the printing code take care of loading the document; just
// create the inner view for it to use.
RefPtr<nsFrameLoader> frameloader = FrameLoader();
if (!frameloader) {
return;
}
if (!frameloader->IsRemoteFrame() && !PresContext()->IsDynamic()) {
// We let the printing code take care of loading the document and
// initializing the shell; just create the inner view for it to use.
(void)EnsureInnerView();
} else {
RefPtr<nsFrameLoader> frameloader = FrameLoader();
if (frameloader) {
AutoWeakFrame weakThis(this);
mCallingShow = true;
bool didCreateDoc = frameloader->Show(this);
if (!weakThis.IsAlive()) {
return;
}
mCallingShow = false;
mDidCreateDoc = didCreateDoc;
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
frameloader->UpdatePositionAndSize(this);
}
if (!weakThis.IsAlive()) {
return;
}
InvalidateFrame();
AutoWeakFrame weakThis(this);
mCallingShow = true;
bool didCreateDoc = frameloader->Show(this);
if (!weakThis.IsAlive()) {
return;
}
mCallingShow = false;
mDidCreateDoc = didCreateDoc;
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
frameloader->UpdatePositionAndSize(this);
}
if (!weakThis.IsAlive()) {
return;
}
InvalidateFrame();
}
}
@ -324,7 +326,9 @@ static void WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) {
if (!IsVisibleForPainting()) return;
if (!IsVisibleForPainting()) {
return;
}
nsFrameLoader* frameLoader = FrameLoader();
bool isRemoteFrame = frameLoader && frameLoader->IsRemoteFrame();
@ -920,15 +924,14 @@ void nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot,
}
nsFrameLoader* nsSubDocumentFrame::FrameLoader() const {
nsIContent* content = GetContent();
if (!content) return nullptr;
if (!mFrameLoader) {
RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(content);
if (loaderOwner) {
mFrameLoader = loaderOwner->GetFrameLoader();
}
if (mFrameLoader) {
return mFrameLoader;
}
if (RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(GetContent())) {
mFrameLoader = loaderOwner->GetFrameLoader();
}
return mFrameLoader;
}

View File

@ -278,6 +278,10 @@ static void BuildNestedPrintObjects(const UniquePtr<nsPrintObject>& aParentPO,
for (auto& bc : aParentPO->mDocShell->GetBrowsingContext()->Children()) {
nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell();
if (!docShell) {
continue;
}
RefPtr<Document> doc = docShell->GetDocument();
auto childPO = MakeUnique<nsPrintObject>();