2013-06-21 07:33:56 -07:00
|
|
|
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
|
|
|
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
const DBG_XUL = "chrome://browser/content/devtools/framework/toolbox-process-window.xul";
|
2013-06-21 07:33:56 -07:00
|
|
|
const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2014-05-20 15:17:03 -07:00
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
|
2013-06-21 07:33:56 -07:00
|
|
|
|
2014-05-20 15:17:03 -07:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "DevToolsLoader",
|
|
|
|
"resource://gre/modules/devtools/Loader.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
|
|
|
"resource://gre/modules/devtools/Loader.jsm");
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "Telemetry", function () {
|
|
|
|
return devtools.require("devtools/shared/telemetry");
|
|
|
|
});
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "EventEmitter", function () {
|
|
|
|
return devtools.require("devtools/toolkit/event-emitter");
|
|
|
|
});
|
2014-04-17 10:27:03 -07:00
|
|
|
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
2013-06-21 07:33:56 -07:00
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
|
2013-06-21 07:33:56 -07:00
|
|
|
|
2014-04-17 10:27:03 -07:00
|
|
|
let processes = Set();
|
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
/**
|
2013-12-02 00:28:01 -08:00
|
|
|
* Constructor for creating a process that will hold a chrome toolbox.
|
2013-06-21 07:33:56 -07:00
|
|
|
*
|
|
|
|
* @param function aOnClose [optional]
|
|
|
|
* A function called when the process stops running.
|
|
|
|
* @param function aOnRun [optional]
|
|
|
|
* A function called when the process starts running.
|
2014-03-25 10:59:14 -07:00
|
|
|
* @param object aOptions [optional]
|
|
|
|
* An object with properties for configuring BrowserToolboxProcess.
|
2013-06-21 07:33:56 -07:00
|
|
|
*/
|
2014-03-25 10:59:14 -07:00
|
|
|
this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) {
|
2014-04-17 10:27:03 -07:00
|
|
|
let emitter = new EventEmitter();
|
|
|
|
this.on = emitter.on.bind(emitter);
|
|
|
|
this.off = emitter.off.bind(emitter);
|
|
|
|
this.once = emitter.once.bind(emitter);
|
|
|
|
// Forward any events to the shared emitter.
|
|
|
|
this.emit = function(...args) {
|
|
|
|
emitter.emit(...args);
|
|
|
|
BrowserToolboxProcess.emit(...args);
|
|
|
|
}
|
|
|
|
|
2014-03-25 10:59:14 -07:00
|
|
|
// If first argument is an object, use those properties instead of
|
|
|
|
// all three arguments
|
|
|
|
if (typeof aOnClose === "object") {
|
2014-04-17 10:27:03 -07:00
|
|
|
if (aOnClose.onClose) {
|
|
|
|
this.on("close", aOnClose.onClose);
|
|
|
|
}
|
|
|
|
if (aOnClose.onRun) {
|
|
|
|
this.on("run", aOnClose.onRun);
|
|
|
|
}
|
2014-03-25 10:59:14 -07:00
|
|
|
this._options = aOnClose;
|
|
|
|
} else {
|
2014-04-17 10:27:03 -07:00
|
|
|
if (aOnClose) {
|
|
|
|
this.on("close", aOnClose);
|
|
|
|
}
|
|
|
|
if (aOnRun) {
|
|
|
|
this.on("run", aOnRun);
|
|
|
|
}
|
2014-03-25 10:59:14 -07:00
|
|
|
this._options = aOptions || {};
|
|
|
|
}
|
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
this._telemetry = new Telemetry();
|
|
|
|
|
2014-01-24 09:29:33 -08:00
|
|
|
this.close = this.close.bind(this);
|
|
|
|
Services.obs.addObserver(this.close, "quit-application", false);
|
2013-06-21 07:33:56 -07:00
|
|
|
this._initServer();
|
|
|
|
this._initProfile();
|
|
|
|
this._create();
|
2014-04-17 10:27:03 -07:00
|
|
|
|
|
|
|
processes.add(this);
|
2013-09-13 06:23:13 -07:00
|
|
|
};
|
2013-06-21 07:33:56 -07:00
|
|
|
|
2014-04-17 10:27:03 -07:00
|
|
|
EventEmitter.decorate(BrowserToolboxProcess);
|
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
/**
|
2013-12-02 00:28:01 -08:00
|
|
|
* Initializes and starts a chrome toolbox process.
|
2013-06-21 07:33:56 -07:00
|
|
|
* @return object
|
|
|
|
*/
|
2014-03-25 10:59:14 -07:00
|
|
|
BrowserToolboxProcess.init = function(aOnClose, aOnRun, aOptions) {
|
|
|
|
return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions);
|
2013-06-21 07:33:56 -07:00
|
|
|
};
|
|
|
|
|
2014-04-17 10:27:03 -07:00
|
|
|
/**
|
|
|
|
* Passes a set of options to the BrowserAddonActors for the given ID.
|
|
|
|
*
|
|
|
|
* @param aId string
|
|
|
|
* The ID of the add-on to pass the options to
|
|
|
|
* @param aOptions object
|
|
|
|
* The options.
|
|
|
|
* @return a promise that will be resolved when complete.
|
|
|
|
*/
|
|
|
|
BrowserToolboxProcess.setAddonOptions = function DSC_setAddonOptions(aId, aOptions) {
|
|
|
|
let promises = [];
|
|
|
|
|
|
|
|
for (let process of processes.values()) {
|
|
|
|
promises.push(process.debuggerServer.setAddonOptions(aId, aOptions));
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.all(promises);
|
|
|
|
};
|
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
BrowserToolboxProcess.prototype = {
|
2013-06-21 07:33:56 -07:00
|
|
|
/**
|
|
|
|
* Initializes the debugger server.
|
|
|
|
*/
|
|
|
|
_initServer: function() {
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Initializing the chrome toolbox server.");
|
2013-09-20 04:51:32 -07:00
|
|
|
|
2013-08-28 07:19:34 -07:00
|
|
|
if (!this.loader) {
|
|
|
|
// Create a separate loader instance, so that we can be sure to receive a
|
|
|
|
// separate instance of the DebuggingServer from the rest of the devtools.
|
|
|
|
// This allows us to safely use the tools against even the actors and
|
2014-01-10 21:37:09 -08:00
|
|
|
// DebuggingServer itself, especially since we can mark this loader as
|
|
|
|
// invisible to the debugger (unlike the usual loader settings).
|
2013-08-28 07:19:34 -07:00
|
|
|
this.loader = new DevToolsLoader();
|
2014-01-10 21:37:09 -08:00
|
|
|
this.loader.invisibleToDebugger = true;
|
2013-08-28 07:19:34 -07:00
|
|
|
this.loader.main("devtools/server/main");
|
|
|
|
this.debuggerServer = this.loader.DebuggerServer;
|
2013-09-20 04:51:32 -07:00
|
|
|
dumpn("Created a separate loader instance for the DebuggerServer.");
|
2014-04-17 10:27:03 -07:00
|
|
|
|
|
|
|
// Forward interesting events.
|
|
|
|
this.debuggerServer.on("connectionchange", this.emit.bind(this));
|
2013-06-21 07:33:56 -07:00
|
|
|
}
|
2013-08-28 07:19:34 -07:00
|
|
|
|
|
|
|
if (!this.debuggerServer.initialized) {
|
|
|
|
this.debuggerServer.init();
|
|
|
|
this.debuggerServer.addBrowserActors();
|
2013-09-20 04:51:32 -07:00
|
|
|
dumpn("initialized and added the browser actors for the DebuggerServer.");
|
2013-08-28 07:19:34 -07:00
|
|
|
}
|
2013-09-20 04:51:32 -07:00
|
|
|
|
2014-05-20 15:17:03 -07:00
|
|
|
let chromeDebuggingPort =
|
|
|
|
Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");
|
|
|
|
this.debuggerServer.openListener(chromeDebuggingPort);
|
2013-09-20 04:51:32 -07:00
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Finished initializing the chrome toolbox server.");
|
2014-05-20 15:17:03 -07:00
|
|
|
dumpn("Started listening on port: " + chromeDebuggingPort);
|
2013-06-21 07:33:56 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes a profile for the remote debugger process.
|
|
|
|
*/
|
|
|
|
_initProfile: function() {
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Initializing the chrome toolbox user profile.");
|
2013-09-20 04:51:32 -07:00
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
|
|
|
|
.createInstance(Ci.nsIToolkitProfileService);
|
|
|
|
|
|
|
|
let profileName;
|
|
|
|
try {
|
|
|
|
// Attempt to get the required chrome debugging profile name string.
|
|
|
|
profileName = profileService.selectedProfile.name + CHROME_DEBUGGER_PROFILE_NAME;
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Using chrome toolbox profile name: " + profileName);
|
2013-06-21 07:33:56 -07:00
|
|
|
} catch (e) {
|
|
|
|
// Requested profile string could not be retrieved.
|
|
|
|
profileName = CHROME_DEBUGGER_PROFILE_NAME;
|
|
|
|
let msg = "Querying the current profile failed. " + e.name + ": " + e.message;
|
|
|
|
dumpn(msg);
|
|
|
|
Cu.reportError(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
let profileObject;
|
|
|
|
try {
|
|
|
|
// Attempt to get the required chrome debugging profile toolkit object.
|
|
|
|
profileObject = profileService.getProfileByName(profileName);
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Using chrome toolbox profile object: " + profileObject);
|
2013-06-21 07:33:56 -07:00
|
|
|
|
|
|
|
// The profile exists but the corresponding folder may have been deleted.
|
|
|
|
var enumerator = Services.dirsvc.get("ProfD", Ci.nsIFile).parent.directoryEntries;
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
let profileDir = enumerator.getNext().QueryInterface(Ci.nsIFile);
|
|
|
|
if (profileDir.leafName.contains(profileName)) {
|
|
|
|
// Requested profile was found and the folder exists.
|
|
|
|
this._dbgProfile = profileObject;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Requested profile was found but the folder was deleted. Cleanup needed.
|
|
|
|
profileObject.remove(true);
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("The already existing chrome toolbox profile was invalid.");
|
2013-06-21 07:33:56 -07:00
|
|
|
} catch (e) {
|
|
|
|
// Requested profile object was not found.
|
|
|
|
let msg = "Creating a profile failed. " + e.name + ": " + e.message;
|
|
|
|
dumpn(msg);
|
|
|
|
Cu.reportError(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new chrome debugging profile.
|
2013-11-18 19:46:00 -08:00
|
|
|
this._dbgProfile = profileService.createProfile(null, profileName);
|
2013-06-21 07:33:56 -07:00
|
|
|
profileService.flush();
|
2013-09-20 04:51:32 -07:00
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Finished creating the chrome toolbox user profile.");
|
2013-09-20 04:51:32 -07:00
|
|
|
dumpn("Flushed profile service with: " + profileName);
|
2013-06-21 07:33:56 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates and initializes the profile & process for the remote debugger.
|
|
|
|
*/
|
|
|
|
_create: function() {
|
|
|
|
dumpn("Initializing chrome debugging process.");
|
|
|
|
let process = this._dbgProcess = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
|
|
|
process.init(Services.dirsvc.get("XREExeF", Ci.nsIFile));
|
|
|
|
|
2014-03-25 10:59:14 -07:00
|
|
|
let xulURI = DBG_XUL;
|
|
|
|
|
|
|
|
if (this._options.addonID) {
|
|
|
|
xulURI += "?addonID=" + this._options.addonID;
|
|
|
|
}
|
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
dumpn("Running chrome debugging process.");
|
2014-03-25 10:59:14 -07:00
|
|
|
let args = ["-no-remote", "-foreground", "-P", this._dbgProfile.name, "-chrome", xulURI];
|
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
process.runwAsync(args, args.length, { observe: () => this.close() });
|
|
|
|
|
|
|
|
this._telemetry.toolOpened("jsbrowserdebugger");
|
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Chrome toolbox is now running...");
|
2014-04-17 10:27:03 -07:00
|
|
|
this.emit("run", this);
|
2013-06-21 07:33:56 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-01-24 09:29:33 -08:00
|
|
|
* Closes the remote debugging server and kills the toolbox process.
|
2013-06-21 07:33:56 -07:00
|
|
|
*/
|
|
|
|
close: function() {
|
2014-01-24 09:29:33 -08:00
|
|
|
if (this.closed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-20 04:51:32 -07:00
|
|
|
dumpn("Cleaning up the chrome debugging process.");
|
2014-01-24 09:29:33 -08:00
|
|
|
Services.obs.removeObserver(this.close, "quit-application");
|
2013-09-20 04:51:32 -07:00
|
|
|
|
2013-06-21 07:33:56 -07:00
|
|
|
if (this._dbgProcess.isRunning) {
|
|
|
|
this._dbgProcess.kill();
|
|
|
|
}
|
|
|
|
|
|
|
|
this._telemetry.toolClosed("jsbrowserdebugger");
|
2014-01-24 09:29:33 -08:00
|
|
|
if (this.debuggerServer) {
|
|
|
|
this.debuggerServer.destroy();
|
|
|
|
}
|
2013-08-28 07:19:34 -07:00
|
|
|
|
2013-12-02 00:28:01 -08:00
|
|
|
dumpn("Chrome toolbox is now closed...");
|
2014-01-24 09:29:33 -08:00
|
|
|
this.closed = true;
|
2014-04-17 10:27:03 -07:00
|
|
|
this.emit("close", this);
|
|
|
|
processes.delete(this);
|
2013-06-21 07:33:56 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper method for debugging.
|
|
|
|
* @param string
|
|
|
|
*/
|
|
|
|
function dumpn(str) {
|
|
|
|
if (wantLogging) {
|
|
|
|
dump("DBG-FRONTEND: " + str + "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
2013-09-20 04:51:32 -07:00
|
|
|
|
|
|
|
Services.prefs.addObserver("devtools.debugger.log", {
|
|
|
|
observe: (...args) => wantLogging = Services.prefs.getBoolPref(args.pop())
|
|
|
|
}, false);
|
2014-05-20 15:17:03 -07:00
|
|
|
|
|
|
|
Services.obs.notifyObservers(null, "ToolboxProcessLoaded", null);
|