diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 54a0f91533d6..f069e2958cf7 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -9709,6 +9709,8 @@ nsIPrincipal* nsDocShell::GetInheritedPrincipal( INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI)); aLoadInfo->SetOriginalFrameSrcLoad( aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC)); + aLoadInfo->SetIsNewWindowTarget( + aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)); bool inheritAttrs = false; if (aLoadState->PrincipalToInherit()) { @@ -9849,10 +9851,6 @@ nsIPrincipal* nsDocShell::GetInheritedPrincipal( // save true referrer for those who need it (e.g. xpinstall whitelisting) // Currently only http and ftp channels support this. props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer); - - if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) { - props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true); - } } nsCOMPtr cacheChannel(do_QueryInterface(channel)); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index a8dabca16abe..79200cbd9f13 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4464,8 +4464,7 @@ ContentParent::AllocPExternalHelperAppParent( const uint32_t& aContentDispositionHint, const nsAString& aContentDispositionFilename, const bool& aForceSave, const int64_t& aContentLength, const bool& aWasFileChannel, - nsIURI* aReferrer, const MaybeDiscarded& aContext, - const bool& aShouldCloseWindow) { + nsIURI* aReferrer, const MaybeDiscarded& aContext) { RefPtr parent = new ExternalHelperAppParent( uri, aContentLength, aWasFileChannel, aContentDisposition, aContentDispositionHint, aContentDispositionFilename); @@ -4479,12 +4478,10 @@ mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor( const uint32_t& aContentDispositionHint, const nsAString& aContentDispositionFilename, const bool& aForceSave, const int64_t& aContentLength, const bool& aWasFileChannel, - nsIURI* aReferrer, const MaybeDiscarded& aContext, - const bool& aShouldCloseWindow) { + nsIURI* aReferrer, const MaybeDiscarded& aContext) { BrowsingContext* context = aContext.IsDiscarded() ? nullptr : aContext.get(); if (!static_cast(actor)->Init( - loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context, - aShouldCloseWindow)) { + loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context)) { return IPC_FAIL(this, "Init failed."); } return IPC_OK(); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 3f84b3a74961..722a3b1ee0f8 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -913,8 +913,7 @@ class ContentParent final : public PContentParent, const uint32_t& aContentDispositionHint, const nsAString& aContentDispositionFilename, const bool& aForceSave, const int64_t& aContentLength, const bool& aWasFileChannel, - nsIURI* aReferrer, const MaybeDiscarded& aContext, - const bool& aShouldCloseWindow); + nsIURI* aReferrer, const MaybeDiscarded& aContext); mozilla::ipc::IPCResult RecvPExternalHelperAppConstructor( PExternalHelperAppParent* actor, nsIURI* uri, @@ -923,8 +922,8 @@ class ContentParent final : public PContentParent, const uint32_t& aContentDispositionHint, const nsAString& aContentDispositionFilename, const bool& aForceSave, const int64_t& aContentLength, const bool& aWasFileChannel, - nsIURI* aReferrer, const MaybeDiscarded& aContext, - const bool& aShouldCloseWindow) override; + nsIURI* aReferrer, + const MaybeDiscarded& aContext) override; already_AddRefed AllocPHandlerServiceParent(); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 54ef3b8e7f19..3b0d9c27111b 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1187,8 +1187,6 @@ parent: // // Pass true for aForceSave to always save this content to disk, regardless of // nsIMIMEInfo and other such influences. - // Pass true for aShouldCloseWindow to specify that aContext was opened specifically - // for this load, and should be closed once we've handled it. async PExternalHelperApp(nullable nsIURI uri, LoadInfoArgs loadInfoArgs, nsCString aMimeContentType, @@ -1199,8 +1197,7 @@ parent: int64_t aContentLength, bool aWasFileChannel, nullable nsIURI aReferrer, - MaybeDiscardedBrowsingContext aContext, - bool aShouldCloseWindow); + MaybeDiscardedBrowsingContext aContext); async PHandlerService(); diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index e5b6535b8e64..213f543e46ea 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -597,7 +597,7 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo, aLoadInfo->GetStoragePermission(), overriddenFingerprintingSettingsArg, aLoadInfo->GetIsMetaRefresh(), aLoadInfo->GetLoadingEmbedderPolicy(), aLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(), - unstrippedURI, interceptionInfoArg); + unstrippedURI, interceptionInfoArg, aLoadInfo->GetIsNewWindowTarget()); return NS_OK; } @@ -890,7 +890,8 @@ nsresult LoadInfoArgsToLoadInfo(const LoadInfoArgs& loadInfoArgs, loadInfoArgs.originTrialCoepCredentiallessEnabledForTopLevel(), loadInfoArgs.unstrippedURI(), interceptionInfo, loadInfoArgs.hasInjectedCookieForCookieBannerHandling(), - loadInfoArgs.wasSchemelessInput(), loadInfoArgs.httpsUpgradeTelemetry()); + loadInfoArgs.wasSchemelessInput(), loadInfoArgs.httpsUpgradeTelemetry(), + loadInfoArgs.isNewWindowTarget()); if (loadInfoArgs.isFromProcessingFrameAttributes()) { loadInfo->SetIsFromProcessingFrameAttributes(); diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index 21d5a5e1b419..4883e54beb32 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -693,7 +693,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) mHasInjectedCookieForCookieBannerHandling( rhs.mHasInjectedCookieForCookieBannerHandling), mWasSchemelessInput(rhs.mWasSchemelessInput), - mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry) { + mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry), + mIsNewWindowTarget(rhs.mIsNewWindowTarget) { } LoadInfo::LoadInfo( @@ -742,7 +743,8 @@ LoadInfo::LoadInfo( bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel, nsIURI* aUnstrippedURI, nsIInterceptionInfo* aInterceptionInfo, bool aHasInjectedCookieForCookieBannerHandling, bool aWasSchemelessInput, - nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry) + nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry, + bool aIsNewWindowTarget) : mLoadingPrincipal(aLoadingPrincipal), mTriggeringPrincipal(aTriggeringPrincipal), mPrincipalToInherit(aPrincipalToInherit), @@ -823,7 +825,8 @@ LoadInfo::LoadInfo( mHasInjectedCookieForCookieBannerHandling( aHasInjectedCookieForCookieBannerHandling), mWasSchemelessInput(aWasSchemelessInput), - mHttpsUpgradeTelemetry(aHttpsUpgradeTelemetry) { + mHttpsUpgradeTelemetry(aHttpsUpgradeTelemetry), + mIsNewWindowTarget(aIsNewWindowTarget) { // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT); @@ -2461,4 +2464,16 @@ LoadInfo::SetHttpsUpgradeTelemetry( return NS_OK; } +NS_IMETHODIMP +LoadInfo::GetIsNewWindowTarget(bool* aIsNewWindowTarget) { + *aIsNewWindowTarget = mIsNewWindowTarget; + return NS_OK; +} + +NS_IMETHODIMP +LoadInfo::SetIsNewWindowTarget(bool aIsNewWindowTarget) { + mIsNewWindowTarget = aIsNewWindowTarget; + return NS_OK; +} + } // namespace mozilla::net diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h index 4edf04294e0f..03bce3e27602 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -259,7 +259,8 @@ class LoadInfo final : public nsILoadInfo { bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel, nsIURI* aUnstrippedURI, nsIInterceptionInfo* aInterceptionInfo, bool aHasInjectedCookieForCookieBannerHandling, bool aWasSchemelessInput, - nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry); + nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry, + bool aIsNewWindowTarget); LoadInfo(const LoadInfo& rhs); @@ -415,6 +416,8 @@ class LoadInfo final : public nsILoadInfo { nsILoadInfo::HTTPSUpgradeTelemetryType mHttpsUpgradeTelemetry = nsILoadInfo::NOT_INITIALIZED; + + bool mIsNewWindowTarget = false; }; // This is exposed solely for testing purposes and should not be used outside of diff --git a/netwerk/base/TRRLoadInfo.cpp b/netwerk/base/TRRLoadInfo.cpp index 9dc2bb0da687..b60ca30200a6 100644 --- a/netwerk/base/TRRLoadInfo.cpp +++ b/netwerk/base/TRRLoadInfo.cpp @@ -903,5 +903,15 @@ TRRLoadInfo::SetHttpsUpgradeTelemetry( return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +TRRLoadInfo::GetIsNewWindowTarget(bool* aIsNewWindowTarget) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TRRLoadInfo::SetIsNewWindowTarget(bool aIsNewWindowTarget) { + return NS_ERROR_NOT_IMPLEMENTED; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index daccd1dc75fb..eeb2e938cd93 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -1590,4 +1590,9 @@ interface nsILoadInfo : nsISupports */ [infallible] attribute nsILoadInfo_HTTPSUpgradeTelemetryType httpsUpgradeTelemetry; + /** + * Is this the first load in a new pop-up window/tab? + */ + [infallible] attribute boolean isNewWindowTarget; + }; diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp index ac4aa2ade84f..8e13711a33cf 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp @@ -1651,13 +1651,8 @@ void DocumentLoadListener::SerializeRedirectData( } static bool IsFirstLoadInWindow(nsIChannel* aChannel) { - if (nsCOMPtr props = do_QueryInterface(aChannel)) { - bool tmp = false; - nsresult rv = - props->GetPropertyAsBool(u"docshell.newWindowTarget"_ns, &tmp); - return NS_SUCCEEDED(rv) && tmp; - } - return false; + nsCOMPtr loadInfo = aChannel->LoadInfo(); + return loadInfo->GetIsNewWindowTarget(); } // Get where the document loaded by this nsIChannel should be rendered. This diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh index c5ea59ce005f..2ebec67ffb5e 100644 --- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -196,6 +196,7 @@ struct LoadInfoArgs bool originTrialCoepCredentiallessEnabledForTopLevel; nullable nsIURI unstrippedURI; InterceptionInfoArg? interceptionInfo; + bool isNewWindowTarget; }; /** diff --git a/uriloader/exthandler/ExternalHelperAppParent.cpp b/uriloader/exthandler/ExternalHelperAppParent.cpp index 96c7444abac7..9b43e4870cc3 100644 --- a/uriloader/exthandler/ExternalHelperAppParent.cpp +++ b/uriloader/exthandler/ExternalHelperAppParent.cpp @@ -62,8 +62,7 @@ ExternalHelperAppParent::ExternalHelperAppParent( bool ExternalHelperAppParent::Init( const mozilla::net::LoadInfoArgs& aLoadInfoArgs, const nsACString& aMimeContentType, const bool& aForceSave, - nsIURI* aReferrer, BrowsingContext* aContext, - const bool& aShouldCloseWindow) { + nsIURI* aReferrer, BrowsingContext* aContext) { nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo( aLoadInfoArgs, ContentParent::Cast(Manager())->GetRemoteType(), getter_AddRefs(mLoadInfo)); @@ -95,18 +94,7 @@ bool ExternalHelperAppParent::Init( helperAppService->CreateListener(aMimeContentType, this, aContext, aForceSave, nullptr, getter_AddRefs(mListener)); - if (!mListener) { - return false; - } - - if (aShouldCloseWindow) { - RefPtr handler = do_QueryObject(mListener); - if (handler) { - handler->SetShouldCloseWindow(); - } - } - - return true; + return mListener != nullptr; } void ExternalHelperAppParent::ActorDestroy(ActorDestroyReason why) { diff --git a/uriloader/exthandler/ExternalHelperAppParent.h b/uriloader/exthandler/ExternalHelperAppParent.h index 540889e648e6..2f9096c2383a 100644 --- a/uriloader/exthandler/ExternalHelperAppParent.h +++ b/uriloader/exthandler/ExternalHelperAppParent.h @@ -87,8 +87,7 @@ class ExternalHelperAppParent const nsAString& aContentDispositionFilename); bool Init(const mozilla::net::LoadInfoArgs& aLoadInfoArgs, const nsACString& aMimeContentType, const bool& aForceSave, - nsIURI* aReferrer, BrowsingContext* aContext, - const bool& aShouldCloseWindow); + nsIURI* aReferrer, BrowsingContext* aContext); protected: virtual ~ExternalHelperAppParent(); diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 139a43a1780d..2c27ae5c6881 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -719,14 +719,6 @@ nsresult nsExternalHelperAppService::DoContentContentProcessHelper( mozilla::net::LoadInfoArgs loadInfoArgs; MOZ_ALWAYS_SUCCEEDS(LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs)); - nsCOMPtr props(do_QueryInterface(aChannel)); - // Determine whether a new window was opened specifically for this request - bool shouldCloseWindow = false; - if (props) { - props->GetPropertyAsBool(u"docshell.newWindowTarget"_ns, - &shouldCloseWindow); - } - // Now we build a protocol for forwarding our data to the parent. The // protocol will act as a listener on the child-side and create a "real" // helperAppService listener on the parent-side, via another call to @@ -735,7 +727,7 @@ nsresult nsExternalHelperAppService::DoContentContentProcessHelper( MOZ_ALWAYS_TRUE(child->SendPExternalHelperAppConstructor( childListener, uri, loadInfoArgs, nsCString(aMimeContentType), disp, contentDisposition, fileName, aForceSave, contentLength, wasFileChannel, - referrer, aContentContext, shouldCloseWindow)); + referrer, aContentContext)); NS_ADDREF(*aStreamListener = childListener); @@ -1328,7 +1320,6 @@ nsExternalAppHandler::nsExternalAppHandler( mCanceled(false), mStopRequestIssued(false), mIsFileChannel(false), - mShouldCloseWindow(false), mHandleInternally(false), mDialogShowing(false), mReason(aReason), @@ -1645,15 +1636,12 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { if (mBrowsingContext) { mMaybeCloseWindowHelper = new MaybeCloseWindowHelper(mBrowsingContext); - mMaybeCloseWindowHelper->SetShouldCloseWindow(mShouldCloseWindow); - nsCOMPtr props(do_QueryInterface(request, &rv)); + // Determine whether a new window was opened specifically for this request - if (props) { - bool tmp = false; - if (NS_SUCCEEDED( - props->GetPropertyAsBool(u"docshell.newWindowTarget"_ns, &tmp))) { - mMaybeCloseWindowHelper->SetShouldCloseWindow(tmp); - } + if (aChannel) { + nsCOMPtr loadInfo = aChannel->LoadInfo(); + mMaybeCloseWindowHelper->SetShouldCloseWindow( + loadInfo->GetIsNewWindowTarget()); } } diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h index e880b90b2df8..2dd4ff87bda3 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -315,8 +315,6 @@ class nsExternalAppHandler final : public nsIStreamListener, */ void MaybeApplyDecodingForExtension(nsIRequest* request); - void SetShouldCloseWindow() { mShouldCloseWindow = true; } - protected: bool IsDownloadSpam(nsIChannel* aChannel); @@ -382,12 +380,6 @@ class nsExternalAppHandler final : public nsIStreamListener, bool mIsFileChannel; - /** - * True if the ExternalHelperAppChild told us that we should close the window - * if we handle the content as a download. - */ - bool mShouldCloseWindow; - /** * True if the file should be handled internally. */ diff --git a/uriloader/exthandler/nsExternalProtocolHandler.cpp b/uriloader/exthandler/nsExternalProtocolHandler.cpp index 37eccac85f92..815f40cd24aa 100644 --- a/uriloader/exthandler/nsExternalProtocolHandler.cpp +++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp @@ -21,7 +21,6 @@ #include "nsNetUtil.h" #include "nsContentSecurityManager.h" #include "nsExternalHelperAppService.h" -#include "nsHashPropertyBag.h" // used to dispatch urls to default protocol handlers #include "nsCExternalHandlerService.h" @@ -36,12 +35,11 @@ class nsILoadInfo; // OpenInputStream to calls in the OS for loading the url. //////////////////////////////////////////////////////////////////////// -class nsExtProtocolChannel : public nsHashPropertyBag, - public nsIChannel, +class nsExtProtocolChannel : public nsIChannel, public nsIChildChannel, public nsIParentChannel { public: - NS_DECL_ISUPPORTS_INHERITED + NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICHANNEL NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER @@ -75,17 +73,18 @@ class nsExtProtocolChannel : public nsHashPropertyBag, nsCOMPtr mListener; }; -NS_IMPL_ADDREF_INHERITED(nsExtProtocolChannel, nsHashPropertyBag) -NS_IMPL_RELEASE_INHERITED(nsExtProtocolChannel, nsHashPropertyBag) +NS_IMPL_ADDREF(nsExtProtocolChannel) +NS_IMPL_RELEASE(nsExtProtocolChannel) NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) NS_INTERFACE_MAP_ENTRY(nsIChannel) NS_INTERFACE_MAP_ENTRY(nsIRequest) NS_INTERFACE_MAP_ENTRY(nsIChildChannel) NS_INTERFACE_MAP_ENTRY(nsIParentChannel) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) -NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) +NS_INTERFACE_MAP_END nsExtProtocolChannel::nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo) : mUrl(aURI), @@ -177,13 +176,10 @@ nsresult nsExtProtocolChannel::OpenURL() { mLoadInfo->RedirectChain().LastElement()->GetPrincipal( getter_AddRefs(redirectPrincipal)); } - bool newWindowTarget = false; - GetPropertyAsBool(u"docshell.newWindowTarget"_ns, &newWindowTarget); - rv = extProtService->LoadURI(mUrl, triggeringPrincipal, redirectPrincipal, ctx, mLoadInfo->GetLoadTriggeredFromExternal(), mLoadInfo->GetHasValidUserGestureActivation(), - newWindowTarget); + mLoadInfo->GetIsNewWindowTarget()); if (NS_SUCCEEDED(rv) && mListener) { mStatus = NS_ERROR_NO_CONTENT; diff --git a/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog.js b/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog.js index 8d77898b29fa..c9fa16f51301 100644 --- a/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog.js +++ b/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog.js @@ -528,3 +528,68 @@ add_task(async function iframe_popup_tab() { gBrowser.removeTab(tab); gBrowser.removeTab(newTab); }); + +/** + * Check that when navigating to a http channel which redirects to a external + * protocol in a noopener pop-up window, we show the dialog in the correct tab. + */ +add_task(async function redirect_popup_tab() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "https://example.com/" + ); + + // Wait for the chooser dialog to open in the background tab. It should not + // open in the foreground tab which is unrelated to the external protocol + // navigation. + let dialogWindowPromise = waitForProtocolAppChooserDialog(gBrowser, true); + + // Wait for the new tab to appear. The URI in this tab will never change from + // `about:blank` as we're going to just end up opening a dialog, so we can't + // use `waitForNewTab`, as that will wait for the tab to actually load + // something. + let newTabPromise = new Promise(resolve => { + gBrowser.tabContainer.addEventListener( + "TabOpen", + openEvent => resolve(openEvent.target), + { once: true } + ); + }); + + info("Navigating to redirect to external proto in pop-up"); + await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_PATH + "redirect_helper.sjs?uri=mailto:example@example.com"], + async function (popupUri) { + content.eval("window.open('" + popupUri + "', '_blank', 'noopener');"); + } + ); + + // Wait for the new tab to be opened. + info("Waiting for new tab to appear"); + let newTab = await newTabPromise; + + // Wait for dialog to open in one of the tabs. + info("Waiting for dialog to appear"); + let dialog = await dialogWindowPromise; + + is( + gBrowser.getTabDialogBox(newTab.linkedBrowser)._tabDialogManager._topDialog, + dialog, + "Dialog opened in the background tab" + ); + + is( + dialog._frame.contentDocument.location.href, + CONTENT_HANDLING_URL, + "Opened dialog is appChooser dialog." + ); + + // Close the dialog: + let dialogClosedPromise = waitForProtocolAppChooserDialog(gBrowser, false); + dialog.close(); + await dialogClosedPromise; + + gBrowser.removeTab(tab); + gBrowser.removeTab(newTab); +});