Skip to content

Commit

Permalink
Bug 1693805 - [remote] Add support for handling WebDriver BiDi connec…
Browse files Browse the repository at this point in the history
…tions. r=webdriver-reviewers,jgraham,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D119266
  • Loading branch information
whimboo committed Jul 8, 2021
1 parent b656aea commit 27dc32a
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 3 deletions.
12 changes: 11 additions & 1 deletion remote/components/RemoteAgent.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
CDP: "chrome://remote/content/cdp/CDP.jsm",
HttpServer: "chrome://remote/content/server/HTTPD.jsm",
Log: "chrome://remote/content/shared/Log.jsm",
WebDriverBiDi: "chrome://remote/content/webdriver-bidi/WebDriverBiDi.jsm",
});

XPCOMUtils.defineLazyGetter(this, "logger", () => Log.get());
Expand All @@ -30,7 +31,7 @@ XPCOMUtils.defineLazyGetter(this, "activeProtocols", () => {
return protocols;
});

// const BIDI_ACTIVE = 0x1;
const WEBDRIVER_BIDI_ACTIVE = 0x1;
const CDP_ACTIVE = 0x2;

// By default force local connections only
Expand All @@ -41,6 +42,13 @@ class RemoteAgentClass {
constructor() {
this.server = null;

if ((activeProtocols & WEBDRIVER_BIDI_ACTIVE) === WEBDRIVER_BIDI_ACTIVE) {
this.webdriverBiDi = new WebDriverBiDi(this);
logger.debug("WebDriver BiDi enabled");
} else {
this.webdriverBiDi = null;
}

if ((activeProtocols & CDP_ACTIVE) === CDP_ACTIVE) {
this.cdp = new CDP(this);
logger.debug("CDP enabled");
Expand Down Expand Up @@ -116,6 +124,7 @@ class RemoteAgentClass {
this.server._start(port, host);

await this.cdp?.start();
await this.webdriverBiDi?.start();
} catch (e) {
await this.close();
logger.error(`Unable to start remote agent: ${e.message}`, e);
Expand All @@ -127,6 +136,7 @@ class RemoteAgentClass {
// Stop the CDP support before stopping the server.
// Otherwise the HTTP server will fail to stop.
this.cdp?.stop();
this.webdriverBiDi?.stop();

if (this.listening) {
return this.server.stop();
Expand Down
1 change: 1 addition & 0 deletions remote/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ DIRS += [
"components",
"marionette",
"shared",
"webdriver-bidi",
]

JAR_MANIFESTS += ["jar.mn"]
Expand Down
2 changes: 1 addition & 1 deletion remote/shared/Log.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ XPCOMUtils.defineLazyGetter(this, "prefLogLevel", () => {
/** E10s compatible wrapper for the standard logger from Log.jsm. */
class Log {
static TYPES = {
BIDI: "BiDi",
CDP: "CDP",
MARIONETTE: "Marionette",
REMOTE_AGENT: "RemoteAgent",
WEBDRIVER_BIDI: "WebDriver BiDi",
};

/**
Expand Down
4 changes: 3 additions & 1 deletion remote/shared/webdriver/Capabilities.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,9 @@ class Capabilities extends Map {
[
"moz:debuggerAddress",
// With bug 1715481 fixed always use the Remote Agent instance
RemoteAgent.cdp ? remoteAgent.debuggerAddress : null,
RemoteAgent.listening && RemoteAgent.cdp
? remoteAgent.debuggerAddress
: null,
],
[
"moz:headless",
Expand Down
58 changes: 58 additions & 0 deletions remote/webdriver-bidi/WebDriverBiDi.jsm
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* 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 = ["WebDriverBiDi"];

const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);

XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",

Log: "chrome://remote/content/shared/Log.jsm",
});

XPCOMUtils.defineLazyGetter(this, "logger", () =>
Log.get(Log.TYPES.WEBDRIVER_BIDI)
);

/**
* Entry class for the WebDriver BiDi support.
*
* @see https://w3c.github.io/webdriver-bidi
*/
class WebDriverBiDi {
/**
* Creates a new instance of the WebDriverBiDi class.
*
* @param {RemoteAgent} agent
* Reference to the Remote Agent instance.
*/
constructor(agent) {
this.agent = agent;
}

get address() {
return `ws://${this.agent.host}:${this.agent.port}`;
}

/**
* Starts the WebDriver BiDi support.
*/
start() {
Services.obs.notifyObservers(
null,
"remote-listening",
`WebDriver BiDi listening on ${this.address}`
);
}

/**
* Stops the WebDriver BiDi support.
*/
stop() {}
}
139 changes: 139 additions & 0 deletions remote/webdriver-bidi/WebDriverBiDiConnection.jsm
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* 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 = ["WebDriverBiDiConnection"];

const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);

XPCOMUtils.defineLazyModuleGetters(this, {
error: "chrome://remote/content/shared/webdriver/Errors.jsm",
Log: "chrome://remote/content/shared/Log.jsm",
truncate: "chrome://remote/content/shared/Format.jsm",
WebSocketConnection: "chrome://remote/content/shared/WebSocketConnection.jsm",
});

XPCOMUtils.defineLazyGetter(this, "logger", () =>
Log.get(Log.TYPES.WEBDRIVER_BIDI)
);

class WebDriverBiDiConnection extends WebSocketConnection {
/**
* @param {WebSocket} webSocket
* The WebSocket server connection to wrap.
* @param {HttpServer} httpdConnection
* Reference to the httpd.js's connection needed for clean-up.
*/
constructor(webSocket, httpdConnection) {
super(webSocket, httpdConnection);

// Each connection has only a single WebDriver session.
this.session = null;
}

/**
* Register a new Session to forward the messages to.
*
* @param {Session} session
* The WebDriverSession to register.
*/
registerSession(session) {
if (this.session) {
throw new error.UnknownError("A session has already been set");
}

this.session = session;
}

/**
* Send the JSON-serializable object to the WebDriver BiDi client.
*
* @param {Object} data
* The object to be sent.
*/
send(data) {
const payload = JSON.stringify(data, null, Log.verbose ? "\t" : null);
logger.trace(truncate`${this.constructor.name} ${this.id} <- ${payload}`);

super.send(data);
}

/**
* Send an error back to the WebDriver BiDi client.
*
* @param {Number} id
* Id of the packet which lead to an error.
* @param {Error} err
* Error object with `message` and `stack` attributes.
*/
sendError(id, error) {}

/**
* Send an event coming from a module to the WebDriver BiDi client.
*
* @param {String} method
* The event name. This is composed by a module name, a dot character
* followed by the event name, e.g. `log.entryAdded`.
* @param {Object} params
* A JSON-serializable object, which is the payload of this event.
*/
sendEvent(method, params) {}

/**
* Send the result of a call to a module's method back to the
* WebDriver BiDi client.
*
* @param {Number} id
* The request id being sent by the client to call the module's method.
* @param {Object} result
* A JSON-serializable object, which is the actual result.
*/
sendResult(id, result) {
this.send({ id, result });
}

// Transport hooks

/**
* Called by the `transport` when the connection is closed.
*/
onClosed() {
super.onClosed();
}

/**
* Receive a packet from the WebSocket layer.
*
* This packet is sent by a WebDriver BiDi client and is meant to execute
* a particular method on a given module.
*
* @param Object packet
* JSON-serializable object sent by the client
*/
async onPacket(packet) {
const payload = JSON.stringify(packet, null, Log.verbose ? "\t" : null);
logger.trace(truncate`${this.constructor.name} ${this.id} -> ${payload}`);

const { id, method /* params */ } = packet;

try {
// First check for mandatory field in the packets
if (typeof id == "undefined") {
throw new TypeError("Message missing 'id' field");
}
if (typeof method == "undefined") {
throw new TypeError("Message missing 'method' field");
}

let result = undefined;

this.sendResult(id, result);
} catch (e) {
this.sendError(packet.id, e);
}
}
}
9 changes: 9 additions & 0 deletions remote/webdriver-bidi/jar.mn
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 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/.

remote.jar:
% content remote %content/

content/webdriver-bidi/WebDriverBiDiConnection.jsm (WebDriverBiDiConnection.jsm)
content/webdriver-bidi/WebDriverBiDi.jsm (WebDriverBiDi.jsm)
8 changes: 8 additions & 0 deletions remote/webdriver-bidi/moz.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 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/.

JAR_MANIFESTS += ["jar.mn"]

with Files("**"):
BUG_COMPONENT = ("Remote Protocol", "WebDriver BiDi")

0 comments on commit 27dc32a

Please sign in to comment.