summaryrefslogtreecommitdiffstats
path: root/devtools/bootstrap.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/bootstrap.js')
-rw-r--r--devtools/bootstrap.js245
1 files changed, 245 insertions, 0 deletions
diff --git a/devtools/bootstrap.js b/devtools/bootstrap.js
new file mode 100644
index 000000000..bedca0c78
--- /dev/null
+++ b/devtools/bootstrap.js
@@ -0,0 +1,245 @@
+/* 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/. */
+
+/* global content */
+/* exported startup, shutdown, install, uninstall */
+
+"use strict";
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+
+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);
+}
+
+// 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);
+};
+
+function reload(event) {
+ // We automatically reload the toolbox if we are on a browser tab
+ // with a toolbox already opened
+ let top = getTopLevelWindow(event.view);
+ let isBrowser = top.location.href.includes("/browser.xul");
+ let reloadToolbox = false;
+ 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", null);
+
+ // This frame script is going to be executed in all processes:
+ // parent and child
+ Services.ppmm.loadProcessScript("data:,new " + function () {
+ /* Flush message manager cached frame scripts as well as chrome locales */
+ let obs = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ obs.notifyObservers(null, "message-manager-flush-caches", null);
+
+ /* Also purge cached modules in child processes, we do it a few lines after
+ in the parent process */
+ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ Services.obs.notifyObservers(null, "devtools-unload", "reload");
+ }
+ }, false);
+
+ // As we can't get a reference to existing Loader.jsm instances, we send them
+ // an observer service notification to unload them.
+ Services.obs.notifyObservers(null, "devtools-unload", "reload");
+
+ // Then spawn a brand new Loader.jsm instance and start the main module
+ Cu.unload("resource://devtools/shared/Loader.jsm");
+ // Also unload all resources loaded as jsm, hopefully all of them are going
+ // to be converted into regular modules
+ Cu.unload("resource://devtools/client/shared/browser-loader.js");
+ Cu.unload("resource://devtools/client/framework/ToolboxProcess.jsm");
+ Cu.unload("resource://devtools/shared/apps/Devices.jsm");
+ Cu.unload("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
+ 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");
+ 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 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");
+}
+
+let prefs = {
+ // Enable dump as some errors are only printed on the stdout
+ "browser.dom.window.dump.enabled": true,
+ // Enable the browser toolbox and various chrome-only features
+ "devtools.chrome.enabled": true,
+ "devtools.debugger.remote-enabled": true,
+ // Disable the prompt to ease usage of the browser toolbox
+ "devtools.debugger.prompt-connection": false,
+};
+let originalPrefValues = {};
+
+let listener;
+function startup() {
+ dump("DevTools addon started.\n");
+ listener = new MultiWindowKeyListener({
+ keyCode: Ci.nsIDOMKeyEvent.DOM_VK_R, ctrlKey: true, altKey: true,
+ callback: reload
+ });
+ listener.start();
+
+ // Toggle development prefs and save original values
+ originalPrefValues = {};
+ for (let name in prefs) {
+ let value = prefs[name];
+ let userValue = Services.prefs.getBoolPref(name);
+ // Only toggle if the pref isn't already set to the right value
+ if (userValue != value) {
+ Services.prefs.setBoolPref(name, value);
+ originalPrefValues[name] = userValue;
+ }
+ }
+}
+function shutdown() {
+ listener.stop();
+ listener = null;
+
+ // Restore preferences that used to be before the addon was installed
+ for (let name in originalPrefValues) {
+ let userValue = Services.prefs.getBoolPref(name);
+ // Only reset the pref if it hasn't changed
+ if (userValue == prefs[name]) {
+ Services.prefs.setBoolPref(name, originalPrefValues[name]);
+ }
+ }
+}
+function install() {
+ actionOccurred("reloadAddonInstalled");
+}
+function uninstall() {}