michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const {Cu, Cc, Ci} = require("chrome"); michael@0: const Services = require("Services"); michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); michael@0: michael@0: exports.OptionsPanel = OptionsPanel; michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "l10n", function() { michael@0: let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties"); michael@0: let l10n = function(aName, ...aArgs) { michael@0: try { michael@0: if (aArgs.length == 0) { michael@0: return bundle.GetStringFromName(aName); michael@0: } else { michael@0: return bundle.formatStringFromName(aName, aArgs, aArgs.length); michael@0: } michael@0: } catch (ex) { michael@0: Services.console.logStringMessage("Error reading '" + aName + "'"); michael@0: } michael@0: }; michael@0: return l10n; michael@0: }); michael@0: michael@0: /** michael@0: * Represents the Options Panel in the Toolbox. michael@0: */ michael@0: function OptionsPanel(iframeWindow, toolbox) { michael@0: this.panelDoc = iframeWindow.document; michael@0: this.panelWin = iframeWindow; michael@0: this.toolbox = toolbox; michael@0: this.isReady = false; michael@0: michael@0: const EventEmitter = require("devtools/toolkit/event-emitter"); michael@0: EventEmitter.decorate(this); michael@0: } michael@0: michael@0: OptionsPanel.prototype = { michael@0: michael@0: get target() { michael@0: return this.toolbox.target; michael@0: }, michael@0: michael@0: open: function() { michael@0: let targetPromise; michael@0: michael@0: // For local debugging we need to make the target remote. michael@0: if (!this.target.isRemote) { michael@0: targetPromise = this.target.makeRemote(); michael@0: } else { michael@0: targetPromise = promise.resolve(this.target); michael@0: } michael@0: michael@0: return targetPromise.then(() => { michael@0: this.setupToolsList(); michael@0: this.setupToolbarButtonsList(); michael@0: this.populatePreferences(); michael@0: michael@0: this._disableJSClicked = this._disableJSClicked.bind(this); michael@0: this._disableCacheClicked = this._disableCacheClicked.bind(this); michael@0: michael@0: let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript"); michael@0: disableJSNode.addEventListener("click", this._disableJSClicked, false); michael@0: michael@0: let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache"); michael@0: disableCacheNode.addEventListener("click", this._disableCacheClicked, false); michael@0: }).then(() => { michael@0: this.isReady = true; michael@0: this.emit("ready"); michael@0: return this; michael@0: }).then(null, function onError(aReason) { michael@0: Cu.reportError("OptionsPanel open failed. " + michael@0: aReason.error + ": " + aReason.message); michael@0: }); michael@0: }, michael@0: michael@0: setupToolbarButtonsList: function() { michael@0: let enabledToolbarButtonsBox = this.panelDoc.getElementById("enabled-toolbox-buttons-box"); michael@0: enabledToolbarButtonsBox.textContent = ""; michael@0: michael@0: let toggleableButtons = this.toolbox.toolboxButtons; michael@0: let setToolboxButtonsVisibility = michael@0: this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox); michael@0: michael@0: let onCheckboxClick = (checkbox) => { michael@0: let toolDefinition = toggleableButtons.filter(tool => tool.id === checkbox.id)[0]; michael@0: Services.prefs.setBoolPref(toolDefinition.visibilityswitch, checkbox.checked); michael@0: setToolboxButtonsVisibility(); michael@0: }; michael@0: michael@0: let createCommandCheckbox = tool => { michael@0: let checkbox = this.panelDoc.createElement("checkbox"); michael@0: checkbox.setAttribute("id", tool.id); michael@0: checkbox.setAttribute("label", tool.label); michael@0: checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch)); michael@0: checkbox.addEventListener("command", onCheckboxClick.bind(this, checkbox)); michael@0: return checkbox; michael@0: }; michael@0: michael@0: for (let tool of toggleableButtons) { michael@0: enabledToolbarButtonsBox.appendChild(createCommandCheckbox(tool)); michael@0: } michael@0: }, michael@0: michael@0: getBoolPref: function(key) { michael@0: try { michael@0: return Services.prefs.getBoolPref(key); michael@0: } michael@0: catch (ex) { michael@0: return true; michael@0: } michael@0: }, michael@0: michael@0: setupToolsList: function() { michael@0: let defaultToolsBox = this.panelDoc.getElementById("default-tools-box"); michael@0: let additionalToolsBox = this.panelDoc.getElementById("additional-tools-box"); michael@0: let toolsNotSupportedLabel = this.panelDoc.getElementById("tools-not-supported-label"); michael@0: let atleastOneToolNotSupported = false; michael@0: michael@0: defaultToolsBox.textContent = ""; michael@0: additionalToolsBox.textContent = ""; michael@0: michael@0: let onCheckboxClick = function(id) { michael@0: let toolDefinition = gDevTools._tools.get(id); michael@0: // Set the kill switch pref boolean to true michael@0: Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked); michael@0: if (this.checked) { michael@0: gDevTools.emit("tool-registered", id); michael@0: } michael@0: else { michael@0: gDevTools.emit("tool-unregistered", toolDefinition); michael@0: } michael@0: }; michael@0: michael@0: let createToolCheckbox = tool => { michael@0: let checkbox = this.panelDoc.createElement("checkbox"); michael@0: checkbox.setAttribute("id", tool.id); michael@0: checkbox.setAttribute("tooltiptext", tool.tooltip || ""); michael@0: if (tool.isTargetSupported(this.target)) { michael@0: checkbox.setAttribute("label", tool.label); michael@0: } michael@0: else { michael@0: atleastOneToolNotSupported = true; michael@0: checkbox.setAttribute("label", michael@0: l10n("options.toolNotSupportedMarker", tool.label)); michael@0: checkbox.setAttribute("unsupported", ""); michael@0: } michael@0: checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch)); michael@0: checkbox.addEventListener("command", onCheckboxClick.bind(checkbox, tool.id)); michael@0: return checkbox; michael@0: }; michael@0: michael@0: // Populating the default tools lists michael@0: let toggleableTools = gDevTools.getDefaultTools().filter(tool => { michael@0: return tool.visibilityswitch; michael@0: }); michael@0: michael@0: for (let tool of toggleableTools) { michael@0: defaultToolsBox.appendChild(createToolCheckbox(tool)); michael@0: } michael@0: michael@0: // Populating the additional tools list that came from add-ons. michael@0: let atleastOneAddon = false; michael@0: for (let tool of gDevTools.getAdditionalTools()) { michael@0: atleastOneAddon = true; michael@0: additionalToolsBox.appendChild(createToolCheckbox(tool)); michael@0: } michael@0: michael@0: if (!atleastOneAddon) { michael@0: additionalToolsBox.style.display = "none"; michael@0: additionalToolsBox.previousSibling.style.display = "none"; michael@0: } michael@0: michael@0: if (!atleastOneToolNotSupported) { michael@0: toolsNotSupportedLabel.style.display = "none"; michael@0: } michael@0: michael@0: this.panelWin.focus(); michael@0: }, michael@0: michael@0: populatePreferences: function() { michael@0: let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]"); michael@0: for (let checkbox of prefCheckboxes) { michael@0: checkbox.checked = Services.prefs.getBoolPref(checkbox.getAttribute("data-pref")); michael@0: checkbox.addEventListener("command", function() { michael@0: let data = { michael@0: pref: this.getAttribute("data-pref"), michael@0: newValue: this.checked michael@0: }; michael@0: data.oldValue = Services.prefs.getBoolPref(data.pref); michael@0: Services.prefs.setBoolPref(data.pref, data.newValue); michael@0: gDevTools.emit("pref-changed", data); michael@0: }.bind(checkbox)); michael@0: } michael@0: let prefRadiogroups = this.panelDoc.querySelectorAll("radiogroup[data-pref]"); michael@0: for (let radiogroup of prefRadiogroups) { michael@0: let selectedValue = Services.prefs.getCharPref(radiogroup.getAttribute("data-pref")); michael@0: for (let radio of radiogroup.childNodes) { michael@0: radiogroup.selectedIndex = -1; michael@0: if (radio.getAttribute("value") == selectedValue) { michael@0: radiogroup.selectedItem = radio; michael@0: break; michael@0: } michael@0: } michael@0: radiogroup.addEventListener("select", function() { michael@0: let data = { michael@0: pref: this.getAttribute("data-pref"), michael@0: newValue: this.selectedItem.getAttribute("value") michael@0: }; michael@0: data.oldValue = Services.prefs.getCharPref(data.pref); michael@0: Services.prefs.setCharPref(data.pref, data.newValue); michael@0: gDevTools.emit("pref-changed", data); michael@0: }.bind(radiogroup)); michael@0: } michael@0: let prefMenulists = this.panelDoc.querySelectorAll("menulist[data-pref]"); michael@0: for (let menulist of prefMenulists) { michael@0: let pref = Services.prefs.getCharPref(menulist.getAttribute("data-pref")); michael@0: let menuitems = menulist.querySelectorAll("menuitem"); michael@0: for (let menuitem of menuitems) { michael@0: let value = menuitem.getAttribute("value"); michael@0: if (value === pref) { michael@0: menulist.selectedItem = menuitem; michael@0: break; michael@0: } michael@0: } michael@0: menulist.addEventListener("command", function() { michael@0: let data = { michael@0: pref: this.getAttribute("data-pref"), michael@0: newValue: this.value michael@0: }; michael@0: data.oldValue = Services.prefs.getCharPref(data.pref); michael@0: Services.prefs.setCharPref(data.pref, data.newValue); michael@0: gDevTools.emit("pref-changed", data); michael@0: }.bind(menulist)); michael@0: } michael@0: michael@0: this.target.client.attachTab(this.target.activeTab._actor, (response) => { michael@0: this._origJavascriptEnabled = response.javascriptEnabled; michael@0: this._origCacheEnabled = response.cacheEnabled; michael@0: michael@0: this._populateDisableJSCheckbox(); michael@0: this._populateDisableCacheCheckbox(); michael@0: }); michael@0: }, michael@0: michael@0: _populateDisableJSCheckbox: function() { michael@0: let cbx = this.panelDoc.getElementById("devtools-disable-javascript"); michael@0: cbx.checked = !this._origJavascriptEnabled; michael@0: }, michael@0: michael@0: _populateDisableCacheCheckbox: function() { michael@0: let cbx = this.panelDoc.getElementById("devtools-disable-cache"); michael@0: cbx.checked = !this._origCacheEnabled; michael@0: }, michael@0: michael@0: /** michael@0: * Disables JavaScript for the currently loaded tab. We force a page refresh michael@0: * here because setting docShell.allowJavascript to true fails to block JS michael@0: * execution from event listeners added using addEventListener(), AJAX calls michael@0: * and timers. The page refresh prevents these things from being added in the michael@0: * first place. michael@0: * michael@0: * @param {Event} event michael@0: * The event sent by checking / unchecking the disable JS checkbox. michael@0: */ michael@0: _disableJSClicked: function(event) { michael@0: let checked = event.target.checked; michael@0: michael@0: let options = { michael@0: "javascriptEnabled": !checked michael@0: }; michael@0: michael@0: this.target.activeTab.reconfigure(options); michael@0: }, michael@0: michael@0: /** michael@0: * Disables the cache for the currently loaded tab. michael@0: * michael@0: * @param {Event} event michael@0: * The event sent by checking / unchecking the disable cache checkbox. michael@0: */ michael@0: _disableCacheClicked: function(event) { michael@0: let checked = event.target.checked; michael@0: michael@0: let options = { michael@0: "cacheEnabled": !checked michael@0: }; michael@0: michael@0: this.target.activeTab.reconfigure(options); michael@0: }, michael@0: michael@0: destroy: function() { michael@0: if (this.destroyPromise) { michael@0: return this.destroyPromise; michael@0: } michael@0: michael@0: let deferred = promise.defer(); michael@0: michael@0: this.destroyPromise = deferred.promise; michael@0: michael@0: let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript"); michael@0: disableJSNode.removeEventListener("click", this._disableJSClicked, false); michael@0: michael@0: let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache"); michael@0: disableCacheNode.removeEventListener("click", this._disableCacheClicked, false); michael@0: michael@0: this.panelWin = this.panelDoc = null; michael@0: this._disableJSClicked = this._disableCacheClicked = null; michael@0: michael@0: // If the cache or JavaScript is disabled we need to revert them to their michael@0: // original values. michael@0: let options = { michael@0: "cacheEnabled": this._origCacheEnabled, michael@0: "javascriptEnabled": this._origJavascriptEnabled michael@0: }; michael@0: this.target.activeTab.reconfigure(options, () => { michael@0: this.toolbox = null; michael@0: deferred.resolve(); michael@0: }, true); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: };