Bug 1369801 - dt-addon: simplify devtools addon bootstrap and extract prefs loading;r=ochameau
MozReview-Commit-ID: 13SxWssW0Xr
--- a/devtools/bootstrap.js
+++ b/devtools/bootstrap.js
@@ -3,186 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* global content, APP_SHUTDOWN */
/* exported startup, shutdown, install, uninstall */
"use strict";
const Cu = Components.utils;
-const Ci = Components.interfaces;
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
-
-// MultiWindowKeyListener instance for Ctrl+Alt+R key
-let listener;
-// nsIURI to the addon root folder
-let resourceURI;
-
-function actionOccurred(id) {
- let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
- let Telemetry = require("devtools/client/shared/telemetry");
- let telemetry = new Telemetry();
- telemetry.actionOccurred(id);
-}
-
-// Synchronously fetch the content of a given URL
-function readURI(uri) {
- let stream = NetUtil.newChannel({
- uri: NetUtil.newURI(uri, "UTF-8"),
- loadUsingSystemPrincipal: true}
- ).open2();
- let count = stream.available();
- let data = NetUtil.readInputStreamToString(stream, count, {
- charset: "UTF-8"
- });
-
- stream.close();
-
- return data;
-}
-
-/**
- * Interpret the processing instructions contained in a preferences file, based on a
- * limited set of supported #if statements. After we ship as an addon, we don't want to
- * introduce anymore processing instructions, so all unrecognized preprocessing
- * instructions will be treated as an error.
- *
- * This function is mostly copied from devtools/client/inspector/webpack/prefs-loader.js
- *
- * @param {String} content
- * The string content of a preferences file.
- * @return {String} the content stripped of preprocessing instructions.
- */
-function interpretPreprocessingInstructions(content) {
- const ifMap = {
- "#if MOZ_UPDATE_CHANNEL == beta": AppConstants.MOZ_UPDATE_CHANNEL === "beta",
- "#if defined(NIGHTLY_BUILD)": AppConstants.NIGHTLY_BUILD,
- "#ifdef MOZ_DEV_EDITION": AppConstants.MOZ_DEV_EDITION,
- "#ifdef RELEASE_OR_BETA": AppConstants.RELEASE_OR_BETA,
- };
-
- let lines = content.split("\n");
- let ignoring = false;
- let newLines = [];
- let continuation = false;
- for (let line of lines) {
- if (line.startsWith("#if")) {
- if (!(line in ifMap)) {
- throw new Error("missing line in ifMap: " + line);
- }
- ignoring = !ifMap[line];
- } else if (line.startsWith("#else")) {
- ignoring = !ignoring;
- } else if (line.startsWith("#endif")) {
- ignoring = false;
- }
-
- let isPrefLine = /^ *(sticky_)?pref\("([^"]+)"/.test(line);
- if (continuation || (!ignoring && isPrefLine)) {
- newLines.push(line);
-
- // The call to pref(...); might span more than one line.
- continuation = !/\);/.test(line);
- }
- }
- return newLines.join("\n");
-}
-// Read a preference file and set all of its defined pref as default values
-// (This replicates the behavior of preferences files from mozilla-central)
-function processPrefFile(url) {
- let content = readURI(url);
- content = interpretPreprocessingInstructions(content);
- content.match(/pref\("[^"]+",\s*.+\s*\)/g).forEach(item => {
- let m = item.match(/pref\("([^"]+)",\s*(.+)\s*\)/);
- let name = m[1];
- let val = m[2].trim();
-
- // Prevent overriding prefs that have been changed by the user
- if (Services.prefs.prefHasUserValue(name)) {
- return;
- }
- let defaultBranch = Services.prefs.getDefaultBranch("");
- if ((val.startsWith("\"") && val.endsWith("\"")) ||
- (val.startsWith("'") && val.endsWith("'"))) {
- val = val.substr(1, val.length - 2);
- val = val.replace(/\\"/g, '"');
- defaultBranch.setCharPref(name, val);
- } else if (val.match(/[0-9]+/)) {
- defaultBranch.setIntPref(name, parseInt(val, 10));
- } else if (val == "true" || val == "false") {
- defaultBranch.setBoolPref(name, val == "true");
- } else {
- console.log("Unable to match preference type for value:", val);
- }
- });
-}
-
-function setPrefs() {
- processPrefFile(resourceURI.spec + "./client/preferences/devtools.js");
- processPrefFile(resourceURI.spec + "./client/preferences/debugger.js");
- processPrefFile(resourceURI.spec + "./client/webide/webide-prefs.js");
-}
-
-// Helper to listen to a key on all windows
-function MultiWindowKeyListener({ keyCode, ctrlKey, altKey, callback }) {
- let keyListener = function (event) {
- if (event.ctrlKey == !!ctrlKey &&
- event.altKey == !!altKey &&
- event.keyCode === keyCode) {
- callback(event);
-
- // Call preventDefault to avoid duplicated events when
- // doing the key stroke within a tab.
- event.preventDefault();
- }
- };
-
- let observer = function (window, topic, data) {
- // Listen on keyup to call keyListener only once per stroke
- if (topic === "domwindowopened") {
- window.addEventListener("keyup", keyListener);
- } else {
- window.removeEventListener("keyup", keyListener);
- }
- };
-
- return {
- start: function () {
- // Automatically process already opened windows
- let e = Services.ww.getWindowEnumerator();
- while (e.hasMoreElements()) {
- let window = e.getNext();
- observer(window, "domwindowopened", null);
- }
- // And listen for new ones to come
- Services.ww.registerNotification(observer);
- },
-
- stop: function () {
- Services.ww.unregisterNotification(observer);
- let e = Services.ww.getWindowEnumerator();
- while (e.hasMoreElements()) {
- let window = e.getNext();
- observer(window, "domwindowclosed", null);
- }
- }
- };
-}
-
-let getTopLevelWindow = function (window) {
- return window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow);
-};
+XPCOMUtils.defineLazyGetter(this, "DevToolsPreferences", function () {
+ return Cu.import("chrome://devtools/content/preferences/DevToolsPreferences.jsm", {}).DevToolsPreferences;
+});
function unload(reason) {
// This frame script is going to be executed in all processes:
// parent and child
Services.ppmm.loadProcessScript("data:,(" + function (scriptReason) {
/* Flush message manager cached frame scripts as well as chrome locales */
let obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
@@ -210,137 +45,25 @@ function unload(reason) {
Cu.unload("resource://devtools/shared/Parser.jsm");
Cu.unload("resource://devtools/client/shared/DOMHelpers.jsm");
Cu.unload("resource://devtools/client/shared/widgets/VariablesView.jsm");
Cu.unload("resource://devtools/client/responsivedesign/responsivedesign.jsm");
Cu.unload("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm");
Cu.unload("resource://devtools/shared/deprecated-sync-thenables.js");
}
-function reload(event) {
- // We automatically reload the toolbox if we are on a browser tab
- // with a toolbox already opened
- let reloadToolbox = false;
- if (event) {
- let top = getTopLevelWindow(event.view);
- let isBrowser = top.location.href.includes("/browser.xul");
- if (isBrowser && top.gBrowser) {
- // We do not use any devtools code before the call to Loader.jsm reload as
- // any attempt to use Loader.jsm to load a module will instanciate a new
- // Loader.
- let nbox = top.gBrowser.getNotificationBox();
- reloadToolbox =
- top.document.getAnonymousElementByAttribute(nbox, "class",
- "devtools-toolbox-bottom-iframe") ||
- top.document.getAnonymousElementByAttribute(nbox, "class",
- "devtools-toolbox-side-iframe") ||
- Services.wm.getMostRecentWindow("devtools:toolbox");
- }
- }
- let browserConsole = Services.wm.getMostRecentWindow("devtools:webconsole");
- let reopenBrowserConsole = false;
- if (browserConsole) {
- browserConsole.close();
- reopenBrowserConsole = true;
- }
-
- dump("Reload DevTools. (reload-toolbox:" + reloadToolbox + ")\n");
-
- // Invalidate xul cache in order to see changes made to chrome:// files
- Services.obs.notifyObservers(null, "startupcache-invalidate");
-
- unload("reload");
-
- // Update the preferences before starting new code
- setPrefs();
-
- const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
- devtools.require("devtools/client/framework/devtools-browser");
-
- // Go over all top level windows to reload all devtools related things
- let windowsEnum = Services.wm.getEnumerator(null);
- while (windowsEnum.hasMoreElements()) {
- let window = windowsEnum.getNext();
- let windowtype = window.document.documentElement.getAttribute("windowtype");
- if (windowtype == "navigator:browser" && window.gBrowser) {
- // Enumerate tabs on firefox windows
- for (let tab of window.gBrowser.tabs) {
- let browser = tab.linkedBrowser;
- let location = browser.documentURI.spec;
- let mm = browser.messageManager;
- // To reload JSON-View tabs and any devtools document
- if (location.startsWith("about:debugging") ||
- location.startsWith("chrome://devtools/")) {
- browser.reload();
- }
- // We have to use a frame script to query "baseURI"
- mm.loadFrameScript("data:text/javascript,new " + function () {
- let isJSONView =
- content.document.baseURI.startsWith("resource://devtools/");
- if (isJSONView) {
- content.location.reload();
- }
- }, false);
- }
- } else if (windowtype === "devtools:webide") {
- window.location.reload();
- }
- }
-
- if (reloadToolbox) {
- // Reopen the toolbox automatically if we are reloading from toolbox
- // shortcut and are on a browser window.
- // Wait for a second before opening the toolbox to avoid races
- // between the old and the new one.
- let {setTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
- setTimeout(() => {
- let { TargetFactory } = devtools.require("devtools/client/framework/target");
- let { gDevTools } = devtools.require("devtools/client/framework/devtools");
- let top = getTopLevelWindow(event.view);
- let target = TargetFactory.forTab(top.gBrowser.selectedTab);
- gDevTools.showToolbox(target);
- }, 1000);
- }
-
- // Browser console document can't just be reloaded.
- // HUDService is going to close it on unload.
- // Instead we have to manually toggle it.
- if (reopenBrowserConsole) {
- let {HUDService} = devtools.require("devtools/client/webconsole/hudservice");
- HUDService.toggleBrowserConsole();
- }
-
- actionOccurred("reloadAddonReload");
+function startup(data) {
+ // Load DevToolsPreferences as early as possible.
+ DevToolsPreferences.loadPrefs();
}
-function startup(data) {
- dump("DevTools addon started.\n");
-
- resourceURI = data.resourceURI;
-
- listener = new MultiWindowKeyListener({
- keyCode: Ci.nsIDOMKeyEvent.DOM_VK_R, ctrlKey: true, altKey: true,
- callback: reload
- });
- listener.start();
-
- reload();
-}
function shutdown(data, reason) {
// On browser shutdown, do not try to cleanup anything
if (reason == APP_SHUTDOWN) {
return;
}
- listener.stop();
- listener = null;
-
unload("disable");
}
-function install() {
- try {
- actionOccurred("reloadAddonInstalled");
- } catch (e) {
- // When installing on Firefox builds without devtools, telemetry doesn't
- // work yet and throws.
- }
-}
+
+function install() {}
+
function uninstall() {}
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -1,16 +1,17 @@
# 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/.
[features/devtools@mozilla.org] chrome.jar:
% content devtools %content/
content/preferences/debugger.js (preferences/debugger.js)
* content/preferences/devtools.js (preferences/devtools.js)
+ content/preferences/DevToolsPreferences.jsm (preferences/DevToolsPreferences.jsm)
content/webide/webide-prefs.js (webide/webide-prefs.js)
content/shared/vendor/d3.js (shared/vendor/d3.js)
content/shared/vendor/dagre-d3.js (shared/vendor/dagre-d3.js)
content/shared/widgets/widgets.css (shared/widgets/widgets.css)
content/netmonitor/src/assets/styles/netmonitor.css (netmonitor/src/assets/styles/netmonitor.css)
content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
content/netmonitor/index.html (netmonitor/index.html)
content/webconsole/webconsole.xhtml (webconsole/webconsole.xhtml)
new file mode 100644
--- /dev/null
+++ b/devtools/client/preferences/DevToolsPreferences.jsm
@@ -0,0 +1,88 @@
+/* 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 Cu = Components.utils;
+
+const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+
+this.EXPORTED_SYMBOLS = ["DevToolsPreferences"];
+
+// Synchronously fetch the content of a given URL
+function readURI(uri) {
+ let stream = NetUtil.newChannel({
+ uri: NetUtil.newURI(uri, "UTF-8"),
+ loadUsingSystemPrincipal: true}
+ ).open2();
+ let count = stream.available();
+ let data = NetUtil.readInputStreamToString(stream, count, {
+ charset: "UTF-8"
+ });
+
+ stream.close();
+
+ return data;
+}
+
+/**
+ * Cleanup the preferences file content from all empty and comment lines.
+ *
+ * @param {String} content
+ * The string content of a preferences file.
+ * @return {String} the content stripped of unnecessary lines.
+ */
+function cleanupPreferencesFileContent(content) {
+ let lines = content.split("\n");
+ let newLines = [];
+ let continuation = false;
+ for (let line of lines) {
+ let isPrefLine = /^ *pref\("([^"]+)"/.test(line);
+ if (continuation || isPrefLine) {
+ newLines.push(line);
+ // The call to pref(...); might span more than one line.
+ continuation = !/\);/.test(line);
+ }
+ }
+ return newLines.join("\n");
+}
+
+// Read a preference file and set all of its defined pref as default values
+// (This replicates the behavior of preferences files from mozilla-central)
+function processPrefFile(url) {
+ let content = readURI(url);
+ content = cleanupPreferencesFileContent(content);
+ content.match(/pref\("[^"]+",\s*.+\s*\)/g).forEach(item => {
+ let m = item.match(/pref\("([^"]+)",\s*(.+)\s*\)/);
+ let name = m[1];
+ let val = m[2].trim();
+
+ // Prevent overriding prefs that have been changed by the user
+ if (Services.prefs.prefHasUserValue(name)) {
+ return;
+ }
+ let defaultBranch = Services.prefs.getDefaultBranch("");
+ if ((val.startsWith("\"") && val.endsWith("\"")) ||
+ (val.startsWith("'") && val.endsWith("'"))) {
+ val = val.substr(1, val.length - 2);
+ val = val.replace(/\\"/g, '"');
+ defaultBranch.setCharPref(name, val);
+ } else if (val.match(/[0-9]+/)) {
+ defaultBranch.setIntPref(name, parseInt(val, 10));
+ } else if (val == "true" || val == "false") {
+ defaultBranch.setBoolPref(name, val == "true");
+ } else {
+ console.log("Unable to match preference type for value:", val);
+ }
+ });
+}
+
+this.DevToolsPreferences = {
+ loadPrefs: function () {
+ processPrefFile("chrome://devtools/content/preferences/devtools.js");
+ processPrefFile("chrome://devtools/content/preferences/debugger.js");
+ processPrefFile("chrome://devtools/content/webide/webide-prefs.js");
+ }
+};