This is an automated email from the git hooks/post-receive script.
richard pushed a commit to branch geckoview-102.2.0esr-12.0-1 in repository tor-browser.
commit 1d2cf87a9a70d10a85bc53385a8ee39356344062 Author: Kathy Brade brade@pearlcrescent.com AuthorDate: Tue Jun 28 15:13:05 2016 -0400
Bug 19273: Avoid JavaScript patching of the external app helper dialog.
When handling an external URI or downloading a file, invoke Torbutton's external app blocker component (which will present a download warning dialog unless the user has checked the "Automatically download files from now on" box).
For e10s compatibility, avoid using a modal dialog and instead use a callback interface (nsIHelperAppWarningLauncher) to allow Torbutton to indicate the user's desire to cancel or continue each request.
Other bugs fixed: Bug 21766: Crash with e10s enabled while trying to download a file Bug 21886: Download is stalled in non-e10s mode Bug 22471: Downloading files via the PDF viewer download button is broken Bug 22472: Fix FTP downloads when external helper app dialog is shown Bug 22610: Avoid crashes when canceling external helper app downloads Bug 22618: Downloading pdf file via file:/// is stalling --- .../exthandler/nsExternalHelperAppService.cpp | 206 +++++++++++++++++---- uriloader/exthandler/nsExternalHelperAppService.h | 3 + .../exthandler/nsIExternalHelperAppService.idl | 47 +++++ 3 files changed, 224 insertions(+), 32 deletions(-)
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 41e1272f0c604..1cd45b46a427a 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -146,6 +146,9 @@ static const char NEVER_ASK_FOR_OPEN_FILE_PREF[] =
StaticRefPtr<nsIFile> sFallbackDownloadDir;
+static const char WARNING_DIALOG_CONTRACT_ID[] = + "@torproject.org/torbutton-extAppBlocker;1"; + // Helper functions for Content-Disposition headers
/** @@ -362,6 +365,22 @@ static nsresult GetDownloadDirectory(nsIFile** _directory, return NS_OK; }
+static already_AddRefed<nsIInterfaceRequestor> GetDialogParentAux( + BrowsingContext* aBrowsingContext, nsIInterfaceRequestor* aWindowContext) { + nsCOMPtr<nsIInterfaceRequestor> dialogParent = aWindowContext; + + if (!dialogParent && aBrowsingContext) { + dialogParent = do_QueryInterface(aBrowsingContext->GetDOMWindow()); + } + if (!dialogParent && aBrowsingContext && XRE_IsParentProcess()) { + RefPtr<Element> element = aBrowsingContext->Top()->GetEmbedderElement(); + if (element) { + dialogParent = do_QueryInterface(element->OwnerDoc()->GetWindow()); + } + } + return dialogParent.forget(); +} + /** * Helper for random bytes for the filename of downloaded part files. */ @@ -593,6 +612,96 @@ static const char* descriptionOverwriteExtensions[] = { "avif", "jxl", "pdf", "svg", "webp", "xml", };
+////////////////////////////////////////////////////////////////////////////////////////////////////// +// begin nsExternalLoadURIHandler class definition and implementation +////////////////////////////////////////////////////////////////////////////////////////////////////// +class nsExternalLoadURIHandler final : public nsIHelperAppWarningLauncher { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIHELPERAPPWARNINGLAUNCHER + + nsExternalLoadURIHandler(nsIHandlerInfo* aHandlerInfo, nsIURI* aURI, + nsIPrincipal* aTriggeringPrincipal, + BrowsingContext* aBrowsingContext, + bool aTriggeredExternally); + + protected: + ~nsExternalLoadURIHandler(); + + nsCOMPtr<nsIHandlerInfo> mHandlerInfo; + nsCOMPtr<nsIURI> mURI; + nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; + RefPtr<BrowsingContext> mBrowsingContext; + bool mTriggeredExternally; + nsCOMPtr<nsIHelperAppWarningDialog> mWarningDialog; +}; + +NS_IMPL_ADDREF(nsExternalLoadURIHandler) +NS_IMPL_RELEASE(nsExternalLoadURIHandler) + +NS_INTERFACE_MAP_BEGIN(nsExternalLoadURIHandler) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHelperAppWarningLauncher) + NS_INTERFACE_MAP_ENTRY(nsIHelperAppWarningLauncher) +NS_INTERFACE_MAP_END + +nsExternalLoadURIHandler::nsExternalLoadURIHandler( + nsIHandlerInfo* aHandlerInfo, nsIURI* aURI, + nsIPrincipal* aTriggeringPrincipal, BrowsingContext* aBrowsingContext, + bool aTriggeredExternally) + : mHandlerInfo(aHandlerInfo), + mURI(aURI), + mTriggeringPrincipal(aTriggeringPrincipal), + mBrowsingContext(aBrowsingContext), + mTriggeredExternally(aTriggeredExternally) + +{ + nsresult rv = NS_OK; + mWarningDialog = do_CreateInstance(WARNING_DIALOG_CONTRACT_ID, &rv); + if (NS_SUCCEEDED(rv) && mWarningDialog) { + // This will create a reference cycle (the dialog holds a reference to us + // as nsIHelperAppWarningLauncher), which will be broken in ContinueRequest + // or CancelRequest. + nsCOMPtr<nsIInterfaceRequestor> dialogParent = + GetDialogParentAux(aBrowsingContext, nullptr); + rv = mWarningDialog->MaybeShow(this, dialogParent); + } + + if (NS_FAILED(rv)) { + // If for some reason we could not open the download warning prompt, + // continue with the request. + ContinueRequest(); + } +} + +nsExternalLoadURIHandler::~nsExternalLoadURIHandler() {} + +NS_IMETHODIMP nsExternalLoadURIHandler::ContinueRequest() { + MOZ_ASSERT(mURI); + MOZ_ASSERT(mHandlerInfo); + + // Break our reference cycle with the download warning dialog (set up in + // LoadURI). + mWarningDialog = nullptr; + + nsresult rv = NS_OK; + nsCOMPtr<nsIContentDispatchChooser> chooser = + do_CreateInstance("@mozilla.org/content-dispatch-chooser;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return chooser->HandleURI(mHandlerInfo, mURI, mTriggeringPrincipal, + mBrowsingContext, mTriggeredExternally); +} + +NS_IMETHODIMP nsExternalLoadURIHandler::CancelRequest(nsresult aReason) { + NS_ENSURE_ARG(NS_FAILED(aReason)); + + // Break our reference cycle with the download warning dialog (set up in + // LoadURI). + mWarningDialog = nullptr; + + return NS_OK; +} + static StaticRefPtr<nsExternalHelperAppService> sExtHelperAppSvcSingleton;
/** @@ -615,6 +724,9 @@ nsExternalHelperAppService::GetSingleton() { return do_AddRef(sExtHelperAppSvcSingleton); }
+////////////////////////////////////////////////////////////////////////////////////////////////////// +// nsExternalHelperAppService definition and implementation +////////////////////////////////////////////////////////////////////////////////////////////////////// NS_IMPL_ISUPPORTS(nsExternalHelperAppService, nsIExternalHelperAppService, nsPIExternalAppLauncher, nsIExternalProtocolService, nsIMIMEService, nsIObserver, nsISupportsWeakReference) @@ -1107,14 +1219,14 @@ nsExternalHelperAppService::LoadURI(nsIURI* aURI, rv = GetProtocolHandlerInfo(scheme, getter_AddRefs(handler)); NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIContentDispatchChooser> chooser = - do_CreateInstance("@mozilla.org/content-dispatch-chooser;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); + RefPtr<nsExternalLoadURIHandler> h = new nsExternalLoadURIHandler( + handler, escapedURI, aTriggeringPrincipal, aBrowsingContext, + aTriggeredExternally); + if (!h) { + return NS_ERROR_OUT_OF_MEMORY; + }
- return chooser->HandleURI( - handler, escapedURI, - aRedirectPrincipal ? aRedirectPrincipal : aTriggeringPrincipal, - aBrowsingContext, aTriggeredExternally); + return NS_OK; }
////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1259,6 +1371,7 @@ NS_INTERFACE_MAP_BEGIN(nsExternalAppHandler) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) NS_INTERFACE_MAP_ENTRY(nsIHelperAppLauncher) + NS_INTERFACE_MAP_ENTRY(nsIHelperAppWarningLauncher) NS_INTERFACE_MAP_ENTRY(nsICancelable) NS_INTERFACE_MAP_ENTRY(nsIBackgroundFileSaverObserver) NS_INTERFACE_MAP_ENTRY(nsINamed) @@ -1513,18 +1626,7 @@ void nsExternalAppHandler::MaybeApplyDecodingForExtension(
already_AddRefed<nsIInterfaceRequestor> nsExternalAppHandler::GetDialogParent() { - nsCOMPtr<nsIInterfaceRequestor> dialogParent = mWindowContext; - - if (!dialogParent && mBrowsingContext) { - dialogParent = do_QueryInterface(mBrowsingContext->GetDOMWindow()); - } - if (!dialogParent && mBrowsingContext && XRE_IsParentProcess()) { - RefPtr<Element> element = mBrowsingContext->Top()->GetEmbedderElement(); - if (element) { - dialogParent = do_QueryInterface(element->OwnerDoc()->GetWindow()); - } - } - return dialogParent.forget(); + return GetDialogParentAux(mBrowsingContext, mWindowContext); }
NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { @@ -1672,6 +1774,34 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { loadInfo->SetForceAllowDataURI(true); }
+ mWarningDialog = do_CreateInstance(WARNING_DIALOG_CONTRACT_ID, &rv); + if (NS_SUCCEEDED(rv) && mWarningDialog) { + // This will create a reference cycle (the dialog holds a reference to us + // as nsIHelperAppWarningLauncher), which will be broken in ContinueRequest + // or CancelRequest. + nsCOMPtr<nsIInterfaceRequestor> dialogParent = GetDialogParent(); + rv = mWarningDialog->MaybeShow(this, dialogParent); + } + + if (NS_FAILED(rv)) { + // If for some reason we could not open the download warning prompt, + // continue with the request. + ContinueRequest(); + } + + return NS_OK; +} + +NS_IMETHODIMP nsExternalAppHandler::ContinueRequest() { + nsAutoCString MIMEType; + if (mMimeInfo) { + mMimeInfo->GetMIMEType(MIMEType); + } + + // Break our reference cycle with the download warning dialog (set up in + // OnStartRequest). + mWarningDialog = nullptr; + // now that the temp file is set up, find out if we need to invoke a dialog // asking the user what they want us to do with this content...
@@ -1833,20 +1963,24 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { } #endif
- nsAutoCString actionTelem; - if (alwaysAsk) { - actionTelem.AssignLiteral("ask"); - } else if (shouldAutomaticallyHandleInternally) { - actionTelem.AssignLiteral("internal"); - } else if (action == nsIMIMEInfo::useHelperApp || - action == nsIMIMEInfo::useSystemDefault) { - actionTelem.AssignLiteral("external"); - } else { - actionTelem.AssignLiteral("save"); - } + nsCOMPtr<nsIChannel> aChannel; + if (mRequest && (aChannel = do_QueryInterface(mRequest))) { + nsAutoCString actionTelem; + if (alwaysAsk) { + actionTelem.AssignLiteral("ask"); + } else if (shouldAutomaticallyHandleInternally) { + actionTelem.AssignLiteral("internal"); + } else if (action == nsIMIMEInfo::useHelperApp || + action == nsIMIMEInfo::useSystemDefault) { + actionTelem.AssignLiteral("external"); + } else { + actionTelem.AssignLiteral("save"); + }
- RecordDownloadTelemetry(aChannel, actionTelem.get()); + RecordDownloadTelemetry(aChannel, actionTelem.get()); + }
+ nsresult rv = NS_OK; if (alwaysAsk) { // Display the dialog mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv); @@ -1972,6 +2106,14 @@ bool nsExternalAppHandler::IsDownloadSpam(nsIChannel* aChannel) { return false; }
+NS_IMETHODIMP nsExternalAppHandler::CancelRequest(nsresult aReason) { + // Break our reference cycle with the download warning dialog (set up in + // OnStartRequest). + mWarningDialog = nullptr; + + return Cancel(aReason); +} + // Convert error info into proper message text and send OnStatusChange // notification to the dialog progress listener or nsITransfer implementation. void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, @@ -2733,7 +2875,7 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) { }
// Break our reference cycle with the helper app dialog (set up in - // OnStartRequest) + // ContinueRequest) mDialog = nullptr;
mRequest = nullptr; diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h index ce142a4f5b6b0..7677bc53ec875 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -256,6 +256,7 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService, */ class nsExternalAppHandler final : public nsIStreamListener, public nsIHelperAppLauncher, + public nsIHelperAppWarningLauncher, public nsIBackgroundFileSaverObserver, public nsINamed { public: @@ -263,6 +264,7 @@ class nsExternalAppHandler final : public nsIStreamListener, NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSIHELPERAPPLAUNCHER + NS_DECL_NSIHELPERAPPWARNINGLAUNCHER NS_DECL_NSICANCELABLE NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER NS_DECL_NSINAMED @@ -533,6 +535,7 @@ class nsExternalAppHandler final : public nsIStreamListener, nsCOMPtr<nsITransfer> mTransfer;
nsCOMPtr<nsIHelperAppLauncherDialog> mDialog; + nsCOMPtr<nsIHelperAppWarningDialog> mWarningDialog;
/**
diff --git a/uriloader/exthandler/nsIExternalHelperAppService.idl b/uriloader/exthandler/nsIExternalHelperAppService.idl index 3554c69aaced1..fdcfc0be194d4 100644 --- a/uriloader/exthandler/nsIExternalHelperAppService.idl +++ b/uriloader/exthandler/nsIExternalHelperAppService.idl @@ -186,3 +186,50 @@ interface nsIHelperAppLauncher : nsICancelable */ readonly attribute uint64_t browsingContextId; }; + +/** + * nsIHelperAppWarningLauncher is implemented by two classes: + * nsExternalLoadURIHandler + * nsExternalAppHandler + */ +[scriptable, uuid(cffd508b-4aaf-43ad-99c6-671d35cbc558)] +interface nsIHelperAppWarningLauncher : nsISupports +{ + /** + * Callback invoked by the external app warning dialog to continue the + * request. + * NOTE: This will release the reference to the nsIHelperAppWarningDialog. + */ + void continueRequest(); + + /** + * Callback invoked by the external app warning dialog to cancel the request. + * NOTE: This will release the reference to the nsIHelperAppWarningDialog. + * + * @param aReason + * Pass a failure code to indicate the reason why this operation is + * being canceled. It is an error to pass a success code. + */ + void cancelRequest(in nsresult aReason); +}; + +/** + * nsIHelperAppWarningDialog is implemented by Torbutton's external app + * blocker (src/components/external-app-blocker.js). + */ +[scriptable, uuid(f4899a3f-0df3-42cc-9db8-bdf599e5a208)] +interface nsIHelperAppWarningDialog : nsISupports +{ + /** + * Possibly show a launch warning dialog (it will not be shown if the user + * has chosen to not see the warning again). + * + * @param aLauncher + * A nsIHelperAppWarningLauncher to be invoked after the user confirms + * or cancels the download. + * @param aWindowContext + * The window associated with the download. + */ + void maybeShow(in nsIHelperAppWarningLauncher aLauncher, + in nsISupports aWindowContext); +};