зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. CLOSED TREE
This commit is contained in:
Коммит
ce06821ff1
|
@ -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!");
|
||||
});
|
Загрузка…
Ссылка в новой задаче