Bug 1712902 - [remote] Conditionally start CDP when protocol is marked as active. r=remote-protocol-reviewers,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D116415
This commit is contained in:
Henrik Skupin 2021-06-01 19:41:01 +00:00
parent 7543ddeb51
commit 555beb9ea5
8 changed files with 113 additions and 37 deletions

71
remote/cdp/CDP.jsm Normal file
View File

@ -0,0 +1,71 @@
/* 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 = ["CDP"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
JSONHandler: "chrome://remote/content/cdp/JSONHandler.jsm",
TargetList: "chrome://remote/content/cdp/targets/TargetList.jsm",
});
/**
* Entry class for the Chrome DevTools Protocol support.
*
* It holds the list of available targets (tabs, main browser), and also
* sets up the necessary handlers for the HTTP server.
*
* @see https://chromedevtools.github.io/devtools-protocol
*/
class CDP {
/**
* Creates a new instance of the CDP class.
*
* @param {HttpServer} server
* The HTTP server that handles new connection requests.
*/
constructor(server) {
this.server = server;
this.server.registerPrefixHandler("/json/", new JSONHandler(this));
this.targetList = new TargetList();
this.targetList.on("target-created", (eventName, target) => {
this.server.registerPathHandler(target.path, target);
});
this.targetList.on("target-destroyed", (eventName, target) => {
this.server.registerPathHandler(target.path, null);
});
}
/**
* Starts the CDP support.
*/
async start() {
await this.targetList.watchForTargets();
// Immediatly instantiate the main process target in order
// to be accessible via HTTP endpoint on startup
const mainTarget = this.targetList.getMainProcessTarget();
Services.obs.notifyObservers(
null,
"remote-listening",
`DevTools listening on ${mainTarget.wsDebuggerURL}`
);
}
/**
* Stops the CDP support.
*/
stop() {
this.targetList.destructor();
}
}

View File

