Bug 1576714 - Part 3: Initiate subframe process switches from the parent, r=kmag

This flips the direction in which the BrowserBridge actor is generally created
such that it is generally created in the parent and sent down to a child
process.

This is done by making the decision about what kind of switch to perform in the
parent, and sending messages down to child processes async to orchestrate these
process changes.

Process launching is changed to use an async `MozPromise`-returning API in this
patch, though the actual process launching still occurs synchronously. A future
patch will enable performing async process launching through the
NewOrUsedBrowserProcess mechanism.

I know of at least a few timing issues which exist with the new logic,
especially around the state of the BrowsingContext during the process
transition. I decided to not try to fix all of these issues in this patch, as
many are complex and will require changing how we manage the lifecycle of
BrowsingContext substantially. I do, however, think that the new logic is more
reliable and has fewer timing issues than the previous logic.

Differential Revision: https://phabricator.services.mozilla.com/D47310

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nika Layzell 2019-10-01 18:09:03 +00:00
parent b0d4c1a661
commit f55d088ec3
17 changed files with 501 additions and 217 deletions

View File

@ -2601,8 +2601,7 @@ var SessionStoreInternal = {
);
}
let wg = aBrowsingContext.embedderWindowGlobal;
return wg.changeFrameRemoteness(aBrowsingContext, aRemoteType, aSwitchId);
return aBrowsingContext.changeFrameRemoteness(aRemoteType, aSwitchId);
},
// Examine the channel response to see if we should change the process

View File

