1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/framework/toolbox-options.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,328 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +const {Cu, Cc, Ci} = require("chrome"); 1.11 +const Services = require("Services"); 1.12 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.13 +XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); 1.14 + 1.15 +exports.OptionsPanel = OptionsPanel; 1.16 + 1.17 +XPCOMUtils.defineLazyGetter(this, "l10n", function() { 1.18 + let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties"); 1.19 + let l10n = function(aName, ...aArgs) { 1.20 + try { 1.21 + if (aArgs.length == 0) { 1.22 + return bundle.GetStringFromName(aName); 1.23 + } else { 1.24 + return bundle.formatStringFromName(aName, aArgs, aArgs.length); 1.25 + } 1.26 + } catch (ex) { 1.27 + Services.console.logStringMessage("Error reading '" + aName + "'"); 1.28 + } 1.29 + }; 1.30 + return l10n; 1.31 +}); 1.32 + 1.33 +/** 1.34 + * Represents the Options Panel in the Toolbox. 1.35 + */ 1.36 +function OptionsPanel(iframeWindow, toolbox) { 1.37 + this.panelDoc = iframeWindow.document; 1.38 + this.panelWin = iframeWindow; 1.39 + this.toolbox = toolbox; 1.40 + this.isReady = false; 1.41 + 1.42 + const EventEmitter = require("devtools/toolkit/event-emitter"); 1.43 + EventEmitter.decorate(this); 1.44 +} 1.45 + 1.46 +OptionsPanel.prototype = { 1.47 + 1.48 + get target() { 1.49 + return this.toolbox.target; 1.50 + }, 1.51 + 1.52 + open: function() { 1.53 + let targetPromise; 1.54 + 1.55 + // For local debugging we need to make the target remote. 1.56 + if (!this.target.isRemote) { 1.57 + targetPromise = this.target.makeRemote(); 1.58 + } else { 1.59 + targetPromise = promise.resolve(this.target); 1.60 + } 1.61 + 1.62 + return targetPromise.then(() => { 1.63 + this.setupToolsList(); 1.64 + this.setupToolbarButtonsList(); 1.65 + this.populatePreferences(); 1.66 + 1.67 + this._disableJSClicked = this._disableJSClicked.bind(this); 1.68 + this._disableCacheClicked = this._disableCacheClicked.bind(this); 1.69 + 1.70 + let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript"); 1.71 + disableJSNode.addEventListener("click", this._disableJSClicked, false); 1.72 + 1.73 + let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache"); 1.74 + disableCacheNode.addEventListener("click", this._disableCacheClicked, false); 1.75 + }).then(() => { 1.76 + this.isReady = true; 1.77 + this.emit("ready"); 1.78 + return this; 1.79 + }).then(null, function onError(aReason) { 1.80 + Cu.reportError("OptionsPanel open failed. " + 1.81 + aReason.error + ": " + aReason.message); 1.82 + }); 1.83 + }, 1.84 + 1.85 + setupToolbarButtonsList: function() { 1.86 + let enabledToolbarButtonsBox = this.panelDoc.getElementById("enabled-toolbox-buttons-box"); 1.87 + enabledToolbarButtonsBox.textContent = ""; 1.88 + 1.89 + let toggleableButtons = this.toolbox.toolboxButtons; 1.90 + let setToolboxButtonsVisibility = 1.91 + this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox); 1.92 + 1.93 + let onCheckboxClick = (checkbox) => { 1.94 + let toolDefinition = toggleableButtons.filter(tool => tool.id === checkbox.id)[0]; 1.95 + Services.prefs.setBoolPref(toolDefinition.visibilityswitch, checkbox.checked); 1.96 + setToolboxButtonsVisibility(); 1.97 + }; 1.98 + 1.99 + let createCommandCheckbox = tool => { 1.100 + let checkbox = this.panelDoc.createElement("checkbox"); 1.101 + checkbox.setAttribute("id", tool.id); 1.102 + checkbox.setAttribute("label", tool.label); 1.103 + checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch)); 1.104 + checkbox.addEventListener("command", onCheckboxClick.bind(this, checkbox)); 1.105 + return checkbox; 1.106 + }; 1.107 + 1.108 + for (let tool of toggleableButtons) { 1.109 + enabledToolbarButtonsBox.appendChild(createCommandCheckbox(tool)); 1.110 + } 1.111 + }, 1.112 + 1.113 + getBoolPref: function(key) { 1.114 + try { 1.115 + return Services.prefs.getBoolPref(key); 1.116 + } 1.117 + catch (ex) { 1.118 + return true; 1.119 + } 1.120 + }, 1.121 + 1.122 + setupToolsList: function() { 1.123 + let defaultToolsBox = this.panelDoc.getElementById("default-tools-box"); 1.124 + let additionalToolsBox = this.panelDoc.getElementById("additional-tools-box"); 1.125 + let toolsNotSupportedLabel = this.panelDoc.getElementById("tools-not-supported-label"); 1.126 + let atleastOneToolNotSupported = false; 1.127 + 1.128 + defaultToolsBox.textContent = ""; 1.129 + additionalToolsBox.textContent = ""; 1.130 + 1.131 + let onCheckboxClick = function(id) { 1.132 + let toolDefinition = gDevTools._tools.get(id); 1.133 + // Set the kill switch pref boolean to true 1.134 + Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked); 1.135 + if (this.checked) { 1.136 + gDevTools.emit("tool-registered", id); 1.137 + } 1.138 + else { 1.139 + gDevTools.emit("tool-unregistered", toolDefinition); 1.140 + } 1.141 + }; 1.142 + 1.143 + let createToolCheckbox = tool => { 1.144 + let checkbox = this.panelDoc.createElement("checkbox"); 1.145 + checkbox.setAttribute("id", tool.id); 1.146 + checkbox.setAttribute("tooltiptext", tool.tooltip || ""); 1.147 + if (tool.isTargetSupported(this.target)) { 1.148 + checkbox.setAttribute("label", tool.label); 1.149 + } 1.150 + else { 1.151 + atleastOneToolNotSupported = true; 1.152 + checkbox.setAttribute("label", 1.153 + l10n("options.toolNotSupportedMarker", tool.label)); 1.154 + checkbox.setAttribute("unsupported", ""); 1.155 + } 1.156 + checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch)); 1.157 + checkbox.addEventListener("command", onCheckboxClick.bind(checkbox, tool.id)); 1.158 + return checkbox; 1.159 + }; 1.160 + 1.161 + // Populating the default tools lists 1.162 + let toggleableTools = gDevTools.getDefaultTools().filter(tool => { 1.163 + return tool.visibilityswitch; 1.164 + }); 1.165 + 1.166 + for (let tool of toggleableTools) { 1.167 + defaultToolsBox.appendChild(createToolCheckbox(tool)); 1.168 + } 1.169 + 1.170 + // Populating the additional tools list that came from add-ons. 1.171 + let atleastOneAddon = false; 1.172 + for (let tool of gDevTools.getAdditionalTools()) { 1.173 + atleastOneAddon = true; 1.174 + additionalToolsBox.appendChild(createToolCheckbox(tool)); 1.175 + } 1.176 + 1.177 + if (!atleastOneAddon) { 1.178 + additionalToolsBox.style.display = "none"; 1.179 + additionalToolsBox.previousSibling.style.display = "none"; 1.180 + } 1.181 + 1.182 + if (!atleastOneToolNotSupported) { 1.183 + toolsNotSupportedLabel.style.display = "none"; 1.184 + } 1.185 + 1.186 + this.panelWin.focus(); 1.187 + }, 1.188 + 1.189 + populatePreferences: function() { 1.190 + let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]"); 1.191 + for (let checkbox of prefCheckboxes) { 1.192 + checkbox.checked = Services.prefs.getBoolPref(checkbox.getAttribute("data-pref")); 1.193 + checkbox.addEventListener("command", function() { 1.194 + let data = { 1.195 + pref: this.getAttribute("data-pref"), 1.196 + newValue: this.checked 1.197 + }; 1.198 + data.oldValue = Services.prefs.getBoolPref(data.pref); 1.199 + Services.prefs.setBoolPref(data.pref, data.newValue); 1.200 + gDevTools.emit("pref-changed", data); 1.201 + }.bind(checkbox)); 1.202 + } 1.203 + let prefRadiogroups = this.panelDoc.querySelectorAll("radiogroup[data-pref]"); 1.204 + for (let radiogroup of prefRadiogroups) { 1.205 + let selectedValue = Services.prefs.getCharPref(radiogroup.getAttribute("data-pref")); 1.206 + for (let radio of radiogroup.childNodes) { 1.207 + radiogroup.selectedIndex = -1; 1.208 + if (radio.getAttribute("value") == selectedValue) { 1.209 + radiogroup.selectedItem = radio; 1.210 + break; 1.211 + } 1.212 + } 1.213 + radiogroup.addEventListener("select", function() { 1.214 + let data = { 1.215 + pref: this.getAttribute("data-pref"), 1.216 + newValue: this.selectedItem.getAttribute("value") 1.217 + }; 1.218 + data.oldValue = Services.prefs.getCharPref(data.pref); 1.219 + Services.prefs.setCharPref(data.pref, data.newValue); 1.220 + gDevTools.emit("pref-changed", data); 1.221 + }.bind(radiogroup)); 1.222 + } 1.223 + let prefMenulists = this.panelDoc.querySelectorAll("menulist[data-pref]"); 1.224 + for (let menulist of prefMenulists) { 1.225 + let pref = Services.prefs.getCharPref(menulist.getAttribute("data-pref")); 1.226 + let menuitems = menulist.querySelectorAll("menuitem"); 1.227 + for (let menuitem of menuitems) { 1.228 + let value = menuitem.getAttribute("value"); 1.229 + if (value === pref) { 1.230 + menulist.selectedItem = menuitem; 1.231 + break; 1.232 + } 1.233 + } 1.234 + menulist.addEventListener("command", function() { 1.235 + let data = { 1.236 + pref: this.getAttribute("data-pref"), 1.237 + newValue: this.value 1.238 + }; 1.239 + data.oldValue = Services.prefs.getCharPref(data.pref); 1.240 + Services.prefs.setCharPref(data.pref, data.newValue); 1.241 + gDevTools.emit("pref-changed", data); 1.242 + }.bind(menulist)); 1.243 + } 1.244 + 1.245 + this.target.client.attachTab(this.target.activeTab._actor, (response) => { 1.246 + this._origJavascriptEnabled = response.javascriptEnabled; 1.247 + this._origCacheEnabled = response.cacheEnabled; 1.248 + 1.249 + this._populateDisableJSCheckbox(); 1.250 + this._populateDisableCacheCheckbox(); 1.251 + }); 1.252 + }, 1.253 + 1.254 + _populateDisableJSCheckbox: function() { 1.255 + let cbx = this.panelDoc.getElementById("devtools-disable-javascript"); 1.256 + cbx.checked = !this._origJavascriptEnabled; 1.257 + }, 1.258 + 1.259 + _populateDisableCacheCheckbox: function() { 1.260 + let cbx = this.panelDoc.getElementById("devtools-disable-cache"); 1.261 + cbx.checked = !this._origCacheEnabled; 1.262 + }, 1.263 + 1.264 + /** 1.265 + * Disables JavaScript for the currently loaded tab. We force a page refresh 1.266 + * here because setting docShell.allowJavascript to true fails to block JS 1.267 + * execution from event listeners added using addEventListener(), AJAX calls 1.268 + * and timers. The page refresh prevents these things from being added in the 1.269 + * first place. 1.270 + * 1.271 + * @param {Event} event 1.272 + * The event sent by checking / unchecking the disable JS checkbox. 1.273 + */ 1.274 + _disableJSClicked: function(event) { 1.275 + let checked = event.target.checked; 1.276 + 1.277 + let options = { 1.278 + "javascriptEnabled": !checked 1.279 + }; 1.280 + 1.281 + this.target.activeTab.reconfigure(options); 1.282 + }, 1.283 + 1.284 + /** 1.285 + * Disables the cache for the currently loaded tab. 1.286 + * 1.287 + * @param {Event} event 1.288 + * The event sent by checking / unchecking the disable cache checkbox. 1.289 + */ 1.290 + _disableCacheClicked: function(event) { 1.291 + let checked = event.target.checked; 1.292 + 1.293 + let options = { 1.294 + "cacheEnabled": !checked 1.295 + }; 1.296 + 1.297 + this.target.activeTab.reconfigure(options); 1.298 + }, 1.299 + 1.300 + destroy: function() { 1.301 + if (this.destroyPromise) { 1.302 + return this.destroyPromise; 1.303 + } 1.304 + 1.305 + let deferred = promise.defer(); 1.306 + 1.307 + this.destroyPromise = deferred.promise; 1.308 + 1.309 + let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript"); 1.310 + disableJSNode.removeEventListener("click", this._disableJSClicked, false); 1.311 + 1.312 + let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache"); 1.313 + disableCacheNode.removeEventListener("click", this._disableCacheClicked, false); 1.314 + 1.315 + this.panelWin = this.panelDoc = null; 1.316 + this._disableJSClicked = this._disableCacheClicked = null; 1.317 + 1.318 + // If the cache or JavaScript is disabled we need to revert them to their 1.319 + // original values. 1.320 + let options = { 1.321 + "cacheEnabled": this._origCacheEnabled, 1.322 + "javascriptEnabled": this._origJavascriptEnabled 1.323 + }; 1.324 + this.target.activeTab.reconfigure(options, () => { 1.325 + this.toolbox = null; 1.326 + deferred.resolve(); 1.327 + }, true); 1.328 + 1.329 + return deferred.promise; 1.330 + } 1.331 +};