browser/metro/components/HelperAppDialog.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 const Cc = Components.classes;
     6 const Ci = Components.interfaces;
     7 const Cu = Components.utils;
     8 const Cr = Components.results;
    10 const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir";
    11 const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
    13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    14 Cu.import("resource://gre/modules/Services.jsm");
    15 Cu.import("resource://gre/modules/DownloadUtils.jsm");
    17 XPCOMUtils.defineLazyGetter(this, "ContentUtil", function() {
    18   Cu.import("resource:///modules/ContentUtil.jsm");
    19   return ContentUtil;
    20 });
    21 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
    22                                   "resource://gre/modules/Downloads.jsm");
    23 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
    24                                   "resource://gre/modules/FileUtils.jsm");
    25 XPCOMUtils.defineLazyModuleGetter(this, "Task",
    26                                   "resource://gre/modules/Task.jsm");
    28 // -----------------------------------------------------------------------
    29 // HelperApp Launcher Dialog
    30 // -----------------------------------------------------------------------
    32 function HelperAppLauncherDialog() { }
    34 HelperAppLauncherDialog.prototype = {
    35   classID: Components.ID("{e9d277a0-268a-4ec2-bb8c-10fdf3e44611}"),
    36   QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
    38   show: function hald_show(aLauncher, aContext, aReason) {
    39     // Check to see if we can open this file or not
    40     // If the file is an executable then launchWithApplication will fail in
    41     // /uriloader nsMIMEInfoWin.cpp code. So always download in that case.
    42     if (aLauncher.MIMEInfo.hasDefaultHandler && !aLauncher.targetFileIsExecutable) {
    43       aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.useSystemDefault;
    44       aLauncher.launchWithApplication(null, false);
    45     } else {
    46       let wasClicked = false;
    47       this._showDownloadInfobar(aLauncher);
    48     }
    49   },
    51   _getDownloadSize: function dv__getDownloadSize (aSize) {
    52     let displaySize = DownloadUtils.convertByteUnits(aSize);
    53     // displaySize[0] is formatted size, displaySize[1] is units
    54     if (aSize > 0)
    55       return displaySize.join("");
    56     else {
    57       let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
    58       return browserBundle.GetStringFromName("downloadsUnknownSize");
    59     }
    60   },
    62   _getChromeWindow: function (aWindow) {
    63       let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
    64                             .getInterface(Ci.nsIWebNavigation)
    65                             .QueryInterface(Ci.nsIDocShellTreeItem)
    66                             .rootTreeItem
    67                             .QueryInterface(Ci.nsIInterfaceRequestor)
    68                             .getInterface(Ci.nsIDOMWindow)
    69                             .QueryInterface(Ci.nsIDOMChromeWindow);
    70      return chromeWin;
    71   },
    73   _showDownloadInfobar: function do_showDownloadInfobar(aLauncher) {
    74     let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
    76     let runButtonText =
    77               browserBundle.GetStringFromName("downloadOpen");
    78     let saveButtonText =
    79               browserBundle.GetStringFromName("downloadSave");
    80     let cancelButtonText =
    81               browserBundle.GetStringFromName("downloadCancel");
    83     let buttons = [
    84       {
    85         isDefault: true,
    86         label: runButtonText,
    87         accessKey: "",
    88         callback: function() {
    89           aLauncher.saveToDisk(null, false);
    90           Services.obs.notifyObservers(aLauncher.targetFile, "dl-run", "true");
    91         }
    92       },
    93       {
    94         label: saveButtonText,
    95         accessKey: "",
    96         callback: function() {
    97           aLauncher.saveToDisk(null, false);
    98           Services.obs.notifyObservers(aLauncher.targetFile, "dl-run", "false");
    99         }
   100       },
   101       {
   102         label: cancelButtonText,
   103         accessKey: "",
   104         callback: function() { aLauncher.cancel(Cr.NS_BINDING_ABORTED); }
   105       }
   106     ];
   108     let window = Services.wm.getMostRecentWindow("navigator:browser");
   109     let chromeWin = this._getChromeWindow(window).wrappedJSObject;
   110     let notificationBox = chromeWin.Browser.getNotificationBox();
   111     let document = notificationBox.ownerDocument;
   112     let downloadSize = this._getDownloadSize(aLauncher.contentLength);
   114     let msg = browserBundle.GetStringFromName("alertDownloadSave2");
   116     let fragment =  ContentUtil.populateFragmentFromString(
   117                       document.createDocumentFragment(),
   118                       msg,
   119                       {
   120                         text: aLauncher.suggestedFileName,
   121                         className: "download-filename-text"
   122                       },
   123                       {
   124                         text: downloadSize,
   125                         className: "download-size-text"
   126                       },
   127                       {
   128                         text: aLauncher.source.host,
   129                         className: "download-host-text"
   130                       }
   131                     );
   132     let newBar = notificationBox.appendNotification("",
   133                                                     "save-download",
   134                                                     URI_GENERIC_ICON_DOWNLOAD,
   135                                                     notificationBox.PRIORITY_WARNING_HIGH,
   136                                                     buttons);
   137     let messageContainer = document.getAnonymousElementByAttribute(newBar, "anonid", "messageText");
   138     messageContainer.appendChild(fragment);
   139   },
   141   promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
   142     throw new Components.Exception("Async version must be used", Cr.NS_ERROR_NOT_AVAILABLE);
   143   },
   145   promptForSaveToFileAsync: function hald_promptForSaveToFileAsync(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
   146     let file = null;
   147     let prefs = Services.prefs;
   149     Task.spawn(function() {
   150       if (!aForcePrompt) {
   151         // Check to see if the user wishes to auto save to the default download
   152         // folder without prompting. Note that preference might not be set.
   153         let autodownload = true;
   154         try {
   155           autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
   156         } catch (e) { }
   158         if (autodownload) {
   159           // Retrieve the user's preferred download directory
   160           let preferredDir = yield Downloads.getPreferredDownloadsDirectory();
   161           let defaultFolder = new FileUtils.File(preferredDir);
   163           try {
   164             file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
   165           }
   166           catch (e) {
   167           }
   169           // Check to make sure we have a valid directory, otherwise, prompt
   170           if (file) {
   171             aLauncher.saveDestinationAvailable(file);
   172             return;
   173           }
   174         }
   175       }
   177       // Use file picker to show dialog.
   178       let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
   179       let windowTitle = "";
   180       let parent = aContext.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
   181       picker.init(parent, windowTitle, Ci.nsIFilePicker.modeSave);
   182       picker.defaultString = aDefaultFile;
   184       if (aSuggestedFileExt) {
   185         // aSuggestedFileExtension includes the period, so strip it
   186         picker.defaultExtension = aSuggestedFileExt.substring(1);
   187       }
   188       else {
   189         try {
   190           picker.defaultExtension = aLauncher.MIMEInfo.primaryExtension;
   191         }
   192         catch (e) { }
   193       }
   195       let wildCardExtension = "*";
   196       if (aSuggestedFileExt) {
   197         wildCardExtension += aSuggestedFileExt;
   198         picker.appendFilter(aLauncher.MIMEInfo.description, wildCardExtension);
   199       }
   201       picker.appendFilters(Ci.nsIFilePicker.filterAll);
   203       // Default to lastDir if it is valid, otherwise use the user's preferred
   204       // downloads directory.  getPreferredDownloadsDirectory should always
   205       // return a valid directory string, so we can safely default to it.
   206       let preferredDir = yield Downloads.getPreferredDownloadsDirectory();
   207       picker.displayDirectory = new FileUtils.File(preferredDir);
   209       // The last directory preference may not exist, which will throw.
   210       try {
   211         let lastDir = prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
   212         if (isUsableDirectory(lastDir))
   213           picker.displayDirectory = lastDir;
   214       }
   215       catch (e) { }
   217       picker.open(function(aResult) {
   218         if (aResult == Ci.nsIFilePicker.returnCancel) {
   219           // null result means user cancelled.
   220           aLauncher.saveDestinationAvailable(null);
   221           return;
   222         }
   224         // Be sure to save the directory the user chose through the Save As...
   225         // dialog  as the new browser.download.dir since the old one
   226         // didn't exist.
   227         file = picker.file;
   229         if (file) {
   230           try {
   231             // Remove the file so that it's not there when we ensure non-existence later;
   232             // this is safe because for the file to exist, the user would have had to
   233             // confirm that he wanted the file overwritten.
   234             if (file.exists())
   235               file.remove(false);
   236           }
   237           catch (e) { }
   238           let newDir = file.parent.QueryInterface(Ci.nsILocalFile);
   239           prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir);
   240           file = this.validateLeafName(newDir, file.leafName, null);
   241         }
   242         aLauncher.saveDestinationAvailable(file);
   243       }.bind(this));
   244     }.bind(this));
   245   },
   247   validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) {
   248     if (!(aLocalFile && this.isUsableDirectory(aLocalFile)))
   249       return null;
   251     // Remove any leading periods, since we don't want to save hidden files
   252     // automatically.
   253     aLeafName = aLeafName.replace(/^\.+/, "");
   255     if (aLeafName == "")
   256       aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
   257     aLocalFile.append(aLeafName);
   259     this.makeFileUnique(aLocalFile);
   260     return aLocalFile;
   261   },
   263   makeFileUnique: function hald_makeFileUnique(aLocalFile) {
   264     try {
   265       // Note - this code is identical to that in
   266       //   toolkit/content/contentAreaUtils.js.
   267       // If you are updating this code, update that code too! We can't share code
   268       // here since this is called in a js component.
   269       var collisionCount = 0;
   270       while (aLocalFile.exists()) {
   271         collisionCount++;
   272         if (collisionCount == 1) {
   273           // Append "(2)" before the last dot in (or at the end of) the filename
   274           // special case .ext.gz etc files so we don't wind up with .tar(2).gz
   275           if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
   276             aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
   277           else
   278             aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
   279         }
   280         else {
   281           // replace the last (n) in the filename with (n+1)
   282           aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
   283         }
   284       }
   285       aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
   286     }
   287     catch (e) {
   288       dump("*** exception in validateLeafName: " + e + "\n");
   290       if (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED)
   291         throw e;
   293       if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
   294         aLocalFile.append("unnamed");
   295         if (aLocalFile.exists())
   296           aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
   297       }
   298     }
   299   },
   301   isUsableDirectory: function hald_isUsableDirectory(aDirectory) {
   302     return aDirectory.exists() && aDirectory.isDirectory() && aDirectory.isWritable();
   303   },
   304 };
   306 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]);

mercurial