Bug 1231128 Display errors when temporary add-on install fails draft
authorAndrew Swan <aswan@mozilla.com>
Thu, 31 Mar 2016 20:53:17 -0700
changeset 348074 7d762bb0d8e36ebfb0f2aed245d9a6f4fa4a9197
parent 346302 bccb11375f2af838cda714d42fd8cef78f5c7bf1
child 517776 3ef40b56a54b46d8d5e3b9471e4c0a6890acf34c
push id14742
push useraswan@mozilla.com
push dateWed, 06 Apr 2016 14:34:21 +0000
bugs1231128
milestone48.0a1
Bug 1231128 Display errors when temporary add-on install fails MozReview-Commit-ID: BG81hWHLGQ2
devtools/client/aboutdebugging/aboutdebugging.css
devtools/client/aboutdebugging/components/addons-controls.js
devtools/client/aboutdebugging/components/addons-install-error.js
devtools/client/aboutdebugging/components/moz.build
devtools/client/aboutdebugging/test/addons/bad/manifest.json
devtools/client/aboutdebugging/test/browser.ini
devtools/client/aboutdebugging/test/browser_addons_install.js
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -75,16 +75,37 @@ button {
   flex: 1;
 }
 
 .addons-controls {
   display: flex;
   flex-direction: row;
 }
 
