diff --git a/devtools/client/aboutdebugging/aboutdebugging.css b/devtools/client/aboutdebugging/aboutdebugging.css index 78c533d3fbb6..d1116c0d78dd 100644 --- a/devtools/client/aboutdebugging/aboutdebugging.css +++ b/devtools/client/aboutdebugging/aboutdebugging.css @@ -101,6 +101,27 @@ button { 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; } diff --git a/devtools/client/aboutdebugging/components/addons-controls.js b/devtools/client/aboutdebugging/components/addons-controls.js index 6ad6f03bf7d2..87f68007add1 100644 --- a/devtools/client/aboutdebugging/components/addons-controls.js +++ b/devtools/client/aboutdebugging/components/addons-controls.js @@ -11,10 +11,12 @@ 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"); @@ -24,10 +26,17 @@ const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" + 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", @@ -49,7 +58,8 @@ module.exports = createClass({ id: "load-addon-from-file", onClick: this.loadAddonFromFile, }, Strings.GetStringFromName("loadTemporaryAddon")) - ); + ), + AddonsInstallError({ error: this.state.installError })); }, onEnableAddonDebuggingChange(event) { @@ -59,6 +69,7 @@ module.exports = createClass({ }, loadAddonFromFile() { + this.setState({ installError: null }); let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); fp.init(window, Strings.GetStringFromName("selectAddonFromFile2"), @@ -73,11 +84,10 @@ module.exports = createClass({ 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 }); + }); }, }); diff --git a/devtools/client/aboutdebugging/components/addons-install-error.js b/devtools/client/aboutdebugging/components/addons-install-error.js new file mode 100644 index 000000000000..a196bbc2524f --- /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)); + } +}); diff --git a/devtools/client/aboutdebugging/components/moz.build b/devtools/client/aboutdebugging/components/moz.build index b41775dee23e..af519f2dd84c 100644 --- a/devtools/client/aboutdebugging/components/moz.build +++ b/devtools/client/aboutdebugging/components/moz.build @@ -6,6 +6,7 @@ DevToolsModules( 'aboutdebugging.js', 'addon-target.js', 'addons-controls.js', + 'addons-install-error.js', 'addons-tab.js', 'service-worker-target.js', 'tab-header.js', diff --git a/devtools/client/aboutdebugging/test/addons/bad/manifest.json b/devtools/client/aboutdebugging/test/addons/bad/manifest.json new file mode 100644 index 000000000000..4ab10b4de726 --- /dev/null +++ b/devtools/client/aboutdebugging/test/addons/bad/manifest.json @@ -0,0 +1 @@ +this is not valid json diff --git a/devtools/client/aboutdebugging/test/browser.ini b/devtools/client/aboutdebugging/test/browser.ini index b2dbd9da60b2..2079f6948185 100644 --- a/devtools/client/aboutdebugging/test/browser.ini +++ b/devtools/client/aboutdebugging/test/browser.ini @@ -5,6 +5,7 @@ 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 diff --git a/devtools/client/aboutdebugging/test/browser_addons_install.js b/devtools/client/aboutdebugging/test/browser_addons_install.js index 70a8e3a54534..64d236253cb8 100644 --- a/devtools/client/aboutdebugging/test/browser_addons_install.js +++ b/devtools/client/aboutdebugging/test/browser_addons_install.js @@ -28,3 +28,30 @@ add_task(function* () { 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); +});