|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const {Cu, Cc, Ci} = require("chrome"); |
|
8 const Services = require("Services"); |
|
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
10 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); |
|
11 |
|
12 exports.OptionsPanel = OptionsPanel; |
|
13 |
|
14 XPCOMUtils.defineLazyGetter(this, "l10n", function() { |
|
15 let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties"); |
|
16 let l10n = function(aName, ...aArgs) { |
|
17 try { |
|
18 if (aArgs.length == 0) { |
|
19 return bundle.GetStringFromName(aName); |
|
20 } else { |
|
21 return bundle.formatStringFromName(aName, aArgs, aArgs.length); |
|
22 } |
|
23 } catch (ex) { |
|
24 Services.console.logStringMessage("Error reading '" + aName + "'"); |
|
25 } |
|
26 }; |
|
27 return l10n; |
|
28 }); |
|
29 |
|
30 /** |
|
31 * Represents the Options Panel in the Toolbox. |
|
32 */ |
|
33 function OptionsPanel(iframeWindow, toolbox) { |
|
34 this.panelDoc = iframeWindow.document; |
|
35 this.panelWin = iframeWindow; |
|
36 this.toolbox = toolbox; |
|
37 this.isReady = false; |
|
38 |
|
39 const EventEmitter = require("devtools/toolkit/event-emitter"); |
|
40 EventEmitter.decorate(this); |
|
41 } |
|
42 |
|
43 OptionsPanel.prototype = { |
|
44 |
|
45 get target() { |
|
46 return this.toolbox.target; |
|
47 }, |
|
48 |
|
49 open: function() { |
|
50 let targetPromise; |
|
51 |
|
52 // For local debugging we need to make the target remote. |
|
53 if (!this.target.isRemote) { |
|
54 targetPromise = this.target.makeRemote(); |
|
55 } else { |
|
56 targetPromise = promise.resolve(this.target); |
|
57 } |
|
58 |
|
59 return targetPromise.then(() => { |
|
60 this.setupToolsList(); |
|
61 this.setupToolbarButtonsList(); |
|
62 this.populatePreferences(); |
|
63 |
|
64 this._disableJSClicked = this._disableJSClicked.bind(this); |
|
65 this._disableCacheClicked = this._disableCacheClicked.bind(this); |
|
66 |
|
67 let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript"); |
|
68 disableJSNode.addEventListener("click", this._disableJSClicked, false); |
|
69 |
|
70 let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache"); |
|
71 disableCacheNode.addEventListener("click", this._disableCacheClicked, false); |
|
72 }).then(() => { |
|
73 this.isReady = true; |
|
74 this.emit("ready"); |
|
75 return this; |
|
76 }).then(null, function onError(aReason) { |
|
77 Cu.reportError("OptionsPanel open failed. " + |
|
78 aReason.error + ": " + aReason.message); |
|
79 }); |
|
80 }, |
|
81 |
|
82 setupToolbarButtonsList: function() { |
|
83 let enabledToolbarButtonsBox = this.panelDoc.getElementById("enabled-toolbox-buttons-box"); |
|
84 enabledToolbarButtonsBox.textContent = ""; |
|
85 |
|
86 let toggleableButtons = this.toolbox.toolboxButtons; |
|
87 let setToolboxButtonsVisibility = |
|
88 this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox); |
|
89 |
|
90 let onCheckboxClick = (checkbox) => { |
|
91 let toolDefinition = toggleableButtons.filter(tool => tool.id === checkbox.id)[0]; |
|
92 Services.prefs.setBoolPref(toolDefinition.visibilityswitch, checkbox.checked); |
|
93 setToolboxButtonsVisibility(); |
|
94 }; |
|
95 |
|
96 let createCommandCheckbox = tool => { |
|
97 let checkbox = this.panelDoc.createElement("checkbox"); |
|
98 checkbox.setAttribute("id", tool.id); |
|
99 checkbox.setAttribute("label", tool.label); |
|
100 checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch)); |
|
101 checkbox.addEventListener("command", onCheckboxClick.bind(this, checkbox)); |
|
102 return checkbox; |
|
103 }; |
|
104 |
|
105 for (let tool of toggleableButtons) { |
|
106 enabledToolbarButtonsBox.appendChild(createCommandCheckbox(tool)); |
|
107 } |
|
108 }, |
|
109 |
|
110 getBoolPref: function(key) { |
|
111 try { |
|
112 return Services.prefs.getBoolPref(key); |
|
113 } |
|
114 catch (ex) { |
|
115 return true; |
|
116 } |
|
117 }, |
|
118 |
|
119 setupToolsList: function() { |
|
120 let defaultToolsBox = this.panelDoc.getElementById("default-tools-box"); |
|
121 let additionalToolsBox = this.panelDoc.getElementById("additional-tools-box"); |
|
122 let toolsNotSupportedLabel = this.panelDoc.getElementById("tools-not-supported-label"); |
|
123 let atleastOneToolNotSupported = false; |
|
124 |
|
125 defaultToolsBox.textContent = ""; |
|
126 additionalToolsBox.textContent = ""; |
|
127 |
|
128 let onCheckboxClick = function(id) { |
|
129 let toolDefinition = gDevTools._tools.get(id); |
|
130 // Set the kill switch pref boolean to true |
|
131 Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked); |
|
132 if (this.checked) { |
|
133 gDevTools.emit("tool-registered", id); |
|
134 } |
|
135 else { |
|
136 gDevTools.emit("tool-unregistered", toolDefinition); |
|
137 } |
|
138 }; |
|
139 |
|
140 let createToolCheckbox = tool => { |
|
141 let checkbox = this.panelDoc.createElement("checkbox"); |
|
142 checkbox.setAttribute("id", tool.id); |
|
143 checkbox.setAttribute("tooltiptext", tool.tooltip || ""); |
|
144 if (tool.isTargetSupported(this.target)) { |
|
145 checkbox.setAttribute("label", tool.label); |
|
146 } |
|
147 else { |
|
148 atleastOneToolNotSupported = true; |
|
149 checkbox.setAttribute("label", |
|
150 l10n("options.toolNotSupportedMarker", tool.label)); |
|
151 checkbox.setAttribute("unsupported", ""); |
|
152 } |
|
153 checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch)); |
|
154 checkbox.addEventListener("command", onCheckboxClick.bind(checkbox, tool.id)); |
|
155 return checkbox; |
|
156 }; |
|
157 |
|
158 // Populating the default tools lists |
|
159 let toggleableTools = gDevTools.getDefaultTools().filter(tool => { |
|
160 return tool.visibilityswitch; |
|
161 }); |
|
162 |
|
163 for (let tool of toggleableTools) { |
|
164 defaultToolsBox.appendChild(createToolCheckbox(tool)); |
|
165 } |
|
166 |
|
167 // Populating the additional tools list that came from add-ons. |
|
168 let atleastOneAddon = false; |
|
169 for (let tool of gDevTools.getAdditionalTools()) { |
|
170 atleastOneAddon = true; |
|
171 additionalToolsBox.appendChild(createToolCheckbox(tool)); |
|
172 } |
|
173 |
|
174 if (!atleastOneAddon) { |
|
175 additionalToolsBox.style.display = "none"; |
|
176 additionalToolsBox.previousSibling.style.display = "none"; |
|
177 } |
|
178 |
|
179 if (!atleastOneToolNotSupported) { |
|
180 toolsNotSupportedLabel.style.display = "none"; |
|
181 } |
|
182 |
|
183 this.panelWin.focus(); |
|
184 }, |
|
185 |
|
186 populatePreferences: function() { |
|
187 let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]"); |
|
188 for (let checkbox of prefCheckboxes) { |
|
189 checkbox.checked = Services.prefs.getBoolPref(checkbox.getAttribute("data-pref")); |
|
190 checkbox.addEventListener("command", function() { |
|
191 let data = { |
|
192 pref: this.getAttribute("data-pref"), |
|
193 newValue: this.checked |
|
194 }; |
|
195 data.oldValue = Services.prefs.getBoolPref(data.pref); |
|
196 Services.prefs.setBoolPref(data.pref, data.newValue); |
|
197 gDevTools.emit("pref-changed", data); |
|
198 }.bind(checkbox)); |
|
199 } |
|
200 let prefRadiogroups = this.panelDoc.querySelectorAll("radiogroup[data-pref]"); |
|
201 for (let radiogroup of prefRadiogroups) { |
|
202 let selectedValue = Services.prefs.getCharPref(radiogroup.getAttribute("data-pref")); |
|
203 for (let radio of radiogroup.childNodes) { |
|
204 radiogroup.selectedIndex = -1; |
|
205 if (radio.getAttribute("value") == selectedValue) { |
|
206 radiogroup.selectedItem = radio; |
|
207 break; |
|
208 } |
|
209 } |
|
210 radiogroup.addEventListener("select", function() { |
|
211 let data = { |
|
212 pref: this.getAttribute("data-pref"), |
|
213 newValue: this.selectedItem.getAttribute("value") |
|
214 }; |
|
215 data.oldValue = Services.prefs.getCharPref(data.pref); |
|
216 Services.prefs.setCharPref(data.pref, data.newValue); |
|
217 gDevTools.emit("pref-changed", data); |
|
218 }.bind(radiogroup)); |
|
219 } |
|
220 let prefMenulists = this.panelDoc.querySelectorAll("menulist[data-pref]"); |
|
221 for (let menulist of prefMenulists) { |
|
222 let pref = Services.prefs.getCharPref(menulist.getAttribute("data-pref")); |
|
223 let menuitems = menulist.querySelectorAll("menuitem"); |
|
224 for (let menuitem of menuitems) { |
|
225 let value = menuitem.getAttribute("value"); |
|
226 if (value === pref) { |
|
227 menulist.selectedItem = menuitem; |
|
228 break; |
|
229 } |
|
230 } |
|
231 menulist.addEventListener("command", function() { |
|
232 let data = { |
|
233 pref: this.getAttribute("data-pref"), |
|
234 newValue: this.value |
|
235 }; |
|
236 data.oldValue = Services.prefs.getCharPref(data.pref); |
|
237 Services.prefs.setCharPref(data.pref, data.newValue); |
|
238 gDevTools.emit("pref-changed", data); |
|
239 }.bind(menulist)); |
|
240 } |
|
241 |
|
242 this.target.client.attachTab(this.target.activeTab._actor, (response) => { |
|
243 this._origJavascriptEnabled = response.javascriptEnabled; |
|
244 this._origCacheEnabled = response.cacheEnabled; |
|
245 |
|
246 this._populateDisableJSCheckbox(); |
|
247 this._populateDisableCacheCheckbox(); |
|
248 }); |
|
249 }, |
|
250 |
|
251 _populateDisableJSCheckbox: function() { |
|
252 let cbx = this.panelDoc.getElementById("devtools-disable-javascript"); |
|
253 cbx.checked = !this._origJavascriptEnabled; |
|
254 }, |
|
255 |
|
256 _populateDisableCacheCheckbox: function() { |
|
257 let cbx = this.panelDoc.getElementById("devtools-disable-cache"); |
|
258 cbx.checked = !this._origCacheEnabled; |
|
259 }, |
|
260 |
|
261 /** |
|
262 * Disables JavaScript for the currently loaded tab. We force a page refresh |
|
263 * here because setting docShell.allowJavascript to true fails to block JS |
|
264 * execution from event listeners added using addEventListener(), AJAX calls |
|
265 * and timers. The page refresh prevents these things from being added in the |
|
266 * first place. |
|
267 * |
|
268 * @param {Event} event |
|
269 * The event sent by checking / unchecking the disable JS checkbox. |
|
270 */ |
|
271 _disableJSClicked: function(event) { |
|
272 let checked = event.target.checked; |
|
273 |
|
274 let options = { |
|
275 "javascriptEnabled": !checked |
|
276 }; |
|
277 |
|
278 this.target.activeTab.reconfigure(options); |
|
279 }, |
|
280 |
|
281 /** |
|
282 * Disables the cache for the currently loaded tab. |
|
283 * |
|
284 * @param {Event} event |
|
285 * The event sent by checking / unchecking the disable cache checkbox. |
|
286 */ |
|
287 _disableCacheClicked: function(event) { |
|
288 let checked = event.target.checked; |
|
289 |
|
290 let options = { |
|
291 "cacheEnabled": !checked |
|
292 }; |
|
293 |
|
294 this.target.activeTab.reconfigure(options); |
|
295 }, |
|
296 |
|
297 destroy: function() { |
|
298 if (this.destroyPromise) { |
|
299 return this.destroyPromise; |
|
300 } |
|
301 |
|
302 let deferred = promise.defer(); |
|
303 |
|
304 this.destroyPromise = deferred.promise; |
|
305 |
|
306 let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript"); |
|
307 disableJSNode.removeEventListener("click", this._disableJSClicked, false); |
|
308 |
|
309 let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache"); |
|
310 disableCacheNode.removeEventListener("click", this._disableCacheClicked, false); |
|
311 |
|
312 this.panelWin = this.panelDoc = null; |
|
313 this._disableJSClicked = this._disableCacheClicked = null; |
|
314 |
|
315 // If the cache or JavaScript is disabled we need to revert them to their |
|
316 // original values. |
|
317 let options = { |
|
318 "cacheEnabled": this._origCacheEnabled, |
|
319 "javascriptEnabled": this._origJavascriptEnabled |
|
320 }; |
|
321 this.target.activeTab.reconfigure(options, () => { |
|
322 this.toolbox = null; |
|
323 deferred.resolve(); |
|
324 }, true); |
|
325 |
|
326 return deferred.promise; |
|
327 } |
|
328 }; |