Merge mozilla-central to autoland. CLOSED TREE

This commit is contained in:
Csoregi Natalia 2019-10-05 00:41:24 +03:00
Родитель 46534bb2e6 489a461af5
Коммит ce06821ff1
27 изменённых файлов: 631 добавлений и 575 удалений

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

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

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

@ -196,6 +196,7 @@ skip-if = (verify && (os == 'mac' || os == 'win'))
[browser_586068-cascade.js]
[browser_586068-multi_window.js]
[browser_586068-reload.js]
fail-if = fission
skip-if = fission && debug # Crashes intermittently: @ mozilla::net::HttpChannelChild::DoOnStartRequest(nsIRequest*, nsISupports*)
[browser_586068-select.js]
[browser_586068-window_state.js]

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

@ -152,6 +152,23 @@
],
"revision": "default"
},
"bo": {
"platforms": [
"linux",
"linux-devedition",
"linux64",
"linux64-devedition",
"macosx64",
"macosx64-devedition",
"win32",
"win32-devedition",
"win64",
"win64-aarch64",
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "default"
},
"br": {
"platforms": [
"linux",
@ -169,6 +186,23 @@
],
"revision": "default"
},
"brx": {
"platforms": [
"linux",
"linux-devedition",
"linux64",
"linux64-devedition",
"macosx64",
"macosx64-devedition",
"win32",
"win32-devedition",
"win64",
"win64-aarch64",
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "default"
},
"bs": {
"platforms": [
"linux",

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

@ -9,10 +9,6 @@
#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;
@ -198,257 +194,5 @@ 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

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

@ -10,7 +10,6 @@
#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"
@ -22,7 +21,6 @@ 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
@ -78,16 +76,6 @@ 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();
@ -101,30 +89,6 @@ 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;
@ -136,9 +100,6 @@ 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

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

@ -7723,11 +7723,9 @@ nsresult nsDocShell::RestoreFromHistory() {
// Order the mContentViewer setup just like Embed does.
mContentViewer = nullptr;
if (!mSkipBrowsingContextDetachOnDestroy) {
// Move the browsing ontext's children to the cache. If we're
// detaching them, we'll detach them from there.
mBrowsingContext->CacheChildren();
}
// 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.

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

@ -93,7 +93,6 @@ 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;

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

@ -15,10 +15,6 @@
#include "mozilla/dom/HTMLIFrameElement.h"
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/BrowserBridgeHost.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/EventStateManager.h"
@ -69,9 +65,8 @@ bool nsFrameLoaderOwner::ShouldPreserveBrowsingContext(
StaticPrefs::fission_preserve_browsing_contexts();
}
void nsFrameLoaderOwner::ChangeRemotenessCommon(
bool aPreserveContext, const nsAString& aRemoteType,
std::function<void()>& aFrameLoaderInit, mozilla::ErrorResult& aRv) {
void nsFrameLoaderOwner::ChangeRemoteness(
const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
RefPtr<mozilla::dom::BrowsingContext> bc;
bool networkCreated = false;
@ -88,12 +83,14 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon(
// 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 (aPreserveContext) {
if (ShouldPreserveBrowsingContext(aOptions)) {
bc = mFrameLoader->GetBrowsingContext();
mFrameLoader->SkipBrowsingContextDetach();
}
@ -106,18 +103,31 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon(
}
mFrameLoader =
nsFrameLoader::Recreate(owner, bc, aRemoteType, networkCreated);
nsFrameLoader::Recreate(owner, bc, aOptions.mRemoteType, networkCreated);
if (NS_WARN_IF(!mFrameLoader)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
// 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;
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);
}
// Now that we've got a new FrameLoader, we need to reset our
@ -140,82 +150,12 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon(
eventManager->RecomputeMouseEnterStateForRemoteFrame(*owner);
}
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);
// 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();
}

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

@ -14,13 +14,8 @@ 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
@ -57,19 +52,10 @@ 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;

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

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

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

@ -54,6 +54,11 @@ 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.
*

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

@ -4,17 +4,11 @@
* 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/. */
#ifdef ACCESSIBILITY
# ifdef XP_WIN
# include "mozilla/a11y/ProxyAccessible.h"
# include "mozilla/a11y/ProxyWrappers.h"
# endif
# include "mozilla/a11y/DocAccessible.h"
# include "mozilla/a11y/DocManager.h"
# include "mozilla/a11y/OuterDocAccessible.h"
#if defined(ACCESSIBILITY) && defined(XP_WIN)
# include "mozilla/a11y/ProxyAccessible.h"
# include "mozilla/a11y/ProxyWrappers.h"
#endif
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/BrowserBridgeHost.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "nsFocusManager.h"
@ -35,6 +29,7 @@ BrowserBridgeChild::BrowserBridgeChild(nsFrameLoader* aFrameLoader,
TabId aId)
: mId{aId},
mLayersId{0},
mIPCOpen(true),
mFrameLoader(aFrameLoader),
mBrowsingContext(aBrowsingContext) {}
@ -46,27 +41,6 @@ BrowserBridgeChild::~BrowserBridgeChild() {
#endif
}
already_AddRefed<BrowserBridgeHost> BrowserBridgeChild::FinishInit() {
RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(owner->GetOwnerGlobal());
MOZ_DIAGNOSTIC_ASSERT(docShell);
nsDocShell::Cast(docShell)->OOPChildLoadStarted(this);
#if defined(ACCESSIBILITY)
if (a11y::DocAccessible* docAcc =
a11y::GetExistingDocAccessible(owner->OwnerDoc())) {
if (a11y::Accessible* ownerAcc = docAcc->GetAccessible(owner)) {
if (a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc()) {
outerAcc->SendEmbedderAccessible(this);
}
}
}
#endif // defined(ACCESSIBILITY)
return MakeAndAddRef<BrowserBridgeHost>(this);
}
void BrowserBridgeChild::NavigateByKey(bool aForward,
bool aForDocumentNavigation) {
Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
@ -242,6 +216,8 @@ mozilla::ipc::IPCResult BrowserBridgeChild::RecvSubFrameCrashed(
}
void BrowserBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
mIPCOpen = false;
// Ensure we unblock our document's 'load' event (in case the OOP-iframe has
// been removed before it finished loading, or its subprocess crashed):
UnblockOwnerDocsLoadEvent();

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

@ -20,7 +20,6 @@ class RemoteIframeDocProxyAccessibleWrap;
namespace dom {
class BrowsingContext;
class ContentChild;
class BrowserBridgeHost;
/**
* BrowserBridgeChild implements the child actor part of the PBrowserBridge
@ -33,7 +32,7 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
NS_INLINE_DECL_REFCOUNTING(BrowserBridgeChild, final);
BrowserChild* Manager() {
MOZ_ASSERT(CanSend());
MOZ_ASSERT(mIPCOpen);
return static_cast<BrowserChild*>(PBrowserBridgeChild::Manager());
}
@ -56,8 +55,6 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
void SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
already_AddRefed<BrowserBridgeHost> FinishInit();
#if defined(ACCESSIBILITY) && defined(XP_WIN)
a11y::RemoteIframeDocProxyAccessibleWrap* GetEmbeddedDocAccessible() {
return mEmbeddedDocAccessible;
@ -68,13 +65,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);
@ -106,6 +103,7 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
TabId mId;
LayersId mLayersId;
bool mIPCOpen;
bool mHadInitialLoad = false;
RefPtr<nsFrameLoader> mFrameLoader;
RefPtr<BrowsingContext> mBrowsingContext;

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

@ -23,13 +23,22 @@ using namespace mozilla::hal;
namespace mozilla {
namespace dom {
BrowserBridgeParent::BrowserBridgeParent() {}
BrowserBridgeParent::BrowserBridgeParent()
:
#ifdef ACCESSIBILITY
mEmbedderAccessibleID(0),
#endif
mIPCOpen(false) {
}
BrowserBridgeParent::~BrowserBridgeParent() { Destroy(); }
nsresult BrowserBridgeParent::InitWithProcess(
ContentParent* aContentParent, const nsString& aPresentationURL,
const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags, TabId aTabId) {
nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
const nsString& aRemoteType,
const WindowGlobalInit& aWindowInit,
const uint32_t& aChromeFlags, TabId aTabId) {
mIPCOpen = true;
RefPtr<CanonicalBrowsingContext> browsingContext =
aWindowInit.browsingContext()->Canonical();
@ -41,19 +50,30 @@ nsresult BrowserBridgeParent::InitWithProcess(
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(aContentParent);
browsingContext->SetOwnerProcessId(aContentParent->ChildID());
browsingContext->Group()->EnsureSubscribed(constructorSender);
browsingContext->SetOwnerProcessId(constructorSender->ChildID());
// Construct the BrowserParent object for our subframe.
auto browserParent = MakeRefPtr<BrowserParent>(
aContentParent, aTabId, tabContext, browsingContext, aChromeFlags);
constructorSender, aTabId, tabContext, browsingContext, aChromeFlags);
browserParent->SetBrowserBridgeParent(this);
// Open a remote endpoint for our PBrowser actor.
ManagedEndpoint<PBrowserChild> childEp =
aContentParent->OpenPBrowserEndpoint(browserParent);
constructorSender->OpenPBrowserEndpoint(browserParent);
if (NS_WARN_IF(!childEp.IsValid())) {
MOZ_ASSERT(false, "Browser Open Endpoint Failed");
return NS_ERROR_FAILURE;
@ -73,10 +93,10 @@ nsresult BrowserBridgeParent::InitWithProcess(
}
// Tell the content process to set up its PBrowserChild.
bool ok = aContentParent->SendConstructBrowser(
bool ok = constructorSender->SendConstructBrowser(
std::move(childEp), std::move(windowChildEp), aTabId, TabId(0),
tabContext.AsIPCTabContext(), aWindowInit, aChromeFlags,
aContentParent->ChildID(), aContentParent->IsForBrowser(),
constructorSender->ChildID(), constructorSender->IsForBrowser(),
/* aIsTopLevel */ false);
if (NS_WARN_IF(!ok)) {
MOZ_ASSERT(false, "Browser Constructor Failed");
@ -95,29 +115,12 @@ nsresult BrowserBridgeParent::InitWithProcess(
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();
}
BrowserParent* BrowserBridgeParent::Manager() {
MOZ_ASSERT(CanSend());
MOZ_ASSERT(mIPCOpen);
return static_cast<BrowserParent*>(PBrowserBridgeParent::Manager());
}
@ -228,7 +231,10 @@ IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
return IPC_OK();
}
void BrowserBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { Destroy(); }
void BrowserBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
mIPCOpen = false;
Destroy();
}
} // namespace dom
} // namespace mozilla

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

@ -33,13 +33,8 @@ class BrowserBridgeParent : public PBrowserBridgeParent {
// Initialize this actor after performing startup.
nsresult Init(const nsString& aPresentationURL, const nsString& aRemoteType,
const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags,
TabId aTabId);
nsresult InitWithProcess(ContentParent* aContentParent,
const nsString& aPresentationURL,
const WindowGlobalInit& aWindowInit,
uint32_t aChromeFlags, TabId aTabId);
const WindowGlobalInit& aWindowInit,
const uint32_t& aChromeFlags, TabId aTabId);
BrowserParent* GetBrowserParent() { return mBrowserParent; }
@ -103,8 +98,9 @@ class BrowserBridgeParent : public PBrowserBridgeParent {
RefPtr<BrowserParent> mBrowserParent;
#if defined(ACCESSIBILITY)
RefPtr<a11y::DocAccessibleParent> mEmbedderAccessibleDoc;
uint64_t mEmbedderAccessibleID = 0;
uint64_t mEmbedderAccessibleID;
#endif // defined(ACCESSIBILITY)
bool mIPCOpen;
};
} // namespace dom

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

@ -2122,11 +2122,30 @@ already_AddRefed<RemoteBrowser> ContentChild::CreateBrowser(
RefPtr<BrowserBridgeChild> browserBridge =
new BrowserBridgeChild(aFrameLoader, aBrowsingContext, tabId);
nsDocShell::Cast(docShell)->OOPChildLoadStarted(browserBridge);
browserChild->SendPBrowserBridgeConstructor(
browserBridge, PromiseFlatString(aContext.PresentationURL()), aRemoteType,
aBrowsingContext, chromeFlags, tabId);
browserBridge->mIPCOpen = true;
return browserBridge->FinishInit();
#if defined(ACCESSIBILITY)
a11y::DocAccessible* docAcc =
a11y::GetExistingDocAccessible(owner->OwnerDoc());
if (docAcc) {
a11y::Accessible* ownerAcc = docAcc->GetAccessible(owner);
if (ownerAcc) {
a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc();
if (outerAcc) {
outerAcc->SendEmbedderAccessible(browserBridge);
}
}
}
#endif // defined(ACCESSIBILITY)
RefPtr<BrowserBridgeHost> browserBridgeHost =
new BrowserBridgeHost(browserBridge);
return browserBridgeHost.forget();
}
PScriptCacheChild* ContentChild::AllocPScriptCacheChild(

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

@ -17,7 +17,6 @@ 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 {
@ -42,10 +41,10 @@ async refcounted protocol PWindowGlobal
child:
async __delete__();
async MakeFrameLocal(BrowsingContext aFrameContext, uint64_t aSwitchId);
async MakeFrameRemote(BrowsingContext aFrameContext,
ManagedEndpoint<PBrowserBridgeChild> aEndpoint,
TabId aTabId) returns (bool success);
async ChangeFrameRemoteness(BrowsingContext aFrameContext,
nsString aRemoteType,
uint64_t aSwitchId)
returns (nsresult rv, nullable PBrowserBridge bridge);
async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor, uint32_t aFlags) returns (PaintFragment retval);

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

@ -13,7 +13,7 @@
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/WindowGlobalActorsBinding.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/ipc/InProcessChild.h"
@ -239,59 +239,85 @@ mozilla::ipc::IPCResult WindowGlobalChild::RecvLoadURIInChild(
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
dom::BrowsingContext* aFrameContext, uint64_t aPendingSwitchId) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
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");
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");
// 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;
}
if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != WindowGlobal())) {
return IPC_FAIL(this, "Wrong actor");
if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != aWgc->WindowGlobal())) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
MOZ_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner!");
// Trigger a process switch into the current process.
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
// Actually perform the remoteness swap.
RemotenessOptions options;
options.mRemoteType.Assign(VoidString());
options.mPendingSwitchID.Construct(aPendingSwitchId);
flo->ChangeRemoteness(options, IgnoreErrors());
return IPC_OK();
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;
}
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
dom::BrowsingContext* aFrameContext,
ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
MakeFrameRemoteResolver&& aResolve) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
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");
MOZ_LOG(aFrameContext->GetLog(), LogLevel::Debug,
("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext->Id()));
RefPtr<BrowserBridgeChild> bbc;
nsresult rv = ChangeFrameRemoteness(this, aBc, aRemoteType, aPendingSwitchId,
getter_AddRefs(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());
// To make the type system happy, we've gotta do some gymnastics.
aResolver(Tuple<const nsresult&, PBrowserBridgeChild*>(rv, bbc));
return IPC_OK();
}

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

@ -116,13 +116,9 @@ class WindowGlobalChild final : public WindowGlobalActor,
mozilla::ipc::IPCResult RecvLoadURIInChild(nsDocShellLoadState* aLoadState);
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 RecvChangeFrameRemoteness(
dom::BrowsingContext* aBc, const nsString& aRemoteType,
uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
const float& aScale,

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

@ -302,6 +302,73 @@ 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) {

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

@ -111,6 +111,11 @@ 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);

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

@ -144,22 +144,36 @@ void DocumentChannelParent::ActorDestroy(ActorDestroyReason why) {
}
}
void DocumentChannelParent::CancelChildForProcessSwitch() {
MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
MOZ_ASSERT(NS_IsMainThread());
mDoingProcessSwitch = true;
if (CanSend()) {
Unused << SendCancelForProcessSwitch();
}
}
bool DocumentChannelParent::RecvCancel(const nsresult& aStatusCode) {
if (mChannel && !mDoingProcessSwitch) {
if (mDoingProcessSwitch) {
return IPC_OK();
}
if (mChannel) {
mChannel->Cancel(aStatusCode);
}
return true;
}
bool DocumentChannelParent::RecvSuspend() {
if (mChannel && !mDoingProcessSwitch) {
if (mChannel) {
mChannel->Suspend();
}
return true;
}
bool DocumentChannelParent::RecvResume() {
if (mChannel && !mDoingProcessSwitch) {
if (mChannel) {
mChannel->Resume();
}
return true;
@ -207,10 +221,6 @@ DocumentChannelParent::ReadyToVerify(nsresult aResultCode) {
void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) {
nsresult rv;
if (mDoingProcessSwitch && CanSend()) {
Unused << SendCancelForProcessSwitch();
}
nsCOMPtr<nsIParentChannel> redirectChannel;
if (mRedirectChannelId) {
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
@ -350,11 +360,7 @@ void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) {
void DocumentChannelParent::TriggerCrossProcessSwitch() {
MOZ_ASSERT(mRedirectContentProcessIdPromise);
MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
MOZ_ASSERT(NS_IsMainThread());
mDoingProcessSwitch = true;
CancelChildForProcessSwitch();
RefPtr<DocumentChannelParent> self = this;
mRedirectContentProcessIdPromise->Then(
GetMainThreadSerialEventTarget(), __func__,

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

@ -94,6 +94,11 @@ class DocumentChannelParent : public nsIInterfaceRequestor,
virtual void ActorDestroy(ActorDestroyReason why) override;
// Notify the DocumentChannelChild that we're switching
// to a different process and that it can notify listeners
// that it's finished.
void CancelChildForProcessSwitch();
private:
virtual ~DocumentChannelParent() = default;

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

@ -2114,6 +2114,16 @@ HttpChannelParent::StartRedirect(nsIChannel* newChannel, uint32_t redirectFlags,
return NS_OK;
}
void HttpChannelParent::CancelChildCrossProcessRedirect() {
MOZ_ASSERT(!mDoingCrossProcessRedirect, "Already redirected");
MOZ_ASSERT(NS_IsMainThread());
mDoingCrossProcessRedirect = true;
if (!mIPCClosed) {
Unused << SendCancelRedirected();
}
}
NS_IMETHODIMP
HttpChannelParent::CompleteRedirect(bool succeeded) {
LOG(("HttpChannelParent::CompleteRedirect [this=%p succeeded=%d]\n", this,
@ -2632,13 +2642,7 @@ HttpChannelParent::OnRedirectResult(bool succeeded) {
nsresult HttpChannelParent::TriggerCrossProcessSwitch(nsIHttpChannel* aChannel,
uint64_t aIdentifier) {
MOZ_ASSERT(NS_IsMainThread());
// Mark ourselves as performing a cross-process redirect. This will prevent
// messages being communicated to the underlying channel, allowing us to keep
// it open.
MOZ_ASSERT(!mDoingCrossProcessRedirect, "Already redirected");
mDoingCrossProcessRedirect = true;
CancelChildCrossProcessRedirect();
nsCOMPtr<nsIChannel> channel = aChannel;
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(channel);
@ -2669,12 +2673,6 @@ nsresult HttpChannelParent::TriggerCrossProcessSwitch(nsIHttpChannel* aChannel,
[=](uint64_t cpId) {
nsresult rv;
// Cancel the channel in the original process, as the switch is
// happening in earnest.
if (!self->mIPCClosed) {
Unused << self->SendCancelRedirected();
}
// Register the new channel and obtain id for it
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
@ -2744,14 +2742,8 @@ nsresult HttpChannelParent::TriggerCrossProcessSwitch(nsIHttpChannel* aChannel,
self->CrossProcessRedirectDone(NS_ERROR_FAILURE, Nothing());
});
},
[=](nsresult aStatus) {
[httpChannel](nsresult aStatus) {
MOZ_ASSERT(NS_FAILED(aStatus), "Status should be error");
// We failed to do a process switch. Make sure the content process has
// canceled the channel, and then resume the load process with an error.
if (!self->mIPCClosed) {
Unused << self->SendCancelRedirected();
}
httpChannel->OnRedirectVerifyCallback(aStatus);
});

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

@ -136,6 +136,12 @@ class HttpChannelParent final : public nsIInterfaceRequestor,
nsresult TriggerCrossProcessSwitch(nsIHttpChannel* aChannel,
uint64_t aIdentifier);
// Calling this method will cancel the HttpChannelChild because the consumer
// needs to be relocated to another process.
// Any OnStart/Stop/DataAvailable calls that follow will not be sent to the
// child channel.
void CancelChildCrossProcessRedirect();
protected:
// used to connect redirected-to channel in parent with just created
// ChildChannel. Used during redirects.

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

@ -15,6 +15,8 @@ skip-if = e10s # protocol handler and channel does not work in content process
[browser_resource_navigation.js]
[browser_test_io_activity.js]
[browser_cookie_sync_across_tabs.js]
[browser_cross_process_redirect.js]
fail-if = fission
[browser_test_favicon.js]
skip-if = (verify && (os == 'linux' || os == 'mac'))
support-files =

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

@ -0,0 +1,292 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
const gRegistrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
let gLoadedInProcess2Promise = null;
function _createProcessChooser(remoteTab, from, to, rejectPromise = false) {
let processChooser = new ProcessChooser(
remoteTab,
"example.com",
"example.org",
rejectPromise
);
registerCleanupFunction(function() {
processChooser.unregister();
});
}
function ProcessChooser(remoteTab, from, to, rejectPromise = false) {
this.remoteTab = remoteTab;
this.fromDomain = from;
this.toDomain = to;
this.rejectPromise = rejectPromise;
this.registered = true;
Services.obs.addObserver(this, "http-on-may-change-process");
}
ProcessChooser.prototype = {
unregister() {
if (!this.registered) {
return;
}
this.registered = false;
Services.obs.removeObserver(this, "http-on-may-change-process");
},
examine(aRequestor) {
const channel = aRequestor.channel;
if (this.channel && this.channel != channel) {
// Hack: this is just so we don't get redirected multiple times.
info("same channel. give null");
return;
}
if (channel.URI.host != this.toDomain) {
info("wrong host for channel " + channel.URI.host);
return;
}
let redirects = channel.loadInfo.redirectChain;
if (redirects[redirects.length - 1].principal.URI.host != this.fromDomain) {
info("didn't find redirect");
return;
}
info("setting channel");
this.channel = channel;
let self = this;
info("unregistering");
this.unregister();
let identifier = 42;
let tabPromise = new Promise((resolve, reject) => {
if (self.rejectPromise) {
info("rejecting");
reject(Cr.NS_ERROR_NOT_AVAILABLE);
return;
}
// Can asyncly create a tab, or can resolve with a tab that was
// previously created.
info("resolving");
resolve(self.remoteTab.contentProcessId);
});
info("calling switchprocessto");
aRequestor.switchProcessTo(tabPromise, identifier);
},
observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "http-on-may-change-process":
this.examine(aSubject.QueryInterface(Ci.nsIProcessSwitchRequestor));
break;
default:
ok(false, "Unexpected topic observed!");
break;
}
},
// nsISupports
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
};
add_task(async function() {
info(
"Check that a redirect in process A may be correctly handled in process B"
);
const kRoot1 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content/",
"https://example.com/"
);
const kRoot2 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content/",
"https://example.org/"
);
const kRoot3 = getRootDirectory(gTestPath);
// This process will attempt to load the page that redirects to a different origin
let tab1 = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: kRoot1 + "dummy.html",
forceNewProcess: true,
});
// This process will eventually receive the redirected channel.
let tab2 = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: kRoot2 + "dummy.html",
forceNewProcess: true,
});
let browser1 = gBrowser.getBrowserForTab(tab1);
let browser2 = gBrowser.getBrowserForTab(tab2);
// This is for testing purposes only.
// This "process chooser" will direct the channel to be opened in the second
// tab, and thus in the second parent.
let processChooser = _createProcessChooser(
browser2.frameLoader.remoteTab,
"example.com",
"example.org"
);
info("Loading redirected URL");
// Open the URL in the first process. We expect it to wind up in the second
// process.
// Define the child listener in the new channel.
await ContentTask.spawn(browser2, null, async function(arg) {
function ChannelListener(childListener) {
this.childListener = childListener;
}
ChannelListener.prototype = {
onStartRequest(aRequest) {
info("onStartRequest");
let channel = aRequest.QueryInterface(Ci.nsIChannel);
Assert.equal(
channel.URI.spec,
this.childListener.URI,
"Make sure the channel has the proper URI"
);
Assert.equal(
channel.originalURI.spec,
this.childListener.originalURI,
"Make sure the originalURI is correct"
);
},
onStopRequest(aRequest, aStatusCode) {
info("onStopRequest");
Assert.equal(aStatusCode, Cr.NS_OK, "Check the status code");
Assert.equal(
this.gotData,
true,
"Check that the channel received data"
);
if (this.childListener.onComplete) {
this.childListener.onComplete();
}
this.childListener.resolve();
},
onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) {
this.gotData = true;
info("onDataAvailable");
},
QueryInterface: ChromeUtils.generateQI([
Ci.nsIStreamListener,
Ci.nsIRequestObserver,
]),
};
function ChildListener(uri, originalURI, resolve) {
this.URI = uri;
this.originalURI = originalURI;
this.resolve = resolve;
}
ChildListener.prototype = {
// nsIChildProcessChannelListener
onChannelReady(aChildChannel, aIdentifier) {
Assert.equal(aIdentifier, 42, "Check the status code");
info("onChannelReady");
aChildChannel.completeRedirectSetup(new ChannelListener(this), null);
},
// nsIFactory
createInstance(aOuter, aIID) {
if (aOuter) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(aIID);
},
lockFactory() {},
// nsISupports
QueryInterface: ChromeUtils.generateQI([
Ci.nsIChildProcessChannelListener,
Ci.nsIFactory,
]),
classID: Components.ID("{a6c142a9-eb38-4a09-a940-b71cdad479e1}"),
};
content.window.ChildListener = ChildListener;
});
// This promise instantiates a ChildListener and is resolved when the redirected
// channel is completed.
let loadedInProcess2Promise = ContentTask.spawn(
browser2,
{
URI: kRoot2 + "dummy.html",
originalURI: kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html",
},
async function(arg) {
// We register the listener in process no. 2
return new Promise(resolve => {
var childListener = new content.window.ChildListener(
arg.URI,
arg.originalURI,
resolve
);
var registrar = Components.manager.QueryInterface(
Ci.nsIComponentRegistrar
);
childListener.onComplete = () => {
registrar.unregisterFactory(childListener.classID, childListener);
};
registrar.registerFactory(
childListener.classID,
"",
"@mozilla.org/network/childProcessChannelListener;1",
childListener
);
});
}
);
let browser1LoadHasStopped = BrowserTestUtils.browserStopped(
browser1,
undefined,
true
);
await BrowserTestUtils.loadURI(
browser1,
kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html"
);
// Check that the channel was delivered to process no. 2
await loadedInProcess2Promise;
info("channel has loaded in second process");
// This is to check that the old channel was cancelled.
await browser1LoadHasStopped;
// check that a rejected promise also works.
processChooser = _createProcessChooser(
browser2.frameLoader.remoteTab,
"example.com",
"example.org",
true
);
let browser1LoadHasStoppedAgain = BrowserTestUtils.browserStopped(
browser1,
undefined,
true
);
await BrowserTestUtils.loadURI(
browser1,
kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html"
);
await browser1LoadHasStoppedAgain;
info("this is done now");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
ok(true, "Got to the end of the test!");
});