Bug 1068660 - Add confirmation dialog to unblock downloads. r=Paolo r=MattN
authorAlex Bardas <alex.bardas@gmail.com>
Tue, 14 Oct 2014 11:19:00 +0200
changeset 210542 2640523d8ef80fb8818023b1f303cd1c497dab22
parent 210541 ff22254c233dc32b4c0db999bee11aed90b9c31f
child 210543 d369649bb2657762c504a0c8a6678051ce7b1f3f
push id50472
push userryanvm@gmail.com
push dateWed, 15 Oct 2014 19:53:39 +0000
treeherdermozilla-inbound@a280a03c9f3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersPaolo, MattN
bugs1068660
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1068660 - Add confirmation dialog to unblock downloads. r=Paolo r=MattN
browser/components/downloads/DownloadsCommon.jsm
browser/components/downloads/test/browser/browser.ini
browser/components/downloads/test/browser/browser_basic_functionality.js
browser/components/downloads/test/browser/browser_confirm_unblock_download.js
browser/components/downloads/test/browser/browser_first_download_panel.js
browser/components/downloads/test/browser/browser_overflow_anchor.js
browser/components/downloads/test/browser/head.js
toolkit/components/prompts/content/commonDialog.xul
toolkit/themes/linux/global/global.css
toolkit/themes/osx/global/global.css
toolkit/themes/windows/global/global.css
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -62,16 +62,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
                                   "resource:///modules/RecentWindow.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsLogger",
                                   "resource:///modules/DownloadsLogger.jsm");
 
 const nsIDM = Ci.nsIDownloadManager;
 
 const kDownloadsStringBundleUrl =
   "chrome://browser/locale/downloads/downloads.properties";
 
