diff --git a/toolkit/mozapps/downloads/nsHelperAppDlg.js b/toolkit/mozapps/downloads/nsHelperAppDlg.js index 91f21e7b0e89..66010f0f77b6 100644 --- a/toolkit/mozapps/downloads/nsHelperAppDlg.js +++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js @@ -189,6 +189,10 @@ nsUnknownContentTypeDialog.prototype = { // Note - this function is called without a dialog, so it cannot access any part // of the dialog XUL as other functions on this object do. promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) { + throw new Components.Exception("Async version must be used", Components.results.NS_ERROR_NOT_AVAILABLE); + }, + + promptForSaveToFileAsync: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) { var result = null; this.mLauncher = aLauncher; @@ -226,13 +230,16 @@ nsUnknownContentTypeDialog.prototype = { bundle.GetStringFromName("badPermissions.title"), bundle.GetStringFromName("badPermissions")); + aLauncher.saveDestinationAvailable(null); return; } } // Check to make sure we have a valid directory, otherwise, prompt - if (result) - return result; + if (result) { + aLauncher.saveDestinationAvailable(result); + return; + } } } @@ -283,7 +290,8 @@ nsUnknownContentTypeDialog.prototype = { if (picker.show() == nsIFilePicker.returnCancel) { // null result means user cancelled. - return null; + aLauncher.saveDestinationAvailable(null); + return; } // Be sure to save the directory the user chose through the Save As... @@ -307,7 +315,8 @@ nsUnknownContentTypeDialog.prototype = { result = this.validateLeafName(newDir, result.leafName, null); } - return result; + aLauncher.saveDestinationAvailable(result); + return; }, /** diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index cc8d5c45cc35..dac4c4618f40 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -1994,7 +1994,17 @@ nsresult nsExternalAppHandler::CreateProgressListener() return rv; } -nsresult nsExternalAppHandler::PromptForSaveToFile(nsIFile ** aNewFile, const nsAFlatString &aDefaultFile, const nsAFlatString &aFileExtension) +nsresult nsExternalAppHandler::SaveDestinationAvailable(nsIFile * aFile) +{ + if (aFile) + ContinueSave(aFile); + else + Cancel(NS_BINDING_ABORTED); + + return NS_OK; +} + +void nsExternalAppHandler::RequestSaveDestination(const nsAFlatString &aDefaultFile, const nsAFlatString &aFileExtension) { // invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request // Convert to use file picker? No, then embeddors could not do any sort of @@ -2004,7 +2014,10 @@ nsresult nsExternalAppHandler::PromptForSaveToFile(nsIFile ** aNewFile, const ns { // Get helper app launcher dialog. mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv ); - NS_ENSURE_SUCCESS(rv, rv); + if (rv != NS_OK) { + Cancel(NS_BINDING_ABORTED); + return; + } } // we want to explicitly unescape aDefaultFile b4 passing into the dialog. we can't unescape @@ -2015,14 +2028,25 @@ nsresult nsExternalAppHandler::PromptForSaveToFile(nsIFile ** aNewFile, const ns // picker is up would cause Cancel() to be called, and the dialog would be // released, which would release this object too, which would crash. // See Bug 249143 + nsIFile* fileToUse; nsRefPtr kungFuDeathGrip(this); nsCOMPtr dlg(mDialog); - rv = mDialog->PromptForSaveToFile(this, + rv = mDialog->PromptForSaveToFile(this, mWindowContext, aDefaultFile.get(), aFileExtension.get(), - mForceSave, aNewFile); - return rv; + mForceSave, &fileToUse); + + if (rv == NS_ERROR_NOT_AVAILABLE) { + // we need to use the async version -> nsIHelperAppLauncherDialog.promptForSaveToFileAsync. + rv = mDialog->PromptForSaveToFileAsync(this, + mWindowContext, + aDefaultFile.get(), + aFileExtension.get(), + mForceSave); + } else { + SaveDestinationAvailable(rv == NS_OK ? fileToUse : nullptr); + } } nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation) @@ -2089,7 +2113,6 @@ nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation) NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool aRememberThisPreference) { - nsresult rv = NS_OK; if (mCanceled) return NS_OK; @@ -2098,11 +2121,9 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool // The helper app dialog has told us what to do. mReceivedDispositionInfo = true; - nsCOMPtr fileToUse = do_QueryInterface(aNewFileLocation); - if (!fileToUse) - { + if (!aNewFileLocation) { if (mSuggestedFileName.IsEmpty()) - rv = PromptForSaveToFile(getter_AddRefs(fileToUse), mTempLeafName, mTempFileExtension); + RequestSaveDestination(mTempLeafName, mTempFileExtension); else { nsAutoString fileExt; @@ -2112,15 +2133,20 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool if (fileExt.IsEmpty()) fileExt = mTempFileExtension; - rv = PromptForSaveToFile(getter_AddRefs(fileToUse), mSuggestedFileName, fileExt); - } - - if (NS_FAILED(rv) || !fileToUse) { - Cancel(NS_BINDING_ABORTED); - return NS_ERROR_FAILURE; + RequestSaveDestination(mSuggestedFileName, fileExt); } + } else { + ContinueSave(aNewFileLocation); } - + + return NS_OK; +} +nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation) +{ + NS_PRECONDITION(aNewFileLocation, "Must be called with a non-null file"); + + nsresult rv = NS_OK; + nsCOMPtr fileToUse = do_QueryInterface(aNewFileLocation); mFinalFileDestination = do_QueryInterface(fileToUse); // Move what we have in the final directory, but append .part diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h index cd0c4a87c55f..dce286d52222 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -342,9 +342,34 @@ protected: * what's going on... */ nsresult CreateProgressListener(); - nsresult PromptForSaveToFile(nsIFile ** aNewFile, - const nsAFlatString &aDefaultFile, - const nsAFlatString &aDefaultFileExt); + + + /* + * The following two functions are part of the split of SaveToDisk + * to make it async, and works as following: + * + * SaveToDisk -------> RequestSaveDestination + * . + * . + * v + * ContinueSave <------- SaveDestinationAvailable + */ + + /** + * This is called by SaveToDisk to decide what's the final + * file destination chosen by the user or by auto-download settings. + */ + void RequestSaveDestination(const nsAFlatString &aDefaultFile, + const nsAFlatString &aDefaultFileExt); + + /** + * When SaveToDisk is called, it possibly delegates to RequestSaveDestination + * to decide the file destination. ContinueSave must then be called when + * the final destination is finally known. + * @param aFile The file that was chosen as the final destination. + * Must not be null. + */ + nsresult ContinueSave(nsIFile* aFile); /** * After we're done prompting the user for any information, if the original diff --git a/uriloader/exthandler/nsIExternalHelperAppService.idl b/uriloader/exthandler/nsIExternalHelperAppService.idl index 6ba877508747..6d2146c46880 100644 --- a/uriloader/exthandler/nsIExternalHelperAppService.idl +++ b/uriloader/exthandler/nsIExternalHelperAppService.idl @@ -76,7 +76,7 @@ interface nsPIExternalAppLauncher : nsISupports * Note that cancelling the load via the nsICancelable interface will release * the reference to the launcher dialog. */ -[scriptable, uuid(d9a19faf-497b-408c-b995-777d956b72c0)] +[scriptable, uuid(acf2a516-7d7f-4771-8b22-6c4a559c088e)] interface nsIHelperAppLauncher : nsICancelable { /** @@ -110,6 +110,13 @@ interface nsIHelperAppLauncher : nsICancelable */ void launchWithApplication(in nsIFile aApplication, in boolean aRememberThisPreference); + /** + * Callback invoked by nsIHelperAppLauncherDialog::promptForSaveToFileAsync + * after the user has chosen a file through the File Picker (or dismissed it). + * @param aFile The file that was chosen by the user (or null if dialog was dismissed). + */ + void saveDestinationAvailable(in nsIFile aFile); + /** * The following methods are used by the progress dialog to get or set * information on the current helper app launcher download. diff --git a/uriloader/exthandler/nsIHelperAppLauncherDialog.idl b/uriloader/exthandler/nsIHelperAppLauncherDialog.idl index 7732cb5c1a82..d15f13321258 100644 --- a/uriloader/exthandler/nsIHelperAppLauncherDialog.idl +++ b/uriloader/exthandler/nsIHelperAppLauncherDialog.idl @@ -21,7 +21,7 @@ interface nsIFile; * will access methods of the nsIHelperAppLauncher passed in to show() * in order to cause a "save to disk" or "open using" action. */ -[scriptable, uuid(f3704fdc-8ae6-4eba-a3c3-f02958ac0649)] +[scriptable, uuid(3ae4dca8-ac91-4891-adcf-3fbebed6170e)] interface nsIHelperAppLauncherDialog : nsISupports { /** * This request is passed to the helper app dialog because Gecko can not @@ -73,12 +73,40 @@ interface nsIHelperAppLauncherDialog : nsISupports { * Set to true to force prompting the user for thet file * name/location, otherwise perferences may control if the user is * prompted. + * + * @throws NS_ERROR_NOT_AVAILABLE if the async version of this function + * should be used. */ nsIFile promptForSaveToFile(in nsIHelperAppLauncher aLauncher, in nsISupports aWindowContext, in wstring aDefaultFileName, in wstring aSuggestedFileExtension, in boolean aForcePrompt); + + /** + * Async invoke a save-to-file dialog instead of the full fledged helper app + * dialog. When the file is chosen (or the dialog is closed), the callback + * in aLauncher (aLauncher.saveDestinationAvailable) is called with the + * selected file. + * + * @param aLauncher + * A nsIHelperAppLauncher to be invoked when a file is selected. + * @param aWindowContext + * Window associated with action. + * @param aDefaultFileName + * Default file name to provide (can be null) + * @param aSuggestedFileExtension + * Sugested file extension + * @param aForcePrompt + * Set to true to force prompting the user for thet file + * name/location, otherwise perferences may control if the user is + * prompted. + */ + void promptForSaveToFileAsync(in nsIHelperAppLauncher aLauncher, + in nsISupports aWindowContext, + in wstring aDefaultFileName, + in wstring aSuggestedFileExtension, + in boolean aForcePrompt); };