Skip to content

Commit

Permalink
Bug 1712902 - [remote] Conditionally start CDP when protocol is marke…
Browse files Browse the repository at this point in the history
…d as active. r=remote-protocol-reviewers,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D116415
  • Loading branch information
whimboo committed Jun 1, 2021
1 parent 7543dde commit 555beb9
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 37 deletions.
71 changes: 71 additions & 0 deletions remote/cdp/CDP.jsm
Original file line number Diff line number Diff line change
@@ -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();
}
}
8 changes: 4 additions & 4 deletions remote/cdp/JSONHandler.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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"
Expand All @@ -52,7 +52,7 @@ class JSONHandler {
}

getTargetList() {
return [...this.agent.targetList];
return [...this.cdp.targetList];
}

// nsIHttpRequestHandler
Expand Down
2 changes: 1 addition & 1 deletion remote/cdp/test/browser/browser_httpd.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
});
Expand Down
2 changes: 1 addition & 1 deletion remote/cdp/test/browser/browser_main_target.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`"
Expand Down
57 changes: 27 additions & 30 deletions remote/components/RemoteAgent.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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();
Expand Down
7 changes: 7 additions & 0 deletions remote/doc/Prefs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion remote/doc/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 });

Expand Down
1 change: 1 addition & 0 deletions remote/jar.mn
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 555beb9

Please sign in to comment.