Professional Documents
Culture Documents
//@line 45
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
*/
///////////////////////////////////////////////////////////////////////////////
//// Helper Functions
/**
* Determines if a given directory is able to be used to download to.
*
* @param aDirectory
* The directory to check.
* @returns true if we can use the directory, false otherwise.
*/
function isUsableDirectory(aDirectory)
{
return aDirectory.exists() && aDirectory.isDirectory() &&
aDirectory.isWritable();
}
///////////////////////////////////////////////////////////////////////////////
//// nsUnkownContentTypeDialog
Components.utils.import("resource://gre/modules/DownloadLastDir.jsm");
/* ctor
*/
function nsUnknownContentTypeDialog() {
// Initialize data properties.
this.mLauncher = null;
this.mContext = null;
this.mSourcePath = null;
this.chosenApp = null;
this.givenDefaultApp = false;
this.updateSelf = true;
this.mTitle = "";
}
nsUnknownContentTypeDialog.prototype = {
nsIMIMEInfo : Components.interfaces.nsIMIMEInfo,
// show: Open XUL dialog using window watcher. Since the dialog is not
// modal, it needs to be a top level window and the way to open
// one of those is via that route).
show: function(aLauncher, aContext, aReason) {
this.mLauncher = aLauncher;
this.mContext = aContext;
// When opening from new tab, if tab closes while dialog is opening,
// (which is a race condition on the XUL file being cached and the timer
// in nsExternalHelperAppService), the dialog gets a blur and doesn't
// activate the OK button. So we wait a bit before doing opening it.
reallyShow: function() {
try {
var ir =
this.mContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
var dwi = ir.getInterface(Components.interfaces.nsIDOMWindowInternal);
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
this.mDialog = ww.openWindow(dwi,
"chrome://mozapps/content/downloads/unknownContentType.xul",
null,
"chrome,centerscreen,titlebar,dialog=yes,dependent",
null);
} catch (ex) {
// The containing window may have gone away. Break reference
// cycles and stop doing the download.
const NS_BINDING_ABORTED = 0x804b0002;
this.mLauncher.cancel(NS_BINDING_ABORTED);
return;
}
this.mLauncher = aLauncher;
createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
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
let dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
.getService(Components.interfaces.nsIDownloadManager);
let defaultFolder = dnldMgr.userDownloadsDirectory;
try {
result = this.validateLeafName(defaultFolder, aDefaultFile,
aSuggestedFileExtension);
}
catch (ex) {
if (ex.result == Components.results.NS_ERROR_FILE_ACCESS_DENIED) {
let prompter = Components.classes["@mozilla.org/embedcomp/prompt-
service;1"].
getService(Components.interfaces.nsIPromptService);
return;
}
}
// Check to make sure we have a valid directory, otherwise, prompt
if (result)
return result;
}
}
if (aSuggestedFileExtension) {
// aSuggestedFileExtension includes the period, so strip it
picker.defaultExtension = aSuggestedFileExtension.substring(1);
}
else {
try {
picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
}
catch (ex) { }
}
picker.appendFilters( nsIFilePicker.filterAll );
.getService(Components.interfaces.nsIPrivateBrowsingService);
inPrivateBrowsing = pbs.privateBrowsingEnabled;
}
catch (e) {
}
.getService(Components.interfaces.nsIDownloadManager);
picker.displayDirectory = dnldMgr.userDownloadsDirectory;
// The last directory preference may not exist, which will throw.
try {
var lastDir;
if (inPrivateBrowsing && gDownloadLastDir.file)
lastDir = gDownloadLastDir.file;
else
lastDir = prefs.getComplexValue("browser.download.lastDir",
Components.interfaces.nsILocalFile);
if (isUsableDirectory(lastDir))
picker.displayDirectory = lastDir;
}
catch (ex) {
}
if (picker.show() == nsIFilePicker.returnCancel) {
// null result means user cancelled.
return null;
}
// Be sure to save the directory the user chose through the Save As...
// dialog as the new browser.download.dir since the old one
// didn't exist.
result = picker.file;
if (result) {
try {
// Remove the file so that it's not there when we ensure non-existence
later;
// this is safe because for the file to exist, the user would have had to
// confirm that he wanted the file overwritten.
if (result.exists())
result.remove(false);
}
catch (e) { }
var newDir =
result.parent.QueryInterface(Components.interfaces.nsILocalFile);
// Do not store the last save directory as a pref inside the private
browsing mode
if (inPrivateBrowsing)
gDownloadLastDir.file = newDir;
else
prefs.setComplexValue("browser.download.lastDir",
Components.interfaces.nsILocalFile, newDir);
/**
* Ensures that a local folder/file combination does not already exist in
* the file system (or finds such a combination with a reasonably similar
* leaf name), creates the corresponding file, and returns it.
*
* @param aLocalFile
* the folder where the file resides
* @param aLeafName
* the string name of the file (may be empty if no name is known,
* in which case a name will be chosen)
* @param aFileExt
* the extension of the file, if one is known; this will be ignored
* if aLeafName is non-empty
* @returns nsILocalFile
* the created file
*/
validateLeafName: function (aLocalFile, aLeafName, aFileExt)
{
if (!(aLocalFile && isUsableDirectory(aLocalFile)))
return null;
// Remove any leading periods, since we don't want to save hidden files
// automatically.
aLeafName = aLeafName.replace(/^\.+/, "");
if (aLeafName == "")
aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
aLocalFile.append(aLeafName);
this.makeFileUnique(aLocalFile);
//@line 336
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
let ext;
try {
// We can fail here if there's no primary extension set
ext = "." + this.mLauncher.MIMEInfo.primaryExtension;
} catch (e) { }
f.remove(false);
this.makeFileUnique(aLocalFile);
}
//@line 354
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
return aLocalFile;
},
/**
* Generates and returns a uniquely-named file from aLocalFile. If
* aLocalFile does not exist, it will be the file returned; otherwise, a
* file whose name is similar to that of aLocalFile will be returned.
*/
makeFileUnique: function (aLocalFile)
{
try {
// Note - this code is identical to that in
// toolkit/content/contentAreaUtils.js.
// If you are updating this code, update that code too! We can't share code
// here since this is called in a js component.
var collisionCount = 0;
while (aLocalFile.exists()) {
collisionCount++;
if (collisionCount == 1) {
// Append "(2)" before the last dot in (or at the end of) the filename
// special case .ext.gz etc files so we don't wind up with .tar(2).gz
if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i)) {
aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|
bz2|Z)$/i, "(2)$&");
}
else {
aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/,
"(2)$&");
}
}
else {
// replace the last (n) in the filename with (n+1)
aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1"
+ (collisionCount+1) + ")");
}
}
aLocalFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
}
catch (e) {
dump("*** exception in validateLeafName: " + e + "\n");
if (e.result == Components.results.NS_ERROR_FILE_ACCESS_DENIED)
throw e;
// nsIWebProgressListener methods.
// Look for error notifications and display alert to user.
onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage ) {
if ( aStatus != Components.results.NS_OK ) {
// Get prompt service.
var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-
service;1" ]
.getService( Components.interfaces.nsIPromptService );
// Display error alert (using text supplied by back-end).
prompter.alert( this.dialog, this.helperAppDlg.mTitle, aMessage );
this.mTitle = this.dialogElement("strings").getFormattedString("title",
[displayName]);
this.mDialog.document.title = this.mTitle;
//@line 549
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
if (shouldntRememberChoice) {
rememberChoice.checked = false;
rememberChoice.disabled = true;
}
else {
rememberChoice.checked = !
this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
}
this.toggleRememberChoice(rememberChoice);
this.mDialog.setTimeout("dialog.postShowCallback()", 0);
this.mDialog.document.documentElement.getButton("accept").disabled = true;
this._showTimer = Components.classes["@mozilla.org/timer;1"]
.createInstance(nsITimer);
this._showTimer.initWithCallback(this, 250, nsITimer.TYPE_ONE_SHOT);
},
postShowCallback: function () {
this.mDialog.sizeToContent();
// initIntro:
initIntro: function(url, filename, displayname) {
this.dialogElement( "location" ).value = displayname;
this.dialogElement( "location" ).setAttribute("realname", filename);
this.dialogElement( "location" ).setAttribute("tooltiptext", displayname);
// if mSourcePath is a local file, then let's use the pretty path name
instead of an ugly
// url...
var pathString = this.mSourcePath;
try
{
var fileURL = url.QueryInterface(Components.interfaces.nsIFileURL);
if (fileURL)
{
var fileObject = fileURL.file;
if (fileObject)
{
var parentObject = fileObject.parent;
if (parentObject)
{
pathString = parentObject.path;
}
}
}
} catch(ex) {}
if (pathString == this.mSourcePath)
{
// wasn't a fileURL
var tmpurl = url.clone(); // don't want to change the real url
try {
tmpurl.userPass = "";
} catch (ex) {}
pathString = tmpurl.prePath;
}
// Set the location text, which is separate from the intro text so it can
be cropped
var location = this.dialogElement( "source" );
location.value = pathString;
location.setAttribute("tooltiptext", this.mSourcePath);
if (typeString == "") {
// 2. If there is none, use the extension to identify the file, e.g. "ZIP
file"
var primaryExtension = "";
try {
primaryExtension = mimeInfo.primaryExtension;
}
catch (ex) {
}
if (primaryExtension != "")
typeString =
this.dialogElement("strings").getFormattedString("fileType",
[primaryExtension.toUpperCase()]);
// 3. If we can't even do that, just give up and show the MIME type.
else
typeString = mimeInfo.MIMEType;
}
type.value = typeString;
},
_blurred: false,
_delayExpired: false,
onBlur: function(aEvent) {
this._blurred = true;
this.mDialog.document.documentElement.getButton("accept").disabled = true;
},
onFocus: function(aEvent) {
this._blurred = false;
if (this._delayExpired) {
var script = "document.documentElement.getButton('accept').disabled =
false";
this.mDialog.setTimeout(script, 250);
}
},
// initAppAndSaveToDiskValues:
initAppAndSaveToDiskValues: function() {
var modeGroup = this.dialogElement("mode");
// We don't let users open .exe files or random binary data directly
// from the browser at the moment because of security concerns.
var openWithDefaultOK = this.openWithDefaultOK();
var mimeType = this.mLauncher.MIMEInfo.MIMEType;
if (this.mLauncher.targetFileIsExecutable || (
(mimeType == "application/octet-stream" ||
mimeType == "application/x-msdownload") &&
!openWithDefaultOK)) {
this.dialogElement("open").disabled = true;
var openHandler = this.dialogElement("openHandler");
openHandler.disabled = true;
openHandler.selectedItem = null;
modeGroup.selectedItem = this.dialogElement("save");
return;
}
//@line 774
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
otherHandler.label = this.chosenApp.executable.leafName;
//@line 776
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
otherHandler.hidden = false;
}
var useDefault = this.dialogElement("useSystemDefault");
var openHandler = this.dialogElement("openHandler");
openHandler.selectedIndex = 0;
if (this.mLauncher.MIMEInfo.preferredAction ==
this.nsIMIMEInfo.useSystemDefault) {
// Open (using system default).
modeGroup.selectedItem = this.dialogElement("open");
} else if (this.mLauncher.MIMEInfo.preferredAction ==
this.nsIMIMEInfo.useHelperApp) {
// Open with given helper app.
modeGroup.selectedItem = this.dialogElement("open");
openHandler.selectedIndex = 1;
} else {
// Save to disk.
modeGroup.selectedItem = this.dialogElement("save");
}
otherHandler.nextSibling.hidden = otherHandler.nextSibling.nextSibling.hidden
= false;
this.updateOKButton();
},
get saveToDisk() {
return this.dialogElement("save").selected;
},
get useOtherHandler() {
return this.dialogElement("open").selected &&
this.dialogElement("openHandler").selectedIndex == 1;
},
get useSystemDefault() {
return this.dialogElement("open").selected &&
this.dialogElement("openHandler").selectedIndex == 0;
},
openHandlerCommand: function () {
var openHandler = this.dialogElement("openHandler");
if (openHandler.selectedItem.id == "choose")
this.chooseApp();
else
openHandler.setAttribute("lastSelectedItemID",
openHandler.selectedItem.id);
},
updateOKButton: function() {
var ok = false;
if (this.dialogElement("save").selected) {
// This is always OK.
ok = true;
}
else if (this.dialogElement("open").selected) {
switch (this.dialogElement("openHandler").selectedIndex) {
case 0:
// No app need be specified in this case.
ok = true;
break;
case 1:
// only enable the OK button if we have a default app to use or if
// the user chose an app....
ok = this.chosenApp ||
/\S/.test(this.dialogElement("otherHandler").getAttribute("path"));
break;
}
}
// Returns true iff the user-specified helper app has been modified.
appChanged: function() {
return this.helperAppChoice() !=
this.mLauncher.MIMEInfo.preferredApplicationHandler;
},
updateMIMEInfo: function() {
var needUpdate = false;
// If current selection differs from what's in the mime info object,
// then we need to update.
if (this.saveToDisk) {
needUpdate = this.mLauncher.MIMEInfo.preferredAction !=
this.nsIMIMEInfo.saveToDisk;
if (needUpdate)
this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.saveToDisk;
}
else if (this.useSystemDefault) {
needUpdate = this.mLauncher.MIMEInfo.preferredAction !=
this.nsIMIMEInfo.useSystemDefault;
if (needUpdate)
this.mLauncher.MIMEInfo.preferredAction =
this.nsIMIMEInfo.useSystemDefault;
}
else {
// For "open with", we need to check both preferred action and whether the
user chose
// a new app.
needUpdate = this.mLauncher.MIMEInfo.preferredAction !=
this.nsIMIMEInfo.useHelperApp || this.appChanged();
if (needUpdate) {
this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useHelperApp;
// App may have changed - Update application
var app = this.helperAppChoice();
this.mLauncher.MIMEInfo.preferredApplicationHandler = app;
}
}
// We will also need to update if the "always ask" flag has changed.
needUpdate = needUpdate || this.mLauncher.MIMEInfo.alwaysAskBeforeHandling !=
(!this.dialogElement("rememberChoice").checked);
// One last special case: If the input "always ask" flag was false, then we
always
// update. In that case we are displaying the helper app dialog for the
first
// time for this mime type and we need to store the user's action in the
mimeTypes.rdf
// data source (whether that action has changed or not; if it didn't change,
then we need
// to store the "always ask" flag so the helper app dialog will or won't
display
// next time, per the user's selection).
needUpdate = needUpdate || !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
// Make sure mime info has updated setting for the "always ask" flag.
this.mLauncher.MIMEInfo.alwaysAskBeforeHandling = !
this.dialogElement("rememberChoice").checked;
return needUpdate;
},
// onOK:
onOK: function() {
// Verify typed app path, if necessary.
if (this.useOtherHandler) {
var helperApp = this.helperAppChoice();
if (!helperApp || !helperApp.executable ||
!helperApp.executable.exists()) {
// Show alert and try again.
var bundle = this.dialogElement("strings");
var msg = bundle.getFormattedString("badApp",
[this.dialogElement("otherHandler").getAttribute("path")]);
var svc = Components.classes["@mozilla.org/embedcomp/prompt-
service;1"].getService(Components.interfaces.nsIPromptService);
svc.alert(this.mDialog, bundle.getString("badApp.title"), msg);
if (this.dialogElement("save").selected) {
// If we're using a default download location, create a path
// for the file to be saved to to pass to |saveToDisk| - otherwise
// we must ask the user to pick a save name.
//@line 977
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
// see @notify
// we cannot use opener's setTimeout, see bug 420405
this._saveToDiskTimer = Components.classes["@mozilla.org/timer;1"]
.createInstance(nsITimer);
this._saveToDiskTimer.initWithCallback(this, 0,
nsITimer.TYPE_ONE_SHOT);
}
else
this.mLauncher.launchWithApplication(null, false);
// Update user pref for this mime type (if necessary). We do not
// store anything in the mime type preferences for the ambiguous
// type application/octet-stream. We do NOT do this for
// application/x-msdownload since we want users to be able to
// autodownload these to disk.
if (needUpdate && this.mLauncher.MIMEInfo.MIMEType != "application/octet-
stream")
this.updateHelperAppPref();
} catch(e) { }
// onCancel:
onCancel: function() {
// Remove our web progress listener.
this.mLauncher.setWebProgressListener(null);
// dialogElement: Convenience.
dialogElement: function(id) {
return this.mDialog.document.getElementById(id);
},
this.mDialog.openDialog("chrome://global/content/appPicker.xul", null,
"chrome,modal,centerscreen,titlebar,dialog=yes",
params);
if (params.handlerApp &&
params.handlerApp.executable &&
params.handlerApp.executable.isFile()) {
// Show the "handler" menulist since we have a (user-specified)
// application now.
this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
// Update dialog
var otherHandler = this.dialogElement("otherHandler");
otherHandler.removeAttribute("hidden");
otherHandler.setAttribute("path",
this.getPath(this.chosenApp.executable));
otherHandler.label =
this.getFileDisplayName(this.chosenApp.executable);
this.dialogElement("openHandler").selectedIndex = 1;
this.dialogElement("openHandler").setAttribute("lastSelectedItemID",
"otherHandler");
this.dialogElement("mode").selectedItem = this.dialogElement("open");
} else {
var openHandler = this.dialogElement("openHandler");
var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
if (!lastSelectedID)
lastSelectedID = "defaultHandler";
openHandler.selectedItem = this.dialogElement(lastSelectedID);
}
//@line 1155
"e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDl
g.js.in"
},
// Turn this on to get debugging messages.
debug: false,
// dumpInfo:
doDebug: function() {
const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
// Open new progress dialog.
var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
.createInstance( nsIProgressDialog );
// Show it.
progress.open( this.mDialog );
},
// dumpObj:
dumpObj: function( spec ) {
var val = "<undefined>";
try {
val = eval( "this."+spec ).toString();
} catch( exception ) {
}
this.dump( spec + "=" + val + "\n" );
},
// dumpObjectProperties
dumpObjectProperties: function( desc, obj ) {
for( prop in obj ) {
this.dump( desc + "." + prop + "=" );
var val = "<undefined>";
try {
val = obj[ prop ];
} catch ( exception ) {
}
this.dump( val + "\n" );
}
}
}
// This Component's module implementation. All the code below is used to get this
// component registered and accessible via XPCOM.
var module = {
// registerSelf: Register this component.
registerSelf: function (compMgr, fileSpec, location, type) {
compMgr =
compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
compMgr.registerFactoryLocation( this.cid,
"Unknown Content Type Dialog",
this.contractId,
fileSpec,
location,
type );
},
if (!iid.equals(Components.interfaces.nsIFactory)) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
return this.factory;
},
/* factory object */
factory: {
// createInstance: Return a new nsProgressDialog object.
createInstance: function (outer, iid) {
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;