1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 var EXPORTED_SYMBOLS = ["RemoteAgent", "RemoteAgentFactory"];
9 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
10 const { XPCOMUtils } = ChromeUtils.import(
11 "resource://gre/modules/XPCOMUtils.jsm"
14 XPCOMUtils.defineLazyModuleGetters(this, {
15 HttpServer: "chrome://remote/content/server/HTTPD.jsm",
16 JSONHandler: "chrome://remote/content/JSONHandler.jsm",
17 Log: "chrome://remote/content/Log.jsm",
18 Preferences: "resource://gre/modules/Preferences.jsm",
19 RecommendedPreferences: "chrome://remote/content/RecommendedPreferences.jsm",
20 TargetList: "chrome://remote/content/targets/TargetList.jsm",
22 XPCOMUtils.defineLazyGetter(this, "log", Log.get);
24 const ENABLED = "remote.enabled";
25 const FORCE_LOCAL = "remote.force-local";
27 const LOOPBACKS = ["localhost", "127.0.0.1", "[::1]"];
29 class RemoteAgentClass {
31 return !!this.server && !this.server.isStopped();
34 get debuggerAddress() {
39 return `${this.host}:${this.port}`;
43 if (!Preferences.get(ENABLED, false)) {
44 throw Components.Exception(
45 "Disabled by preference",
46 Cr.NS_ERROR_NOT_AVAILABLE
49 if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
50 throw Components.Exception(
51 "May only be instantiated in parent process",
52 Cr.NS_ERROR_LAUNCHED_CHILD_PROCESS
57 return Promise.resolve();
60 if (!(url instanceof Ci.nsIURI)) {
61 url = Services.io.newURI(url);
64 let { host, port } = url;
65 if (Preferences.get(FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
66 throw Components.Exception(
67 "Restricted to loopback devices",
68 Cr.NS_ERROR_ILLEGAL_VALUE
72 // nsIServerSocket uses -1 for atomic port allocation
77 Preferences.set(RecommendedPreferences);
79 this.server = new HttpServer();
80 this.server.registerPrefixHandler("/json/", new JSONHandler(this));
82 this.targets = new TargetList();
83 this.targets.on("target-created", (eventName, target) => {
84 this.server.registerPathHandler(target.path, target);
86 this.targets.on("target-destroyed", (eventName, target) => {
87 this.server.registerPathHandler(target.path, null);
90 return this.asyncListen(host, port);
93 async asyncListen(host, port) {
95 await this.targets.watchForTargets();
97 // Immediatly instantiate the main process target in order
98 // to be accessible via HTTP endpoint on startup
99 const mainTarget = this.targets.getMainProcessTarget();
101 this.server._start(port, host);
102 Services.obs.notifyObservers(
105 mainTarget.wsDebuggerURL
109 log.error(`Unable to start remote agent: ${e.message}`, e);
115 // if called early at startup, preferences may not be available
117 Preferences.reset(Object.keys(RecommendedPreferences));
120 // destroy targets before stopping server,
121 // otherwise the HTTP will fail to stop
123 this.targets.destructor();
126 if (this.listening) {
127 return this.server.stop();
130 // this function must never fail
131 log.error("unable to stop listener", e);
137 return Promise.resolve();
144 return this.server.identity.primaryScheme;
152 // Bug 1675471: When using the nsIRemoteAgent interface the HTTPd server's
153 // primary identity ("this.server.identity.primaryHost") is lazily set.
154 return this.server._host;
162 // Bug 1675471: When using the nsIRemoteAgent interface the HTTPd server's
163 // primary identity ("this.server.identity.primaryPort") is lazily set.
164 return this.server._port;
169 get QueryInterface() {
170 return ChromeUtils.generateQI(["nsIRemoteAgent"]);
174 var RemoteAgent = new RemoteAgentClass();
176 // This is used by the XPCOM codepath which expects a constructor
177 var RemoteAgentFactory = function() {