@ -9,6 +9,10 @@
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/NullPrincipal.h"
using namespace mozilla::ipc;
extern mozilla::LazyLogModule gAutoplayPermissionLog;
@ -194,5 +198,257 @@ void CanonicalBrowsingContext::UpdateMediaAction(MediaControlActions aAction) {
});
}
namespace {
using NewOrUsedPromise = MozPromise<RefPtr<ContentParent>, nsresult, false>;
// NOTE: This method is currently a dummy, and always actually spawns sync. It
// mostly exists so I can test out the async API right now.
RefPtr<NewOrUsedPromise> GetNewOrUsedBrowserProcessAsync(
const nsAString& aRemoteType) {
RefPtr<ContentParent> contentParent =
ContentParent::GetNewOrUsedBrowserProcess(
nullptr, aRemoteType, hal::PROCESS_PRIORITY_FOREGROUND, nullptr,
false);
if (!contentParent) {
return NewOrUsedPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
return NewOrUsedPromise::CreateAndResolve(contentParent, __func__);
}
} // anonymous namespace
void CanonicalBrowsingContext::PendingRemotenessChange::Complete(
ContentParent* aContentParent) {
if (!mPromise) {
return;
}
RefPtr<CanonicalBrowsingContext> target(mTarget);
RefPtr<WindowGlobalParent> embedderWindow = target->GetEmbedderWindowGlobal();
if (NS_WARN_IF(!embedderWindow) || NS_WARN_IF(!embedderWindow->CanSend())) {
Cancel(NS_ERROR_FAILURE);
return;
}
RefPtr<BrowserParent> embedderBrowser = embedderWindow->GetBrowserParent();
if (NS_WARN_IF(!embedderBrowser)) {
Cancel(NS_ERROR_FAILURE);
return;
}
// Pull load flags from our embedder browser.
nsCOMPtr<nsILoadContext> loadContext = embedderBrowser->GetLoadContext();
MOZ_DIAGNOSTIC_ASSERT(
loadContext->UseRemoteTabs() && loadContext->UseRemoteSubframes(),
"Not supported without fission");
// NOTE: These are the only flags we actually care about
uint32_t chromeFlags = nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
if (loadContext->UsePrivateBrowsing()) {
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
}
TabId tabId(nsContentUtils::GenerateTabId());
RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent();
ManagedEndpoint<PBrowserBridgeChild> endpoint =
embedderBrowser->OpenPBrowserBridgeEndpoint(bridge);
if (NS_WARN_IF(!endpoint.IsValid())) {
Cancel(NS_ERROR_UNEXPECTED);
return;
}
RefPtr<WindowGlobalParent> oldWindow = target->mCurrentWindowGlobal;
RefPtr<BrowserParent> oldBrowser =
oldWindow ? oldWindow->GetBrowserParent() : nullptr;
bool wasRemote = oldWindow && oldWindow->IsProcessRoot();
// Update which process is considered the current owner
uint64_t inFlightProcessId = target->OwnerProcessId();
target->SetInFlightProcessId(inFlightProcessId);
target->SetOwnerProcessId(aContentParent->ChildID());
auto resetInFlightId = [target, inFlightProcessId] {
MOZ_DIAGNOSTIC_ASSERT(target->GetInFlightProcessId() == inFlightProcessId);
target->SetInFlightProcessId(0);
};
// If we were in a remote frame, trigger unloading of the remote window. When
// the original remote window acknowledges, we can clear the in-flight ID.
if (wasRemote) {
MOZ_DIAGNOSTIC_ASSERT(oldBrowser);
MOZ_DIAGNOSTIC_ASSERT(oldBrowser != embedderBrowser);
MOZ_DIAGNOSTIC_ASSERT(oldBrowser->GetBrowserBridgeParent());
oldBrowser->SendSkipBrowsingContextDetach(
[resetInFlightId](bool aSuccess) { resetInFlightId(); },
[resetInFlightId](mozilla::ipc::ResponseRejectReason aReason) {
resetInFlightId();
});
oldBrowser->Destroy();
}
// Tell the embedder process a remoteness change is in-process. When this is
// acknowledged, reset the in-flight ID if it used to be an in-process load.
embedderWindow->SendMakeFrameRemote(
target, std::move(endpoint), tabId,
[wasRemote, resetInFlightId](bool aSuccess) {
if (!wasRemote) {
resetInFlightId();
}
},
[wasRemote, resetInFlightId](mozilla::ipc::ResponseRejectReason aReason) {
if (!wasRemote) {
resetInFlightId();
}
});
// FIXME: We should get the correct principal for the to-be-created window so
// we can avoid creating unnecessary extra windows in the new process.
nsCOMPtr<nsIPrincipal> initialPrincipal =
NullPrincipal::CreateWithInheritedAttributes(
embedderBrowser->OriginAttributesRef(),
/* isFirstParty */ false);
WindowGlobalInit windowInit =
WindowGlobalActor::AboutBlankInitializer(target, initialPrincipal);
// Actually create the new BrowserParent actor and finish initialization of
// our new BrowserBridgeParent.
nsresult rv = bridge->InitWithProcess(aContentParent, EmptyString(),
windowInit, chromeFlags, tabId);
if (NS_WARN_IF(NS_FAILED(rv))) {
Cancel(rv);
return;
}
RefPtr<BrowserParent> newBrowser = bridge->GetBrowserParent();
newBrowser->ResumeLoad(mPendingSwitchId);
// We did it! The process switch is complete.
mPromise->Resolve(newBrowser, __func__);
Clear();
}
void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv) {
if (!mPromise) {
return;
}
mPromise->Reject(aRv, __func__);
Clear();
}
void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
// Make sure we don't die while we're doing cleanup.
RefPtr<PendingRemotenessChange> kungFuDeathGrip(this);
if (mTarget) {
MOZ_DIAGNOSTIC_ASSERT(mTarget->mPendingRemotenessChange == this);
mTarget->mPendingRemotenessChange = nullptr;
}
mPromise = nullptr;
mTarget = nullptr;
}
CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
MOZ_ASSERT(!mPromise && !mTarget,
"should've already been Cancel() or Complete()-ed");
}
RefPtr<CanonicalBrowsingContext::RemotenessPromise>
CanonicalBrowsingContext::ChangeFrameRemoteness(const nsAString& aRemoteType,
uint64_t aPendingSwitchId) {
// Ensure our embedder hasn't been destroyed already.
RefPtr<WindowGlobalParent> embedderWindowGlobal = GetEmbedderWindowGlobal();
if (!embedderWindowGlobal) {
NS_WARNING("Non-embedded BrowsingContext");
return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__);
}
if (!embedderWindowGlobal->CanSend()) {
NS_WARNING("Embedder already been destroyed.");
return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
}
RefPtr<ContentParent> oldContent = GetContentParent();
if (!oldContent || aRemoteType.IsEmpty()) {
NS_WARNING("Cannot switch to or from non-remote frame");
return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED,
__func__);
}
if (aRemoteType.Equals(oldContent->GetRemoteType())) {
NS_WARNING("Already in the correct process");
return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Cancel ongoing remoteness changes.
if (mPendingRemotenessChange) {
mPendingRemotenessChange->Cancel(NS_ERROR_ABORT);
MOZ_ASSERT(!mPendingRemotenessChange, "Should have cleared");
}
RefPtr<BrowserParent> embedderBrowser =
embedderWindowGlobal->GetBrowserParent();
MOZ_ASSERT(embedderBrowser);
// Switching to local. No new process, so perform switch sync.
if (aRemoteType.Equals(embedderBrowser->Manager()->GetRemoteType())) {
if (mCurrentWindowGlobal) {
MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowGlobal->IsProcessRoot());
RefPtr<BrowserParent> oldBrowser =
mCurrentWindowGlobal->GetBrowserParent();
RefPtr<CanonicalBrowsingContext> target(this);
SetInFlightProcessId(OwnerProcessId());
oldBrowser->SendSkipBrowsingContextDetach(
[target](bool aSuccess) { target->SetInFlightProcessId(0); },
[target](mozilla::ipc::ResponseRejectReason aReason) {
target->SetInFlightProcessId(0);
});
oldBrowser->Destroy();
}
SetOwnerProcessId(embedderBrowser->Manager()->ChildID());
Unused << embedderWindowGlobal->SendMakeFrameLocal(this, aPendingSwitchId);
return RemotenessPromise::CreateAndResolve(embedderBrowser, __func__);
}
// Switching to remote. Wait for new process to launch before switch.
auto promise = MakeRefPtr<RemotenessPromise::Private>(__func__);
RefPtr<PendingRemotenessChange> change =
new PendingRemotenessChange(this, promise, aPendingSwitchId);
mPendingRemotenessChange = change;
GetNewOrUsedBrowserProcessAsync(aRemoteType)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[change](ContentParent* aContentParent) {
change->Complete(aContentParent);
},
[change](nsresult aRv) { change->Cancel(aRv); });
return promise.forget();
}
already_AddRefed<Promise> CanonicalBrowsingContext::ChangeFrameRemoteness(
const nsAString& aRemoteType, uint64_t aPendingSwitchId, ErrorResult& aRv) {
nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
ChangeFrameRemoteness(aRemoteType, aPendingSwitchId)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[promise](BrowserParent* aBrowserParent) {
promise->MaybeResolve(aBrowserParent->Manager()->ChildID());
},
[promise](nsresult aRv) { promise->MaybeReject(aRv); });
return promise.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/MediaController.h"
#include "mozilla/RefPtr.h"
#include "mozilla/MozPromise.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsTHashtable.h"
@ -21,6 +22,7 @@ namespace mozilla {
namespace dom {
class WindowGlobalParent;
class BrowserParent;
// CanonicalBrowsingContext is a BrowsingContext living in the parent
// process, with whatever extra data that a BrowsingContext in the
@ -76,6 +78,16 @@ class CanonicalBrowsingContext final : public BrowsingContext {
// and propogate the action to other browsing contexts in content processes.
void UpdateMediaAction(MediaControlActions aAction);
using RemotenessPromise = MozPromise<RefPtr<BrowserParent>, nsresult, false>;
RefPtr<RemotenessPromise> ChangeFrameRemoteness(const nsAString& aRemoteType,
uint64_t aPendingSwitchId);
// Helper version for WebIDL - resolves to the PID where the load is being
// resumed.
already_AddRefed<Promise> ChangeFrameRemoteness(const nsAString& aRemoteType,
uint64_t aPendingSwitchId,
ErrorResult& aRv);
protected:
void Traverse(nsCycleCollectionTraversalCallback& cb);
void Unlink();
@ -89,6 +101,30 @@ class CanonicalBrowsingContext final : public BrowsingContext {
private:
friend class BrowsingContext;
class PendingRemotenessChange {
public:
NS_INLINE_DECL_REFCOUNTING(PendingRemotenessChange)
PendingRemotenessChange(CanonicalBrowsingContext* aTarget,
RemotenessPromise::Private* aPromise,
uint64_t aPendingSwitchId)
: mTarget(aTarget),
mPromise(aPromise),
mPendingSwitchId(aPendingSwitchId) {}
void Cancel(nsresult aRv);
void Complete(ContentParent* aContentParent);
private:
~PendingRemotenessChange();
void Clear();
RefPtr<CanonicalBrowsingContext> mTarget;
RefPtr<RemotenessPromise::Private> mPromise;
uint64_t mPendingSwitchId;
};
// XXX(farre): Store a ContentParent pointer here rather than mProcessId?
// Indicates which process owns the docshell.
uint64_t mProcessId;
@ -100,6 +136,9 @@ class CanonicalBrowsingContext final : public BrowsingContext {
// All live window globals within this browsing context.
nsTHashtable<nsRefPtrHashKey<WindowGlobalParent>> mWindowGlobals;
RefPtr<WindowGlobalParent> mCurrentWindowGlobal;
// The current remoteness change which is in a pending state.
RefPtr<PendingRemotenessChange> mPendingRemotenessChange;
};
} // namespace dom

View File

@ -7723,9 +7723,11 @@ nsresult nsDocShell::RestoreFromHistory() {
// Order the mContentViewer setup just like Embed does.
mContentViewer = nullptr;
// Move the browsing ontext's children to the cache. If we're
// detaching them, we'll detach them from there.
mBrowsingContext->CacheChildren();
if (!mSkipBrowsingContextDetachOnDestroy) {
// Move the browsing ontext's children to the cache. If we're
// detaching them, we'll detach them from there.
mBrowsingContext->CacheChildren();
}
// Now that we're about to switch documents, forget all of our children.
// Note that we cached them as needed up in CaptureState above.

View File

@ -93,6 +93,7 @@ class nsFrameLoader final : public nsStubMutationObserver,
public nsWrapperCache {
friend class AutoResetInShow;
friend class AutoResetInFrameSwap;
friend class nsFrameLoaderOwner;
typedef mozilla::dom::Document Document;
typedef mozilla::dom::Element Element;
typedef mozilla::dom::BrowserParent BrowserParent;

View File

@ -15,6 +15,7 @@
#include "mozilla/dom/HTMLIFrameElement.h"
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/EventStateManager.h"
@ -65,8 +66,9 @@ bool nsFrameLoaderOwner::ShouldPreserveBrowsingContext(
StaticPrefs::fission_preserve_browsing_contexts();
}
void nsFrameLoaderOwner::ChangeRemoteness(
const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
void nsFrameLoaderOwner::ChangeRemotenessCommon(
bool aPreserveContext, const nsAString& aRemoteType,
std::function<void()>& aFrameLoaderInit, mozilla::ErrorResult& aRv) {
RefPtr<mozilla::dom::BrowsingContext> bc;
bool networkCreated = false;
@ -83,14 +85,12 @@ void nsFrameLoaderOwner::ChangeRemoteness(
// blocker until the process is complete.
Document* doc = owner->OwnerDoc();
doc->BlockOnload();
auto cleanup = MakeScopeExit([&]() {
doc->UnblockOnload(false);
});
auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });
// If we already have a Frameloader, destroy it, possibly preserving its
// browsing context.
if (mFrameLoader) {
if (ShouldPreserveBrowsingContext(aOptions)) {
if (aPreserveContext) {
bc = mFrameLoader->GetBrowsingContext();
mFrameLoader->SkipBrowsingContextDetach();
}
@ -103,31 +103,18 @@ void nsFrameLoaderOwner::ChangeRemoteness(
}
mFrameLoader =
nsFrameLoader::Recreate(owner, bc, aOptions.mRemoteType, networkCreated);
nsFrameLoader::Recreate(owner, bc, aRemoteType, networkCreated);
if (NS_WARN_IF(!mFrameLoader)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (aOptions.mError.WasPassed()) {
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
if (NS_WARN_IF(rv.Failed())) {
return;
}
nsDocShell* docShell = mFrameLoader->GetDocShell(rv);
if (NS_WARN_IF(rv.Failed())) {
return;
}
bool displayed = false;
docShell->DisplayLoadError(static_cast<nsresult>(aOptions.mError.Value()),
uri, u"about:blank", nullptr, &displayed);
} else if (aOptions.mPendingSwitchID.WasPassed()) {
mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
} else {
mFrameLoader->LoadFrame(false);
// Invoke the frame loader initialization callback to perform setup on our new
// nsFrameLoader. This may cause our ErrorResult to become errored, so
// double-check after calling.
aFrameLoaderInit();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
// Now that we've got a new FrameLoader, we need to reset our
@ -150,12 +137,82 @@ void nsFrameLoaderOwner::ChangeRemoteness(
eventManager->RecomputeMouseEnterStateForRemoteFrame(*owner);
}
// Assuming this element is a XULFrameElement, once we've reset our
// FrameLoader, fire an event to act like we've recreated ourselves, similar
// to what XULFrameElement does after rebinding to the tree.
// ChromeOnlyDispatch is turns on to make sure this isn't fired into content.
(new mozilla::AsyncEventDispatcher(
owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
mozilla::CanBubble::eYes, mozilla::ChromeOnlyDispatch::eYes))
->RunDOMEventWhenSafe();
if (owner->IsXULElement()) {
// Assuming this element is a XULFrameElement, once we've reset our
// FrameLoader, fire an event to act like we've recreated ourselves, similar
// to what XULFrameElement does after rebinding to the tree.
// ChromeOnlyDispatch is turns on to make sure this isn't fired into
// content.
(new mozilla::AsyncEventDispatcher(
owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
mozilla::CanBubble::eYes, mozilla::ChromeOnlyDispatch::eYes))
->RunDOMEventWhenSafe();
}
}
void nsFrameLoaderOwner::ChangeRemoteness(
const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
std::function<void()> frameLoaderInit = [&] {
if (aOptions.mError.WasPassed()) {
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
if (NS_WARN_IF(rv.Failed())) {
return;
}
nsDocShell* docShell = mFrameLoader->GetDocShell(rv);
if (NS_WARN_IF(rv.Failed())) {
return;
}
bool displayed = false;
docShell->DisplayLoadError(static_cast<nsresult>(aOptions.mError.Value()),
uri, u"about:blank", nullptr, &displayed);
} else if (aOptions.mPendingSwitchID.WasPassed()) {
mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
} else {
mFrameLoader->LoadFrame(false);
}
};
ChangeRemotenessCommon(ShouldPreserveBrowsingContext(aOptions),
aOptions.mRemoteType, frameLoaderInit, rv);
}
void nsFrameLoaderOwner::ChangeRemotenessWithBridge(
mozilla::ipc::ManagedEndpoint<mozilla::dom::PBrowserBridgeChild> aEndpoint,
uint64_t aTabId, mozilla::ErrorResult& rv) {
MOZ_ASSERT(XRE_IsContentProcess());
if (NS_WARN_IF(!mFrameLoader)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return;
}
std::function<void()> frameLoaderInit = [&] {
RefPtr<BrowsingContext> browsingContext = mFrameLoader->mBrowsingContext;
RefPtr<BrowserBridgeChild> bridge =
new BrowserBridgeChild(mFrameLoader, browsingContext, TabId(aTabId));
Document* ownerDoc = mFrameLoader->GetOwnerDoc();
if (NS_WARN_IF(!ownerDoc)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return;
}
RefPtr<BrowserChild> browser =
BrowserChild::GetFrom(ownerDoc->GetDocShell());
if (!browser->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return;
}
RefPtr<BrowserBridgeHost> host = bridge->FinishInit();
browsingContext->SetEmbedderElement(mFrameLoader->GetOwnerContent());
mFrameLoader->mRemoteBrowser = host;
};
// NOTE: We always use the DEFAULT_REMOTE_TYPE here, because we don't actually
// know the real remote type, and don't need to, as we're a content process.
ChangeRemotenessCommon(
/* preserve */ true, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
frameLoaderInit, rv);
}

View File

@ -14,8 +14,13 @@ namespace mozilla {
class ErrorResult;
namespace dom {
class BrowsingContext;
class PBrowserBridgeChild;
struct RemotenessOptions;
} // namespace dom
namespace ipc {
template <typename T>
class ManagedEndpoint;
} // namespace ipc
} // namespace mozilla
// IID for the FrameLoaderOwner interface
@ -52,10 +57,19 @@ class nsFrameLoaderOwner : public nsISupports {
void ChangeRemoteness(const mozilla::dom::RemotenessOptions& aOptions,
mozilla::ErrorResult& rv);
void ChangeRemotenessWithBridge(
mozilla::ipc::ManagedEndpoint<mozilla::dom::PBrowserBridgeChild>
aEndpoint,
uint64_t aTabId, mozilla::ErrorResult& rv);
private:
bool UseRemoteSubframes();
bool ShouldPreserveBrowsingContext(
const mozilla::dom::RemotenessOptions& aOptions);
void ChangeRemotenessCommon(bool aPreserveContext,
const nsAString& aRemoteType,
std::function<void()>& aFrameLoaderInit,
mozilla::ErrorResult& aRv);
protected:
virtual ~nsFrameLoaderOwner() = default;

View File

@ -49,6 +49,10 @@ interface CanonicalBrowsingContext : BrowsingContext {
void notifyStartDelayedAutoplayMedia();
void notifyMediaMutedChanged(boolean muted);
[Throws]
Promise<unsigned long long> changeFrameRemoteness(
DOMString remoteType, unsigned long long pendingSwitchId);
};
[Exposed=Window, ChromeOnly]

View File

@ -54,11 +54,6 @@ interface WindowGlobalParent {
[Throws]
JSWindowActorParent getActor(DOMString name);
[Throws]
Promise<unsigned long long> changeFrameRemoteness(
BrowsingContext? bc, DOMString remoteType,
unsigned long long pendingSwitchId);
/**
* Renders a region of the frame into an image bitmap.
*

View File

@ -68,13 +68,13 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
static BrowserBridgeChild* GetFrom(nsIContent* aContent);
BrowserBridgeChild(nsFrameLoader* aFrameLoader,
BrowsingContext* aBrowsingContext, TabId aId);
protected:
friend class ContentChild;
friend class PBrowserBridgeChild;
BrowserBridgeChild(nsFrameLoader* aFrameLoader,
BrowsingContext* aBrowsingContext, TabId aId);
mozilla::ipc::IPCResult RecvSetLayersId(
const mozilla::layers::LayersId& aLayersId);

View File

@ -27,10 +27,9 @@ BrowserBridgeParent::BrowserBridgeParent() {}
BrowserBridgeParent::~BrowserBridgeParent() { Destroy(); }
nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
const nsString& aRemoteType,
const WindowGlobalInit& aWindowInit,
const uint32_t& aChromeFlags, TabId aTabId) {
nsresult BrowserBridgeParent::InitWithProcess(
ContentParent* aContentParent, const nsString& aPresentationURL,
const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags, TabId aTabId) {
RefPtr<CanonicalBrowsingContext> browsingContext =
aWindowInit.browsingContext()->Canonical();
@ -42,30 +41,19 @@ nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
Manager()->OriginAttributesRef(), aPresentationURL,
Manager()->GetMaxTouchPoints());
ProcessPriority initialPriority = PROCESS_PRIORITY_FOREGROUND;
// Get our ConstructorSender object.
RefPtr<ContentParent> constructorSender =
ContentParent::GetNewOrUsedBrowserProcess(
nullptr, aRemoteType, initialPriority, nullptr, false);
if (NS_WARN_IF(!constructorSender)) {
MOZ_ASSERT(false, "Unable to allocate content process!");
return NS_ERROR_FAILURE;
}
// Ensure that our content process is subscribed to our newly created
// BrowsingContextGroup.
browsingContext->Group()->EnsureSubscribed(constructorSender);
browsingContext->SetOwnerProcessId(constructorSender->ChildID());
browsingContext->Group()->EnsureSubscribed(aContentParent);
browsingContext->SetOwnerProcessId(aContentParent->ChildID());
// Construct the BrowserParent object for our subframe.
auto browserParent = MakeRefPtr<BrowserParent>(
constructorSender, aTabId, tabContext, browsingContext, aChromeFlags);
aContentParent, aTabId, tabContext, browsingContext, aChromeFlags);
browserParent->SetBrowserBridgeParent(this);
// Open a remote endpoint for our PBrowser actor.
ManagedEndpoint<PBrowserChild> childEp =
constructorSender->OpenPBrowserEndpoint(browserParent);
aContentParent->OpenPBrowserEndpoint(browserParent);
if (NS_WARN_IF(!childEp.IsValid())) {
MOZ_ASSERT(false, "Browser Open Endpoint Failed");
return NS_ERROR_FAILURE;
@ -85,10 +73,10 @@ nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
}
// Tell the content process to set up its PBrowserChild.
bool ok = constructorSender->SendConstructBrowser(
bool ok = aContentParent->SendConstructBrowser(
std::move(childEp), std::move(windowChildEp), aTabId, TabId(0),
tabContext.AsIPCTabContext(), aWindowInit, aChromeFlags,
constructorSender->ChildID(), constructorSender->IsForBrowser(),
aContentParent->ChildID(), aContentParent->IsForBrowser(),
/* aIsTopLevel */ false);
if (NS_WARN_IF(!ok)) {
MOZ_ASSERT(false, "Browser Constructor Failed");
@ -107,6 +95,23 @@ nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
return NS_OK;
}
nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
const nsString& aRemoteType,
const WindowGlobalInit& aWindowInit,
uint32_t aChromeFlags, TabId aTabId) {
// Get our ConstructorSender object.
RefPtr<ContentParent> constructorSender =
ContentParent::GetNewOrUsedBrowserProcess(
nullptr, aRemoteType, PROCESS_PRIORITY_FOREGROUND, nullptr, false);
if (NS_WARN_IF(!constructorSender)) {
MOZ_ASSERT(false, "Unable to allocate content process!");
return NS_ERROR_FAILURE;
}
return InitWithProcess(constructorSender, aPresentationURL, aWindowInit,
aChromeFlags, aTabId);
}
CanonicalBrowsingContext* BrowserBridgeParent::GetBrowsingContext() {
return mBrowserParent->GetBrowsingContext();
}

View File

@ -33,8 +33,13 @@ class BrowserBridgeParent : public PBrowserBridgeParent {
// Initialize this actor after performing startup.
nsresult Init(const nsString& aPresentationURL, const nsString& aRemoteType,
const WindowGlobalInit& aWindowInit,
const uint32_t& aChromeFlags, TabId aTabId);
const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags,
TabId aTabId);
nsresult InitWithProcess(ContentParent* aContentParent,
const nsString& aPresentationURL,
const WindowGlobalInit& aWindowInit,
uint32_t aChromeFlags, TabId aTabId);
BrowserParent* GetBrowserParent() { return mBrowserParent; }

View File

@ -17,6 +17,7 @@ using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
using nscolor from "nsColor.h";
using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
namespace mozilla {
namespace dom {
@ -41,10 +42,10 @@ async refcounted protocol PWindowGlobal
child:
async __delete__();
async ChangeFrameRemoteness(BrowsingContext aFrameContext,
nsString aRemoteType,
uint64_t aSwitchId)
returns (nsresult rv, nullable PBrowserBridge bridge);
async MakeFrameLocal(BrowsingContext aFrameContext, uint64_t aSwitchId);
async MakeFrameRemote(BrowsingContext aFrameContext,
ManagedEndpoint<PBrowserBridgeChild> aEndpoint,
TabId aTabId) returns (bool success);
async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor, uint32_t aFlags) returns (PaintFragment retval);

View File

@ -13,7 +13,7 @@
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/WindowGlobalActorsBinding.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/ipc/InProcessChild.h"
@ -239,85 +239,59 @@ mozilla::ipc::IPCResult WindowGlobalChild::RecvLoadURIInChild(
return IPC_OK();
}
static nsresult ChangeFrameRemoteness(WindowGlobalChild* aWgc,
BrowsingContext* aBc,
const nsString& aRemoteType,
uint64_t aPendingSwitchId,
BrowserBridgeChild** aBridge) {
MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent");
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
dom::BrowsingContext* aFrameContext, uint64_t aPendingSwitchId) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
// Get the target embedder's FrameLoaderOwner, and make sure we're in the
// right place.
RefPtr<Element> embedderElt = aBc->GetEmbedderElement();
if (!embedderElt) {
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(aFrameContext->GetLog(), LogLevel::Debug,
("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext->Id()));
RefPtr<Element> embedderElt = aFrameContext->GetEmbedderElement();
if (NS_WARN_IF(!embedderElt)) {
return IPC_FAIL(this, "No embedder element in this process");
}
if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != aWgc->WindowGlobal())) {
return NS_ERROR_UNEXPECTED;
if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != WindowGlobal())) {
return IPC_FAIL(this, "Wrong actor");
}
RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
MOZ_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner!");
MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
// Actually perform the remoteness swap.
// Trigger a process switch into the current process.
RemotenessOptions options;
options.mRemoteType.Assign(VoidString());
options.mPendingSwitchID.Construct(aPendingSwitchId);
options.mRemoteType.Assign(aRemoteType);
// Clear mRemoteType to VoidString() (for non-remote) if it matches the
// current process' remote type.
if (ContentChild::GetSingleton()->GetRemoteType().Equals(aRemoteType)) {
options.mRemoteType.Assign(VoidString());
}
ErrorResult error;
flo->ChangeRemoteness(options, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
// Make sure we successfully created either an in-process nsDocShell or a
// cross-process BrowserBridgeChild. If we didn't, produce an error.
RefPtr<nsFrameLoader> frameLoader = flo->GetFrameLoader();
if (NS_WARN_IF(!frameLoader)) {
return NS_ERROR_FAILURE;
}
RefPtr<BrowserBridgeChild> bbc;
if (frameLoader->IsRemoteFrame()) {
bbc = frameLoader->GetBrowserBridgeChild();
if (NS_WARN_IF(!bbc)) {
return NS_ERROR_FAILURE;
}
} else {
nsDocShell* ds = frameLoader->GetDocShell(error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (NS_WARN_IF(!ds)) {
return NS_ERROR_FAILURE;
}
}
bbc.forget(aBridge);
return NS_OK;
flo->ChangeRemoteness(options, IgnoreErrors());
return IPC_OK();
}
IPCResult WindowGlobalChild::RecvChangeFrameRemoteness(
dom::BrowsingContext* aBc, const nsString& aRemoteType,
uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver) {
MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent");
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
dom::BrowsingContext* aFrameContext,
ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
MakeFrameRemoteResolver&& aResolve) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
RefPtr<BrowserBridgeChild> bbc;
nsresult rv = ChangeFrameRemoteness(this, aBc, aRemoteType, aPendingSwitchId,
getter_AddRefs(bbc));
MOZ_LOG(aFrameContext->GetLog(), LogLevel::Debug,
("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext->Id()));
// To make the type system happy, we've gotta do some gymnastics.
aResolver(Tuple<const nsresult&, PBrowserBridgeChild*>(rv, bbc));
// Immediately resolve the promise, acknowledging the request.
aResolve(true);
RefPtr<Element> embedderElt = aFrameContext->GetEmbedderElement();
if (NS_WARN_IF(!embedderElt)) {
return IPC_FAIL(this, "No embedder element in this process");
}
if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != WindowGlobal())) {
return IPC_FAIL(this, "Wrong actor");
}
RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
// Trgger a process switch into the specified process.
flo->ChangeRemotenessWithBridge(std::move(aEndpoint), aTabId, IgnoreErrors());
return IPC_OK();
}

View File

@ -116,9 +116,13 @@ class WindowGlobalChild final : public WindowGlobalActor,
mozilla::ipc::IPCResult RecvLoadURIInChild(nsDocShellLoadState* aLoadState);
mozilla::ipc::IPCResult RecvChangeFrameRemoteness(
dom::BrowsingContext* aBc, const nsString& aRemoteType,
uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
mozilla::ipc::IPCResult RecvMakeFrameLocal(
dom::BrowsingContext* aFrameContext, uint64_t aPendingSwitchId);
mozilla::ipc::IPCResult RecvMakeFrameRemote(
dom::BrowsingContext* aFrameContext,
ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
MakeFrameRemoteResolver&& aResolve);
mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
const float& aScale,

View File

@ -302,73 +302,6 @@ bool WindowGlobalParent::IsCurrentGlobal() {
return CanSend() && mBrowsingContext->GetCurrentWindowGlobal() == this;
}
already_AddRefed<Promise> WindowGlobalParent::ChangeFrameRemoteness(
dom::BrowsingContext* aBc, const nsAString& aRemoteType,
uint64_t aPendingSwitchId, ErrorResult& aRv) {
RefPtr<BrowserParent> embedderBrowserParent = GetBrowserParent();
if (NS_WARN_IF(!embedderBrowserParent)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsIGlobalObject* global = GetParentObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<CanonicalBrowsingContext> browsingContext =
CanonicalBrowsingContext::Cast(aBc);
// When the reply comes back from content, either resolve or reject.
auto resolve =
[=](mozilla::Tuple<nsresult, PBrowserBridgeParent*>&& aResult) {
nsresult rv = Get<0>(aResult);
RefPtr<BrowserBridgeParent> bridge =
static_cast<BrowserBridgeParent*>(Get<1>(aResult));
if (NS_FAILED(rv)) {
promise->MaybeReject(rv);
return;
}
// If we got a `BrowserBridgeParent`, the frame is out-of-process, so we
// can get the target off of it. Otherwise, it's an in-process frame, so
// we can use the embedder `BrowserParent`.
RefPtr<BrowserParent> browserParent;
if (bridge) {
browserParent = bridge->GetBrowserParent();
} else {
browserParent = embedderBrowserParent;
}
MOZ_ASSERT(browserParent);
if (!browserParent || !browserParent->CanSend()) {
promise->MaybeReject(NS_ERROR_FAILURE);
return;
}
// Update our BrowsingContext to its new owner, if it hasn't been
// updated yet. This can happen when switching from a out-of-process to
// in-process frame. For remote frames, the BrowserBridgeParent::Init
// method should've already set up the OwnerProcessId.
uint64_t childId = browserParent->Manager()->ChildID();
MOZ_ASSERT_IF(bridge,
browsingContext == browserParent->GetBrowsingContext());
MOZ_ASSERT_IF(bridge, browsingContext->IsOwnedByProcess(childId));
browsingContext->SetOwnerProcessId(childId);
promise->MaybeResolve(childId);
};
auto reject = [=](ResponseRejectReason aReason) {
promise->MaybeReject(NS_ERROR_FAILURE);
};
SendChangeFrameRemoteness(aBc, PromiseFlatString(aRemoteType),
aPendingSwitchId, resolve, reject);
return promise.forget();
}
already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
const DOMRect* aRect, double aScale, const nsAString& aBackgroundColor,
mozilla::ErrorResult& aRv) {

View File

@ -111,11 +111,6 @@ class WindowGlobalParent final : public WindowGlobalActor,
bool HasBeforeUnload() { return mHasBeforeUnload; }
already_AddRefed<Promise> ChangeFrameRemoteness(dom::BrowsingContext* aBc,
const nsAString& aRemoteType,
uint64_t aPendingSwitchId,
ErrorResult& aRv);
already_AddRefed<mozilla::dom::Promise> DrawSnapshot(
const DOMRect* aRect, double aScale, const nsAString& aBackgroundColor,
mozilla::ErrorResult& aRv);