2019-01-29 18:18:42 +03:00
|
|
|
/* 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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
|
|
|
ActorManagerParent: "resource://gre/modules/ActorManagerParent.jsm",
|
|
|
|
HttpServer: "chrome://remote/content/server/HTTPD.jsm",
|
|
|
|
Log: "chrome://remote/content/Log.jsm",
|
|
|
|
NetUtil: "resource://gre/modules/NetUtil.jsm",
|
|
|
|
Observer: "chrome://remote/content/Observer.jsm",
|
|
|
|
Preferences: "resource://gre/modules/Preferences.jsm",
|
|
|
|
ProtocolHandler: "chrome://remote/content/Handler.jsm",
|
|
|
|
RecommendedPreferences: "chrome://remote/content/Prefs.jsm",
|
|
|
|
TabObserver: "chrome://remote/content/WindowManager.jsm",
|
|
|
|
Target: "chrome://remote/content/Target.jsm",
|
2019-02-17 19:57:01 +03:00
|
|
|
Targets: "chrome://remote/content/Targets.jsm",
|
2019-01-29 18:18:42 +03:00
|
|
|
TargetListHandler: "chrome://remote/content/Handler.jsm",
|
|
|
|
});
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
|
|
|
|
|
|
|
const ENABLED = "remote.enabled";
|
|
|
|
const FORCE_LOCAL = "remote.force-local";
|
|
|
|
const HTTPD = "remote.httpd";
|
|
|
|
const SCHEME = `${HTTPD}.scheme`;
|
|
|
|
const HOST = `${HTTPD}.host`;
|
|
|
|
const PORT = `${HTTPD}.port`;
|
|
|
|
|
|
|
|
const DEFAULT_HOST = "localhost";
|
|
|
|
const DEFAULT_PORT = 9222;
|
2019-02-17 20:33:59 +03:00
|
|
|
const LOOPBACKS = ["localhost", "127.0.0.1", "[::1]"];
|
2019-01-29 18:18:42 +03:00
|
|
|
|
|
|
|
class ParentRemoteAgent {
|
|
|
|
constructor() {
|
|
|
|
this.server = null;
|
|
|
|
this.targets = new Targets();
|
|
|
|
|
|
|
|
this.tabs = new TabObserver({registerExisting: true});
|
|
|
|
this.tabs.on("open", tab => this.targets.connect(tab.linkedBrowser));
|
|
|
|
this.tabs.on("close", tab => this.targets.disconnect(tab.linkedBrowser));
|
2019-02-12 18:22:56 +03:00
|
|
|
|
|
|
|
// This allows getting access to the underlying JS object
|
|
|
|
// of the '@mozilla.org/remote/agent' XPCOM components.
|
|
|
|
this.wrappedJSObject = this;
|
2019-01-29 18:18:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
get listening() {
|
|
|
|
return !!this.server && !this.server._socketClosed;
|
|
|
|
}
|
|
|
|
|
|
|
|
listen(address) {
|
|
|
|
if (!(address instanceof Ci.nsIURI)) {
|
|
|
|
throw new TypeError(`Expected nsIURI: ${address}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
let {host, port} = address;
|
|
|
|
if (Preferences.get(FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
|
|
|
|
throw new Error("Restricted to loopback devices");
|
|
|
|
}
|
|
|
|
|
|
|
|
// nsIServerSocket uses -1 for atomic port allocation
|
|
|
|
if (port === 0) {
|
|
|
|
port = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.listening) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.server = new HttpServer();
|
|
|
|
const targetList = new TargetListHandler(this.targets);
|
|
|
|
const protocol = new ProtocolHandler();
|
|
|
|
targetList.register(this.server);
|
|
|
|
protocol.register(this.server);
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.server._start(port, host);
|
|
|
|
log.info(`Remote debugging agent listening on ${this.scheme}://${this.host}:${this.port}/`);
|
|
|
|
} catch (e) {
|
|
|
|
throw new Error(`Unable to start agent on ${port}: ${e.message}`, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
Preferences.set(RecommendedPreferences);
|
|
|
|
Preferences.set(SCHEME, this.scheme);
|
|
|
|
Preferences.set(HOST, this.host);
|
|
|
|
Preferences.set(PORT, this.port);
|
|
|
|
}
|
|
|
|
|
|
|
|
async close() {
|
|
|
|
if (this.listening) {
|
|
|
|
try {
|
|
|
|
await this.server.stop();
|
|
|
|
|
|
|
|
Preferences.resetBranch(HTTPD);
|
|
|
|
Preferences.reset(Object.keys(RecommendedPreferences));
|
|
|
|
|
|
|
|
this.tabs.stop();
|
|
|
|
this.targets.clear();
|
|
|
|
} catch (e) {
|
|
|
|
throw new Error(`Unable to stop agent: ${e.message}`, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
log.info("Stopped listening");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get scheme() {
|
|
|
|
if (!this.server) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.server.identity.primaryScheme;
|
|
|
|
}
|
|
|
|
|
|
|
|
get host() {
|
|
|
|
if (!this.server) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.server.identity.primaryHost;
|
|
|
|
}
|
|
|
|
|
|
|
|
get port() {
|
|
|
|
if (!this.server) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.server.identity.primaryPort;
|
|
|
|
}
|
|
|
|
|
|
|
|
// nsICommandLineHandler
|
|
|
|
|
|
|
|
async handle(cmdLine) {
|
2019-02-17 19:14:01 +03:00
|
|
|
function flag(name) {
|
|
|
|
const caseSensitive = true;
|
|
|
|
try {
|
|
|
|
return cmdLine.handleFlagWithParam(name, caseSensitive);
|
|
|
|
} catch (e) {
|
|
|
|
return cmdLine.handleFlag(name, caseSensitive);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const remoteDebugger = flag("remote-debugger");
|
|
|
|
const remoteDebuggingPort = flag("remote-debugging-port");
|
|
|
|
|
|
|
|
if (remoteDebugger && remoteDebuggingPort) {
|
|
|
|
log.fatal("Conflicting flags --remote-debugger and --remote-debugging-port");
|
|
|
|
cmdLine.preventDefault = true;
|
|
|
|
return;
|
2019-01-29 18:18:42 +03:00
|
|
|
}
|
|
|
|
|
2019-02-17 19:14:01 +03:00
|
|
|
if (!remoteDebugger && !remoteDebuggingPort) {
|
2019-01-29 18:18:42 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let host, port;
|
2019-02-17 19:14:01 +03:00
|
|
|
if (typeof remoteDebugger == "string") {
|
|
|
|
[host, port] = remoteDebugger.split(":");
|
|
|
|
} else if (typeof remoteDebuggingPort == "string") {
|
|
|
|
port = remoteDebuggingPort;
|
2019-01-29 18:18:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
let addr;
|
|
|
|
try {
|
|
|
|
addr = NetUtil.newURI(`http://${host || DEFAULT_HOST}:${port || DEFAULT_PORT}/`);
|
|
|
|
} catch (e) {
|
2019-02-17 19:14:01 +03:00
|
|
|
log.fatal(`Expected address syntax [<host>]:<port>: ${remoteDebugger || remoteDebuggingPort}`);
|
2019-01-29 18:18:42 +03:00
|
|
|
cmdLine.preventDefault = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
await Observer.once("sessionstore-windows-restored");
|
|
|
|
await this.tabs.start();
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.listen(addr);
|
|
|
|
} catch ({message}) {
|
|
|
|
this.close();
|
|
|
|
log.fatal(`Unable to start remote agent on ${addr.spec}: ${message}`);
|
|
|
|
cmdLine.preventDefault = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get helpInfo() {
|
2019-02-17 19:14:01 +03:00
|
|
|
return " --remote-debugger [<host>][:<port>] Start the Firefox remote agent, which is \n" +
|
|
|
|
" a low-level debugging interface based on the CDP protocol.\n" +
|
2019-02-17 19:41:26 +03:00
|
|
|
" Defaults to listen on localhost:9222.\n";
|
2019-01-29 18:18:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// XPCOM
|
|
|
|
|
|
|
|
get QueryInterface() {
|
2019-02-17 19:14:01 +03:00
|
|
|
return ChromeUtils.generateQI([Ci.nsICommandLineHandler]);
|
2019-01-29 18:18:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const RemoteAgentFactory = {
|
|
|
|
instance_: null,
|
|
|
|
|
|
|
|
createInstance(outer, iid) {
|
|
|
|
if (outer) {
|
|
|
|
throw Cr.NS_ERROR_NO_AGGREGATION;
|
|
|
|
}
|
|
|
|
if (!Preferences.get(ENABLED, false)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-02-08 19:49:15 +03:00
|
|
|
if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) {
|
|
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
2019-01-29 18:18:42 +03:00
|
|
|
}
|
|
|
|
|
2019-02-08 19:49:15 +03:00
|
|
|
if (!this.instance_) {
|
|
|
|
this.instance_ = new ParentRemoteAgent();
|
|
|
|
}
|
2019-01-29 18:18:42 +03:00
|
|
|
return this.instance_.QueryInterface(iid);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
function RemoteAgent() {}
|
|
|
|
|
|
|
|
RemoteAgent.prototype = {
|
|
|
|
classDescription: "Remote Agent",
|
|
|
|
classID: Components.ID("{8f685a9d-8181-46d6-a71d-869289099c6d}"),
|
|
|
|
contractID: "@mozilla.org/remote/agent",
|
|
|
|
_xpcom_factory: RemoteAgentFactory, /* eslint-disable-line */
|
|
|
|
};
|
|
|
|
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RemoteAgent]);
|