diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 62738afe629f..67d78caa6317 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -12765,7 +12765,7 @@ nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent, extProtService->IsExposedProtocol(scheme.get(), &isExposed); if (NS_SUCCEEDED(rv) && !isExposed) { return extProtService->LoadURI(aLoadState->URI(), triggeringPrincipal, - mBrowsingContext, + nullptr, mBrowsingContext, /* aTriggeredExternally */ false); } } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 1f5277b29bf5..cb8c3e47c1af 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4448,6 +4448,7 @@ mozilla::ipc::IPCResult ContentParent::RecvAccumulateMixedContentHSTS( mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal( nsIURI* uri, nsIPrincipal* aTriggeringPrincipal, + nsIPrincipal* aRedirectPrincipal, const MaybeDiscarded& aContext, bool aWasExternallyTriggered) { if (aContext.IsDiscarded()) { @@ -4465,7 +4466,7 @@ mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal( } BrowsingContext* bc = aContext.get(); - extProtService->LoadURI(uri, aTriggeringPrincipal, bc, + extProtService->LoadURI(uri, aTriggeringPrincipal, aRedirectPrincipal, bc, aWasExternallyTriggered); return IPC_OK(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 44440d57451f..954fec4538a5 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1062,6 +1062,7 @@ class ContentParent final mozilla::ipc::IPCResult RecvLoadURIExternal( nsIURI* uri, nsIPrincipal* triggeringPrincipal, + nsIPrincipal* redirectPrincipal, const MaybeDiscarded& aContext, bool aWasExternallyTriggered); mozilla::ipc::IPCResult RecvExtProtocolChannelConnectParent( diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 8965e94a725a..d7717c11664b 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1076,6 +1076,7 @@ parent: async LoadURIExternal(nsIURI uri, nsIPrincipal triggeringPrincipal, + nsIPrincipal redirectPrincipal, MaybeDiscardedBrowsingContext browsingContext, bool wasExternallyTriggered); async ExtProtocolChannelConnectParent(uint64_t registrarId); diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 6aead66af011..d9d656ff9498 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -1061,13 +1061,15 @@ nsresult nsExternalHelperAppService::EscapeURI(nsIURI* aURI, nsIURI** aResult) { NS_IMETHODIMP nsExternalHelperAppService::LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal, + nsIPrincipal* aRedirectPrincipal, BrowsingContext* aBrowsingContext, bool aTriggeredExternally) { NS_ENSURE_ARG_POINTER(aURI); if (XRE_IsContentProcess()) { mozilla::dom::ContentChild::GetSingleton()->SendLoadURIExternal( - aURI, aTriggeringPrincipal, aBrowsingContext, aTriggeredExternally); + aURI, aTriggeringPrincipal, aRedirectPrincipal, aBrowsingContext, + aTriggeredExternally); return NS_OK; } @@ -1159,8 +1161,10 @@ nsExternalHelperAppService::LoadURI(nsIURI* aURI, do_CreateInstance("@mozilla.org/content-dispatch-chooser;1", &rv); NS_ENSURE_SUCCESS(rv, rv); - return chooser->HandleURI(handler, escapedURI, aTriggeringPrincipal, - aBrowsingContext, aTriggeredExternally); + return chooser->HandleURI( + handler, escapedURI, + aRedirectPrincipal ? aRedirectPrincipal : aTriggeringPrincipal, + aBrowsingContext, aTriggeredExternally); } ////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h index af6ed97f28ad..6121ab22e4d5 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -85,6 +85,7 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService, nsIHandlerInfo** aHandlerInfo) override; NS_IMETHOD LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal, + nsIPrincipal* aRedirectPrincipal, mozilla::dom::BrowsingContext* aBrowsingContext, bool aWasTriggeredExternally) override; NS_IMETHOD SetProtocolHandlerDefaults(nsIHandlerInfo* aHandlerInfo, diff --git a/uriloader/exthandler/nsExternalProtocolHandler.cpp b/uriloader/exthandler/nsExternalProtocolHandler.cpp index dbb1b2d0a43e..075be2123012 100644 --- a/uriloader/exthandler/nsExternalProtocolHandler.cpp +++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp @@ -164,9 +164,15 @@ nsresult nsExtProtocolChannel::OpenURL() { goto finish; } - RefPtr principal = mLoadInfo->TriggeringPrincipal(); - rv = extProtService->LoadURI(mUrl, principal, ctx, - mLoadInfo->GetLoadTriggeredFromExternal()); + RefPtr triggeringPrincipal = mLoadInfo->TriggeringPrincipal(); + RefPtr redirectPrincipal; + if (!mLoadInfo->RedirectChain().IsEmpty()) { + mLoadInfo->RedirectChain().LastElement()->GetPrincipal( + getter_AddRefs(redirectPrincipal)); + } + rv = + extProtService->LoadURI(mUrl, triggeringPrincipal, redirectPrincipal, + ctx, mLoadInfo->GetLoadTriggeredFromExternal()); if (NS_SUCCEEDED(rv) && mListener) { mStatus = NS_ERROR_NO_CONTENT; diff --git a/uriloader/exthandler/nsIExternalProtocolService.idl b/uriloader/exthandler/nsIExternalProtocolService.idl index bfc6cec006de..dd91ad7c3528 100644 --- a/uriloader/exthandler/nsIExternalProtocolService.idl +++ b/uriloader/exthandler/nsIExternalProtocolService.idl @@ -101,6 +101,11 @@ interface nsIExternalProtocolService : nsISupports * @param aTriggeringPrincipal * The principal triggering this load. * + * @param aRedirectPrincipal + * The last post-redirect principal triggering this load. + * Used for display and permission purposes. If null, we'll + * use the triggering principal. + * * @param aBrowsingContext * The context to parent the dialog against, and, if a web handler * is chosen, it is loaded in this window as well. This parameter @@ -118,6 +123,7 @@ interface nsIExternalProtocolService : nsISupports */ void loadURI(in nsIURI aURI, [optional] in nsIPrincipal aTriggeringPrincipal, + [optional] in nsIPrincipal aRedirectPrincipal, [optional] in BrowsingContext aBrowsingContext, [optional] in bool aWasTriggeredExternally); diff --git a/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog_external.js b/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog_external.js index f2e714f682b1..02f619bfe2c5 100644 --- a/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog_external.js +++ b/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog_external.js @@ -9,7 +9,7 @@ let gHandlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService( const TEST_PATH = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", - "http://example.com" + "https://example.com" ); /** @@ -176,15 +176,23 @@ add_task(async function test_web_app_doesnt_ask() { }); add_task(async function external_https_redirect_doesnt_ask() { + Services.perms.addFromPrincipal( + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://example.com" + ), + "open-protocol-handler^local-app-test", + Services.perms.ALLOW_ACTION + ); // Listen for a dialog open and fail the test if it does: let dialogOpenListener = () => ok(false, "Shouldn't have opened a dialog!"); document.documentElement.addEventListener("dialogopen", dialogOpenListener); - registerCleanupFunction(() => + registerCleanupFunction(() => { document.documentElement.removeEventListener( "dialogopen", dialogOpenListener - ) - ); + ); + Services.perms.removeAll(); + }); let initialTab = gBrowser.selectedTab;