@ -21,8 +21,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
});
class JSONHandler {
constructor(agent) {
this.agent = agent;
constructor(cdp) {
this.cdp = cdp;
this.routes = {
"/json/version": this.getVersion.bind(this),
"/json/protocol": this.getProtocol.bind(this),
@ -31,7 +31,7 @@ class JSONHandler {
}
getVersion() {
const mainProcessTarget = this.agent.targetList.getMainProcessTarget();
const mainProcessTarget = this.cdp.targetList.getMainProcessTarget();
const { userAgent } = Cc[
"@mozilla.org/network/protocol;1?name=http"
@ -52,7 +52,7 @@ class JSONHandler {
}
getTargetList() {
return [...this.agent.targetList];
return [...this.cdp.targetList];
}
// nsIHttpRequestHandler

View File

@ -22,7 +22,7 @@ add_task(async function json_version() {
is(json["WebKit-Version"], "1.0", "Webkit version found");
is(
json.webSocketDebuggerUrl,
RemoteAgent.targetList.getMainProcessTarget().wsDebuggerURL,
RemoteAgent.cdp.targetList.getMainProcessTarget().wsDebuggerURL,
"Websocket URL for main process target found"
);
});

View File

@ -6,7 +6,7 @@
// Test very basic CDP features.
add_task(async function({ CDP }) {
const { mainProcessTarget } = RemoteAgent.targetList;
const { mainProcessTarget } = RemoteAgent.cdp.targetList;
ok(
mainProcessTarget,
"The main process target is instantiated after the call to `listen`"

View File

@ -6,30 +6,43 @@
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, {
Services: "resource://gre/modules/Services.jsm",
CDP: "chrome://remote/content/cdp/CDP.jsm",
HttpServer: "chrome://remote/content/server/HTTPD.jsm",
JSONHandler: "chrome://remote/content/cdp/JSONHandler.jsm",
Log: "chrome://remote/content/shared/Log.jsm",
Preferences: "resource://gre/modules/Preferences.jsm",
RecommendedPreferences:
"chrome://remote/content/cdp/RecommendedPreferences.jsm",
TargetList: "chrome://remote/content/cdp/targets/TargetList.jsm",
});
XPCOMUtils.defineLazyGetter(this, "logger", () => Log.get());
const FORCE_LOCAL = "remote.force-local";
const PREF_ACTIVE_PROTOCOLS = "remote.active-protocols";
const PREF_FORCE_LOCAL = "remote.force-local";
// const BIDI_ACTIVE = 0x1;
const CDP_ACTIVE = 0x2;
const LOOPBACKS = ["localhost", "127.0.0.1", "[::1]"];
class RemoteAgentClass {
constructor() {
this.cdp = null;
this.server = null;
this.alteredPrefs = new Set();
const protocols = Services.prefs.getIntPref(PREF_ACTIVE_PROTOCOLS);
if (protocols < 1 || protocols > 3) {
throw Error(`Invalid remote protocol identifier: ${protocols}`);
}
this.activeProtocols = protocols;
}
get listening() {
@ -61,7 +74,7 @@ class RemoteAgentClass {
}
let { host, port } = url;
if (Preferences.get(FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
if (Preferences.get(PREF_FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
throw Components.Exception(
"Restricted to loopback devices",
Cr.NS_ERROR_ILLEGAL_VALUE
@ -82,33 +95,19 @@ class RemoteAgentClass {
}
this.server = new HttpServer();
this.server.registerPrefixHandler("/json/", new JSONHandler(this));
this.targetList = new TargetList();
this.targetList.on("target-created", (eventName, target) => {
this.server.registerPathHandler(target.path, target);
});
this.targetList.on("target-destroyed", (eventName, target) => {
this.server.registerPathHandler(target.path, null);
});
if ((this.activeProtocols & CDP_ACTIVE) === CDP_ACTIVE) {
this.cdp = new CDP(this.server);
}
return this.asyncListen(host, port);
}
async asyncListen(host, port) {
try {
await this.targetList.watchForTargets();
// Immediatly instantiate the main process target in order
// to be accessible via HTTP endpoint on startup
const mainTarget = this.targetList.getMainProcessTarget();
this.server._start(port, host);
Services.obs.notifyObservers(
null,
"remote-listening",
`DevTools listening on ${mainTarget.wsDebuggerURL}`
);
await this.cdp?.start();
} catch (e) {
await this.close();
logger.error(`Unable to start remote agent: ${e.message}`, e);
@ -123,11 +122,9 @@ class RemoteAgentClass {
}
this.alteredPrefs.clear();
// destroy targetList before stopping server,
// otherwise the HTTP will fail to stop
if (this.targetList) {
this.targetList.destructor();
}
// Stop the CDP support before stopping the server.
// Otherwise the HTTP server will fail to stop.
this.cdp?.stop();
if (this.listening) {
return this.server.stop();
@ -136,8 +133,8 @@ class RemoteAgentClass {
// this function must never fail
logger.error("unable to stop listener", e);
} finally {
this.cdp = null;
this.server = null;
this.targetList = null;
}
return Promise.resolve();

View File

@ -7,6 +7,13 @@ There are a couple of preferences associated with the Remote Agent:
Configurable preferences
------------------------
### `remote.active-protocols`
Defines the remote protocols that are active. Available protocols are,
WebDriver BiDi (`1`), and CDP (`2`). Multiple protocols can be activated
at the same time by using bitwise or with the values. Defaults to `3`
in Nightly builds, and `2` otherwise.
### `remote.force-local`
Limits the Remote Agent to be allowed to listen on loopback devices,

View File

@ -76,7 +76,7 @@ This is what it looks like all put together:
info("Current URL: " + tab.linkedBrowser.currentURI.spec);
// manually connect to a specific target
const { mainProcessTarget } = RemoteAgent.targetList;
const { mainProcessTarget } = RemoteAgent.cdp.targetList;
const target = mainProcessTarget.wsDebuggerURL;
const client = await CDP({ target });

View File

@ -7,6 +7,7 @@ remote.jar:
content/components/RemoteAgent.jsm (components/RemoteAgent.jsm)
## CDP ##
content/cdp/CDP.jsm (cdp/CDP.jsm)
content/cdp/Connection.jsm (cdp/Connection.jsm)
content/cdp/Error.jsm (cdp/Error.jsm)
content/cdp/JSONHandler.jsm (cdp/JSONHandler.jsm)