Bug 1369771 - Confirm launch of executables other than .exe on Windows r?paolo draft
authorMark Striemer <mstriemer@mozilla.com>
Mon, 29 May 2017 16:38:54 -0500
changeset 589790 0c3fe094fdf5c862aec05ddeb35a26eab71b2d52
parent 586208 acca723541c25889cad344f19087983c9d31fa1b
child 632014 f794f3a87b04014becf56fad3a19122b073a3229
push id62514
push userbmo:mstriemer@mozilla.com
push dateTue, 06 Jun 2017 20:46:34 +0000
reviewerspaolo
bugs1369771
milestone55.0a1
Bug 1369771 - Confirm launch of executables other than .exe on Windows r?paolo MozReview-Commit-ID: 2TbLbdMc3d3
browser/components/downloads/DownloadsCommon.jsm
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -36,16 +36,18 @@ const { classes: Cc, interfaces: Ci, uti
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
                                   "resource://gre/modules/AppMenuNotifications.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
                                   "resource://gre/modules/DownloadUIHelper.jsm");
@@ -421,18 +423,22 @@ this.DownloadsCommon = {
     }
     if (aMimeInfo && !(aMimeInfo instanceof Ci.nsIMIMEInfo)) {
       throw new Error("Invalid value passed for aMimeInfo");
     }
     if (!(aOwnerWindow instanceof Ci.nsIDOMWindow)) {
       throw new Error("aOwnerWindow must be a dom-window object");
     }
 
+    let isWindowsExe = AppConstants.platform == "win" &&
+      aFile.leafName.toLowerCase().endsWith(".exe");
+
     let promiseShouldLaunch;
-    if (aFile.isExecutable()) {
+    // Don't prompt on Windows for .exe since there will be a native prompt.
+    if (aFile.isExecutable() && !isWindowsExe) {
       // We get a prompter for the provided window here, even though anchoring
       // to the most recently active window should work as well.
       promiseShouldLaunch =
         DownloadUIHelper.getPrompter(aOwnerWindow)
                         .confirmLaunchExecutable(aFile.path);
     } else {
       promiseShouldLaunch = Promise.resolve(true);
     }
--- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
+++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
@@ -23,16 +23,18 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/Integration.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
                                   "resource://gre/modules/DeferredTask.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore",
                                   "resource://gre/modules/DownloadStore.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadImport",
                                   "resource://gre/modules/DownloadImport.jsm");
@@ -630,39 +632,40 @@ this.DownloadIntegration = {
    *           the OS might still take a while until the file is actually
    *           launched.
    * @rejects  JavaScript exception if there was an error trying to launch
    *           the file.
    */
   async launchDownload(aDownload) {
     let file = new FileUtils.File(aDownload.target.path);
 
-#ifndef XP_WIN
-    // Ask for confirmation if the file is executable, except on Windows where
-    // the operating system will show the prompt based on the security zone.
-    // We do this here, instead of letting the caller handle the prompt
-    // separately in the user interface layer, for two reasons.  The first is
-    // because of its security nature, so that add-ons cannot forget to do
-    // this check.  The second is that the system-level security prompt would
-    // be displayed at launch time in any case.
-    if (file.isExecutable() &&
-        !(await this.confirmLaunchExecutable(file.path))) {
-      return;
-    }
-#endif
-
     // In case of a double extension, like ".tar.gz", we only
     // consider the last one, because the MIME service cannot
     // handle multiple extensions.
     let fileExtension = null, mimeInfo = null;
     let match = file.leafName.match(/\.([^.]+)$/);
     if (match) {
       fileExtension = match[1];
     }
 
+    let isWindowsExe = AppConstants.platform == "win" &&
+      fileExtension.toLowerCase() == "exe";
+
+    // Ask for confirmation if the file is executable, except for .exe on
+    // Windows where the operating system will show the prompt based on the
+    // security zone.  We do this here, instead of letting the caller handle
+    // the prompt separately in the user interface layer, for two reasons.  The
+    // first is because of its security nature, so that add-ons cannot forget
+    // to do this check.  The second is that the system-level security prompt
+    // would be displayed at launch time in any case.
+    if (file.isExecutable() && !isWindowsExe &&
+        !(await this.confirmLaunchExecutable(file.path))) {
+      return;
+    }
+
     try {
       // The MIME service might throw if contentType == "" and it can't find
       // a MIME type for the given extension, so we'll treat this case as
       // an unknown mimetype.
       mimeInfo = gMIMEService.getFromTypeAndExtension(aDownload.contentType,
                                                       fileExtension);
     } catch (e) { }