Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 "use strict";
8 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
10 const DBG_XUL = "chrome://browser/content/devtools/framework/toolbox-process-window.xul";
11 const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
13 Cu.import("resource://gre/modules/Services.jsm");
14 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
16 Cu.import("resource://gre/modules/devtools/Loader.jsm");
17 let require = devtools.require;
18 let Telemetry = require("devtools/shared/telemetry");
19 let EventEmitter = require("devtools/toolkit/event-emitter");
20 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
22 this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
24 let processes = Set();
26 /**
27 * Constructor for creating a process that will hold a chrome toolbox.
28 *
29 * @param function aOnClose [optional]
30 * A function called when the process stops running.
31 * @param function aOnRun [optional]
32 * A function called when the process starts running.
33 * @param object aOptions [optional]
34 * An object with properties for configuring BrowserToolboxProcess.
35 */
36 this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) {
37 let emitter = new EventEmitter();
38 this.on = emitter.on.bind(emitter);
39 this.off = emitter.off.bind(emitter);
40 this.once = emitter.once.bind(emitter);
41 // Forward any events to the shared emitter.
42 this.emit = function(...args) {
43 emitter.emit(...args);
44 BrowserToolboxProcess.emit(...args);
45 }
47 // If first argument is an object, use those properties instead of
48 // all three arguments
49 if (typeof aOnClose === "object") {
50 if (aOnClose.onClose) {
51 this.on("close", aOnClose.onClose);
52 }
53 if (aOnClose.onRun) {
54 this.on("run", aOnClose.onRun);
55 }
56 this._options = aOnClose;
57 } else {
58 if (aOnClose) {
59 this.on("close", aOnClose);
60 }
61 if (aOnRun) {
62 this.on("run", aOnRun);
63 }
64 this._options = aOptions || {};
65 }
67 this._telemetry = new Telemetry();
69 this.close = this.close.bind(this);
70 Services.obs.addObserver(this.close, "quit-application", false);
71 this._initServer();
72 this._initProfile();
73 this._create();
75 processes.add(this);
76 };
78 EventEmitter.decorate(BrowserToolboxProcess);
80 /**
81 * Initializes and starts a chrome toolbox process.
82 * @return object
83 */
84 BrowserToolboxProcess.init = function(aOnClose, aOnRun, aOptions) {
85 return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions);
86 };
88 /**
89 * Passes a set of options to the BrowserAddonActors for the given ID.
90 *
91 * @param aId string
92 * The ID of the add-on to pass the options to
93 * @param aOptions object
94 * The options.
95 * @return a promise that will be resolved when complete.
96 */
97 BrowserToolboxProcess.setAddonOptions = function DSC_setAddonOptions(aId, aOptions) {
98 let promises = [];
100 for (let process of processes.values()) {
101 promises.push(process.debuggerServer.setAddonOptions(aId, aOptions));
102 }
104 return promise.all(promises);
105 };
107 BrowserToolboxProcess.prototype = {
108 /**
109 * Initializes the debugger server.
110 */
111 _initServer: function() {
112 dumpn("Initializing the chrome toolbox server.");
114 if (!this.loader) {
115 // Create a separate loader instance, so that we can be sure to receive a
116 // separate instance of the DebuggingServer from the rest of the devtools.
117 // This allows us to safely use the tools against even the actors and
118 // DebuggingServer itself, especially since we can mark this loader as
119 // invisible to the debugger (unlike the usual loader settings).
120 this.loader = new DevToolsLoader();
121 this.loader.invisibleToDebugger = true;
122 this.loader.main("devtools/server/main");
123 this.debuggerServer = this.loader.DebuggerServer;
124 dumpn("Created a separate loader instance for the DebuggerServer.");
126 // Forward interesting events.
127 this.debuggerServer.on("connectionchange", this.emit.bind(this));
128 }
130 if (!this.debuggerServer.initialized) {
131 this.debuggerServer.init();
132 this.debuggerServer.addBrowserActors();
133 dumpn("initialized and added the browser actors for the DebuggerServer.");
134 }
136 this.debuggerServer.openListener(Prefs.chromeDebuggingPort);
138 dumpn("Finished initializing the chrome toolbox server.");
139 dumpn("Started listening on port: " + Prefs.chromeDebuggingPort);
140 },
142 /**
143 * Initializes a profile for the remote debugger process.
144 */
145 _initProfile: function() {
146 dumpn("Initializing the chrome toolbox user profile.");
148 let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
149 .createInstance(Ci.nsIToolkitProfileService);
151 let profileName;
152 try {
153 // Attempt to get the required chrome debugging profile name string.
154 profileName = profileService.selectedProfile.name + CHROME_DEBUGGER_PROFILE_NAME;
155 dumpn("Using chrome toolbox profile name: " + profileName);
156 } catch (e) {
157 // Requested profile string could not be retrieved.
158 profileName = CHROME_DEBUGGER_PROFILE_NAME;
159 let msg = "Querying the current profile failed. " + e.name + ": " + e.message;
160 dumpn(msg);
161 Cu.reportError(msg);
162 }
164 let profileObject;
165 try {
166 // Attempt to get the required chrome debugging profile toolkit object.
167 profileObject = profileService.getProfileByName(profileName);
168 dumpn("Using chrome toolbox profile object: " + profileObject);
170 // The profile exists but the corresponding folder may have been deleted.
171 var enumerator = Services.dirsvc.get("ProfD", Ci.nsIFile).parent.directoryEntries;
172 while (enumerator.hasMoreElements()) {
173 let profileDir = enumerator.getNext().QueryInterface(Ci.nsIFile);
174 if (profileDir.leafName.contains(profileName)) {
175 // Requested profile was found and the folder exists.
176 this._dbgProfile = profileObject;
177 return;
178 }
179 }
180 // Requested profile was found but the folder was deleted. Cleanup needed.
181 profileObject.remove(true);
182 dumpn("The already existing chrome toolbox profile was invalid.");
183 } catch (e) {
184 // Requested profile object was not found.
185 let msg = "Creating a profile failed. " + e.name + ": " + e.message;
186 dumpn(msg);
187 Cu.reportError(msg);
188 }
190 // Create a new chrome debugging profile.
191 this._dbgProfile = profileService.createProfile(null, profileName);
192 profileService.flush();
194 dumpn("Finished creating the chrome toolbox user profile.");
195 dumpn("Flushed profile service with: " + profileName);
196 },
198 /**
199 * Creates and initializes the profile & process for the remote debugger.
200 */
201 _create: function() {
202 dumpn("Initializing chrome debugging process.");
203 let process = this._dbgProcess = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
204 process.init(Services.dirsvc.get("XREExeF", Ci.nsIFile));
206 let xulURI = DBG_XUL;
208 if (this._options.addonID) {
209 xulURI += "?addonID=" + this._options.addonID;
210 }
212 dumpn("Running chrome debugging process.");
213 let args = ["-no-remote", "-foreground", "-P", this._dbgProfile.name, "-chrome", xulURI];
215 process.runwAsync(args, args.length, { observe: () => this.close() });
217 this._telemetry.toolOpened("jsbrowserdebugger");
219 dumpn("Chrome toolbox is now running...");
220 this.emit("run", this);
221 },
223 /**
224 * Closes the remote debugging server and kills the toolbox process.
225 */
226 close: function() {
227 if (this.closed) {
228 return;
229 }
231 dumpn("Cleaning up the chrome debugging process.");
232 Services.obs.removeObserver(this.close, "quit-application");
234 if (this._dbgProcess.isRunning) {
235 this._dbgProcess.kill();
236 }
238 this._telemetry.toolClosed("jsbrowserdebugger");
239 if (this.debuggerServer) {
240 this.debuggerServer.destroy();
241 }
243 dumpn("Chrome toolbox is now closed...");
244 this.closed = true;
245 this.emit("close", this);
246 processes.delete(this);
247 }
248 };
250 /**
251 * Shortcuts for accessing various debugger preferences.
252 */
253 let Prefs = new ViewHelpers.Prefs("devtools.debugger", {
254 chromeDebuggingHost: ["Char", "chrome-debugging-host"],
255 chromeDebuggingPort: ["Int", "chrome-debugging-port"]
256 });
258 /**
259 * Helper method for debugging.
260 * @param string
261 */
262 function dumpn(str) {
263 if (wantLogging) {
264 dump("DBG-FRONTEND: " + str + "\n");
265 }
266 }
268 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
270 Services.prefs.addObserver("devtools.debugger.log", {
271 observe: (...args) => wantLogging = Services.prefs.getBoolPref(args.pop())
272 }, false);