зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1630323 - Do not override user preferences when clicking on a service worker notification to open a new document, r=Gijs,nika,geckoview-reviewers,snorp
In Bug 1622749 a user preference for where to open new documents (from a service worker notification) was temporarily overriden in order to quickly fix a crash that was happening in mozilla::dom::ClientOpenWindow. The crash was ocurring when the pref "browser.link.open_newwindow" was set to 2, meaning new documents are opened in a new window, instead of a new tab. The reason the browser crashed is because the path for opening a new document is different depending on the current user setting, and in NEWWINDOW case we did not get a browsing context returned when calling mozilla::dom::OpenWindow which resulted in a failed assertion. The solution is to pass in a callback to mozilla::dom::OpenWindow as part of nsOpenWindowInfo object, and invoke that callback with a corresponding BrowsingContext in nsFrameLoader when that browsing context is ready. After we call mozilla::dom::OpenWindow, we wait on a promise, that will be resolved when the callback is invoked, before executing the rest of the code that depends on the browsing context for a newly opened document being available. Differential Revision: https://phabricator.services.mozilla.com/D72745
This commit is contained in:
Родитель
34915064c5
Коммит
28243d5736
|
@ -2218,6 +2218,7 @@ var gBrowserInit = {
|
||||||
// [8]: triggeringPrincipal (nsIPrincipal)
|
// [8]: triggeringPrincipal (nsIPrincipal)
|
||||||
// [9]: allowInheritPrincipal (bool)
|
// [9]: allowInheritPrincipal (bool)
|
||||||
// [10]: csp (nsIContentSecurityPolicy)
|
// [10]: csp (nsIContentSecurityPolicy)
|
||||||
|
// [11]: nsOpenWindowInfo
|
||||||
let userContextId =
|
let userContextId =
|
||||||
window.arguments[5] != undefined
|
window.arguments[5] != undefined
|
||||||
? window.arguments[5]
|
? window.arguments[5]
|
||||||
|
@ -6106,7 +6107,7 @@ nsBrowserAccess.prototype = {
|
||||||
case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
|
case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
|
||||||
// FIXME: Bug 408379. So how come this doesn't send the
|
// FIXME: Bug 408379. So how come this doesn't send the
|
||||||
// referrer like the other loads do?
|
// referrer like the other loads do?
|
||||||
var url = aURI ? aURI.spec : "about:blank";
|
var url = aURI && aURI.spec;
|
||||||
let features = "all,dialog=no";
|
let features = "all,dialog=no";
|
||||||
if (isPrivate) {
|
if (isPrivate) {
|
||||||
features += ",private";
|
features += ",private";
|
||||||
|
@ -6129,18 +6130,14 @@ nsBrowserAccess.prototype = {
|
||||||
null,
|
null,
|
||||||
aTriggeringPrincipal,
|
aTriggeringPrincipal,
|
||||||
null,
|
null,
|
||||||
aCsp
|
aCsp,
|
||||||
|
aOpenWindowInfo
|
||||||
);
|
);
|
||||||
// At this point, the new browser window is just starting to load, and
|
// At this point, the new browser window is just starting to load, and
|
||||||
// hasn't created the content <browser> that we should return. So we
|
// hasn't created the content <browser> that we should return.
|
||||||
// can't actually return a valid BrowsingContext for this load without
|
// If the caller of this function is originating in C++, they can pass a
|
||||||
// spinning the event loop.
|
// callback in nsOpenWindowInfo and it will be invoked when the browsing
|
||||||
//
|
// context for a newly opened window is ready.
|
||||||
// Fortunately, no current callers of this API who pass OPEN_NEWWINDOW
|
|
||||||
// actually use the return value, so we're safe returning null for
|
|
||||||
// now.
|
|
||||||
//
|
|
||||||
// Ideally this should be fixed.
|
|
||||||
browsingContext = null;
|
browsingContext = null;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Cu.reportError(ex);
|
Cu.reportError(ex);
|
||||||
|
@ -6153,7 +6150,7 @@ nsBrowserAccess.prototype = {
|
||||||
// we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
|
// we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
|
||||||
// will do the job of shuttling off the newly opened browser to run in
|
// will do the job of shuttling off the newly opened browser to run in
|
||||||
// the right process once it starts loading a URI.
|
// the right process once it starts loading a URI.
|
||||||
let forceNotRemote = aOpenWindowInfo && !aOpenWindowInfo.remote;
|
let forceNotRemote = aOpenWindowInfo && !aOpenWindowInfo.isRemote;
|
||||||
let userContextId = aOpenWindowInfo
|
let userContextId = aOpenWindowInfo
|
||||||
? aOpenWindowInfo.originAttributes.userContextId
|
? aOpenWindowInfo.originAttributes.userContextId
|
||||||
: Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
|
: Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
|
||||||
|
@ -6177,8 +6174,7 @@ nsBrowserAccess.prototype = {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// OPEN_CURRENTWINDOW or an illegal value
|
// OPEN_CURRENTWINDOW or an illegal value
|
||||||
browsingContext =
|
browsingContext = window.gBrowser.selectedBrowser.browsingContext;
|
||||||
window.content && BrowsingContext.getFromWindow(window.content);
|
|
||||||
if (aURI) {
|
if (aURI) {
|
||||||
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||||
if (isExternal) {
|
if (isExternal) {
|
||||||
|
|
|
@ -326,6 +326,10 @@
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Ci.nsIAppWindow).initialOpenWindowInfo;
|
.getInterface(Ci.nsIAppWindow).initialOpenWindowInfo;
|
||||||
|
|
||||||
|
if (!openWindowInfo && window.arguments && window.arguments[11]) {
|
||||||
|
openWindowInfo = window.arguments[11];
|
||||||
|
}
|
||||||
|
|
||||||
let tabArgument = gBrowserInit.getTabToAdopt();
|
let tabArgument = gBrowserInit.getTabToAdopt();
|
||||||
|
|
||||||
// We only need sameProcessAsFrameLoader in the case where we're passed a tab
|
// We only need sameProcessAsFrameLoader in the case where we're passed a tab
|
||||||
|
|
|
@ -275,8 +275,8 @@ static already_AddRefed<BrowsingContext> CreateBrowsingContext(
|
||||||
RefPtr<BrowsingContext> opener;
|
RefPtr<BrowsingContext> opener;
|
||||||
if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
|
if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
|
||||||
opener = aOpenWindowInfo->GetParent();
|
opener = aOpenWindowInfo->GetParent();
|
||||||
MOZ_ASSERT(opener->IsInProcess(),
|
// Must create BrowsingContext with opener in-process.
|
||||||
"Must create BrowsingContext with opener in-process");
|
MOZ_ASSERT_IF(opener, opener->IsInProcess());
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<nsGlobalWindowInner> parentInner =
|
RefPtr<nsGlobalWindowInner> parentInner =
|
||||||
|
@ -2067,6 +2067,8 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
|
||||||
mPendingBrowsingContext->SetEmbedderElement(mOwnerContent);
|
mPendingBrowsingContext->SetEmbedderElement(mOwnerContent);
|
||||||
mPendingBrowsingContext->Embed();
|
mPendingBrowsingContext->Embed();
|
||||||
|
|
||||||
|
InvokeBrowsingContextReadyCallback();
|
||||||
|
|
||||||
mIsTopLevelContent = mPendingBrowsingContext->IsContent() &&
|
mIsTopLevelContent = mPendingBrowsingContext->IsContent() &&
|
||||||
!mPendingBrowsingContext->GetParent();
|
!mPendingBrowsingContext->GetParent();
|
||||||
if (!mNetworkCreated && !mIsTopLevelContent) {
|
if (!mNetworkCreated && !mIsTopLevelContent) {
|
||||||
|
@ -2588,6 +2590,7 @@ bool nsFrameLoader::TryRemoteBrowserInternal() {
|
||||||
mRemoteBrowser->GetBrowsingContext());
|
mRemoteBrowser->GetBrowsingContext());
|
||||||
|
|
||||||
mRemoteBrowser->GetBrowsingContext()->Embed();
|
mRemoteBrowser->GetBrowsingContext()->Embed();
|
||||||
|
InvokeBrowsingContextReadyCallback();
|
||||||
|
|
||||||
// Grab the reference to the actor
|
// Grab the reference to the actor
|
||||||
RefPtr<BrowserParent> browserParent = GetBrowserParent();
|
RefPtr<BrowserParent> browserParent = GetBrowserParent();
|
||||||
|
@ -3554,3 +3557,12 @@ bool nsFrameLoader::EnsureBrowsingContextAttached() {
|
||||||
mPendingBrowsingContext->EnsureAttached();
|
mPendingBrowsingContext->EnsureAttached();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nsFrameLoader::InvokeBrowsingContextReadyCallback() {
|
||||||
|
if (mOpenWindowInfo) {
|
||||||
|
if (RefPtr<nsIBrowsingContextReadyCallback> callback =
|
||||||
|
mOpenWindowInfo->BrowsingContextReadyCallback()) {
|
||||||
|
callback->BrowsingContextReady(mPendingBrowsingContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -465,6 +465,10 @@ class nsFrameLoader final : public nsStubMutationObserver,
|
||||||
|
|
||||||
bool EnsureBrowsingContextAttached();
|
bool EnsureBrowsingContextAttached();
|
||||||
|
|
||||||
|
// Invoke the callback from nsOpenWindowInfo to indicate that a
|
||||||
|
// browsing context for a newly opened tab/window is ready.
|
||||||
|
void InvokeBrowsingContextReadyCallback();
|
||||||
|
|
||||||
RefPtr<mozilla::dom::BrowsingContext> mPendingBrowsingContext;
|
RefPtr<mozilla::dom::BrowsingContext> mPendingBrowsingContext;
|
||||||
nsCOMPtr<nsIURI> mURIToLoad;
|
nsCOMPtr<nsIURI> mURIToLoad;
|
||||||
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
|
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "nsPIWindowWatcher.h"
|
#include "nsPIWindowWatcher.h"
|
||||||
#include "nsPrintfCString.h"
|
#include "nsPrintfCString.h"
|
||||||
#include "nsWindowWatcher.h"
|
#include "nsWindowWatcher.h"
|
||||||
|
#include "nsOpenWindowInfo.h"
|
||||||
|
|
||||||
#include "mozilla/dom/BrowserParent.h"
|
#include "mozilla/dom/BrowserParent.h"
|
||||||
#include "mozilla/dom/BrowsingContext.h"
|
#include "mozilla/dom/BrowsingContext.h"
|
||||||
|
@ -180,43 +181,18 @@ class WebProgressListener final : public nsIWebProgressListener,
|
||||||
NS_IMPL_ISUPPORTS(WebProgressListener, nsIWebProgressListener,
|
NS_IMPL_ISUPPORTS(WebProgressListener, nsIWebProgressListener,
|
||||||
nsISupportsWeakReference);
|
nsISupportsWeakReference);
|
||||||
|
|
||||||
void OpenWindow(const ClientOpenWindowArgs& aArgs, BrowsingContext** aBC,
|
struct ClientOpenWindowArgsParsed {
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
nsCOMPtr<nsIURI> baseURI;
|
||||||
|
nsCOMPtr<nsIPrincipal> principal;
|
||||||
|
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||||
|
};
|
||||||
|
|
||||||
|
void OpenWindow(const ClientOpenWindowArgsParsed& aArgsValidated,
|
||||||
|
nsOpenWindowInfo* aOpenInfo, BrowsingContext** aBC,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(aBC);
|
MOZ_DIAGNOSTIC_ASSERT(aBC);
|
||||||
|
|
||||||
// [[1. Let url be the result of parsing url with entry settings object's API
|
|
||||||
// base URL.]]
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> baseURI;
|
|
||||||
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
nsPrintfCString err("Invalid base URL \"%s\"", aArgs.baseURL().get());
|
|
||||||
aRv.ThrowTypeError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = NS_NewURI(getter_AddRefs(uri), aArgs.url(), nullptr, baseURI);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
nsPrintfCString err("Invalid URL \"%s\"", aArgs.url().get());
|
|
||||||
aRv.ThrowTypeError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto principalOrErr = PrincipalInfoToPrincipal(aArgs.principalInfo());
|
|
||||||
if (NS_WARN_IF(principalOrErr.isErr())) {
|
|
||||||
nsPrintfCString err("Failed to obtain principal");
|
|
||||||
aRv.ThrowTypeError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
|
|
||||||
MOZ_DIAGNOSTIC_ASSERT(principal);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
|
||||||
if (aArgs.cspInfo().isSome()) {
|
|
||||||
csp = CSPInfoToCSP(aArgs.cspInfo().ref(), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// [[6.1 Open Window]]
|
// [[6.1 Open Window]]
|
||||||
|
|
||||||
// Find the most recent browser window and open a new tab in it.
|
// Find the most recent browser window and open a new tab in it.
|
||||||
|
@ -244,33 +220,23 @@ void OpenWindow(const ClientOpenWindowArgs& aArgs, BrowsingContext** aBC,
|
||||||
aRv.ThrowTypeError("Unable to open window");
|
aRv.ThrowTypeError("Unable to open window");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// annyG: This is a hack to fix bug 1622749.
|
nsresult rv = bwin->CreateContentWindow(
|
||||||
// We will force to open new windows in tabs so we don't crash later.
|
nullptr, aOpenInfo, nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
|
||||||
rv = bwin->OpenURI(uri, nullptr, nsIBrowserDOMWindow::OPEN_NEWTAB,
|
nsIBrowserDOMWindow::OPEN_NEW, aArgsValidated.principal,
|
||||||
nsIBrowserDOMWindow::OPEN_NEW, principal, csp, aBC);
|
aArgsValidated.csp, aBC);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
aRv.ThrowTypeError("Unable to open window");
|
aRv.ThrowTypeError("Unable to open window");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitForLoad(const ClientOpenWindowArgs& aArgs,
|
void WaitForLoad(const ClientOpenWindowArgsParsed& aArgsValidated,
|
||||||
BrowsingContext* aBrowsingContext,
|
BrowsingContext* aBrowsingContext,
|
||||||
ClientOpPromise::Private* aPromise) {
|
ClientOpPromise::Private* aPromise) {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext);
|
MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext);
|
||||||
|
|
||||||
RefPtr<ClientOpPromise::Private> promise = aPromise;
|
RefPtr<ClientOpPromise::Private> promise = aPromise;
|
||||||
|
nsresult rv;
|
||||||
nsCOMPtr<nsIURI> baseURI;
|
|
||||||
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
// Shouldn't really happen, since we passed in the serialization of a URI.
|
|
||||||
CopyableErrorResult result;
|
|
||||||
result.ThrowSyntaxError("Bad URL");
|
|
||||||
promise->Reject(result, __func__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIWebProgress> webProgress;
|
nsCOMPtr<nsIWebProgress> webProgress;
|
||||||
if (nsIDocShell* docShell = aBrowsingContext->GetDocShell()) {
|
if (nsIDocShell* docShell = aBrowsingContext->GetDocShell()) {
|
||||||
// We're dealing with a non-remote frame. We have access to an nsDocShell,
|
// We're dealing with a non-remote frame. We have access to an nsDocShell,
|
||||||
|
@ -307,14 +273,11 @@ void WaitForLoad(const ClientOpenWindowArgs& aArgs,
|
||||||
promise->Reject(result, __func__);
|
promise->Reject(result, __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BrowserParent* browserParent = BrowserParent::GetFrom(element)) {
|
|
||||||
browserParent->Activate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<WebProgressListener> listener =
|
// Add a progress listener before we start the load of the service worker URI
|
||||||
new WebProgressListener(aBrowsingContext, baseURI, do_AddRef(promise));
|
RefPtr<WebProgressListener> listener = new WebProgressListener(
|
||||||
|
aBrowsingContext, aArgsValidated.baseURI, do_AddRef(promise));
|
||||||
|
|
||||||
rv = webProgress->AddProgressListener(listener,
|
rv = webProgress->AddProgressListener(listener,
|
||||||
nsIWebProgress::NOTIFY_STATE_WINDOW);
|
nsIWebProgress::NOTIFY_STATE_WINDOW);
|
||||||
|
@ -326,6 +289,22 @@ void WaitForLoad(const ClientOpenWindowArgs& aArgs,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the service worker URI
|
||||||
|
RefPtr<nsDocShellLoadState> loadState =
|
||||||
|
new nsDocShellLoadState(aArgsValidated.uri);
|
||||||
|
loadState->SetTriggeringPrincipal(aArgsValidated.principal);
|
||||||
|
loadState->SetFirstParty(true);
|
||||||
|
loadState->SetLoadFlags(
|
||||||
|
nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
|
||||||
|
|
||||||
|
rv = aBrowsingContext->LoadURI(loadState, true);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
CopyableErrorResult result;
|
||||||
|
result.ThrowInvalidStateError("Unable to start the load of the actual URI");
|
||||||
|
promise->Reject(result, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Hold the listener alive until the promise settles.
|
// Hold the listener alive until the promise settles.
|
||||||
promise->Then(
|
promise->Then(
|
||||||
GetMainThreadSerialEventTarget(), __func__,
|
GetMainThreadSerialEventTarget(), __func__,
|
||||||
|
@ -335,14 +314,15 @@ void WaitForLoad(const ClientOpenWindowArgs& aArgs,
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_ANDROID
|
#ifdef MOZ_WIDGET_ANDROID
|
||||||
|
|
||||||
void GeckoViewOpenWindow(const ClientOpenWindowArgs& aArgs,
|
void GeckoViewOpenWindow(const ClientOpenWindowArgsParsed& aArgsValidated,
|
||||||
ClientOpPromise::Private* aPromise) {
|
ClientOpPromise::Private* aPromise) {
|
||||||
RefPtr<ClientOpPromise::Private> promise = aPromise;
|
RefPtr<ClientOpPromise::Private> promise = aPromise;
|
||||||
|
|
||||||
// passes the request to open a new window to GeckoView. Allowing the
|
// passes the request to open a new window to GeckoView. Allowing the
|
||||||
// application to decide how to hand the open window request.
|
// application to decide how to hand the open window request.
|
||||||
auto genericResult =
|
nsAutoCString uri;
|
||||||
java::GeckoRuntime::ServiceWorkerOpenWindow(aArgs.baseURL(), aArgs.url());
|
MOZ_ALWAYS_SUCCEEDS(aArgsValidated.uri->GetSpec(uri));
|
||||||
|
auto genericResult = java::GeckoRuntime::ServiceWorkerOpenWindow(uri);
|
||||||
auto typedResult = java::GeckoResult::LocalRef(std::move(genericResult));
|
auto typedResult = java::GeckoResult::LocalRef(std::move(genericResult));
|
||||||
|
|
||||||
// MozPromise containing the ID for the handling GeckoSession
|
// MozPromise containing the ID for the handling GeckoSession
|
||||||
|
@ -352,7 +332,7 @@ void GeckoViewOpenWindow(const ClientOpenWindowArgs& aArgs,
|
||||||
|
|
||||||
promiseResult->Then(
|
promiseResult->Then(
|
||||||
GetMainThreadSerialEventTarget(), __func__,
|
GetMainThreadSerialEventTarget(), __func__,
|
||||||
[aArgs, promise](nsString sessionId) {
|
[aArgsValidated, promise](nsString sessionId) {
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsCOMPtr<nsIWindowWatcher> wwatch =
|
nsCOMPtr<nsIWindowWatcher> wwatch =
|
||||||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
||||||
|
@ -372,7 +352,7 @@ void GeckoViewOpenWindow(const ClientOpenWindowArgs& aArgs,
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitForLoad(aArgs, browsingContext, promise);
|
WaitForLoad(aArgsValidated, browsingContext, promise);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
},
|
},
|
||||||
[promise](nsString aResult) {
|
[promise](nsString aResult) {
|
||||||
|
@ -390,23 +370,87 @@ RefPtr<ClientOpPromise> ClientOpenWindow(const ClientOpenWindowArgs& aArgs) {
|
||||||
RefPtr<ClientOpPromise::Private> promise =
|
RefPtr<ClientOpPromise::Private> promise =
|
||||||
new ClientOpPromise::Private(__func__);
|
new ClientOpPromise::Private(__func__);
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_ANDROID
|
// [[1. Let url be the result of parsing url with entry settings object's API
|
||||||
// If we are on Android we are GeckoView.
|
// base URL.]]
|
||||||
GeckoViewOpenWindow(aArgs, promise);
|
nsCOMPtr<nsIURI> baseURI;
|
||||||
return promise.forget();
|
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
|
||||||
#endif // MOZ_WIDGET_ANDROID
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
nsPrintfCString err("Invalid base URL \"%s\"", aArgs.baseURL().get());
|
||||||
RefPtr<BrowsingContext> bc;
|
CopyableErrorResult errResult;
|
||||||
ErrorResult rv;
|
errResult.ThrowTypeError(err);
|
||||||
OpenWindow(aArgs, getter_AddRefs(bc), rv);
|
promise->Reject(errResult, __func__);
|
||||||
if (NS_WARN_IF(rv.Failed())) {
|
|
||||||
promise->Reject(rv, __func__);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_DIAGNOSTIC_ASSERT(bc);
|
nsCOMPtr<nsIURI> uri;
|
||||||
WaitForLoad(aArgs, bc, promise);
|
rv = NS_NewURI(getter_AddRefs(uri), aArgs.url(), nullptr, baseURI);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
nsPrintfCString err("Invalid URL \"%s\"", aArgs.url().get());
|
||||||
|
CopyableErrorResult errResult;
|
||||||
|
errResult.ThrowTypeError(err);
|
||||||
|
promise->Reject(errResult, __func__);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto principalOrErr = PrincipalInfoToPrincipal(aArgs.principalInfo());
|
||||||
|
if (NS_WARN_IF(principalOrErr.isErr())) {
|
||||||
|
CopyableErrorResult errResult;
|
||||||
|
errResult.ThrowTypeError("Failed to obtain principal");
|
||||||
|
promise->Reject(errResult, __func__);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(principal);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||||
|
if (aArgs.cspInfo().isSome()) {
|
||||||
|
csp = CSPInfoToCSP(aArgs.cspInfo().ref(), nullptr);
|
||||||
|
}
|
||||||
|
ClientOpenWindowArgsParsed argsValidated;
|
||||||
|
argsValidated.uri = uri;
|
||||||
|
argsValidated.baseURI = baseURI;
|
||||||
|
argsValidated.principal = principal;
|
||||||
|
argsValidated.csp = csp;
|
||||||
|
|
||||||
|
#ifdef MOZ_WIDGET_ANDROID
|
||||||
|
// If we are on Android we are GeckoView.
|
||||||
|
GeckoViewOpenWindow(argsValidated, promise);
|
||||||
|
return promise.forget();
|
||||||
|
#endif // MOZ_WIDGET_ANDROID
|
||||||
|
|
||||||
|
RefPtr<BrowsingContextCallbackReceivedPromise::Private>
|
||||||
|
browsingContextReadyPromise =
|
||||||
|
new BrowsingContextCallbackReceivedPromise::Private(__func__);
|
||||||
|
RefPtr<nsIBrowsingContextReadyCallback> callback =
|
||||||
|
new nsBrowsingContextReadyCallback(browsingContextReadyPromise);
|
||||||
|
|
||||||
|
RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
|
||||||
|
openInfo->mBrowsingContextReadyCallback = callback;
|
||||||
|
openInfo->mOriginAttributes = principal->OriginAttributesRef();
|
||||||
|
openInfo->mIsRemote = true;
|
||||||
|
|
||||||
|
RefPtr<BrowsingContext> bc;
|
||||||
|
ErrorResult errResult;
|
||||||
|
OpenWindow(argsValidated, openInfo, getter_AddRefs(bc), errResult);
|
||||||
|
if (NS_WARN_IF(errResult.Failed())) {
|
||||||
|
promise->Reject(errResult, __func__);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
browsingContextReadyPromise->Then(
|
||||||
|
GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
|
[argsValidated, promise](const RefPtr<BrowsingContext>& aBC) {
|
||||||
|
WaitForLoad(argsValidated, aBC, promise);
|
||||||
|
},
|
||||||
|
[promise]() {
|
||||||
|
// in case of failure, reject the original promise
|
||||||
|
CopyableErrorResult result;
|
||||||
|
result.ThrowTypeError("Unable to open window");
|
||||||
|
promise->Reject(result, __func__);
|
||||||
|
});
|
||||||
|
if (bc) {
|
||||||
|
browsingContextReadyPromise->Resolve(bc, __func__);
|
||||||
|
}
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
typedef MozPromise<RefPtr<BrowsingContext>, CopyableErrorResult, false>
|
||||||
|
BrowsingContextCallbackReceivedPromise;
|
||||||
|
|
||||||
MOZ_MUST_USE RefPtr<ClientOpPromise> ClientOpenWindow(
|
MOZ_MUST_USE RefPtr<ClientOpPromise> ClientOpenWindow(
|
||||||
const ClientOpenWindowArgs& aArgs);
|
const ClientOpenWindowArgs& aArgs);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ EXPORTS.mozilla.dom += [
|
||||||
'ClientManager.h',
|
'ClientManager.h',
|
||||||
'ClientManagerActors.h',
|
'ClientManagerActors.h',
|
||||||
'ClientManagerService.h',
|
'ClientManagerService.h',
|
||||||
|
'ClientOpenWindowUtils.h',
|
||||||
'ClientOpPromise.h',
|
'ClientOpPromise.h',
|
||||||
'ClientSource.h',
|
'ClientSource.h',
|
||||||
'ClientState.h',
|
'ClientState.h',
|
||||||
|
|
|
@ -48,6 +48,13 @@ add_task(async function test() {
|
||||||
SpecialPowers.Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW,
|
SpecialPowers.Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW,
|
||||||
SpecialPowers.Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
|
SpecialPowers.Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
|
||||||
]) {
|
]) {
|
||||||
|
if (prefValue == SpecialPowers.Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW) {
|
||||||
|
// Let's open a new tab and focus on it. When the service
|
||||||
|
// worker notification is shown, the document will open in the focused tab.
|
||||||
|
// If we don't open a new tab, the document will be opened in the
|
||||||
|
// current test-runner tab and mess up the test setup.
|
||||||
|
window.open("");
|
||||||
|
}
|
||||||
info(`Setting browser.link.open_newwindow to ${prefValue}.`);
|
info(`Setting browser.link.open_newwindow to ${prefValue}.`);
|
||||||
await SpecialPowers.pushPrefEnv({
|
await SpecialPowers.pushPrefEnv({
|
||||||
set: [["browser.link.open_newwindow", prefValue]],
|
set: [["browser.link.open_newwindow", prefValue]],
|
||||||
|
@ -72,6 +79,8 @@ add_task(async function test() {
|
||||||
|
|
||||||
// If we make it here, then we didn't crash.
|
// If we make it here, then we didn't crash.
|
||||||
ok(true, "Didn't crash!");
|
ok(true, "Didn't crash!");
|
||||||
|
|
||||||
|
navigator.serviceWorker.onmessage = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ import org.yaml.snakeyaml.error.YAMLException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.net.URI;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -195,26 +194,24 @@ public final class GeckoRuntime implements Parcelable {
|
||||||
/**
|
/**
|
||||||
* Called by mozilla::dom::ClientOpenWindow to retrieve the window id to use
|
* Called by mozilla::dom::ClientOpenWindow to retrieve the window id to use
|
||||||
* for a ServiceWorkerClients.openWindow() request.
|
* for a ServiceWorkerClients.openWindow() request.
|
||||||
* @param baseUrl The base Url for the request.
|
* @param url validated Url being requested to be opened in a new window.
|
||||||
* @param url Url being requested to be opened in a new window.
|
|
||||||
* @return SessionID to use for the request.
|
* @return SessionID to use for the request.
|
||||||
*/
|
*/
|
||||||
@WrapForJNI(calledFrom = "gecko")
|
@WrapForJNI(calledFrom = "gecko")
|
||||||
private static @NonNull GeckoResult<String> serviceWorkerOpenWindow(final @NonNull String baseUrl, final @NonNull String url) {
|
private static @NonNull GeckoResult<String> serviceWorkerOpenWindow(final @NonNull String url) {
|
||||||
if (sRuntime != null && sRuntime.mServiceWorkerDelegate != null) {
|
if (sRuntime != null && sRuntime.mServiceWorkerDelegate != null) {
|
||||||
final URI actual = URI.create(baseUrl).resolve(url);
|
|
||||||
GeckoResult<String> result = new GeckoResult<>();
|
GeckoResult<String> result = new GeckoResult<>();
|
||||||
// perform the onOpenWindow call in the UI thread
|
// perform the onOpenWindow call in the UI thread
|
||||||
ThreadUtils.runOnUiThread(() -> {
|
ThreadUtils.runOnUiThread(() -> {
|
||||||
sRuntime
|
sRuntime
|
||||||
.mServiceWorkerDelegate
|
.mServiceWorkerDelegate
|
||||||
.onOpenWindow(actual.toString())
|
.onOpenWindow(url)
|
||||||
.accept( session -> {
|
.accept( session -> {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
if (!session.isOpen()) {
|
if (!session.isOpen()) {
|
||||||
result.completeExceptionally(new RuntimeException("Returned GeckoSession must be open."));
|
result.completeExceptionally(new RuntimeException("Returned GeckoSession must be open."));
|
||||||
} else {
|
} else {
|
||||||
session.loadUri(actual.toString());
|
session.loadUri(url);
|
||||||
result.complete(session.getId());
|
result.complete(session.getId());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,6 +19,17 @@ class BrowserParent;
|
||||||
[ref] native const_OriginAttributes(const mozilla::OriginAttributes);
|
[ref] native const_OriginAttributes(const mozilla::OriginAttributes);
|
||||||
[ptr] native BrowserParent(mozilla::dom::BrowserParent);
|
[ptr] native BrowserParent(mozilla::dom::BrowserParent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nsIBrowsingContextReadyCallback.browsingContextReady() is called within
|
||||||
|
* nsFrameLoader to indicate that the browsing context for a newly opened
|
||||||
|
* window/tab is ready.
|
||||||
|
*/
|
||||||
|
[uuid(0524ee06-7f4c-4cd3-ab80-084562745cad)]
|
||||||
|
interface nsIBrowsingContextReadyCallback : nsISupports
|
||||||
|
{
|
||||||
|
void browsingContextReady(in BrowsingContext bc);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nsIOpenWindowInfo is a helper type which contains details used when opening
|
* nsIOpenWindowInfo is a helper type which contains details used when opening
|
||||||
* new content windows. This object is used to correctly create new initial
|
* new content windows. This object is used to correctly create new initial
|
||||||
|
@ -48,4 +59,8 @@ interface nsIOpenWindowInfo : nsISupports {
|
||||||
|
|
||||||
[notxpcom, nostdcall, binaryname(GetOriginAttributes)]
|
[notxpcom, nostdcall, binaryname(GetOriginAttributes)]
|
||||||
const_OriginAttributes binaryGetOriginAttributes();
|
const_OriginAttributes binaryGetOriginAttributes();
|
||||||
|
|
||||||
|
/* Callback to invoke when the browsing context for a new window is ready. */
|
||||||
|
[notxpcom, nostdcall]
|
||||||
|
nsIBrowsingContextReadyCallback browsingContextReadyCallback();
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,3 +41,33 @@ const OriginAttributes& nsOpenWindowInfo::GetOriginAttributes() {
|
||||||
BrowserParent* nsOpenWindowInfo::GetNextRemoteBrowser() {
|
BrowserParent* nsOpenWindowInfo::GetNextRemoteBrowser() {
|
||||||
return mNextRemoteBrowser;
|
return mNextRemoteBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsIBrowsingContextReadyCallback*
|
||||||
|
nsOpenWindowInfo::BrowsingContextReadyCallback() {
|
||||||
|
return mBrowsingContextReadyCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(nsBrowsingContextReadyCallback,
|
||||||
|
nsIBrowsingContextReadyCallback)
|
||||||
|
|
||||||
|
nsBrowsingContextReadyCallback::nsBrowsingContextReadyCallback(
|
||||||
|
RefPtr<BrowsingContextCallbackReceivedPromise::Private> aPromise)
|
||||||
|
: mPromise(std::move(aPromise)) {}
|
||||||
|
|
||||||
|
nsBrowsingContextReadyCallback::~nsBrowsingContextReadyCallback() {
|
||||||
|
if (mPromise) {
|
||||||
|
mPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
mPromise = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP nsBrowsingContextReadyCallback::BrowsingContextReady(
|
||||||
|
BrowsingContext* aBC) {
|
||||||
|
if (aBC) {
|
||||||
|
mPromise->Resolve(aBC, __func__);
|
||||||
|
} else {
|
||||||
|
mPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
mPromise = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "nsISupportsImpl.h"
|
#include "nsISupportsImpl.h"
|
||||||
#include "mozilla/OriginAttributes.h"
|
#include "mozilla/OriginAttributes.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
|
#include "mozilla/dom/ClientOpenWindowUtils.h"
|
||||||
|
|
||||||
class nsOpenWindowInfo : public nsIOpenWindowInfo {
|
class nsOpenWindowInfo : public nsIOpenWindowInfo {
|
||||||
public:
|
public:
|
||||||
|
@ -19,12 +20,29 @@ class nsOpenWindowInfo : public nsIOpenWindowInfo {
|
||||||
|
|
||||||
bool mForceNoOpener = false;
|
bool mForceNoOpener = false;
|
||||||
bool mIsRemote = false;
|
bool mIsRemote = false;
|
||||||
RefPtr<BrowserParent> mNextRemoteBrowser;
|
RefPtr<mozilla::dom::BrowserParent> mNextRemoteBrowser;
|
||||||
OriginAttributes mOriginAttributes;
|
mozilla::OriginAttributes mOriginAttributes;
|
||||||
RefPtr<BrowsingContext> mParent;
|
RefPtr<mozilla::dom::BrowsingContext> mParent;
|
||||||
|
RefPtr<nsIBrowsingContextReadyCallback> mBrowsingContextReadyCallback;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~nsOpenWindowInfo() = default;
|
virtual ~nsOpenWindowInfo() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class nsBrowsingContextReadyCallback : public nsIBrowsingContextReadyCallback {
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIBROWSINGCONTEXTREADYCALLBACK
|
||||||
|
|
||||||
|
explicit nsBrowsingContextReadyCallback(
|
||||||
|
RefPtr<mozilla::dom::BrowsingContextCallbackReceivedPromise::Private>
|
||||||
|
aPromise);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~nsBrowsingContextReadyCallback();
|
||||||
|
|
||||||
|
RefPtr<mozilla::dom::BrowsingContextCallbackReceivedPromise::Private>
|
||||||
|
mPromise;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // nsOpenWindowInfo_h
|
#endif // nsOpenWindowInfo_h
|
||||||
|
|
Загрузка…
Ссылка в новой задаче