+.addons-install-error {
+  background-color: #f3b0b0;
+  padding: 5px 10px;
+  margin: 5px 4px 5px 0px;
+}
+
+.addons-install-error .warning {
+  background-image: url(chrome://devtools/skin/images/alerticon-warning.png);
+  background-size: 13px 12px;
+  margin-right: 10px;
+  display: inline-block;
+  width: 13px;
+  height: 12px;
+}
+
+@media (min-resolution: 1.1dppx) {
+  .addons-install-error .warning {
+    background-image: url(chrome://devtools/skin/images/alerticon-warning@2x.png);
+  }
+}
+
 .addons-options {
   flex: 1;
 }
 
 .addons-debugging-label {
   display: inline-block;
   margin: 0 5px 5px 0;
 }
--- a/devtools/client/aboutdebugging/components/addons-controls.js
+++ b/devtools/client/aboutdebugging/components/addons-controls.js
@@ -6,33 +6,42 @@
 /* globals AddonManager */
 
 "use strict";
 
 loader.lazyImporter(this, "AddonManager",
   "resource://gre/modules/AddonManager.jsm");
 
 const { Cc, Ci } = require("chrome");
-const { createClass, DOM: dom } =
+const { createFactory, createClass, DOM: dom } =
   require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
+const AddonsInstallError = createFactory(require("./addons-install-error"));
+
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
                       "/about:debugging#Enabling_add-on_debugging";
 
 module.exports = createClass({
   displayName: "AddonsControls",
 
+  getInitialState() {
+    return {
+      installError: null,
+    };
+  },
+
   render() {
     let { debugDisabled } = this.props;
 
-    return dom.div({ className: "addons-controls" },
+    return dom.div({ className: "addons-top" },
+      dom.div({ className: "addons-controls" },
         dom.div({ className: "addons-options" },
           dom.input({
             id: "enable-addon-debugging",
             type: "checkbox",
             checked: !debugDisabled,
             onChange: this.onEnableAddonDebuggingChange,
           }),
           dom.label({
@@ -44,40 +53,41 @@ module.exports = createClass({
           dom.a({ href: MORE_INFO_URL, target: "_blank" },
             Strings.GetStringFromName("addonDebugging.moreInfo")),
           ")"
         ),
         dom.button({
           id: "load-addon-from-file",
           onClick: this.loadAddonFromFile,
         }, Strings.GetStringFromName("loadTemporaryAddon"))
-      );
+      ),
+      AddonsInstallError({ error: this.state.installError }));
   },
 
   onEnableAddonDebuggingChange(event) {
     let enabled = event.target.checked;
     Services.prefs.setBoolPref("devtools.chrome.enabled", enabled);
     Services.prefs.setBoolPref("devtools.debugger.remote-enabled", enabled);
   },
 
   loadAddonFromFile() {
+    this.setState({ installError: null });
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     fp.init(window,
       Strings.GetStringFromName("selectAddonFromFile2"),
       Ci.nsIFilePicker.modeOpen);
     let res = fp.show();
     if (res == Ci.nsIFilePicker.returnCancel || !fp.file) {
       return;
     }
     let file = fp.file;
     // AddonManager.installTemporaryAddon accepts either
     // addon directory or final xpi file.
     if (!file.isDirectory() && !file.leafName.endsWith(".xpi")) {
       file = file.parent;
     }
-    try {
-      AddonManager.installTemporaryAddon(file);
-    } catch (e) {
-      window.alert("Error while installing the addon:\n" + e.message + "\n");
-      throw e;
-    }
+
+    AddonManager.installTemporaryAddon(file)
+      .catch(e => {
+        this.setState({ installError: e.message });
+      });
   },
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/components/addons-install-error.js
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* eslint-env browser */
+"use strict";
+
+const { createClass, DOM: dom } = require("devtools/client/shared/vendor/react");
+
+module.exports = createClass({
+  displayName: "AddonsInstallError",
+
+  render() {
+    if (!this.props.error) {
+      return null;
+    }
+    let text = `There was an error during installation: ${this.props.error}`;
+    return dom.div({ className: "addons-install-error" },
+                   dom.div({ className: "warning" }),
+                   dom.span({}, text));
+  }
+});
--- a/devtools/client/aboutdebugging/components/moz.build
+++ b/devtools/client/aboutdebugging/components/moz.build
@@ -1,16 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'aboutdebugging.js',
     'addon-target.js',
     'addons-controls.js',
+    'addons-install-error.js',
     'addons-tab.js',
     'tab-header.js',
     'tab-menu-entry.js',
     'tab-menu.js',
     'target-list.js',
     'worker-target.js',
     'workers-tab.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/addons/bad/manifest.json
@@ -0,0 +1,1 @@
+this is not valid json
--- a/devtools/client/aboutdebugging/test/browser.ini
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   addons/unpacked/bootstrap.js
   addons/unpacked/install.rdf
+  addons/bad/manifest.json
   service-workers/empty-sw.html
   service-workers/empty-sw.js
   service-workers/push-sw.html
   service-workers/push-sw.js
 
 [browser_addons_debugging_initial_state.js]
 [browser_addons_install.js]
 [browser_addons_toggle_debug.js]
--- a/devtools/client/aboutdebugging/test/browser_addons_install.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_install.js
@@ -23,8 +23,35 @@ add_task(function* () {
   names = [...document.querySelectorAll("#addons .target-name")];
   names = names.map(element => element.textContent);
   ok(!names.includes(ADDON_NAME),
     "After uninstall, the addon name disappears from the list of addons: "
     + names);
 
   yield closeAboutDebugging(tab);
 });
+
+add_task(function* () {
+  let { tab, document } = yield openAboutDebugging("addons");
+
+  // Start an observer that looks for the install error before
+  // actually doing the install
+  let top = document.querySelector(".addons-top");
+  let promise = waitForMutation(top, { childList: true });
+
+  // Mock the file picker to select a test addon
+  let MockFilePicker = SpecialPowers.MockFilePicker;
+  MockFilePicker.init(null);
+  let file = getSupportsFile("addons/bad/manifest.json");
+  MockFilePicker.returnFiles = [file.file];
+
+  // Trigger the file picker by clicking on the button
+  document.getElementById("load-addon-from-file").click();
+
+  // Now wait for the install error to appear.
+  yield promise;
+
+  // And check that it really is there.
+  let err = document.querySelector(".addons-install-error");
+  isnot(err, null, "Addon install error message appeared");
+
+  yield closeAboutDebugging(tab);
+});