diff options
Diffstat (limited to 'devtools/bootstrap.js')
-rw-r--r-- | devtools/bootstrap.js | 245 |
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() {} |