From 280fdf002f870efaa775f58adf17b3e905edf55d Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Fri, 8 Dec 2017 14:46:43 -0500 Subject: [PATCH] Bug 1424338 P4 Implement ClientManager::OpenWindow(). r=baku --- dom/clients/manager/ClientIPCTypes.ipdlh | 4 + dom/clients/manager/ClientManager.cpp | 9 + dom/clients/manager/ClientManager.h | 5 + dom/clients/manager/ClientManagerOpParent.cpp | 11 + dom/clients/manager/ClientManagerService.cpp | 90 ++++ dom/clients/manager/ClientManagerService.h | 4 + .../manager/ClientOpenWindowOpChild.cpp | 20 + dom/clients/manager/ClientOpenWindowOpChild.h | 6 + dom/clients/manager/ClientOpenWindowUtils.cpp | 463 ++++++++++++++++++ dom/clients/manager/ClientOpenWindowUtils.h | 21 + dom/clients/manager/moz.build | 1 + dom/ipc/ContentParent.h | 4 +- modules/libpref/init/all.js | 7 + 13 files changed, 643 insertions(+), 2 deletions(-) create mode 100644 dom/clients/manager/ClientOpenWindowUtils.cpp create mode 100644 dom/clients/manager/ClientOpenWindowUtils.h diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh index 39aa310b257d..5a439fa782ab 100644 --- a/dom/clients/manager/ClientIPCTypes.ipdlh +++ b/dom/clients/manager/ClientIPCTypes.ipdlh @@ -100,6 +100,9 @@ struct ClientGetInfoAndStateArgs struct ClientOpenWindowArgs { + PrincipalInfo principalInfo; + nsCString url; + nsCString baseURL; }; union ClientOpConstructorArgs @@ -109,6 +112,7 @@ union ClientOpConstructorArgs ClientMatchAllArgs; ClientClaimArgs; ClientGetInfoAndStateArgs; + ClientOpenWindowArgs; }; struct ClientList diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp index f366608c28fc..a66d2aa92568 100644 --- a/dom/clients/manager/ClientManager.cpp +++ b/dom/clients/manager/ClientManager.cpp @@ -293,5 +293,14 @@ ClientManager::Navigate(const ClientNavigateArgs& aArgs, return mgr->StartOp(aArgs, aSerialEventTarget); } +// static +RefPtr +ClientManager::OpenWindow(const ClientOpenWindowArgs& aArgs, + nsISerialEventTarget* aSerialEventTarget) +{ + RefPtr mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + } // namespace dom } // namespace mozilla diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h index 0c9bdaa18f27..b6ae1144e0a9 100644 --- a/dom/clients/manager/ClientManager.h +++ b/dom/clients/manager/ClientManager.h @@ -26,6 +26,7 @@ class ClientManagerChild; class ClientMatchAllArgs; class ClientNavigateArgs; class ClientOpConstructorArgs; +class ClientOpenWindowArgs; class ClientSource; enum class ClientType : uint8_t; @@ -111,6 +112,10 @@ public: Navigate(const ClientNavigateArgs& aArgs, nsISerialEventTarget* aSerialEventTarget); + static RefPtr + OpenWindow(const ClientOpenWindowArgs& aArgs, + nsISerialEventTarget* aSerialEventTarget); + NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager) }; diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp index e7de7ed3a680..108effafe779 100644 --- a/dom/clients/manager/ClientManagerOpParent.cpp +++ b/dom/clients/manager/ClientManagerOpParent.cpp @@ -7,10 +7,13 @@ #include "ClientManagerOpParent.h" #include "ClientManagerService.h" +#include "mozilla/ipc/BackgroundParent.h" namespace mozilla { namespace dom { +using mozilla::ipc::BackgroundParent; + template void ClientManagerOpParent::DoServiceOp(Method aMethod, Args&&... aArgs) @@ -71,6 +74,14 @@ ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs) aArgs.get_ClientGetInfoAndStateArgs()); break; } + case ClientOpConstructorArgs::TClientOpenWindowArgs: + { + RefPtr contentParent = + BackgroundParent::GetContentParent(Manager()->Manager()); + DoServiceOp(&ClientManagerService::OpenWindow, + aArgs.get_ClientOpenWindowArgs(), contentParent.forget()); + break; + } default: { MOZ_ASSERT_UNREACHABLE("Unknown Client operation!"); diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp index f81865e4f904..953b254ca4a9 100644 --- a/dom/clients/manager/ClientManagerService.cpp +++ b/dom/clients/manager/ClientManagerService.cpp @@ -8,12 +8,17 @@ #include "ClientManagerParent.h" #include "ClientNavigateOpParent.h" +#include "ClientOpenWindowOpParent.h" +#include "ClientOpenWindowUtils.h" #include "ClientSourceParent.h" +#include "mozilla/dom/ContentParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/SystemGroup.h" #include "nsIAsyncShutdown.h" +#include "nsIXULRuntime.h" +#include "nsProxyRelease.h" namespace mozilla { namespace dom { @@ -551,5 +556,90 @@ ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs) return source->StartOp(aArgs); } +namespace { + +class OpenWindowRunnable final : public Runnable +{ + RefPtr mPromise; + const ClientOpenWindowArgs mArgs; + RefPtr mSourceProcess; + + ~OpenWindowRunnable() + { + NS_ReleaseOnMainThreadSystemGroup(mSourceProcess.forget()); + } + +public: + OpenWindowRunnable(ClientOpPromise::Private* aPromise, + const ClientOpenWindowArgs& aArgs, + already_AddRefed aSourceProcess) + : Runnable("ClientManagerService::OpenWindowRunnable") + , mPromise(aPromise) + , mArgs(aArgs) + , mSourceProcess(aSourceProcess) + { + MOZ_DIAGNOSTIC_ASSERT(mPromise); + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!BrowserTabsRemoteAutostart()) { + RefPtr p = ClientOpenWindowInCurrentProcess(mArgs); + p->ChainTo(mPromise.forget(), __func__); + return NS_OK; + } + + RefPtr targetProcess; + + // Possibly try to open the window in the same process that called + // openWindow(). This is a temporary compat setting until the + // multi-e10s service worker refactor is complete. + if (Preferences::GetBool("dom.clients.openwindow_favors_same_process", + false)) { + targetProcess = mSourceProcess; + } + + // Otherwise, use our normal remote process selection mechanism for + // opening the window. This will start a process if one is not + // present. + if (!targetProcess) { + targetProcess = + ContentParent::GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), + ContentParent::GetInitialProcessPriority(nullptr), + nullptr); + } + + ClientOpenWindowOpParent* actor = + new ClientOpenWindowOpParent(mArgs, mPromise); + + // If this fails the actor will be automatically destroyed which will + // reject the promise. + Unused << targetProcess->SendPClientOpenWindowOpConstructor(actor, mArgs); + + return NS_OK; + } +}; + +} // anonymous namespace + +RefPtr +ClientManagerService::OpenWindow(const ClientOpenWindowArgs& aArgs, + already_AddRefed aSourceProcess) +{ + RefPtr promise = + new ClientOpPromise::Private(__func__); + + nsCOMPtr r = new OpenWindowRunnable(promise, aArgs, + Move(aSourceProcess)); + MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, + r.forget())); + + RefPtr ref = promise; + return ref.forget(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h index 340ecc14a886..825900701a56 100644 --- a/dom/clients/manager/ClientManagerService.h +++ b/dom/clients/manager/ClientManagerService.h @@ -71,6 +71,10 @@ public: RefPtr GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs); + RefPtr + OpenWindow(const ClientOpenWindowArgs& aArgs, + already_AddRefed aSourceProcess); + NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService) }; diff --git a/dom/clients/manager/ClientOpenWindowOpChild.cpp b/dom/clients/manager/ClientOpenWindowOpChild.cpp index c1cfaef62b0a..84b4c7c5b064 100644 --- a/dom/clients/manager/ClientOpenWindowOpChild.cpp +++ b/dom/clients/manager/ClientOpenWindowOpChild.cpp @@ -5,18 +5,38 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ClientOpenWindowOpChild.h" +#include "ClientOpenWindowUtils.h" +#include "mozilla/SystemGroup.h" namespace mozilla { namespace dom { +already_AddRefed +ClientOpenWindowOpChild::DoOpenWindow(const ClientOpenWindowArgs& aArgs) +{ + RefPtr ref = + ClientOpenWindowInCurrentProcess(aArgs); + return ref.forget(); +} + void ClientOpenWindowOpChild::ActorDestroy(ActorDestroyReason aReason) { + mPromiseRequestHolder.DisconnectIfExists(); } void ClientOpenWindowOpChild::Init(const ClientOpenWindowArgs& aArgs) { + RefPtr promise = DoOpenWindow(aArgs); + promise->Then(SystemGroup::EventTargetFor(TaskCategory::Other), __func__, + [this] (const ClientOpResult& aResult) { + mPromiseRequestHolder.Complete(); + PClientOpenWindowOpChild::Send__delete__(this, aResult); + }, [this] (nsresult aResult) { + mPromiseRequestHolder.Complete(); + PClientOpenWindowOpChild::Send__delete__(this, aResult); + })->Track(mPromiseRequestHolder); } } // namespace dom diff --git a/dom/clients/manager/ClientOpenWindowOpChild.h b/dom/clients/manager/ClientOpenWindowOpChild.h index 3aaf79fbdb9d..acc021ea5312 100644 --- a/dom/clients/manager/ClientOpenWindowOpChild.h +++ b/dom/clients/manager/ClientOpenWindowOpChild.h @@ -7,12 +7,18 @@ #define _mozilla_dom_ClientOpenWindowOpChild_h #include "mozilla/dom/PClientOpenWindowOpChild.h" +#include "ClientOpPromise.h" namespace mozilla { namespace dom { class ClientOpenWindowOpChild final : public PClientOpenWindowOpChild { + MozPromiseRequestHolder mPromiseRequestHolder; + + already_AddRefed + DoOpenWindow(const ClientOpenWindowArgs& aArgs); + // PClientOpenWindowOpChild interface void ActorDestroy(ActorDestroyReason aReason) override; diff --git a/dom/clients/manager/ClientOpenWindowUtils.cpp b/dom/clients/manager/ClientOpenWindowUtils.cpp new file mode 100644 index 000000000000..055ca1373985 --- /dev/null +++ b/dom/clients/manager/ClientOpenWindowUtils.cpp @@ -0,0 +1,463 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ClientOpenWindowUtils.h" + +#include "ClientInfo.h" +#include "ClientState.h" +#include "nsContentUtils.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIDOMChromeWindow.h" +#include "nsIURI.h" +#include "nsIWebProgress.h" +#include "nsIWebProgressListener.h" +#include "nsIWindowWatcher.h" +#include "nsNetUtil.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowWatcher.h" + +#ifdef MOZ_WIDGET_ANDROID +#include "FennecJNIWrappers.h" +#endif + +namespace mozilla { +namespace dom { + +namespace { + +class WebProgressListener final : public nsIWebProgressListener + , public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + + WebProgressListener(nsPIDOMWindowOuter* aWindow, + nsIURI* aBaseURI, + already_AddRefed aPromise) + : mPromise(aPromise) + , mWindow(aWindow) + , mBaseURI(aBaseURI) + { + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aBaseURI); + MOZ_ASSERT(NS_IsMainThread()); + } + + NS_IMETHOD + OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, nsresult aStatus) override + { + if (!(aStateFlags & STATE_IS_DOCUMENT) || + !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) { + return NS_OK; + } + + // Our caller keeps a strong reference, so it is safe to remove the listener + // from ServiceWorkerPrivate. + aWebProgress->RemoveProgressListener(this); + + nsCOMPtr doc = mWindow->GetExtantDoc(); + if (NS_WARN_IF(!doc)) { + mPromise->Reject(NS_ERROR_FAILURE, __func__); + mPromise = nullptr; + return NS_OK; + } + + // Check same origin. + nsCOMPtr securityManager = + nsContentUtils::GetSecurityManager(); + nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(), + mBaseURI, false); + if (NS_FAILED(rv)) { + mPromise->Resolve(NS_OK, __func__); + mPromise = nullptr; + return NS_OK; + } + + nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow(); + if (NS_WARN_IF(!innerWindow)) { + mPromise->Reject(NS_ERROR_FAILURE, __func__); + mPromise = nullptr; + return NS_OK; + } + + Maybe info = innerWindow->GetClientInfo(); + Maybe state = innerWindow->GetClientState(); + + if (NS_WARN_IF(info.isNothing() || state.isNothing())) { + mPromise->Reject(NS_ERROR_FAILURE, __func__); + mPromise = nullptr; + return NS_OK; + } + + mPromise->Resolve(ClientInfoAndState(info.ref().ToIPC(), state.ref().ToIPC()), + __func__); + mPromise = nullptr; + + return NS_OK; + } + + NS_IMETHOD + OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) override + { + MOZ_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + + NS_IMETHOD + OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) override + { + MOZ_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + + NS_IMETHOD + OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, const char16_t* aMessage) override + { + MOZ_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + + NS_IMETHOD + OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) override + { + MOZ_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + +private: + ~WebProgressListener() + { + if (mPromise) { + mPromise->Reject(NS_ERROR_ABORT, __func__); + mPromise = nullptr; + } + } + + RefPtr mPromise; + // TODO: make window a weak ref and stop cycle collecting + nsCOMPtr mWindow; + nsCOMPtr mBaseURI; +}; + +NS_IMPL_ISUPPORTS(WebProgressListener, nsIWebProgressListener, + nsISupportsWeakReference); + +nsresult +OpenWindow(const ClientOpenWindowArgs& aArgs, + nsPIDOMWindowOuter** aWindow) +{ + MOZ_DIAGNOSTIC_ASSERT(aWindow); + + // [[1. Let url be the result of parsing url with entry settings object's API + // base URL.]] + nsCOMPtr uri; + + nsCOMPtr baseURI; + nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_TYPE_ERR; + } + + rv = NS_NewURI(getter_AddRefs(uri), aArgs.url(), nullptr, baseURI); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_TYPE_ERR; + } + + nsCOMPtr principal = + PrincipalInfoToPrincipal(aArgs.principalInfo()); + MOZ_DIAGNOSTIC_ASSERT(principal); + + // [[6.1 Open Window]] + if (XRE_IsContentProcess()) { + + // Let's create a sandbox in order to have a valid JSContext and correctly + // propagate the SubjectPrincipal. + AutoJSAPI jsapi; + jsapi.Init(); + + JSContext* cx = jsapi.cx(); + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + MOZ_DIAGNOSTIC_ASSERT(xpc); + + JS::Rooted sandbox(cx); + rv = xpc->CreateSandbox(cx, principal, sandbox.address()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_TYPE_ERR; + } + + JSAutoCompartment ac(cx, sandbox); + + // ContentProcess + nsCOMPtr wwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsCOMPtr pwwatch(do_QueryInterface(wwatch)); + NS_ENSURE_STATE(pwwatch); + + nsCString spec; + rv = uri->GetSpec(spec); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr newWindow; + rv = pwwatch->OpenWindow2(nullptr, + spec.get(), + nullptr, + nullptr, + false, false, true, nullptr, + // Not a spammy popup; we got permission, we swear! + /* aIsPopupSpam = */ false, + // Don't force noopener. We're not passing in an + // opener anyway, and we _do_ want the returned + // window. + /* aForceNoOpener = */ false, + /* aLoadInfp = */ nullptr, + getter_AddRefs(newWindow)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsCOMPtr pwindow = nsPIDOMWindowOuter::From(newWindow); + pwindow.forget(aWindow); + MOZ_DIAGNOSTIC_ASSERT(*aWindow); + return NS_OK; + } + + // Find the most recent browser window and open a new tab in it. + nsCOMPtr browserWindow = + nsContentUtils::GetMostRecentNonPBWindow(); + if (!browserWindow) { + // It is possible to be running without a browser window on Mac OS, so + // we need to open a new chrome window. + // TODO(catalinb): open new chrome window. Bug 1218080 + return NS_ERROR_NOT_AVAILABLE; + } + + nsCOMPtr chromeWin = do_QueryInterface(browserWindow); + if (NS_WARN_IF(!chromeWin)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr bwin; + chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin)); + + if (NS_WARN_IF(!bwin)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr win; + rv = bwin->OpenURI(uri, nullptr, + nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW, + nsIBrowserDOMWindow::OPEN_NEW, + principal, + getter_AddRefs(win)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + NS_ENSURE_STATE(win); + + nsCOMPtr pWin = nsPIDOMWindowOuter::From(win); + pWin.forget(aWindow); + MOZ_DIAGNOSTIC_ASSERT(*aWindow); + + return NS_OK; +} + +void +WaitForLoad(const ClientOpenWindowArgs& aArgs, + nsPIDOMWindowOuter* aOuterWindow, + ClientOpPromise::Private* aPromise) +{ + MOZ_DIAGNOSTIC_ASSERT(aOuterWindow); + + RefPtr promise = aPromise; + + nsresult rv = nsContentUtils::DispatchFocusChromeEvent(aOuterWindow); + if (NS_WARN_IF(NS_FAILED(rv))) { + promise->Reject(rv, __func__); + return; + } + + nsCOMPtr baseURI; + rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL()); + if (NS_WARN_IF(NS_FAILED(rv))) { + promise->Reject(rv, __func__); + return; + } + + nsCOMPtr docShell = aOuterWindow->GetDocShell(); + nsCOMPtr webProgress = do_GetInterface(docShell); + + if (NS_WARN_IF(!webProgress)) { + promise->Reject(NS_ERROR_FAILURE, __func__); + return; + } + + RefPtr ref = promise; + + RefPtr listener = + new WebProgressListener(aOuterWindow, baseURI, promise.forget()); + + + rv = webProgress->AddProgressListener(listener, + nsIWebProgress::NOTIFY_STATE_DOCUMENT); + if (NS_WARN_IF(NS_FAILED(rv))) { + promise->Reject(rv, __func__); + return; + } + + // Hold the listener alive until the promise settles + ref->Then(aOuterWindow->EventTargetFor(TaskCategory::Other), __func__, + [listener] (const ClientOpResult& aResult) { }, + [listener] (nsresult aResult) { }); +} + +#ifdef MOZ_WIDGET_ANDROID + +class LaunchObserver final : public nsIObserver +{ + RefPtr mPromise; + + LaunchObserver() + : mPromise(new GenericPromise::Private(__func__)) + { + } + + ~LaunchObserver() = default; + + NS_IMETHOD + Observe(nsISupports* aSubject, const char* aTopic, const char16_t * aData) override + { + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->RemoveObserver(this, "BrowserChrome:Ready"); + } + mPromise->Resolve(true, __func__); + return NS_OK; + } + +public: + static already_AddRefed + Create() + { + nsCOMPtr os = services::GetObserverService(); + if (NS_WARN_IF(!os)) { + return nullptr; + } + + RefPtr ref = new LaunchObserver(); + + nsresult rv = os->AddObserver(ref, "BrowserChrome:Ready", /* weakRef */ false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + return ref.forget(); + } + + void + Cancel() + { + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->RemoveObserver(this, "BrowserChrome:Ready"); + } + mPromise->Reject(NS_ERROR_ABORT, __func__); + } + + GenericPromise* + Promise() + { + return mPromise; + } + + NS_DECL_ISUPPORTS +}; + +NS_IMPL_ISUPPORTS(LaunchObserver, nsIObserver); + +#endif // MOZ_WIDGET_ANDROID + +} // anonymous namespace + +already_AddRefed +ClientOpenWindowInCurrentProcess(const ClientOpenWindowArgs& aArgs) +{ + RefPtr promise = + new ClientOpPromise::Private(__func__); + RefPtr ref = promise; + +#ifdef MOZ_WIDGET_ANDROID + // This fires an intent that will start launching Fennec and foreground it, + // if necessary. We create an observer so that we can determine when + // the launch has completed. + RefPtr launchObserver = LaunchObserver::Create(); + java::GeckoApp::LaunchOrBringToFront(); +#endif // MOZ_WIDGET_ANDROID + + nsCOMPtr outerWindow; + nsresult rv = OpenWindow(aArgs, getter_AddRefs(outerWindow)); + +#ifdef MOZ_WIDGET_ANDROID + // If we get the NOT_AVAILABLE error that means the browser is still + // launching on android. Use the observer we created above to wait + // until the launch completes and then try to open the window again. + if (rv == NS_ERROR_NOT_AVAILABLE && launchObserver) { + RefPtr p = launchObserver->Promise(); + p->Then(outerWindow->EventTargetFor(TaskCategory::Other), __func__, + [aArgs, promise] (bool aResult) { + nsCOMPtr outerWindow; + nsresult rv = OpenWindow(aArgs, getter_AddRefs(outerWindow)); + if (NS_WARN_IF(NS_FAILED(rv))) { + promise->Reject(rv, __func__); + } + + WaitForLoad(aArgs, outerWindow, promise); + }, [promise] (nsresult aResult) { + promise->Reject(aResult, __func__); + }); + return ref.forget(); + } + + // If we didn't get the NOT_AVAILABLE error then there is no need + // wait for the browser to launch. Cancel the observer so that it + // will release. + if (launchObserver) { + launchObserver->Cancel(); + } +#endif // MOZ_WIDGET_ANDROID + + if (NS_WARN_IF(NS_FAILED(rv))) { + promise->Reject(rv, __func__); + return ref.forget(); + } + + MOZ_DIAGNOSTIC_ASSERT(outerWindow); + WaitForLoad(aArgs, outerWindow, promise); + + return ref.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/clients/manager/ClientOpenWindowUtils.h b/dom/clients/manager/ClientOpenWindowUtils.h new file mode 100644 index 000000000000..5e6d4c0950ed --- /dev/null +++ b/dom/clients/manager/ClientOpenWindowUtils.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _mozilla_dom_ClientOpenWindowUtils_h +#define _mozilla_dom_ClientOpenWindowUtils_h + +#include "ClientOpPromise.h" +#include "mozilla/dom/ClientIPCTypes.h" + +namespace mozilla { +namespace dom { + +already_AddRefed +ClientOpenWindowInCurrentProcess(const ClientOpenWindowArgs &aArgs); + +} // namespace dom +} // namespace mozilla + +#endif // _mozilla_dom_ClientOpenWindowUtils_h diff --git a/dom/clients/manager/moz.build b/dom/clients/manager/moz.build index 3ce6e0070789..00c4a588cc80 100644 --- a/dom/clients/manager/moz.build +++ b/dom/clients/manager/moz.build @@ -38,6 +38,7 @@ UNIFIED_SOURCES += [ 'ClientOpenWindowOpActors.cpp', 'ClientOpenWindowOpChild.cpp', 'ClientOpenWindowOpParent.cpp', + 'ClientOpenWindowUtils.cpp', 'ClientPrefs.cpp', 'ClientSource.cpp', 'ClientSourceChild.cpp', diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index b3b879837f8e..3151b389e101 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -647,6 +647,8 @@ public: virtual bool DeallocPClientOpenWindowOpParent(PClientOpenWindowOpParent* aActor) override; + static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement); + // Control the priority of the IPC messages for input events. void SetInputPriorityEventEnabled(bool aEnabled); bool IsInputPriorityEventEnabled() @@ -680,8 +682,6 @@ private: static nsDataHashtable *sJSPluginContentParents; static StaticAutoPtr > sContentParents; - static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement); - static ContentBridgeParent* CreateContentBridgeParent(const TabContext& aContext, const hal::ProcessPriority& aPriority, const TabId& aOpenerTabId, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 4fe5831531eb..b390e6130c43 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5843,6 +5843,13 @@ pref("layers.advanced.text-layers", 2); // Enable lowercased response header name pref("dom.xhr.lowercase_header.enabled", false); +// Control whether clients.openWindow() opens windows in the same process +// that called the API vs following our normal multi-process selection +// algorithm. Restricting openWindow to same process improves service worker +// web compat in the short term. Once the SW multi-e10s refactor is complete +// this can be removed. +pref("dom.clients.openwindow_favors_same_process", true); + // When a crash happens, whether to include heap regions of the crash context // in the minidump. Enabled by default on nightly and aurora. #ifdef RELEASE_OR_BETA