devtools-startup.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. /**
  5. * This XPCOM component is loaded very early.
  6. * It handles command line arguments like -jsconsole, but also ensures starting
  7. * core modules like 'devtools-browser.js' that hooks the browser windows
  8. * and ensure setting up tools.
  9. *
  10. * Be careful to lazy load dependencies as much as possible.
  11. **/
  12. "use strict";
  13. const { interfaces: Ci, utils: Cu } = Components;
  14. const kDebuggerPrefs = [
  15. "devtools.debugger.remote-enabled",
  16. "devtools.chrome.enabled"
  17. ];
  18. const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
  19. XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
  20. function DevToolsStartup() {}
  21. DevToolsStartup.prototype = {
  22. handle: function (cmdLine) {
  23. let consoleFlag = cmdLine.handleFlag("browserconsole", false);
  24. let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
  25. let devtoolsFlag = cmdLine.handleFlag("devtools", false);
  26. if (consoleFlag) {
  27. this.handleConsoleFlag(cmdLine);
  28. }
  29. if (debuggerFlag) {
  30. this.handleDebuggerFlag(cmdLine);
  31. }
  32. let debuggerServerFlag;
  33. try {
  34. debuggerServerFlag =
  35. cmdLine.handleFlagWithParam("start-debugger-server", false);
  36. } catch (e) {
  37. // We get an error if the option is given but not followed by a value.
  38. // By catching and trying again, the value is effectively optional.
  39. debuggerServerFlag = cmdLine.handleFlag("start-debugger-server", false);
  40. }
  41. if (debuggerServerFlag) {
  42. this.handleDebuggerServerFlag(cmdLine, debuggerServerFlag);
  43. }
  44. let onStartup = function (window) {
  45. Services.obs.removeObserver(onStartup,
  46. "browser-delayed-startup-finished");
  47. // Ensure loading core module once firefox is ready
  48. this.initDevTools();
  49. if (devtoolsFlag) {
  50. this.handleDevToolsFlag(window);
  51. }
  52. }.bind(this);
  53. Services.obs.addObserver(onStartup, "browser-delayed-startup-finished",
  54. false);
  55. },
  56. initDevTools: function () {
  57. let { loader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
  58. // Ensure loading main devtools module that hooks up into browser UI
  59. // and initialize all devtools machinery.
  60. loader.require("devtools/client/framework/devtools-browser");
  61. },
  62. handleConsoleFlag: function (cmdLine) {
  63. let window = Services.wm.getMostRecentWindow("devtools:webconsole");
  64. if (!window) {
  65. this.initDevTools();
  66. let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
  67. let { HUDService } = require("devtools/client/webconsole/hudservice");
  68. let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
  69. HUDService.toggleBrowserConsole().then(null, console.error);
  70. } else {
  71. // the Browser Console was already open
  72. window.focus();
  73. }
  74. if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
  75. cmdLine.preventDefault = true;
  76. }
  77. },
  78. // Open the toolbox on the selected tab once the browser starts up.
  79. handleDevToolsFlag: function (window) {
  80. const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
  81. const {gDevTools} = require("devtools/client/framework/devtools");
  82. const {TargetFactory} = require("devtools/client/framework/target");
  83. let target = TargetFactory.forTab(window.gBrowser.selectedTab);
  84. gDevTools.showToolbox(target);
  85. },
  86. _isRemoteDebuggingEnabled() {
  87. let remoteDebuggingEnabled = false;
  88. try {
  89. remoteDebuggingEnabled = kDebuggerPrefs.every(pref => {
  90. return Services.prefs.getBoolPref(pref);
  91. });
  92. } catch (ex) {
  93. let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
  94. console.error(ex);
  95. return false;
  96. }
  97. if (!remoteDebuggingEnabled) {
  98. let errorMsg = "Could not run chrome debugger! You need the following " +
  99. "prefs to be set to true: " + kDebuggerPrefs.join(", ");
  100. let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
  101. console.error(new Error(errorMsg));
  102. // Dump as well, as we're doing this from a commandline, make sure people
  103. // don't miss it:
  104. dump(errorMsg + "\n");
  105. }
  106. return remoteDebuggingEnabled;
  107. },
  108. handleDebuggerFlag: function (cmdLine) {
  109. if (!this._isRemoteDebuggingEnabled()) {
  110. return;
  111. }
  112. const { BrowserToolboxProcess } = Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
  113. BrowserToolboxProcess.init();
  114. if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
  115. cmdLine.preventDefault = true;
  116. }
  117. },
  118. /**
  119. * Handle the --start-debugger-server command line flag. The options are:
  120. * --start-debugger-server
  121. * The portOrPath parameter is boolean true in this case. Reads and uses the defaults
  122. * from devtools.debugger.remote-port and devtools.debugger.remote-websocket prefs.
  123. * The default values of these prefs are port 6000, WebSocket disabled.
  124. *
  125. * --start-debugger-server 6789
  126. * Start the non-WebSocket server on port 6789.
  127. *
  128. * --start-debugger-server /path/to/filename
  129. * Start the server on a Unix domain socket.
  130. *
  131. * --start-debugger-server ws:6789
  132. * Start the WebSocket server on port 6789.
  133. *
  134. * --start-debugger-server ws:
  135. * Start the WebSocket server on the default port (taken from d.d.remote-port)
  136. */
  137. handleDebuggerServerFlag: function (cmdLine, portOrPath) {
  138. if (!this._isRemoteDebuggingEnabled()) {
  139. return;
  140. }
  141. let webSocket = false;
  142. let defaultPort = Services.prefs.getIntPref("devtools.debugger.remote-port");
  143. if (portOrPath === true) {
  144. // Default to pref values if no values given on command line
  145. webSocket = Services.prefs.getBoolPref("devtools.debugger.remote-websocket");
  146. portOrPath = defaultPort;
  147. } else if (portOrPath.startsWith("ws:")) {
  148. webSocket = true;
  149. let port = portOrPath.slice(3);
  150. portOrPath = Number(port) ? port : defaultPort;
  151. }
  152. let { DevToolsLoader } =
  153. Cu.import("resource://devtools/shared/Loader.jsm", {});
  154. try {
  155. // Create a separate loader instance, so that we can be sure to receive
  156. // a separate instance of the DebuggingServer from the rest of the
  157. // devtools. This allows us to safely use the tools against even the
  158. // actors and DebuggingServer itself, especially since we can mark
  159. // serverLoader as invisible to the debugger (unlike the usual loader
  160. // settings).
  161. let serverLoader = new DevToolsLoader();
  162. serverLoader.invisibleToDebugger = true;
  163. let { DebuggerServer: debuggerServer } =
  164. serverLoader.require("devtools/server/main");
  165. debuggerServer.init();
  166. debuggerServer.addBrowserActors();
  167. debuggerServer.allowChromeProcess = true;
  168. let listener = debuggerServer.createListener();
  169. listener.portOrPath = portOrPath;
  170. listener.webSocket = webSocket;
  171. listener.open();
  172. dump("Started debugger server on " + portOrPath + "\n");
  173. } catch (e) {
  174. let _error = "Unable to start debugger server on " + portOrPath + ": "
  175. + e;
  176. Cu.reportError(_error);
  177. dump(_error + "\n");
  178. }
  179. if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
  180. cmdLine.preventDefault = true;
  181. }
  182. },
  183. /* eslint-disable max-len */
  184. helpInfo: " --browserconsole Open the Browser Console.\n" +
  185. " --jsdebugger Open the Browser Toolbox.\n" +
  186. " --devtools Open DevTools on initial load.\n" +
  187. " --start-debugger-server [ws:][<port>|<path>] Start the debugger server on\n" +
  188. " a TCP port or Unix domain socket path.\n" +
  189. " Defaults to TCP port 6000.\n" +
  190. " Use WebSocket protocol if ws: prefix\n" +
  191. " is specified.\n",
  192. /* eslint-disable max-len */
  193. classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
  194. QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
  195. };
  196. this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
  197. [DevToolsStartup]);