зеркало из https://github.com/mozilla/gecko-dev.git
168 строки
4.4 KiB
JavaScript
168 строки
4.4 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",
|
|
Targets: "chrome://remote/content/targets/Targets.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();
|
|
}
|
|
|
|
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 (!(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;
|
|
}
|
|
|
|
if (this.listening) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
Preferences.set(RecommendedPreferences);
|
|
|
|
this.server = new HttpServer();
|
|
this.server.registerPrefixHandler("/json/", new JSONHandler(this));
|
|
|
|
this.targets = new Targets();
|
|
this.targets.on("target-created", (eventName, target) => {
|
|
this.server.registerPathHandler(target.path, target);
|
|
});
|
|
this.targets.on("target-destroyed", (eventName, target) => {
|
|
// TODO: removes the entry added by registerPathHandler,
|
|
// but we should instead have nsIHttpServer.unregisterPathHandler
|
|
delete this.server._handler._overridePaths[target.path];
|
|
});
|
|
|
|
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;
|
|
}
|
|
return this.server.identity.primaryHost;
|
|
}
|
|
|
|
get port() {
|
|
if (!this.server) {
|
|
return null;
|
|
}
|
|
return this.server.identity.primaryPort;
|
|
}
|
|
|
|
// XPCOM
|
|
|
|
get QueryInterface() {
|
|
return ChromeUtils.generateQI([Ci.nsIRemoteAgent]);
|
|
}
|
|
}
|
|
|
|
var RemoteAgent = new RemoteAgentClass();
|
|
|
|
// This is used by the XPCOM codepath which expects a constructor
|
|
var RemoteAgentFactory = function() {
|
|
return RemoteAgent;
|
|
};
|