Backed out 7 changesets (bug 1576714) for fission permafailures on test_bug590812.html. a=backout

Backed out changeset d0c49f00eb91 (bug 1576714)
Backed out changeset faecc9f35b49 (bug 1576714)
Backed out changeset 2e156655c31e (bug 1576714)
Backed out changeset eece722082c7 (bug 1576714)
Backed out changeset ebda40f96884 (bug 1576714)
Backed out changeset 7dce423417d8 (bug 1576714)
Backed out changeset 9a5072019168 (bug 1576714)
This commit is contained in:
Csoregi Natalia 2019-10-05 00:08:33 +03:00
parent 20508b932b
commit 8768a4f5e3
26 changed files with 597 additions and 575 deletions

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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]

View File

@ -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.
*

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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);

View File

@ -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();
}

View File

@ -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,

View File

@ -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) {

View File

@ -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);

View File

@ -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__,

View File

@ -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;

View File

@ -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);
});

View File

@ -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.

View File

@ -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 =

View File

@ -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!");
});