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:
Anny Gakhokidze 2020-05-27 18:15:36 +00:00
Родитель 34915064c5
Коммит 28243d5736
12 изменённых файлов: 233 добавлений и 100 удалений

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

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