@@ -138,16 +140,23 @@ PrefObserver.register({
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsCommon
 
 /**
  * This object is exposed directly to the consumers of this JavaScript module,
  * and provides shared methods for all the instances of the user interface.
  */
 this.DownloadsCommon = {
+  /**
+   * Constants with the different types of unblock messages.
+   */
+  BLOCK_VERDICT_MALWARE: "Malware",
+  BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted",
+  BLOCK_VERDICT_UNCOMMON: "Uncommon",
+
   log: function DC_log(...aMessageArgs) {
     delete this.log;
     this.log = function DC_log(...aMessageArgs) {
       if (!PrefObserver.debug) {
         return;
       }
       DownloadsLogger.log.apply(DownloadsLogger, aMessageArgs);
     }
@@ -506,17 +515,79 @@ this.DownloadsCommon = {
           // If launch also fails (probably because it's not implemented), let
           // the OS handler try to open the parent.
           Cc["@mozilla.org/uriloader/external-protocol-service;1"]
             .getService(Ci.nsIExternalProtocolService)
             .loadUrl(NetUtil.newURI(parent));
         }
       }
     }
-  }
+  },
+
+  /**
+   * Displays an alert message box which asks the user if they want to
+   * unblock the downloaded file or not.
+   *
+   * @param aType
+   *        The type of malware the downloaded file contains.
+   * @param aOwnerWindow
+   *        The window with which this action is associated.
+   *
+   * @return True to unblock the file, false to keep the user safe and
+   *         cancel the operation.
+   */
+  confirmUnblockDownload: Task.async(function* DP_confirmUnblockDownload(aType, aOwnerWindow) {
+    let s = DownloadsCommon.strings;
+    let title = s.unblockHeader;
+    let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+                      (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
+    let type = "";
+    let message = s.unblockTip;
+    let okButton = s.unblockButtonContinue;
+    let cancelButton = s.unblockButtonCancel;
+
+    switch (aType) {
+      case this.BLOCK_VERDICT_MALWARE:
+        type = s.unblockTypeMalware;
+        break;
+      case this.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
+        type = s.unblockTypePotentiallyUnwanted;
+        break;
+      case this.BLOCK_VERDICT_UNCOMMON:
+        type = s.unblockTypeUncommon;
+        break;
+    }
+
+    if (type) {
+      message = type + "\n\n" + message;
+    }
+
+    Services.ww.registerNotification(function onOpen(subj, topic) {
+      if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+        // Make sure to listen for "DOMContentLoaded" because it is fired
+        // before the "load" event.
+        subj.addEventListener("DOMContentLoaded", function onLoad() {
+          subj.removeEventListener("DOMContentLoaded", onLoad);
+          if (subj.document.documentURI ==
+              "chrome://global/content/commonDialog.xul") {
+            Services.ww.unregisterNotification(onOpen);
+            let dialog = subj.document.getElementById("commonDialog");
+            if (dialog) {
+              // Change the dialog to use a warning icon.
+              dialog.classList.add("alert-dialog");
+            }
+          }
+        });
+      }
+    });
+
+    let rv = Services.prompt.confirmEx(aOwnerWindow, title, message, buttonFlags,
+                                       cancelButton, okButton, null, null, {});
+    return (rv == 1);
+  }),
 };
 
 /**
  * Returns true if we are executing on Windows Vista or a later version.
  */
 XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
   let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
   if (os != "WINNT") {
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -2,8 +2,9 @@
 support-files = head.js
 
 [browser_basic_functionality.js]
 skip-if = buildapp == "mulet" || e10s
 [browser_first_download_panel.js]
 skip-if = os == "linux" # Bug 949434
 [browser_overflow_anchor.js]
 skip-if = os == "linux" # Bug 952422
+[browser_confirm_unblock_download.js]
--- a/browser/components/downloads/test/browser/browser_basic_functionality.js
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -1,57 +1,55 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+registerCleanupFunction(function*() {
+  yield task_resetState();
+});
+
 /**
  * Make sure the downloads panel can display items in the right order and
  * contains the expected data.
  */
-function test_task()
-{
+add_task(function* test_basic_functionality() {
   // Display one of each download state.
   const DownloadData = [
     { state: nsIDM.DOWNLOAD_NOTSTARTED },
     { state: nsIDM.DOWNLOAD_PAUSED },
     { state: nsIDM.DOWNLOAD_FINISHED },
     { state: nsIDM.DOWNLOAD_FAILED },
     { state: nsIDM.DOWNLOAD_CANCELED },
   ];
 
-  try {
-    // Wait for focus first
-    yield promiseFocus();
+  // Wait for focus first
+  yield promiseFocus();
 
-    // Ensure that state is reset in case previous tests didn't finish.
-    yield task_resetState();
+  // Ensure that state is reset in case previous tests didn't finish.
+  yield task_resetState();
 
-    // For testing purposes, show all the download items at once.
-    var originalCountLimit = DownloadsView.kItemCountLimit;
-    DownloadsView.kItemCountLimit = DownloadData.length;
-    registerCleanupFunction(function () {
-      DownloadsView.kItemCountLimit = originalCountLimit;
-    });
+  // For testing purposes, show all the download items at once.
+  var originalCountLimit = DownloadsView.kItemCountLimit;
+  DownloadsView.kItemCountLimit = DownloadData.length;
+  registerCleanupFunction(function () {
+    DownloadsView.kItemCountLimit = originalCountLimit;
+  });
 
-    // Populate the downloads database with the data required by this test.
-    yield task_addDownloads(DownloadData);
+  // Populate the downloads database with the data required by this test.
+  yield task_addDownloads(DownloadData);
 
-    // Open the user interface and wait for data to be fully loaded.
-    yield task_openPanel();
+  // Open the user interface and wait for data to be fully loaded.
+  yield task_openPanel();
 
-    // Test item data and count.  This also tests the ordering of the display.
-    let richlistbox = document.getElementById("downloadsListBox");
-/* disabled for failing intermittently (bug 767828)
+  // Test item data and count.  This also tests the ordering of the display.
+  let richlistbox = document.getElementById("downloadsListBox");
+  /* disabled for failing intermittently (bug 767828)
     is(richlistbox.children.length, DownloadData.length,
        "There is the correct number of richlistitems");
-*/
-    let itemCount = richlistbox.children.length;
-    for (let i = 0; i < itemCount; i++) {
-      let element = richlistbox.children[itemCount - i - 1];
-      let dataItem = new DownloadsViewItemController(element).dataItem;
-      is(dataItem.state, DownloadData[i].state, "Download states match up");
-    }
-  } finally {
-    // Clean up when the test finishes.
-    yield task_resetState();
+  */
+  let itemCount = richlistbox.children.length;
+  for (let i = 0; i < itemCount; i++) {
+    let element = richlistbox.children[itemCount - i - 1];
+    let dataItem = new DownloadsViewItemController(element).dataItem;
+    is(dataItem.state, DownloadData[i].state, "Download states match up");
   }
-}
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/browser_confirm_unblock_download.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the dialog which allows the user to unblock a downloaded file.
+
+registerCleanupFunction(() => {});
+
+function addDialogOpenObserver(buttonAction) {
+  Services.ww.registerNotification(function onOpen(subj, topic, data) {
+    if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+      // The test listens for the "load" event which guarantees that the alert
+      // class has already been added (it is added when "DOMContentLoaded" is
+      // fired).
+      subj.addEventListener("load", function onLoad() {
+        subj.removeEventListener("load", onLoad);
+        if (subj.document.documentURI ==
+            "chrome://global/content/commonDialog.xul") {
+          Services.ww.unregisterNotification(onOpen);
+
+          let dialog = subj.document.getElementById("commonDialog");
+          ok(dialog.classList.contains("alert-dialog"),
+             "The dialog element should contain an alert class.");
+
+          let doc = subj.document.documentElement;
+          doc.getButton(buttonAction).click();
+        }
+      });
+    }
+  });
+}
+
+add_task(function* test_confirm_unblock_dialog_unblock() {
+  addDialogOpenObserver("cancel");
+  let result = yield DownloadsCommon.confirmUnblockDownload(DownloadsCommon.UNBLOCK_MALWARE,
+                                                            window);
+  ok(result, "Should return true when the user clicks on `Unblock` button.");
+});
+
+add_task(function* test_confirm_unblock_dialog_keep_safe() {
+  addDialogOpenObserver("accept");
+  let result = yield DownloadsCommon.confirmUnblockDownload(DownloadsCommon.UNBLOCK_MALWARE,
+                                                            window);
+  ok(!result, "Should return false when the user clicks on `Keep me safe` button.");
+});
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -3,63 +3,55 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Make sure the downloads panel only opens automatically on the first
  * download it notices. All subsequent downloads, even across sessions, should
  * not open the panel automatically.
  */
-function test_task()
-{
+add_task(function* test_first_download_panel() {
   // Clear the download panel has shown preference first as this test is used to
   // verify this preference's behaviour.
-  let oldPrefValue = true;
-  try {
-    oldPrefValue = Services.prefs.getBoolPref("browser.download.panel.shown");
-  } catch(ex) { }
+  let oldPrefValue = Services.prefs.getBoolPref("browser.download.panel.shown");
   Services.prefs.setBoolPref("browser.download.panel.shown", false);
 
-  try {
-    // Ensure that state is reset in case previous tests didn't finish.
+  registerCleanupFunction(function*() {
+    // Clean up when the test finishes.
     yield task_resetState();
 
-    // With this set to false, we should automatically open the panel the first
-    // time a download is started.
-    DownloadsCommon.getData(window).panelHasShownBefore = false;
-
-    let promise = promisePanelOpened();
-    DownloadsCommon.getData(window)._notifyDownloadEvent("start");
-    yield promise;
-
-    // If we got here, that means the panel opened.
-    DownloadsPanel.hidePanel();
-
-    ok(DownloadsCommon.getData(window).panelHasShownBefore,
-       "Should have recorded that the panel was opened on a download.")
-
-    // Next, make sure that if we start another download, we don't open the
-    // panel automatically.
-    let originalOnPopupShown = DownloadsPanel.onPopupShown;
-    DownloadsPanel.onPopupShown = function () {
-      originalOnPopupShown.apply(this, arguments);
-      ok(false, "Should not have opened the downloads panel.");
-    };
-
-    try {
-      DownloadsCommon.getData(window)._notifyDownloadEvent("start");
-
-      // Wait 2 seconds to ensure that the panel does not open.
-      let deferTimeout = Promise.defer();
-      setTimeout(deferTimeout.resolve, 2000);
-      yield deferTimeout.promise;
-    } finally {
-      DownloadsPanel.onPopupShown = originalOnPopupShown;
-    }
-  } finally {
-    // Clean up when the test finishes.
-    yield task_resetState();
     // Set the preference instead of clearing it afterwards to ensure the
     // right value is used no matter what the default was. This ensures the
     // panel doesn't appear and affect other tests.
     Services.prefs.setBoolPref("browser.download.panel.shown", oldPrefValue);
-  }
-}
+  });
+
+  // Ensure that state is reset in case previous tests didn't finish.
+  yield task_resetState();
+
+  // With this set to false, we should automatically open the panel the first
+  // time a download is started.
+  DownloadsCommon.getData(window).panelHasShownBefore = false;
+
+  let promise = promisePanelOpened();
+  DownloadsCommon.getData(window)._notifyDownloadEvent("start");
+  yield promise;
+
+  // If we got here, that means the panel opened.
+  DownloadsPanel.hidePanel();
+
+  ok(DownloadsCommon.getData(window).panelHasShownBefore,
+     "Should have recorded that the panel was opened on a download.")
+
+  // Next, make sure that if we start another download, we don't open the
+  // panel automatically.
+  let originalOnPopupShown = DownloadsPanel.onPopupShown;
+  DownloadsPanel.onPopupShown = function () {
+    originalOnPopupShown.apply(this, arguments);
+    ok(false, "Should not have opened the downloads panel.");
+  };
+
+  DownloadsCommon.getData(window)._notifyDownloadEvent("start");
+
+  // Wait 2 seconds to ensure that the panel does not open.
+  yield new Promise(resolve => setTimeout(resolve, 2000));
+  DownloadsPanel.onPopupShown = originalOnPopupShown;
+});
--- a/browser/components/downloads/test/browser/browser_overflow_anchor.js
+++ b/browser/components/downloads/test/browser/browser_overflow_anchor.js
@@ -1,74 +1,74 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+registerCleanupFunction(function*() {
+  // Clean up when the test finishes.
+  yield task_resetState();
+});
+
 /**
  * Make sure the downloads button and indicator overflows into the nav-bar
  * chevron properly, and then when those buttons are clicked in the overflow
  * panel that the downloads panel anchors to the chevron.
  */
-function test_task() {
-  try {
-    // Ensure that state is reset in case previous tests didn't finish.
-    yield task_resetState();
+add_task(function* test_overflow_anchor() {
+  // Ensure that state is reset in case previous tests didn't finish.
+  yield task_resetState();
 
-    // Record the original width of the window so we can put it back when
-    // this test finishes.
-    let oldWidth = window.outerWidth;
+  // Record the original width of the window so we can put it back when
+  // this test finishes.
+  let oldWidth = window.outerWidth;
 
-    // The downloads button should not be overflowed to begin with.
-    let button = CustomizableUI.getWidget("downloads-button")
-                               .forWindow(window);
-    ok(!button.overflowed, "Downloads button should not be overflowed.");
+  // The downloads button should not be overflowed to begin with.
+  let button = CustomizableUI.getWidget("downloads-button")
+                             .forWindow(window);
+  ok(!button.overflowed, "Downloads button should not be overflowed.");
 
-    // Hack - we lock the size of the default flex-y items in the nav-bar,
-    // namely, the URL and search inputs. That way we can resize the
-    // window without worrying about them flexing.
-    const kFlexyItems = ["urlbar-container", "search-container"];
-    registerCleanupFunction(() => unlockWidth(kFlexyItems));
-    lockWidth(kFlexyItems);
+  // Hack - we lock the size of the default flex-y items in the nav-bar,
+  // namely, the URL and search inputs. That way we can resize the
+  // window without worrying about them flexing.
+  const kFlexyItems = ["urlbar-container", "search-container"];
+  registerCleanupFunction(() => unlockWidth(kFlexyItems));
+  lockWidth(kFlexyItems);
 
-    // Resize the window to half of its original size. That should
-    // be enough to overflow the downloads button.
-    window.resizeTo(oldWidth / 2, window.outerHeight);
-    yield waitForOverflowed(button, true);
+  // Resize the window to half of its original size. That should
+  // be enough to overflow the downloads button.
+  window.resizeTo(oldWidth / 2, window.outerHeight);
+  yield waitForOverflowed(button, true);
 
-    let promise = promisePanelOpened();
-    button.node.doCommand();
-    yield promise;
-
-    let panel = DownloadsPanel.panel;
-    let chevron = document.getElementById("nav-bar-overflow-button");
-    is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
+  let promise = promisePanelOpened();
+  button.node.doCommand();
+  yield promise;
 
-    DownloadsPanel.hidePanel();
+  let panel = DownloadsPanel.panel;
+  let chevron = document.getElementById("nav-bar-overflow-button");
+  is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
 
-    // Unlock the widths on the flex-y items.
-    unlockWidth(kFlexyItems);
+  DownloadsPanel.hidePanel();
 
-    // Put the window back to its original dimensions.
-    window.resizeTo(oldWidth, window.outerHeight);
+  // Unlock the widths on the flex-y items.
+  unlockWidth(kFlexyItems);
 
-    // The downloads button should eventually be un-overflowed.
-    yield waitForOverflowed(button, false);
+  // Put the window back to its original dimensions.
+  window.resizeTo(oldWidth, window.outerHeight);
 
-    // Now try opening the panel again.
-    promise = promisePanelOpened();
-    button.node.doCommand();
-    yield promise;
+  // The downloads button should eventually be un-overflowed.
+  yield waitForOverflowed(button, false);
 
-    is(panel.anchorNode.id, "downloads-indicator-anchor");
+  // Now try opening the panel again.
+  promise = promisePanelOpened();
+  button.node.doCommand();
+  yield promise;
 
-    DownloadsPanel.hidePanel();
-  } finally {
-    // Clean up when the test finishes.
-    yield task_resetState();
-  }
-}
+  is(panel.anchorNode.id, "downloads-indicator-anchor");
+
+  DownloadsPanel.hidePanel();
+});
 
 /**
  * For some node IDs, finds the nodes and sets their min-width's to their
  * current width, preventing them from flex-shrinking.
  *
  * @param aItemIDs an array of item IDs to set min-width on.
  */
 function lockWidth(aItemIDs) {
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -24,25 +24,16 @@ const nsIDM = Ci.nsIDownloadManager;
 
 let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
 gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
 registerCleanupFunction(function () {
   gTestTargetFile.remove(false);
 });
 
 ////////////////////////////////////////////////////////////////////////////////
-//// Infrastructure
-
-function test()
-{
-  waitForExplicitFinish();
-  Task.spawn(test_task).then(null, ex => ok(false, ex)).then(finish);
-}
-
-////////////////////////////////////////////////////////////////////////////////
 //// Asynchronous support subroutines
 
 function promiseFocus()
 {
   let deferred = Promise.defer();
   waitForFocus(deferred.resolve);
   return deferred.promise;
 }
--- a/toolkit/components/prompts/content/commonDialog.xul
+++ b/toolkit/components/prompts/content/commonDialog.xul
@@ -8,26 +8,30 @@
 <?xml-stylesheet href="chrome://global/content/commonDialog.css" type="text/css"?>
 <?xml-stylesheet href="chrome://global/skin/commonDialog.css" type="text/css"?>
 
 <!DOCTYPE dialog SYSTEM "chrome://global/locale/commonDialog.dtd">
 
 <dialog id="commonDialog"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         aria-describedby="info.body"
-        onload="commonDialogOnLoad();"
         onunload="commonDialogOnUnload();"
         ondialogaccept="Dialog.onButton0(); return true;"
         ondialogcancel="Dialog.onButton1(); return true;"
         ondialogextra1="Dialog.onButton2(); window.close();"
         ondialogextra2="Dialog.onButton3(); window.close();"
         buttonpack="center">
 
   <script type="application/javascript" src="chrome://global/content/commonDialog.js"/>
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+  <script type="application/javascript">
+    document.addEventListener("DOMContentLoaded", function() {
+      commonDialogOnLoad();
+    });
+  </script>
 
   <commandset id="selectEditMenuItems">
     <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')" disabled="true"/>
     <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
   </commandset>
 
   <popupset id="contentAreaContextSet">
     <menupopup id="contentAreaContextMenu"
--- a/toolkit/themes/linux/global/global.css
+++ b/toolkit/themes/linux/global/global.css
@@ -55,16 +55,17 @@ window.dialog {
 }
 
 /* ::::: alert icons :::::*/
 
 .message-icon {
   list-style-image: url("moz-icon://stock/gtk-dialog-info?size=dialog");
 }
 
+.alert-dialog #info\.icon,
 .alert-icon {
   list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=dialog");
 }
 
 .error-icon {
   list-style-image: url("moz-icon://stock/gtk-dialog-error?size=dialog");
 }
 
--- a/toolkit/themes/osx/global/global.css
+++ b/toolkit/themes/osx/global/global.css
@@ -73,16 +73,17 @@ window.dialog {
   margin: 6px;
   -moz-margin-end: 20px;
 }
 
 .message-icon {
   list-style-image: url("chrome://global/skin/icons/information-64.png");
 }
 
+.alert-dialog #info\.icon,
 .alert-icon {
   list-style-image: url("chrome://global/skin/icons/warning-64.png");
 }
 
 .error-icon {
   list-style-image: url("chrome://global/skin/icons/error-64.png");
 }
 
--- a/toolkit/themes/windows/global/global.css
+++ b/toolkit/themes/windows/global/global.css
@@ -51,16 +51,17 @@ window.dialog {
   width: 32px;
   height: 32px;
 }
 
 .message-icon {
   list-style-image: url("chrome://global/skin/icons/information-32.png");
 }
 
+.alert-dialog #info\.icon,
 .alert-icon {
   list-style-image: url("chrome://global/skin/icons/Warning.png");
 }
 
 .error-icon {
   list-style-image: url("chrome://global/skin/icons/Error.png");
 }