diff options
Diffstat (limited to 'devtools/client/aboutdebugging/components/addons')
5 files changed, 363 insertions, 0 deletions
diff --git a/devtools/client/aboutdebugging/components/addons/controls.js b/devtools/client/aboutdebugging/components/addons/controls.js new file mode 100644 index 000000000..7f985528c --- /dev/null +++ b/devtools/client/aboutdebugging/components/addons/controls.js @@ -0,0 +1,97 @@ +/* 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 */ +/* globals AddonManager */ + +"use strict"; + +loader.lazyImporter(this, "AddonManager", + "resource://gre/modules/AddonManager.jsm"); + +const { Cc, Ci } = require("chrome"); +const { createFactory, createClass, DOM: dom, PropTypes } = + require("devtools/client/shared/vendor/react"); +const Services = require("Services"); +const AddonsInstallError = createFactory(require("./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", + + propTypes: { + debugDisabled: PropTypes.bool + }, + + getInitialState() { + return { + installError: null, + }; + }, + + 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; + } + + AddonManager.installTemporaryAddon(file) + .catch(e => { + console.error(e); + this.setState({ installError: e.message }); + }); + }, + + render() { + let { debugDisabled } = this.props; + + return dom.div({ className: "addons-top" }, + dom.div({ className: "addons-controls" }, + dom.div({ className: "addons-options toggle-container-with-text" }, + dom.input({ + id: "enable-addon-debugging", + type: "checkbox", + checked: !debugDisabled, + onChange: this.onEnableAddonDebuggingChange, + }), + dom.label({ + className: "addons-debugging-label", + htmlFor: "enable-addon-debugging", + title: Strings.GetStringFromName("addonDebugging.tooltip") + }, Strings.GetStringFromName("addonDebugging.label")), + "(", + dom.a({ href: MORE_INFO_URL, target: "_blank" }, + Strings.GetStringFromName("moreInfo")), + ")" + ), + dom.button({ + id: "load-addon-from-file", + onClick: this.loadAddonFromFile, + }, Strings.GetStringFromName("loadTemporaryAddon")) + ), + AddonsInstallError({ error: this.state.installError })); + } +}); 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 000000000..aea1c4f09 --- /dev/null +++ b/devtools/client/aboutdebugging/components/addons/install-error.js @@ -0,0 +1,26 @@ +/* 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, PropTypes } = require("devtools/client/shared/vendor/react"); + +module.exports = createClass({ + displayName: "AddonsInstallError", + + propTypes: { + error: PropTypes.string + }, + + 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/addons/moz.build b/devtools/client/aboutdebugging/components/addons/moz.build new file mode 100644 index 000000000..378554f78 --- /dev/null +++ b/devtools/client/aboutdebugging/components/addons/moz.build @@ -0,0 +1,10 @@ +# 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( + 'controls.js', + 'install-error.js', + 'panel.js', + 'target.js', +) diff --git a/devtools/client/aboutdebugging/components/addons/panel.js b/devtools/client/aboutdebugging/components/addons/panel.js new file mode 100644 index 000000000..425a10a8d --- /dev/null +++ b/devtools/client/aboutdebugging/components/addons/panel.js @@ -0,0 +1,146 @@ +/* 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/. */ + +"use strict"; + +const { AddonManager } = require("resource://gre/modules/AddonManager.jsm"); +const { createFactory, createClass, DOM: dom, PropTypes } = + require("devtools/client/shared/vendor/react"); +const Services = require("Services"); + +const AddonsControls = createFactory(require("./controls")); +const AddonTarget = createFactory(require("./target")); +const PanelHeader = createFactory(require("../panel-header")); +const TargetList = createFactory(require("../target-list")); + +loader.lazyRequireGetter(this, "DebuggerClient", + "devtools/shared/client/main", true); + +const Strings = Services.strings.createBundle( + "chrome://devtools/locale/aboutdebugging.properties"); + +const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg"; +const CHROME_ENABLED_PREF = "devtools.chrome.enabled"; +const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled"; + +module.exports = createClass({ + displayName: "AddonsPanel", + + propTypes: { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + id: PropTypes.string.isRequired + }, + + getInitialState() { + return { + extensions: [], + debugDisabled: false, + }; + }, + + componentDidMount() { + AddonManager.addAddonListener(this); + + Services.prefs.addObserver(CHROME_ENABLED_PREF, + this.updateDebugStatus, false); + Services.prefs.addObserver(REMOTE_ENABLED_PREF, + this.updateDebugStatus, false); + + this.updateDebugStatus(); + this.updateAddonsList(); + }, + + componentWillUnmount() { + AddonManager.removeAddonListener(this); + Services.prefs.removeObserver(CHROME_ENABLED_PREF, + this.updateDebugStatus); + Services.prefs.removeObserver(REMOTE_ENABLED_PREF, + this.updateDebugStatus); + }, + + updateDebugStatus() { + let debugDisabled = + !Services.prefs.getBoolPref(CHROME_ENABLED_PREF) || + !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF); + + this.setState({ debugDisabled }); + }, + + updateAddonsList() { + this.props.client.listAddons() + .then(({addons}) => { + let extensions = addons.filter(addon => addon.debuggable).map(addon => { + return { + name: addon.name, + icon: addon.iconURL || ExtensionIcon, + addonID: addon.id, + addonActor: addon.actor, + temporarilyInstalled: addon.temporarilyInstalled + }; + }); + + this.setState({ extensions }); + }, error => { + throw new Error("Client error while listing addons: " + error); + }); + }, + + /** + * Mandatory callback as AddonManager listener. + */ + onInstalled() { + this.updateAddonsList(); + }, + + /** + * Mandatory callback as AddonManager listener. + */ + onUninstalled() { + this.updateAddonsList(); + }, + + /** + * Mandatory callback as AddonManager listener. + */ + onEnabled() { + this.updateAddonsList(); + }, + + /** + * Mandatory callback as AddonManager listener. + */ + onDisabled() { + this.updateAddonsList(); + }, + + render() { + let { client, id } = this.props; + let { debugDisabled, extensions: targets } = this.state; + let name = Strings.GetStringFromName("extensions"); + let targetClass = AddonTarget; + + return dom.div({ + id: id + "-panel", + className: "panel", + role: "tabpanel", + "aria-labelledby": id + "-header" + }, + PanelHeader({ + id: id + "-header", + name: Strings.GetStringFromName("addons") + }), + AddonsControls({ debugDisabled }), + dom.div({ id: "addons" }, + TargetList({ + id: "extensions", + name, + targets, + client, + debugDisabled, + targetClass, + sort: true + }) + )); + } +}); diff --git a/devtools/client/aboutdebugging/components/addons/target.js b/devtools/client/aboutdebugging/components/addons/target.js new file mode 100644 index 000000000..c21499650 --- /dev/null +++ b/devtools/client/aboutdebugging/components/addons/target.js @@ -0,0 +1,84 @@ +/* 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, PropTypes } = + require("devtools/client/shared/vendor/react"); +const { debugAddon } = require("../../modules/addon"); +const Services = require("Services"); + +loader.lazyImporter(this, "BrowserToolboxProcess", + "resource://devtools/client/framework/ToolboxProcess.jsm"); + +loader.lazyRequireGetter(this, "DebuggerClient", + "devtools/shared/client/main", true); + +const Strings = Services.strings.createBundle( + "chrome://devtools/locale/aboutdebugging.properties"); + +module.exports = createClass({ + displayName: "AddonTarget", + + propTypes: { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + debugDisabled: PropTypes.bool, + target: PropTypes.shape({ + addonActor: PropTypes.string.isRequired, + addonID: PropTypes.string.isRequired, + icon: PropTypes.string, + name: PropTypes.string.isRequired, + temporarilyInstalled: PropTypes.bool + }).isRequired + }, + + debug() { + let { target } = this.props; + debugAddon(target.addonID); + }, + + reload() { + let { client, target } = this.props; + // This function sometimes returns a partial promise that only + // implements then(). + client.request({ + to: target.addonActor, + type: "reload" + }).then(() => {}, error => { + throw new Error( + "Error reloading addon " + target.addonID + ": " + error); + }); + }, + + render() { + let { target, debugDisabled } = this.props; + // Only temporarily installed add-ons can be reloaded. + const canBeReloaded = target.temporarilyInstalled; + + return dom.li({ className: "target-container" }, + dom.img({ + className: "target-icon", + role: "presentation", + src: target.icon + }), + dom.div({ className: "target" }, + dom.div({ className: "target-name", title: target.name }, target.name) + ), + dom.button({ + className: "debug-button", + onClick: this.debug, + disabled: debugDisabled, + }, Strings.GetStringFromName("debug")), + dom.button({ + className: "reload-button", + onClick: this.reload, + disabled: !canBeReloaded, + title: !canBeReloaded ? + Strings.GetStringFromName("reloadDisabledTooltip") : "" + }, Strings.GetStringFromName("reload")) + ); + } +}); |