gecko-dev/remote/RemoteAgent.jsm
Henrik Skupin 28bce1f7d9 Bug 1675266 - [remote] Add debuggerAddress as attribute on the Remote Agent interface. r=remote-protocol-reviewers,maja_zf
To allow other components in Firefox to easily find the address
under which the Remote Agent is accessible, add the host and port
information as "debuggerAddress" to the Remote Agent's interface
definition.

Differential Revision: https://phabricator.services.mozilla.com/D96008
2020-11-06 18:36:49 +00:00

180 lines
4.6 KiB
JavaScript

/* 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";
var EXPORTED_SYMBOLS = ["RemoteAgent", "RemoteAgentFactory"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
HttpServer: "chrome://remote/content/server/HTTPD.jsm",
JSONHandler: "chrome://remote/content/JSONHandler.jsm",
Log: "chrome://remote/content/Log.jsm",
Preferences: "resource://gre/modules/Preferences.jsm",
RecommendedPreferences: "chrome://remote/content/RecommendedPreferences.jsm",
TargetList: "chrome://remote/content/targets/TargetList.jsm",
});
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
const ENABLED = "remote.enabled";
const FORCE_LOCAL = "remote.force-local";
const LOOPBACKS = ["localhost", "127.0.0.1", "[::1]"];
class RemoteAgentClass {
get listening() {
return !!this.server && !this.server.isStopped();
}
get debuggerAddress() {
if (!this.server) {
return "";
}
return `${this.host}:${this.port}`;
}
listen(url) {
if (!Preferences.get(ENABLED, false)) {
throw Components.Exception(
"Disabled by preference",
Cr.NS_ERROR_NOT_AVAILABLE
);
}
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
throw Components.Exception(
"May only be instantiated in parent process",
Cr.NS_ERROR_LAUNCHED_CHILD_PROCESS
);
}
if (this.listening) {
return Promise.resolve();
}
if (!(url instanceof Ci.nsIURI)) {
url = Services.io.newURI(url);
}
let { host, port } = url;
if (Preferences.get(FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
throw Components.Exception(
"Restricted to loopback devices",
Cr.NS_ERROR_ILLEGAL_VALUE
);
}
// nsIServerSocket uses -1 for atomic port allocation
if (port === 0) {
port = -1;
}
Preferences.set(RecommendedPreferences);
this.server = new HttpServer();
this.server.registerPrefixHandler("/json/", new JSONHandler(this));
this.targets = new TargetList();
this.targets.on("target-created", (eventName, target) => {
this.server.registerPathHandler(target.path, target);
});
this.targets.on("target-destroyed", (eventName, target) => {
this.server.registerPathHandler(target.path, null);
});
return this.asyncListen(host, port);
}
async asyncListen(host, port) {
try {
await this.targets.watchForTargets();
// Immediatly instantiate the main process target in order
// to be accessible via HTTP endpoint on startup
const mainTarget = this.targets.getMainProcessTarget();
this.server._start(port, host);
Services.obs.notifyObservers(
null,
"remote-listening",
mainTarget.wsDebuggerURL
);
} catch (e) {
await this.close();
log.error(`Unable to start remote agent: ${e.message}`, e);
}
}
close() {
try {
// if called early at startup, preferences may not be available
try {
Preferences.reset(Object.keys(RecommendedPreferences));
} catch (e) {}
// destroy targets before stopping server,
// otherwise the HTTP will fail to stop
if (this.targets) {
this.targets.destructor();
}
if (this.listening) {
return this.server.stop();
}
} catch (e) {
// this function must never fail
log.error("unable to stop listener", e);
} finally {
this.server = null;
this.targets = null;
}
return Promise.resolve();
}
get scheme() {
if (!this.server) {
return null;
}
return this.server.identity.primaryScheme;
}
get host() {
if (!this.server) {
return null;
}
// Bug 1675471: When using the nsIRemoteAgent interface the HTTPd server's
// primary identity ("this.server.identity.primaryHost") is lazily set.
return this.server._host;
}
get port() {
if (!this.server) {
return null;
}
// Bug 1675471: When using the nsIRemoteAgent interface the HTTPd server's
// primary identity ("this.server.identity.primaryPort") is lazily set.
return this.server._port;
}
// XPCOM
get QueryInterface() {
return ChromeUtils.generateQI(["nsIRemoteAgent"]);
}
}
var RemoteAgent = new RemoteAgentClass();
// This is used by the XPCOM codepath which expects a constructor
var RemoteAgentFactory = function() {
return RemoteAgent;
};