bootstrap.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. /* global content */
  5. /* exported startup, shutdown, install, uninstall */
  6. "use strict";
  7. const Cu = Components.utils;
  8. const Ci = Components.interfaces;
  9. const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
  10. function actionOccurred(id) {
  11. let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
  12. let Telemetry = require("devtools/client/shared/telemetry");
  13. let telemetry = new Telemetry();
  14. telemetry.actionOccurred(id);
  15. }
  16. // Helper to listen to a key on all windows
  17. function MultiWindowKeyListener({ keyCode, ctrlKey, altKey, callback }) {
  18. let keyListener = function (event) {
  19. if (event.ctrlKey == !!ctrlKey &&
  20. event.altKey == !!altKey &&
  21. event.keyCode === keyCode) {
  22. callback(event);
  23. // Call preventDefault to avoid duplicated events when
  24. // doing the key stroke within a tab.
  25. event.preventDefault();
  26. }
  27. };
  28. let observer = function (window, topic, data) {
  29. // Listen on keyup to call keyListener only once per stroke
  30. if (topic === "domwindowopened") {
  31. window.addEventListener("keyup", keyListener);
  32. } else {
  33. window.removeEventListener("keyup", keyListener);
  34. }
  35. };
  36. return {
  37. start: function () {
  38. // Automatically process already opened windows
  39. let e = Services.ww.getWindowEnumerator();
  40. while (e.hasMoreElements()) {
  41. let window = e.getNext();
  42. observer(window, "domwindowopened", null);
  43. }
  44. // And listen for new ones to come
  45. Services.ww.registerNotification(observer);
  46. },
  47. stop: function () {
  48. Services.ww.unregisterNotification(observer);
  49. let e = Services.ww.getWindowEnumerator();
  50. while (e.hasMoreElements()) {
  51. let window = e.getNext();
  52. observer(window, "domwindowclosed", null);
  53. }
  54. }
  55. };
  56. }
  57. let getTopLevelWindow = function (window) {
  58. return window.QueryInterface(Ci.nsIInterfaceRequestor)
  59. .getInterface(Ci.nsIWebNavigation)
  60. .QueryInterface(Ci.nsIDocShellTreeItem)
  61. .rootTreeItem
  62. .QueryInterface(Ci.nsIInterfaceRequestor)
  63. .getInterface(Ci.nsIDOMWindow);
  64. };
  65. function reload(event) {
  66. // We automatically reload the toolbox if we are on a browser tab
  67. // with a toolbox already opened
  68. let top = getTopLevelWindow(event.view);
  69. let isBrowser = top.location.href.includes("/browser.xul");
  70. let reloadToolbox = false;
  71. if (isBrowser && top.gBrowser) {
  72. // We do not use any devtools code before the call to Loader.jsm reload as
  73. // any attempt to use Loader.jsm to load a module will instanciate a new
  74. // Loader.
  75. let nbox = top.gBrowser.getNotificationBox();
  76. reloadToolbox =
  77. top.document.getAnonymousElementByAttribute(nbox, "class",
  78. "devtools-toolbox-bottom-iframe") ||
  79. top.document.getAnonymousElementByAttribute(nbox, "class",
  80. "devtools-toolbox-side-iframe") ||
  81. Services.wm.getMostRecentWindow("devtools:toolbox");
  82. }
  83. let browserConsole = Services.wm.getMostRecentWindow("devtools:webconsole");
  84. let reopenBrowserConsole = false;
  85. if (browserConsole) {
  86. browserConsole.close();
  87. reopenBrowserConsole = true;
  88. }
  89. dump("Reload DevTools. (reload-toolbox:" + reloadToolbox + ")\n");
  90. // Invalidate xul cache in order to see changes made to chrome:// files
  91. Services.obs.notifyObservers(null, "startupcache-invalidate", null);
  92. // This frame script is going to be executed in all processes:
  93. // parent and child
  94. Services.ppmm.loadProcessScript("data:,new " + function () {
  95. /* Flush message manager cached frame scripts as well as chrome locales */
  96. let obs = Components.classes["@mozilla.org/observer-service;1"]
  97. .getService(Components.interfaces.nsIObserverService);
  98. obs.notifyObservers(null, "message-manager-flush-caches", null);
  99. /* Also purge cached modules in child processes, we do it a few lines after
  100. in the parent process */
  101. if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
  102. Services.obs.notifyObservers(null, "devtools-unload", "reload");
  103. }
  104. }, false);
  105. // As we can't get a reference to existing Loader.jsm instances, we send them
  106. // an observer service notification to unload them.
  107. Services.obs.notifyObservers(null, "devtools-unload", "reload");
  108. // Then spawn a brand new Loader.jsm instance and start the main module
  109. Cu.unload("resource://devtools/shared/Loader.jsm");
  110. // Also unload all resources loaded as jsm, hopefully all of them are going
  111. // to be converted into regular modules
  112. Cu.unload("resource://devtools/client/shared/browser-loader.js");
  113. Cu.unload("resource://devtools/client/framework/ToolboxProcess.jsm");
  114. Cu.unload("resource://devtools/shared/apps/Devices.jsm");
  115. Cu.unload("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
  116. Cu.unload("resource://devtools/shared/Parser.jsm");
  117. Cu.unload("resource://devtools/client/shared/DOMHelpers.jsm");
  118. Cu.unload("resource://devtools/client/shared/widgets/VariablesView.jsm");
  119. Cu.unload("resource://devtools/client/responsivedesign/responsivedesign.jsm");
  120. Cu.unload("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm");
  121. Cu.unload("resource://devtools/shared/deprecated-sync-thenables.js");
  122. const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
  123. devtools.require("devtools/client/framework/devtools-browser");
  124. // Go over all top level windows to reload all devtools related things
  125. let windowsEnum = Services.wm.getEnumerator(null);
  126. while (windowsEnum.hasMoreElements()) {
  127. let window = windowsEnum.getNext();
  128. let windowtype = window.document.documentElement.getAttribute("windowtype");
  129. if (windowtype == "navigator:browser" && window.gBrowser) {
  130. // Enumerate tabs on firefox windows
  131. for (let tab of window.gBrowser.tabs) {
  132. let browser = tab.linkedBrowser;
  133. let location = browser.documentURI.spec;
  134. let mm = browser.messageManager;
  135. // To reload JSON-View tabs and any devtools document
  136. if (location.startsWith("about:debugging") ||
  137. location.startsWith("chrome://devtools/")) {
  138. browser.reload();
  139. }
  140. // We have to use a frame script to query "baseURI"
  141. mm.loadFrameScript("data:text/javascript,new " + function () {
  142. let isJSONView =
  143. content.document.baseURI.startsWith("resource://devtools/");
  144. if (isJSONView) {
  145. content.location.reload();
  146. }
  147. }, false);
  148. }
  149. } else if (windowtype === "devtools:webide") {
  150. window.location.reload();
  151. }
  152. }
  153. if (reloadToolbox) {
  154. // Reopen the toolbox automatically if we are reloading from toolbox
  155. // shortcut and are on a browser window.
  156. // Wait for a second before opening the toolbox to avoid races
  157. // between the old and the new one.
  158. let {setTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
  159. setTimeout(() => {
  160. let { TargetFactory } = devtools.require("devtools/client/framework/target");
  161. let { gDevTools } = devtools.require("devtools/client/framework/devtools");
  162. let target = TargetFactory.forTab(top.gBrowser.selectedTab);
  163. gDevTools.showToolbox(target);
  164. }, 1000);
  165. }
  166. // Browser console document can't just be reloaded.
  167. // HUDService is going to close it on unload.
  168. // Instead we have to manually toggle it.
  169. if (reopenBrowserConsole) {
  170. let {HUDService} = devtools.require("devtools/client/webconsole/hudservice");
  171. HUDService.toggleBrowserConsole();
  172. }
  173. actionOccurred("reloadAddonReload");
  174. }
  175. let prefs = {
  176. // Enable dump as some errors are only printed on the stdout
  177. "browser.dom.window.dump.enabled": true,
  178. // Enable the browser toolbox and various chrome-only features
  179. "devtools.chrome.enabled": true,
  180. "devtools.debugger.remote-enabled": true,
  181. // Disable the prompt to ease usage of the browser toolbox
  182. "devtools.debugger.prompt-connection": false,
  183. };
  184. let originalPrefValues = {};
  185. let listener;
  186. function startup() {
  187. dump("DevTools addon started.\n");
  188. listener = new MultiWindowKeyListener({
  189. keyCode: Ci.nsIDOMKeyEvent.DOM_VK_R, ctrlKey: true, altKey: true,
  190. callback: reload
  191. });
  192. listener.start();
  193. // Toggle development prefs and save original values
  194. originalPrefValues = {};
  195. for (let name in prefs) {
  196. let value = prefs[name];
  197. let userValue = Services.prefs.getBoolPref(name);
  198. // Only toggle if the pref isn't already set to the right value
  199. if (userValue != value) {
  200. Services.prefs.setBoolPref(name, value);
  201. originalPrefValues[name] = userValue;
  202. }
  203. }
  204. }
  205. function shutdown() {
  206. listener.stop();
  207. listener = null;
  208. // Restore preferences that used to be before the addon was installed
  209. for (let name in originalPrefValues) {
  210. let userValue = Services.prefs.getBoolPref(name);
  211. // Only reset the pref if it hasn't changed
  212. if (userValue == prefs[name]) {
  213. Services.prefs.setBoolPref(name, originalPrefValues[name]);
  214. }
  215. }
  216. }
  217. function install() {
  218. actionOccurred("reloadAddonInstalled");
  219. }
  220. function uninstall() {}