--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -219,16 +219,22 @@ pref("browser.urlbar.filter.javascript",
// the maximum number of results to show in autocomplete when doing richResults
pref("browser.urlbar.maxRichResults", 12);
// Size of "chunks" affects the number of places to process between each search
// timeout (ms). Too big and the UI will be unresponsive; too small and we'll
// be waiting on the timeout too often without many results.
pref("browser.urlbar.search.chunkSize", 1000);
pref("browser.urlbar.search.timeout", 100);
+// Number of milliseconds to wait for the http headers (and thus
+// the Content-Disposition filename) before giving up and falling back to
+// picking a filename without that info in hand so that the user sees some
+// feedback from their action.
+pref("browser.download.saveLinkAsFilenameTimeout", 1000);
+
pref("browser.download.useDownloadDir", true);
pref("browser.download.folderList", 0);
pref("browser.download.manager.showAlertOnComplete", true);
pref("browser.download.manager.showAlertInterval", 2000);
pref("browser.download.manager.retention", 2);
pref("browser.download.manager.showWhenStarting", true);
pref("browser.download.manager.useWindow", true);
pref("browser.download.manager.closeWhenDone", false);
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -36,16 +36,17 @@
# Tom Germeau <tom.germeau@epigoon.com>
# Jesse Ruderman <jruderman@gmail.com>
# Joe Hughes <joe@retrovirus.com>
# Pamela Greene <pamg.bugs@gmail.com>
# Michael Ventnor <ventnors_dogs234@yahoo.com.au>
# Simon Bünzli <zeniko@gmail.com>
# Gijs Kruitbosch <gijskruitbosch@gmail.com>
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
+# Dan Mosedale <dmose@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
@@ -821,20 +822,133 @@ nsContextMenu.prototype = {
// Save URL of clicked-on frame.
saveFrame: function () {
saveDocument(this.target.ownerDocument);
},
// Save URL of clicked-on link.
saveLink: function() {
+ // canonical def in nsURILoader.h
+ const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
+
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, doc.nodePrincipal);
- saveURL(this.linkURL, this.linkText(), null, true, false,
- doc.documentURIObject);
+ var linkText = this.linkText();
+ var linkURL = this.linkURL;
+
+
+ // an object to proxy the data through to
+ // nsIExternalHelperAppService.doContent, which will wait for the
+ // appropriate MIME-type headers and then prompt the user with a
+ // file picker
+ function saveAsListener() {}
+ saveAsListener.prototype = {
+ extListener: null,
+
+ onStartRequest: function saveLinkAs_onStartRequest(aRequest, aContext) {
+
+ // if the timer fired, the error status will have been caused by that,
+ // and we'll be restarting in onStopRequest, so no reason to notify
+ // the user
+ if (aRequest.status == NS_ERROR_SAVE_LINK_AS_TIMEOUT)
+ return;
+
+ timer.cancel();
+
+ // some other error occured; notify the user...
+ if (!Components.isSuccessCode(aRequest.status)) {
+ try {
+ const sbs = Cc["@mozilla.org/intl/stringbundle;1"].
+ getService(Ci.nsIStringBundleService);
+ const bundle = sbs.createBundle(
+ "chrome://mozapps/locale/downloads/downloads.properties");
+
+ const title = bundle.GetStringFromName("downloadErrorAlertTitle");
+ const msg = bundle.GetStringFromName("downloadErrorGeneric");
+
+ const promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Ci.nsIPromptService);
+ promptSvc.alert(doc.defaultView, title, msg);
+ } catch (ex) {}
+ return;
+ }
+
+ var extHelperAppSvc =
+ Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
+ getService(Ci.nsIExternalHelperAppService);
+ var channel = aRequest.QueryInterface(Ci.nsIChannel);
+ this.extListener =
+ extHelperAppSvc.doContent(channel.contentType, aRequest,
+ doc.defaultView, true);
+ this.extListener.onStartRequest(aRequest, aContext);
+ },
+
+ onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext,
+ aStatusCode) {
+ if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
+ // do it the old fashioned way, which will pick the best filename
+ // it can without waiting.
+ saveURL(linkURL, linkText, null, true, false, doc.documentURIObject);
+ }
+ if (this.extListener)
+ this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
+ },
+
+ onDataAvailable: function saveLinkAs_onDataAvailable(aRequest, aContext,
+ aInputStream,
+ aOffset, aCount) {
+ this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
+ aOffset, aCount);
+ }
+ }
+
+ // in case we need to prompt the user for authentication
+ function callbacks() {}
+ callbacks.prototype = {
+ getInterface: function sLA_callbacks_getInterface(aIID) {
+ if (aIID.equals(Ci.nsIAuthPrompt) || aIID.equals(Ci.nsIAuthPrompt2)) {
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIPromptFactory);
+ return ww.getPrompt(doc.defaultView, aIID);
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ }
+
+ // if it we don't have the headers after a short time, the user
+ // won't have received any feedback from their click. that's bad. so
+ // we give up waiting for the filename.
+ function timerCallback() {}
+ timerCallback.prototype = {
+ notify: function sLA_timer_notify(aTimer) {
+ channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
+ return;
+ }
+ }
+
+ // set up a channel to do the saving
+ var ioService = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+ var channel = ioService.newChannelFromURI(this.getLinkURI());
+ channel.notificationCallbacks = new callbacks();
+ channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
+ Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
+ if (channel instanceof Ci.nsIHttpChannel)
+ channel.referrer = doc.documentURIObject;
+
+ // fallback to the old way if we don't see the headers quickly
+ var timeToWait =
+ gPrefService.getIntPref("browser.download.saveLinkAsFilenameTimeout");
+ var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(new timerCallback(), timeToWait,
+ timer.TYPE_ONE_SHOT);
+
+ // kick off the channel with our proxy object as the listener
+ channel.asyncOpen(new saveAsListener(), null);
},
sendLink: function() {
// we don't know the title of the link so pass in an empty string
MailIntegration.sendMessage( this.linkURL, "" );
},
// Save URL of clicked-on image.
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1888,17 +1888,19 @@ SessionStoreService.prototype = {
saveState: function sss_saveState(aUpdateAll) {
// if crash recovery is disabled, only save session resuming information
if (!this._resume_from_crash && this._loadState == STATE_RUNNING)
return;
this._dirty = aUpdateAll;
var oState = this._getCurrentState();
oState.session = { state: ((this._loadState == STATE_RUNNING) ? STATE_RUNNING_STR : STATE_STOPPED_STR) };
- this._writeFile(this._sessionFile, oState.toSource());
+ //var oStateString = this._toJSONString(oState);
+ var oStateString = oState.toSource();
+ this._writeFile(this._sessionFile, oStateString);
this._lastSaveTime = Date.now();
},
/**
* delete session datafile and backup
*/
_clearDisk: function sss_clearDisk() {
if (this._sessionFile.exists()) {
--- a/embedding/browser/activex/src/control/HelperAppDlg.cpp
+++ b/embedding/browser/activex/src/control/HelperAppDlg.cpp
@@ -469,16 +469,17 @@ CHelperAppLauncherDlg::Show(nsIHelperApp
}
/* nsILocalFile promptForSaveToFile (in nsIHelperAppLauncher aLauncher, in nsISupports aWindowContext, in wstring aDefaultFile, in wstring aSuggestedFileExtension); */
NS_IMETHODIMP
CHelperAppLauncherDlg::PromptForSaveToFile(nsIHelperAppLauncher *aLauncher,
nsISupports *aWindowContext,
const PRUnichar *aDefaultFile,
const PRUnichar *aSuggestedFileExtension,
+ PRBool aForcePrompt,
nsILocalFile **_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
USES_CONVERSION;
TCHAR szPath[MAX_PATH + 1];
memset(szPath, 0, sizeof(szPath));
_tcsncpy(szPath, W2T(aDefaultFile), MAX_PATH);
--- a/embedding/browser/gtk/src/EmbedDownloadMgr.cpp
+++ b/embedding/browser/gtk/src/EmbedDownloadMgr.cpp
@@ -207,16 +207,17 @@ EmbedDownloadMgr::GetDownloadInfo(nsIHel
return NS_OK;
}
NS_IMETHODIMP EmbedDownloadMgr::PromptForSaveToFile(nsIHelperAppLauncher *aLauncher,
nsISupports *aWindowContext,
const PRUnichar *aDefaultFile,
const PRUnichar *aSuggestedFileExtension,
+ PRBool aForcePrompt,
nsILocalFile **_retval)
{
*_retval = nsnull;
nsCAutoString filePath;
filePath.Assign(mDownload->file_name_with_path);
nsCOMPtr<nsILocalFile> destFile;
--- a/embedding/browser/photon/src/nsUnknownContentTypeHandler.cpp
+++ b/embedding/browser/photon/src/nsUnknownContentTypeHandler.cpp
@@ -65,16 +65,17 @@ NS_IMETHODIMP nsUnknownContentTypeHandle
{
return aLauncher->SaveToDisk( nsnull, PR_FALSE );
}
NS_IMETHODIMP nsUnknownContentTypeHandler::PromptForSaveToFile( nsIHelperAppLauncher* aLauncher,
nsISupports *aWindowContext,
const PRUnichar *aDefaultFile,
const PRUnichar *aSuggestedFileExtension,
+ PRBool aForcePrompt,
nsILocalFile **_retval )
{
///* ATENTIE */ printf("PromptForSaveToFile!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n");
NS_ENSURE_ARG_POINTER(_retval);
*_retval = nsnull;
/* try to get the PtMozillawidget_t* pointer form the aContext - use the fact the the WebBrowserContainer is
registering itself as nsIDocumentLoaderObserver ( SetDocLoaderObserver ) */
--- a/embedding/components/ui/helperAppDlg/nsHelperAppDlg.js
+++ b/embedding/components/ui/helperAppDlg/nsHelperAppDlg.js
@@ -114,17 +114,17 @@ nsHelperAppDialog.prototype = {
this.mDialog.dialog = this;
// Watch for error notifications.
this.mIsMac = (this.mDialog.navigator.platform.indexOf( "Mac" ) != -1);
this.progressListener.helperAppDlg = this;
this.mLauncher.setWebProgressListener( this.progressListener );
},
// promptForSaveToFile: Display file picker dialog and return selected file.
- promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension) {
+ promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
var result = "";
const prefSvcContractID = "@mozilla.org/preferences-service;1";
const prefSvcIID = Components.interfaces.nsIPrefService;
var branch = Components.classes[prefSvcContractID].getService(prefSvcIID)
.getBranch("browser.download.");
var dir = null;
@@ -137,17 +137,17 @@ nsHelperAppDialog.prototype = {
} catch (e) { }
var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle("chrome://global/locale/nsHelperAppDlg.properties");
var autoDownload = branch.getBoolPref("autoDownload");
// If the autoDownload pref is set then just download to default download directory
- if (autoDownload && dir && dir.exists()) {
+ if (!aForcePrompt && autoDownload && dir && dir.exists()) {
if (aDefaultFile == "")
aDefaultFile = bundle.GetStringFromName("noDefaultFile") + (aSuggestedFileExtension || "");
dir.append(aDefaultFile);
return uniqueFile(dir);
}
// Use file picker to show dialog.
var nsIFilePicker = Components.interfaces.nsIFilePicker;
--- a/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
+++ b/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
@@ -136,43 +136,45 @@ nsUnknownContentTypeDialog.prototype = {
// target filename (no target, therefore user must pick).
//
// Alternatively, if the user has selected to have all
// files download to a specific location, return that
// location and don't ask via the dialog.
//
// 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) {
+ promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
var result = null;
this.mLauncher = aLauncher;
- // Check to see if the user wishes to auto save to the default download
- // folder without prompting. This preferences may not be set, so default
- // to not prompting.
let prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
- let autodownload = true;
- try {
- autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
- } catch (e) { }
+
+ if (!aForcePrompt) {
+ // Check to see if the user wishes to auto save to the default download
+ // folder without prompting. Note that preference might not be set.
+ let autodownload = false;
+ try {
+ autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
+ } catch (e) { }
- if (autodownload) {
- // Retrieve the user's default download directory
- var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
- .getService(Components.interfaces.nsIDownloadManager);
- var defaultFolder = dnldMgr.userDownloadsDirectory;
- result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
+ if (autodownload) {
+ // Retrieve the user's default download directory
+ let dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
+ .getService(Components.interfaces.nsIDownloadManager);
+ let defaultFolder = dnldMgr.userDownloadsDirectory;
+ result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
+
+ // Check to make sure we have a valid directory, otherwise, prompt
+ if (result)
+ return result;
+ }
}
- // Check to make sure we have a valid directory, otherwise, prompt
- if (result)
- return result;
-
// Use file picker to show dialog.
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
var windowTitle = bundle.GetStringFromName("saveDialogTitle");
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -589,16 +589,17 @@ nsresult nsDocumentOpenInfo::DispatchCon
if (isGuessFromExt) {
mContentType = APPLICATION_GUESS_FROM_EXT;
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
}
rv = helperAppService->DoContent(mContentType,
request,
m_originalContext,
+ PR_FALSE,
getter_AddRefs(m_targetStreamListener));
if (NS_FAILED(rv)) {
request->SetLoadFlags(loadFlags);
m_targetStreamListener = nsnull;
}
}
NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
--- a/uriloader/base/nsURILoader.h
+++ b/uriloader/base/nsURILoader.h
@@ -94,9 +94,15 @@ protected:
/**
* The load has been cancelled because it was found on a malware or phishing blacklist.
* XXX: this belongs in an nsDocShellErrors.h file of some sort.
*/
#define NS_ERROR_MALWARE_URI NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_URILOADER, 30)
#define NS_ERROR_PHISHING_URI NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_URILOADER, 31)
+/**
+ * Used when "Save Link As..." doesn't see the headers quickly enough to choose
+ * a filename. See nsContextMenu.js.
+ */
+#define NS_ERROR_SAVE_LINK_AS_TIMEOUT NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_URILOADER, 32);
+
#endif /* nsURILoader_h__ */
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -519,16 +519,17 @@ nsresult nsExternalHelperAppService::Ini
nsExternalHelperAppService::~nsExternalHelperAppService()
{
gExtProtSvc = nsnull;
}
NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeContentType,
nsIRequest *aRequest,
nsIInterfaceRequestor *aWindowContext,
+ PRBool aForceSave,
nsIStreamListener ** aStreamListener)
{
nsAutoString fileName;
nsCAutoString fileExtension;
PRUint32 reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
nsresult rv;
// Get the file extension and name that we will need later
@@ -635,17 +636,18 @@ NS_IMETHODIMP nsExternalHelperAppService
// nsExternalAppHandler
nsCAutoString buf;
mimeInfo->GetPrimaryExtension(buf);
nsExternalAppHandler * handler = new nsExternalAppHandler(mimeInfo,
buf,
aWindowContext,
fileName,
- reason);
+ reason,
+ aForceSave);
if (!handler)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aStreamListener = handler);
return NS_OK;
}
NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(const nsACString& aExtension,
@@ -986,21 +988,22 @@ NS_INTERFACE_MAP_BEGIN(nsExternalAppHand
NS_INTERFACE_MAP_ENTRY(nsICancelable)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_END_THREADSAFE
nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo,
const nsCSubstring& aTempFileExtension,
nsIInterfaceRequestor* aWindowContext,
const nsAString& aSuggestedFilename,
- PRUint32 aReason)
+ PRUint32 aReason, PRBool aForceSave)
: mMimeInfo(aMIMEInfo)
, mWindowContext(aWindowContext)
, mWindowToClose(nsnull)
, mSuggestedFileName(aSuggestedFilename)
+, mForceSave(aForceSave)
, mCanceled(PR_FALSE)
, mShouldCloseWindow(PR_FALSE)
, mReceivedDispositionInfo(PR_FALSE)
, mStopRequestIssued(PR_FALSE)
, mProgressListenerInitialized(PR_FALSE)
, mReason(aReason)
, mContentLength(-1)
, mProgress(0)
@@ -1465,16 +1468,23 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt
// OK, now check why we're here
if (!alwaysAsk && mReason != nsIHelperAppLauncherDialog::REASON_CANTHANDLE) {
// Force asking if we're not saving. See comment back when we fetched the
// alwaysAsk boolean for details.
alwaysAsk = (action != nsIMIMEInfo::saveToDisk);
}
+ // if we were told that we _must_ save to disk without asking, all the stuff
+ // before this is irrelevant; override it
+ if (mForceSave) {
+ alwaysAsk = PR_FALSE;
+ action = nsIMIMEInfo::saveToDisk;
+ }
+
if (alwaysAsk)
{
// do this first! make sure we don't try to take an action until the user tells us what they want to do
// with it...
mReceivedDispositionInfo = PR_FALSE;
// invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request
mDialog = do_CreateInstance( NS_IHELPERAPPLAUNCHERDLG_CONTRACTID, &rv );
@@ -1899,17 +1909,17 @@ nsresult nsExternalAppHandler::PromptFor
// released, which would release this object too, which would crash.
// See Bug 249143
nsRefPtr<nsExternalAppHandler> kungFuDeathGrip(this);
nsCOMPtr<nsIHelperAppLauncherDialog> dlg(mDialog);
rv = mDialog->PromptForSaveToFile(this,
mWindowContext,
aDefaultFile.get(),
aFileExtension.get(),
- aNewFile);
+ mForceSave, aNewFile);
return rv;
}
nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation)
{
nsresult rv = NS_OK;
NS_ASSERTION(mStopRequestIssued, "uhoh, how did we get here if we aren't done getting data?");
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -246,17 +246,17 @@ public:
* @param aWindowContext Window context, as passed to DoContent
* @param aFileName The filename to use
* @param aReason A constant from nsIHelperAppLauncherDialog indicating
* why the request is handled by a helper app.
*/
nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aFileExtension,
nsIInterfaceRequestor * aWindowContext,
const nsAString& aFilename,
- PRUint32 aReason);
+ PRUint32 aReason, PRBool aForceSave);
~nsExternalAppHandler();
protected:
nsCOMPtr<nsIFile> mTempFile;
nsCOMPtr<nsIURI> mSourceUrl;
nsString mTempFileExtension;
/**
@@ -276,16 +276,23 @@ protected:
/**
* The following field is set if we were processing an http channel that had
* a content disposition header which specified the SUGGESTED file name we
* should present to the user in the save to disk dialog.
*/
nsString mSuggestedFileName;
/**
+ * If set, this handler should forcibly save the file to disk regardless of
+ * MIME info settings or anything else, without ever popping up the
+ * unknown content type handling dialog.
+ */
+ PRPackedBool mForceSave;
+
+ /**
* The canceled flag is set if the user canceled the launching of this
* application before we finished saving the data to a temp file.
*/
PRPackedBool mCanceled;
/**
* This is set based on whether the channel indicates that a new window
* was opened specifically for this download. If so, then we
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
@@ -46,33 +46,36 @@ interface nsIFile;
interface nsIMIMEInfo;
interface nsIWebProgressListener2;
interface nsIInterfaceRequestor;
/**
* The external helper app service is used for finding and launching
* platform specific external applications for a given mime content type.
*/
-[scriptable, uuid(0ea90cf3-2dd9-470f-8f76-f141743c5678)]
+[scriptable, uuid(9e456297-ba3e-42b1-92bd-b7db014268cb)]
interface nsIExternalHelperAppService : nsISupports
{
/**
* Binds an external helper application to a stream listener. The caller
* should pump data into the returned stream listener. When the OnStopRequest
* is issued, the stream listener implementation will launch the helper app
* with this data.
* @param aMimeContentType The content type of the incoming data
* @param aRequest The request corresponding to the incoming data
* @param aWindowContext Use GetInterface to retrieve properties like the
* dom window or parent window...
* The service might need this in order to bring up dialogs.
+ * @param aForceSave True to always save this content to disk, regardless of
+ * nsIMIMEInfo and other such influences.
* @return A nsIStreamListener which the caller should pump the data into.
*/
nsIStreamListener doContent (in ACString aMimeContentType, in nsIRequest aRequest,
- in nsIInterfaceRequestor aWindowContext);
+ in nsIInterfaceRequestor aWindowContext,
+ in boolean aForceSave);
/**
* Returns true if data from a URL with this extension combination
* is to be decoded from aEncodingType prior to saving or passing
* off to helper apps, false otherwise.
*/
boolean applyDecodingForExtension(in AUTF8String aExtension,
in ACString aEncodingType);
--- a/uriloader/exthandler/nsIHelperAppLauncherDialog.idl
+++ b/uriloader/exthandler/nsIHelperAppLauncherDialog.idl
@@ -48,17 +48,17 @@ interface nsILocalFile;
* Usage: Clients (of which there is one: the nsIExternalHelperAppService
* implementation in mozilla/uriloader/exthandler) create an instance of
* this interface (using the contract ID) and then call the show() method.
*
* The dialog is shown non-modally. The implementation of the dialog
* will access methods of the nsIHelperAppLauncher passed in to show()
* in order to cause a "save to disk" or "open using" action.
*/
-[scriptable, uuid(64355793-988d-40a5-ba8e-fcde78cac631)]
+[scriptable, uuid(f3704fdc-8ae6-4eba-a3c3-f02958ac0649)]
interface nsIHelperAppLauncherDialog : nsISupports {
/**
* This request is passed to the helper app dialog because Gecko can not
* handle content of this type.
*/
const unsigned long REASON_CANTHANDLE = 0;
/**
@@ -67,32 +67,54 @@ interface nsIHelperAppLauncherDialog : n
const unsigned long REASON_SERVERREQUEST = 1;
/**
* Gecko detected that the type sent by the server (e.g. text/plain) does
* not match the actual type.
*/
const unsigned long REASON_TYPESNIFFED = 2;
- // Show confirmation dialog for launching application (or "save to
- // disk") for content specified by aLauncher.
- // aReason is one of the constants from above. It indicates why the dialog is
- // shown.
- // Implementors should treat unknown reasons like REASON_CANTHANDLE.
+ /**
+ * Show confirmation dialog for launching application (or "save to
+ * disk") for content specified by aLauncher.
+ *
+ * @param aLauncher
+ * A nsIHelperAppLauncher to be invoked when a file is selected.
+ * @param aWindowContext
+ * Window associated with action.
+ * @param aReason
+ * One of the constants from above. It indicates why the dialog is
+ * shown. Implementors should treat unknown reasons like
+ * REASON_CANTHANDLE.
+ */
void show(in nsIHelperAppLauncher aLauncher,
- in nsISupports aContext,
+ in nsISupports aWindowContext,
in unsigned long aReason);
-
- // invoke a save to file dialog instead of the full fledged helper app dialog.
- // aDefaultFileName --> default file name to provide (can be null)
- // aSuggestedFileExtension --> sugested file extension
- // aFileLocation --> return value for the file location
+
+ /**
+ * Invoke a save-to-file dialog instead of the full fledged helper app dialog.
+ * Returns the a nsILocalFile for the file name/location selected.
+ *
+ * @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.
+ */
nsILocalFile promptForSaveToFile(in nsIHelperAppLauncher aLauncher,
in nsISupports aWindowContext,
- in wstring aDefaultFile,
- in wstring aSuggestedFileExtension);
+ in wstring aDefaultFileName,
+ in wstring aSuggestedFileExtension,
+ in boolean aForcePrompt);
};
%{C++
#define NS_IHELPERAPPLAUNCHERDLG_CONTRACTID "@mozilla.org/helperapplauncherdialog;1"
#define NS_IHELPERAPPLAUNCHERDLG_CLASSNAME "Mozilla Helper App Launcher Confirmation Dialog"
%}