123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
- /* 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 Services = require("Services");
- const defer = require("devtools/shared/defer");
- const {Task} = require("devtools/shared/task");
- const {gDevTools} = require("devtools/client/framework/devtools");
- const {LocalizationHelper} = require("devtools/shared/l10n");
- const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
- exports.OptionsPanel = OptionsPanel;
- function GetPref(name) {
- let type = Services.prefs.getPrefType(name);
- switch (type) {
- case Services.prefs.PREF_STRING:
- return Services.prefs.getCharPref(name);
- case Services.prefs.PREF_INT:
- return Services.prefs.getIntPref(name);
- case Services.prefs.PREF_BOOL:
- return Services.prefs.getBoolPref(name);
- default:
- throw new Error("Unknown type");
- }
- }
- function SetPref(name, value) {
- let type = Services.prefs.getPrefType(name);
- switch (type) {
- case Services.prefs.PREF_STRING:
- return Services.prefs.setCharPref(name, value);
- case Services.prefs.PREF_INT:
- return Services.prefs.setIntPref(name, value);
- case Services.prefs.PREF_BOOL:
- return Services.prefs.setBoolPref(name, value);
- default:
- throw new Error("Unknown type");
- }
- }
- function InfallibleGetBoolPref(key) {
- try {
- return Services.prefs.getBoolPref(key);
- } catch (ex) {
- return true;
- }
- }
- /**
- * Represents the Options Panel in the Toolbox.
- */
- function OptionsPanel(iframeWindow, toolbox) {
- this.panelDoc = iframeWindow.document;
- this.panelWin = iframeWindow;
- this.toolbox = toolbox;
- this.isReady = false;
- this._prefChanged = this._prefChanged.bind(this);
- this._themeRegistered = this._themeRegistered.bind(this);
- this._themeUnregistered = this._themeUnregistered.bind(this);
- this._disableJSClicked = this._disableJSClicked.bind(this);
- this.disableJSNode = this.panelDoc.getElementById(
- "devtools-disable-javascript");
- this._addListeners();
- const EventEmitter = require("devtools/shared/event-emitter");
- EventEmitter.decorate(this);
- }
- OptionsPanel.prototype = {
- get target() {
- return this.toolbox.target;
- },
- open: Task.async(function* () {
- // For local debugging we need to make the target remote.
- if (!this.target.isRemote) {
- yield this.target.makeRemote();
- }
- this.setupToolsList();
- this.setupToolbarButtonsList();
- this.setupThemeList();
- yield this.populatePreferences();
- this.isReady = true;
- this.emit("ready");
- return this;
- }),
- _addListeners: function () {
- Services.prefs.addObserver("devtools.cache.disabled", this._prefChanged, false);
- Services.prefs.addObserver("devtools.theme", this._prefChanged, false);
- gDevTools.on("theme-registered", this._themeRegistered);
- gDevTools.on("theme-unregistered", this._themeUnregistered);
- },
- _removeListeners: function () {
- Services.prefs.removeObserver("devtools.cache.disabled", this._prefChanged);
- Services.prefs.removeObserver("devtools.theme", this._prefChanged);
- gDevTools.off("theme-registered", this._themeRegistered);
- gDevTools.off("theme-unregistered", this._themeUnregistered);
- },
- _prefChanged: function (subject, topic, prefName) {
- if (prefName === "devtools.cache.disabled") {
- let cacheDisabled = data.newValue;
- let cbx = this.panelDoc.getElementById("devtools-disable-cache");
- cbx.checked = cacheDisabled;
- } else if (prefName === "devtools.theme") {
- this.updateCurrentTheme();
- }
- },
- _themeRegistered: function (event, themeId) {
- this.setupThemeList();
- },
- _themeUnregistered: function (event, theme) {
- let themeBox = this.panelDoc.getElementById("devtools-theme-box");
- let themeInput = themeBox.querySelector(`[value=${theme.id}]`);
- if (themeInput) {
- themeInput.parentNode.remove();
- }
- },
- setupToolbarButtonsList: function () {
- let enabledToolbarButtonsBox = this.panelDoc.getElementById(
- "enabled-toolbox-buttons-box");
- let toggleableButtons = this.toolbox.toolboxButtons;
- let setToolboxButtonsVisibility =
- this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox);
- let onCheckboxClick = (checkbox) => {
- let toolDefinition = toggleableButtons.filter(
- toggleableButton => toggleableButton.id === checkbox.id)[0];
- Services.prefs.setBoolPref(
- toolDefinition.visibilityswitch, checkbox.checked);
- setToolboxButtonsVisibility();
- };
- let createCommandCheckbox = tool => {
- let checkboxLabel = this.panelDoc.createElement("label");
- let checkboxSpanLabel = this.panelDoc.createElement("span");
- checkboxSpanLabel.textContent = tool.label;
- let checkboxInput = this.panelDoc.createElement("input");
- checkboxInput.setAttribute("type", "checkbox");
- checkboxInput.setAttribute("id", tool.id);
- if (InfallibleGetBoolPref(tool.visibilityswitch)) {
- checkboxInput.setAttribute("checked", true);
- }
- checkboxInput.addEventListener("change",
- onCheckboxClick.bind(this, checkboxInput));
- checkboxLabel.appendChild(checkboxInput);
- checkboxLabel.appendChild(checkboxSpanLabel);
- return checkboxLabel;
- };
- for (let tool of toggleableButtons) {
- if (!tool.isTargetSupported(this.toolbox.target)) {
- continue;
- }
- enabledToolbarButtonsBox.appendChild(createCommandCheckbox(tool));
- }
- },
- setupToolsList: function () {
- let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");
- let additionalToolsBox = this.panelDoc.getElementById(
- "additional-tools-box");
- let toolsNotSupportedLabel = this.panelDoc.getElementById(
- "tools-not-supported-label");
- let atleastOneToolNotSupported = false;
- let onCheckboxClick = function (id) {
- let toolDefinition = gDevTools._tools.get(id);
- // Set the kill switch pref boolean to true
- Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked);
- if (this.checked) {
- gDevTools.emit("tool-registered", id);
- } else {
- gDevTools.emit("tool-unregistered", toolDefinition);
- }
- };
- let createToolCheckbox = tool => {
- let checkboxLabel = this.panelDoc.createElement("label");
- let checkboxInput = this.panelDoc.createElement("input");
- checkboxInput.setAttribute("type", "checkbox");
- checkboxInput.setAttribute("id", tool.id);
- checkboxInput.setAttribute("title", tool.tooltip || "");
- let checkboxSpanLabel = this.panelDoc.createElement("span");
- if (tool.isTargetSupported(this.target)) {
- checkboxSpanLabel.textContent = tool.label;
- } else {
- atleastOneToolNotSupported = true;
- checkboxSpanLabel.textContent =
- L10N.getFormatStr("options.toolNotSupportedMarker", tool.label);
- checkboxInput.setAttribute("data-unsupported", "true");
- checkboxInput.setAttribute("disabled", "true");
- }
- if (InfallibleGetBoolPref(tool.visibilityswitch)) {
- checkboxInput.setAttribute("checked", "true");
- }
- checkboxInput.addEventListener("change",
- onCheckboxClick.bind(checkboxInput, tool.id));
- checkboxLabel.appendChild(checkboxInput);
- checkboxLabel.appendChild(checkboxSpanLabel);
- return checkboxLabel;
- };
- // Populating the default tools lists
- let toggleableTools = gDevTools.getDefaultTools().filter(tool => {
- return tool.visibilityswitch && !tool.hiddenInOptions;
- });
- for (let tool of toggleableTools) {
- defaultToolsBox.appendChild(createToolCheckbox(tool));
- }
- // Populating the additional tools list that came from add-ons.
- let atleastOneAddon = false;
- for (let tool of gDevTools.getAdditionalTools()) {
- atleastOneAddon = true;
- additionalToolsBox.appendChild(createToolCheckbox(tool));
- }
- if (!atleastOneAddon) {
- additionalToolsBox.style.display = "none";
- }
- if (!atleastOneToolNotSupported) {
- toolsNotSupportedLabel.style.display = "none";
- }
- this.panelWin.focus();
- },
- setupThemeList: function () {
- let themeBox = this.panelDoc.getElementById("devtools-theme-box");
- let themeLabels = themeBox.querySelectorAll("label");
- for (let label of themeLabels) {
- label.remove();
- }
- let createThemeOption = theme => {
- let inputLabel = this.panelDoc.createElement("label");
- let inputRadio = this.panelDoc.createElement("input");
- inputRadio.setAttribute("type", "radio");
- inputRadio.setAttribute("value", theme.id);
- inputRadio.setAttribute("name", "devtools-theme-item");
- inputRadio.addEventListener("change", function (e) {
- setPrefAndEmit(themeBox.getAttribute("data-pref"),
- e.target.value);
- });
- let inputSpanLabel = this.panelDoc.createElement("span");
- inputSpanLabel.textContent = theme.label;
- inputLabel.appendChild(inputRadio);
- inputLabel.appendChild(inputSpanLabel);
- return inputLabel;
- };
- // Populating the default theme list
- let themes = gDevTools.getThemeDefinitionArray();
- for (let theme of themes) {
- themeBox.appendChild(createThemeOption(theme));
- }
- this.updateCurrentTheme();
- },
- populatePreferences: function () {
- let prefCheckboxes = this.panelDoc.querySelectorAll(
- "input[type=checkbox][data-pref]");
- for (let prefCheckbox of prefCheckboxes) {
- if (GetPref(prefCheckbox.getAttribute("data-pref"))) {
- prefCheckbox.setAttribute("checked", true);
- }
- prefCheckbox.addEventListener("change", function (e) {
- let checkbox = e.target;
- setPrefAndEmit(checkbox.getAttribute("data-pref"), checkbox.checked);
- });
- }
- // Themes radio inputs are handled in setupThemeList
- let prefRadiogroups = this.panelDoc.querySelectorAll(
- ".radiogroup[data-pref]:not(#devtools-theme-box)");
- for (let radioGroup of prefRadiogroups) {
- let selectedValue = GetPref(radioGroup.getAttribute("data-pref"));
- for (let radioInput of radioGroup.querySelectorAll("input[type=radio]")) {
- if (radioInput.getAttribute("value") == selectedValue) {
- radioInput.setAttribute("checked", true);
- }
- radioInput.addEventListener("change", function (e) {
- setPrefAndEmit(radioGroup.getAttribute("data-pref"),
- e.target.value);
- });
- }
- }
- let prefSelects = this.panelDoc.querySelectorAll("select[data-pref]");
- for (let prefSelect of prefSelects) {
- let pref = GetPref(prefSelect.getAttribute("data-pref"));
- let options = [...prefSelect.options];
- options.some(function (option) {
- let value = option.value;
- // non strict check to allow int values.
- if (value == pref) {
- prefSelect.selectedIndex = options.indexOf(option);
- return true;
- }
- });
- prefSelect.addEventListener("change", function (e) {
- let select = e.target;
- setPrefAndEmit(select.getAttribute("data-pref"),
- select.options[select.selectedIndex].value);
- });
- }
- if (this.target.activeTab) {
- return this.target.client.attachTab(this.target.activeTab._actor)
- .then(([response, client]) => {
- this._origJavascriptEnabled = !response.javascriptEnabled;
- this.disableJSNode.checked = this._origJavascriptEnabled;
- this.disableJSNode.addEventListener("click",
- this._disableJSClicked, false);
- });
- }
- this.disableJSNode.hidden = true;
- },
- updateCurrentTheme: function () {
- let currentTheme = GetPref("devtools.theme");
- let themeBox = this.panelDoc.getElementById("devtools-theme-box");
- let themeRadioInput = themeBox.querySelector(`[value=${currentTheme}]`);
- if (themeRadioInput) {
- themeRadioInput.checked = true;
- } else {
- // If the current theme does not exist anymore, switch to light theme
- let lightThemeInputRadio = themeBox.querySelector("[value=light]");
- lightThemeInputRadio.checked = true;
- }
- },
- /**
- * Disables JavaScript for the currently loaded tab. We force a page refresh
- * here because setting docShell.allowJavascript to true fails to block JS
- * execution from event listeners added using addEventListener(), AJAX calls
- * and timers. The page refresh prevents these things from being added in the
- * first place.
- *
- * @param {Event} event
- * The event sent by checking / unchecking the disable JS checkbox.
- */
- _disableJSClicked: function (event) {
- let checked = event.target.checked;
- let options = {
- "javascriptEnabled": !checked
- };
- this.target.activeTab.reconfigure(options);
- },
- destroy: function () {
- if (this.destroyPromise) {
- return this.destroyPromise;
- }
- let deferred = defer();
- this.destroyPromise = deferred.promise;
- this._removeListeners();
- if (this.target.activeTab) {
- this.disableJSNode.removeEventListener("click", this._disableJSClicked);
- // FF41+ automatically cleans up state in actor on disconnect
- if (!this.target.activeTab.traits.noTabReconfigureOnClose) {
- let options = {
- "javascriptEnabled": this._origJavascriptEnabled,
- "performReload": false
- };
- this.target.activeTab.reconfigure(options, deferred.resolve);
- } else {
- deferred.resolve();
- }
- } else {
- deferred.resolve();
- }
- this.panelWin = this.panelDoc = this.disableJSNode = this.toolbox = null;
- return this.destroyPromise;
- }
- };
- /* Set a pref and emit the pref-changed event if needed. */
- function setPrefAndEmit(prefName, newValue) {
- let data = {
- pref: prefName,
- newValue: newValue
- };
- data.oldValue = GetPref(data.pref);
- SetPref(data.pref, data.newValue);
- if (data.newValue != data.oldValue) {
- gDevTools.emit("pref-changed", data);
- }
- }
|