forked from mirrors/gecko-dev
Bug 1884090 - [remote] Add support for "HTTP flag" in WebDriver Session. r=webdriver-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D211815
This commit is contained in:
parent
9406e9ecd1
commit
9c60f55dee
8 changed files with 263 additions and 129 deletions
|
|
@ -115,6 +115,9 @@ export function GeckoDriver(server) {
|
|||
// WebDriver Session
|
||||
this._currentSession = null;
|
||||
|
||||
// Flag to indicate a WebDriver HTTP session
|
||||
this._flags = new Set([lazy.WebDriverSession.SESSION_FLAG_HTTP]);
|
||||
|
||||
// Flag to indicate that the application is shutting down
|
||||
this._isShuttingDown = false;
|
||||
|
||||
|
|
@ -401,14 +404,20 @@ GeckoDriver.prototype.newSession = async function (cmd) {
|
|||
const { parameters: capabilities } = cmd;
|
||||
|
||||
try {
|
||||
// If the WebDriver BiDi protocol is active always use the Remote Agent
|
||||
// to handle the WebDriver session. If it's not the case then Marionette
|
||||
// itself needs to handle it, and has to nullify the "webSocketUrl"
|
||||
// capability.
|
||||
if (lazy.RemoteAgent.webDriverBiDi) {
|
||||
await lazy.RemoteAgent.webDriverBiDi.createSession(capabilities);
|
||||
// If the WebDriver BiDi protocol is active always use the Remote Agent
|
||||
// to handle the WebDriver session.
|
||||
await lazy.RemoteAgent.webDriverBiDi.createSession(
|
||||
capabilities,
|
||||
this._flags
|
||||
);
|
||||
} else {
|
||||
this._currentSession = new lazy.WebDriverSession(capabilities);
|
||||
// If it's not the case then Marionette itself needs to handle it, and
|
||||
// has to nullify the "webSocketUrl" capability.
|
||||
this._currentSession = new lazy.WebDriverSession(
|
||||
capabilities,
|
||||
this._flags
|
||||
);
|
||||
this._currentSession.capabilities.delete("webSocketUrl");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const { error } = ChromeUtils.importESModule(
|
|||
);
|
||||
|
||||
add_task(async function test_execute_missing_command_error() {
|
||||
const session = new WebDriverSession();
|
||||
const session = new WebDriverSession({}, new Set());
|
||||
|
||||
info("Attempt to execute an unknown protocol command");
|
||||
await Assert.rejects(
|
||||
|
|
@ -24,7 +24,7 @@ add_task(async function test_execute_missing_command_error() {
|
|||
});
|
||||
|
||||
add_task(async function test_execute_missing_internal_command_error() {
|
||||
const session = new WebDriverSession();
|
||||
const session = new WebDriverSession({}, new Set());
|
||||
|
||||
info(
|
||||
"Attempt to execute a protocol command which relies on an unknown internal method"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
* 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/. */
|
||||
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
|
|
@ -14,21 +16,45 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
"chrome://remote/content/shared/webdriver/UserPromptHandler.sys.mjs",
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "debuggerAddress", () => {
|
||||
return lazy.RemoteAgent.running && lazy.RemoteAgent.cdp
|
||||
? lazy.remoteAgent.debuggerAddress
|
||||
: null;
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "isHeadless", () => {
|
||||
return Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless;
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "remoteAgent", () => {
|
||||
return Cc["@mozilla.org/remote/agent;1"].createInstance(Ci.nsIRemoteAgent);
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "userAgent", () => {
|
||||
return Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||
Ci.nsIHttpProtocolHandler
|
||||
).userAgent;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"shutdownTimeout",
|
||||
"toolkit.asyncshutdown.crash_timeout"
|
||||
);
|
||||
|
||||
// List of capabilities which are only relevant for Webdriver Classic.
|
||||
export const WEBDRIVER_CLASSIC_CAPABILITIES = [
|
||||
"pageLoadStrategy",
|
||||
"timeouts",
|
||||
"strictFileInteractability",
|
||||
"timeouts",
|
||||
"unhandledPromptBehavior",
|
||||
"webSocketUrl",
|
||||
"moz:useNonSpecCompliantPointerOrigin",
|
||||
"moz:webdriverClick",
|
||||
|
||||
// Gecko specific capabilities
|
||||
"moz:debuggerAddress",
|
||||
"moz:firefoxOptions",
|
||||
"moz:useNonSpecCompliantPointerOrigin",
|
||||
"moz:webdriverClick",
|
||||
];
|
||||
|
||||
/** Representation of WebDriver session timeouts. */
|
||||
|
|
@ -90,7 +116,7 @@ export class Timeouts {
|
|||
|
||||
default:
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
"Unrecognised timeout: " + type
|
||||
`Unrecognized timeout: ${type}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -407,51 +433,36 @@ export class Proxy {
|
|||
}
|
||||
}
|
||||
|
||||
/** WebDriver session capabilities representation. */
|
||||
export class Capabilities extends Map {
|
||||
/** @class */
|
||||
/**
|
||||
* WebDriver session capabilities representation.
|
||||
*/
|
||||
constructor() {
|
||||
// Default values for capabilities supported by both WebDriver protocols
|
||||
super([
|
||||
// webdriver
|
||||
["browserName", getWebDriverBrowserName()],
|
||||
["browserVersion", lazy.AppInfo.version],
|
||||
["platformName", getWebDriverPlatformName()],
|
||||
["acceptInsecureCerts", false],
|
||||
["pageLoadStrategy", PageLoadStrategy.Normal],
|
||||
["proxy", new Proxy()],
|
||||
["setWindowRect", !lazy.AppInfo.isAndroid],
|
||||
["timeouts", new Timeouts()],
|
||||
["strictFileInteractability", false],
|
||||
["unhandledPromptBehavior", new lazy.UserPromptHandler()],
|
||||
[
|
||||
"userAgent",
|
||||
Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||
Ci.nsIHttpProtocolHandler
|
||||
).userAgent,
|
||||
],
|
||||
["webSocketUrl", null],
|
||||
["userAgent", lazy.userAgent],
|
||||
|
||||
// proprietary
|
||||
// HTTP only capabilities
|
||||
["pageLoadStrategy", PageLoadStrategy.Normal],
|
||||
["timeouts", new Timeouts()],
|
||||
["setWindowRect", !lazy.AppInfo.isAndroid],
|
||||
["strictFileInteractability", false],
|
||||
|
||||
// Gecko specific capabilities
|
||||
["moz:accessibilityChecks", false],
|
||||
["moz:buildID", lazy.AppInfo.appBuildID],
|
||||
[
|
||||
"moz:debuggerAddress",
|
||||
// With bug 1715481 fixed always use the Remote Agent instance
|
||||
lazy.RemoteAgent.running && lazy.RemoteAgent.cdp
|
||||
? lazy.remoteAgent.debuggerAddress
|
||||
: null,
|
||||
],
|
||||
[
|
||||
"moz:headless",
|
||||
Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless,
|
||||
],
|
||||
["moz:debuggerAddress", lazy.debuggerAddress],
|
||||
["moz:headless", lazy.isHeadless],
|
||||
["moz:platformVersion", Services.sysinfo.getProperty("version")],
|
||||
["moz:processID", lazy.AppInfo.processID],
|
||||
["moz:profile", maybeProfile()],
|
||||
[
|
||||
"moz:shutdownTimeout",
|
||||
Services.prefs.getIntPref("toolkit.asyncshutdown.crash_timeout"),
|
||||
],
|
||||
["moz:shutdownTimeout", lazy.shutdownTimeout],
|
||||
["moz:webdriverClick", true],
|
||||
["moz:windowless", false],
|
||||
]);
|
||||
|
|
@ -478,7 +489,7 @@ export class Capabilities extends Map {
|
|||
}
|
||||
|
||||
/**
|
||||
* JSON serialisation of capabilities object.
|
||||
* JSON serialization of capabilities object.
|
||||
*
|
||||
* @returns {Object<string, ?>}
|
||||
*/
|
||||
|
|
@ -501,11 +512,13 @@ export class Capabilities extends Map {
|
|||
*
|
||||
* @param {Object<string, *>=} json
|
||||
* WebDriver capabilities.
|
||||
* @param {boolean=} isHttp
|
||||
* Flag indicating that it is a WebDriver classic session. Defaults to false.
|
||||
*
|
||||
* @returns {Capabilities}
|
||||
* Internal representation of WebDriver capabilities.
|
||||
*/
|
||||
static fromJSON(json) {
|
||||
static fromJSON(json, isHttp = false) {
|
||||
if (typeof json == "undefined" || json === null) {
|
||||
json = {};
|
||||
}
|
||||
|
|
@ -518,6 +531,11 @@ export class Capabilities extends Map {
|
|||
// TODO: Bug 1823907. We can start using here spec compliant method `validate`,
|
||||
// as soon as `desiredCapabilities` and `requiredCapabilities` are not supported.
|
||||
for (let [k, v] of Object.entries(json)) {
|
||||
if (!isHttp && WEBDRIVER_CLASSIC_CAPABILITIES.includes(k)) {
|
||||
// Ignore any WebDriver classic capability for a WebDriver BiDi session.
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (k) {
|
||||
case "acceptInsecureCerts":
|
||||
lazy.assert.boolean(
|
||||
|
|
|
|||
|
|
@ -32,10 +32,31 @@ ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
|
|||
// Global singleton that holds active WebDriver sessions
|
||||
const webDriverSessions = new Map();
|
||||
|
||||
/**
|
||||
* @typedef {Set} SessionConfigurationFlags
|
||||
* A set of flags defining the features of a WebDriver session. It can be
|
||||
* empty or contain entries as listed below. External specifications may
|
||||
* define additional flags, or create sessions without the HTTP flag.
|
||||
*
|
||||
* <dl>
|
||||
* <dt><code>"http"</code> (string)
|
||||
* <dd>Flag indicating a WebDriver classic (HTTP) session.
|
||||
* </dl>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Representation of WebDriver session.
|
||||
*/
|
||||
export class WebDriverSession {
|
||||
#capabilities;
|
||||
#connections;
|
||||
#http;
|
||||
#id;
|
||||
#messageHandler;
|
||||
#path;
|
||||
|
||||
static SESSION_FLAG_HTTP = "http";
|
||||
|
||||
/**
|
||||
* Construct a new WebDriver session.
|
||||
*
|
||||
|
|
@ -52,23 +73,22 @@ export class WebDriverSession {
|
|||
* are implicitly trusted on navigation for the duration of the session.
|
||||
*
|
||||
* <dt><code>pageLoadStrategy</code> (string)
|
||||
* <dd>The page load strategy to use for the current session. Must be
|
||||
* <dd>(HTTP only) The page load strategy to use for the current session. Must be
|
||||
* one of "<tt>none</tt>", "<tt>eager</tt>", and "<tt>normal</tt>".
|
||||
*
|
||||
* <dt><code>proxy</code> (Proxy object)
|
||||
* <dd>Defines the proxy configuration.
|
||||
*
|
||||
* <dt><code>setWindowRect</code> (boolean)
|
||||
* <dd>Indicates whether the remote end supports all of the resizing
|
||||
* <dd>(HTTP only) Indicates whether the remote end supports all of the resizing
|
||||
* and repositioning commands.
|
||||
*
|
||||
* <dt><code>strictFileInteractability</code> (boolean)
|
||||
* <dd>Defines the current session’s strict file interactability.
|
||||
* <dd>(HTTP only) Defines the current session’s strict file interactability.
|
||||
*
|
||||
* <dt><code>timeouts</code> (Timeouts object)
|
||||
* <dd>Describes the timeouts imposed on certian session operations.
|
||||
* <dd>(HTTP only) Describes the timeouts imposed on certain session operations.
|
||||
*
|
||||
* TODO: update for WebDriver BiDi type
|
||||
* <dt><code>unhandledPromptBehavior</code> (string)
|
||||
* <dd>Describes the current session’s user prompt handler. Must be one of
|
||||
* "<tt>accept</tt>", "<tt>accept and notify</tt>", "<tt>dismiss</tt>",
|
||||
|
|
@ -76,13 +96,13 @@ export class WebDriverSession {
|
|||
* "<tt>dismiss and notify</tt>" state.
|
||||
*
|
||||
* <dt><code>moz:accessibilityChecks</code> (boolean)
|
||||
* <dd>Run a11y checks when clicking elements.
|
||||
* <dd>(HTTP only) Run a11y checks when clicking elements.
|
||||
*
|
||||
* <dt><code>moz:debuggerAddress</code> (boolean)
|
||||
* <dd>Indicate that the Chrome DevTools Protocol (CDP) has to be enabled.
|
||||
*
|
||||
* <dt><code>moz:webdriverClick</code> (boolean)
|
||||
* <dd>Use a WebDriver conforming <i>WebDriver::ElementClick</i>.
|
||||
* <dd>(HTTP only) Use a WebDriver conforming <i>WebDriver::ElementClick</i>.
|
||||
* </dl>
|
||||
*
|
||||
* <h4>WebAuthn</h4>
|
||||
|
|
@ -140,7 +160,7 @@ export class WebDriverSession {
|
|||
* <code>proxyType</code> is "<tt>manual</tt>".
|
||||
*
|
||||
* <dt><code>noProxy</code> (string)
|
||||
* <dd>Lists the adress for which the proxy should be bypassed when
|
||||
* <dd>Lists the address for which the proxy should be bypassed when
|
||||
* the <code>proxyType</code> is "<tt>manual</tt>". Must be a JSON
|
||||
* List containing any number of any of domains, IPv4 addresses, or IPv6
|
||||
* addresses.
|
||||
|
|
@ -168,9 +188,10 @@ export class WebDriverSession {
|
|||
* </code></pre>
|
||||
*
|
||||
* @param {Object<string, *>=} capabilities
|
||||
* JSON Object containing any of the recognised capabilities listed
|
||||
* JSON Object containing any of the recognized capabilities listed
|
||||
* above.
|
||||
*
|
||||
* @param {SessionConfigurationFlags} flags
|
||||
* Session configuration flags.
|
||||
* @param {WebDriverBiDiConnection=} connection
|
||||
* An optional existing WebDriver BiDi connection to associate with the
|
||||
* new session.
|
||||
|
|
@ -178,19 +199,20 @@ export class WebDriverSession {
|
|||
* @throws {SessionNotCreatedError}
|
||||
* If, for whatever reason, a session could not be created.
|
||||
*/
|
||||
constructor(capabilities, connection) {
|
||||
constructor(capabilities, flags, connection) {
|
||||
// WebSocket connections that use this session. This also accounts for
|
||||
// possible disconnects due to network outages, which require clients
|
||||
// to reconnect.
|
||||
this._connections = new Set();
|
||||
this.#connections = new Set();
|
||||
|
||||
this.id = lazy.generateUUID();
|
||||
this.#id = lazy.generateUUID();
|
||||
this.#http = flags.has(WebDriverSession.SESSION_FLAG_HTTP);
|
||||
|
||||
// Define the HTTP path to query this session via WebDriver BiDi
|
||||
this.path = `/session/${this.id}`;
|
||||
this.#path = `/session/${this.#id}`;
|
||||
|
||||
try {
|
||||
this.capabilities = lazy.Capabilities.fromJSON(capabilities, this.path);
|
||||
this.#capabilities = lazy.Capabilities.fromJSON(capabilities, this.#http);
|
||||
} catch (e) {
|
||||
throw new lazy.error.SessionNotCreatedError(e);
|
||||
}
|
||||
|
|
@ -201,7 +223,7 @@ export class WebDriverSession {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.capabilities.get("acceptInsecureCerts")) {
|
||||
if (this.acceptInsecureCerts) {
|
||||
lazy.logger.warn(
|
||||
"TLS certificate errors will be ignored for this session"
|
||||
);
|
||||
|
|
@ -219,7 +241,7 @@ export class WebDriverSession {
|
|||
// immediately register the newly created session for it.
|
||||
if (connection) {
|
||||
connection.registerSession(this);
|
||||
this._connections.add(connection);
|
||||
this.#connections.add(connection);
|
||||
}
|
||||
|
||||
// Maps a Navigable (browsing context or content browser for top-level
|
||||
|
|
@ -228,11 +250,11 @@ export class WebDriverSession {
|
|||
|
||||
lazy.registerProcessDataActor();
|
||||
|
||||
webDriverSessions.set(this.id, this);
|
||||
webDriverSessions.set(this.#id, this);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
webDriverSessions.delete(this.id);
|
||||
webDriverSessions.delete(this.#id);
|
||||
|
||||
lazy.unregisterProcessDataActor();
|
||||
|
||||
|
|
@ -241,20 +263,20 @@ export class WebDriverSession {
|
|||
lazy.allowAllCerts.disable();
|
||||
|
||||
// Close all open connections which unregister themselves.
|
||||
this._connections.forEach(connection => connection.close());
|
||||
if (this._connections.size > 0) {
|
||||
this.#connections.forEach(connection => connection.close());
|
||||
if (this.#connections.size > 0) {
|
||||
lazy.logger.warn(
|
||||
`Failed to close ${this._connections.size} WebSocket connections`
|
||||
`Failed to close ${this.#connections.size} WebSocket connections`
|
||||
);
|
||||
}
|
||||
|
||||
// Destroy the dedicated MessageHandler instance if we created one.
|
||||
if (this._messageHandler) {
|
||||
this._messageHandler.off(
|
||||
if (this.#messageHandler) {
|
||||
this.#messageHandler.off(
|
||||
"message-handler-protocol-event",
|
||||
this._onMessageHandlerProtocolEvent
|
||||
);
|
||||
this._messageHandler.destroy();
|
||||
this.#messageHandler.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,46 +304,66 @@ export class WebDriverSession {
|
|||
}
|
||||
|
||||
get a11yChecks() {
|
||||
return this.capabilities.get("moz:accessibilityChecks");
|
||||
return this.#capabilities.get("moz:accessibilityChecks");
|
||||
}
|
||||
|
||||
get acceptInsecureCerts() {
|
||||
return this.#capabilities.get("acceptInsecureCerts");
|
||||
}
|
||||
|
||||
get capabilities() {
|
||||
return this.#capabilities;
|
||||
}
|
||||
|
||||
get http() {
|
||||
return this.#http;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
get messageHandler() {
|
||||
if (!this._messageHandler) {
|
||||
this._messageHandler =
|
||||
lazy.RootMessageHandlerRegistry.getOrCreateMessageHandler(this.id);
|
||||
if (!this.#messageHandler) {
|
||||
this.#messageHandler =
|
||||
lazy.RootMessageHandlerRegistry.getOrCreateMessageHandler(this.#id);
|
||||
this._onMessageHandlerProtocolEvent =
|
||||
this._onMessageHandlerProtocolEvent.bind(this);
|
||||
this._messageHandler.on(
|
||||
this.#messageHandler.on(
|
||||
"message-handler-protocol-event",
|
||||
this._onMessageHandlerProtocolEvent
|
||||
);
|
||||
}
|
||||
|
||||
return this._messageHandler;
|
||||
return this.#messageHandler;
|
||||
}
|
||||
|
||||
get pageLoadStrategy() {
|
||||
return this.capabilities.get("pageLoadStrategy");
|
||||
return this.#capabilities.get("pageLoadStrategy");
|
||||
}
|
||||
|
||||
get path() {
|
||||
return this.#path;
|
||||
}
|
||||
|
||||
get proxy() {
|
||||
return this.capabilities.get("proxy");
|
||||
return this.#capabilities.get("proxy");
|
||||
}
|
||||
|
||||
get strictFileInteractability() {
|
||||
return this.capabilities.get("strictFileInteractability");
|
||||
return this.#capabilities.get("strictFileInteractability");
|
||||
}
|
||||
|
||||
get timeouts() {
|
||||
return this.capabilities.get("timeouts");
|
||||
return this.#capabilities.get("timeouts");
|
||||
}
|
||||
|
||||
set timeouts(timeouts) {
|
||||
this.capabilities.set("timeouts", timeouts);
|
||||
this.#capabilities.set("timeouts", timeouts);
|
||||
}
|
||||
|
||||
get userPromptHandler() {
|
||||
return this.capabilities.get("unhandledPromptBehavior");
|
||||
return this.#capabilities.get("unhandledPromptBehavior");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -330,15 +372,15 @@ export class WebDriverSession {
|
|||
* @param {WebDriverBiDiConnection} connection
|
||||
*/
|
||||
removeConnection(connection) {
|
||||
if (this._connections.has(connection)) {
|
||||
this._connections.delete(connection);
|
||||
if (this.#connections.has(connection)) {
|
||||
this.#connections.delete(connection);
|
||||
} else {
|
||||
lazy.logger.warn("Trying to remove a connection that doesn't exist.");
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `[object ${this.constructor.name} ${this.id}]`;
|
||||
return `[object ${this.constructor.name} ${this.#id}]`;
|
||||
}
|
||||
|
||||
// nsIHttpRequestHandler
|
||||
|
|
@ -362,12 +404,12 @@ export class WebDriverSession {
|
|||
response._connection
|
||||
);
|
||||
conn.registerSession(this);
|
||||
this._connections.add(conn);
|
||||
this.#connections.add(conn);
|
||||
}
|
||||
|
||||
_onMessageHandlerProtocolEvent(eventName, messageHandlerEvent) {
|
||||
const { name, data } = messageHandlerEvent;
|
||||
this._connections.forEach(connection => connection.sendEvent(name, data));
|
||||
this.#connections.forEach(connection => connection.sendEvent(name, data));
|
||||
}
|
||||
|
||||
// XPCOM
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ add_task(function test_Timeouts_fromJSON() {
|
|||
equal(ts.script, json.script);
|
||||
});
|
||||
|
||||
add_task(function test_Timeouts_fromJSON_unrecognised_field() {
|
||||
add_task(function test_Timeouts_fromJSON_unrecognized_field() {
|
||||
let json = {
|
||||
sessionId: "foobar",
|
||||
};
|
||||
|
|
@ -58,7 +58,7 @@ add_task(function test_Timeouts_fromJSON_unrecognised_field() {
|
|||
Timeouts.fromJSON(json);
|
||||
} catch (e) {
|
||||
equal(e.name, error.InvalidArgumentError.name);
|
||||
equal(e.message, "Unrecognised timeout: sessionId");
|
||||
equal(e.message, "Unrecognized timeout: sessionId");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -439,27 +439,53 @@ add_task(function test_Capabilities_fromJSON() {
|
|||
caps = fromJSON({ acceptInsecureCerts: false });
|
||||
equal(false, caps.get("acceptInsecureCerts"));
|
||||
|
||||
for (let strategy of Object.values(PageLoadStrategy)) {
|
||||
caps = fromJSON({ pageLoadStrategy: strategy });
|
||||
equal(strategy, caps.get("pageLoadStrategy"));
|
||||
}
|
||||
|
||||
let proxyConfig = { proxyType: "manual" };
|
||||
caps = fromJSON({ proxy: proxyConfig });
|
||||
equal("manual", caps.get("proxy").proxyType);
|
||||
|
||||
// HTTP only capabilities
|
||||
for (let strategy of Object.values(PageLoadStrategy)) {
|
||||
caps = fromJSON({ pageLoadStrategy: strategy }, true);
|
||||
equal(strategy, caps.get("pageLoadStrategy"));
|
||||
|
||||
caps = fromJSON({ pageLoadStrategy: strategy });
|
||||
equal("normal", caps.get("pageLoadStrategy"));
|
||||
}
|
||||
|
||||
let timeoutsConfig = { implicit: 123 };
|
||||
caps = fromJSON({ timeouts: timeoutsConfig });
|
||||
equal(0, caps.get("timeouts").implicit);
|
||||
caps = fromJSON({ timeouts: timeoutsConfig }, true);
|
||||
equal(123, caps.get("timeouts").implicit);
|
||||
|
||||
caps = fromJSON({ strictFileInteractability: false });
|
||||
caps = fromJSON({ strictFileInteractability: false }, true);
|
||||
equal(false, caps.get("strictFileInteractability"));
|
||||
caps = fromJSON({ strictFileInteractability: true });
|
||||
caps = fromJSON({ strictFileInteractability: true }, true);
|
||||
equal(true, caps.get("strictFileInteractability"));
|
||||
|
||||
caps = fromJSON({ webSocketUrl: true });
|
||||
caps = fromJSON({ webSocketUrl: true }, true);
|
||||
equal(true, caps.get("webSocketUrl"));
|
||||
|
||||
// Mozilla specific capabilities
|
||||
caps = fromJSON({ "moz:accessibilityChecks": true });
|
||||
equal(true, caps.get("moz:accessibilityChecks"));
|
||||
caps = fromJSON({ "moz:accessibilityChecks": false });
|
||||
equal(false, caps.get("moz:accessibilityChecks"));
|
||||
|
||||
caps = fromJSON({ "moz:webdriverClick": true }, true);
|
||||
equal(true, caps.get("moz:webdriverClick"));
|
||||
caps = fromJSON({ "moz:webdriverClick": false }, true);
|
||||
equal(false, caps.get("moz:webdriverClick"));
|
||||
|
||||
// capability is always populated with null if remote agent is not listening
|
||||
caps = fromJSON({});
|
||||
equal(null, caps.get("moz:debuggerAddress"));
|
||||
caps = fromJSON({ "moz:debuggerAddress": "foo" });
|
||||
equal(null, caps.get("moz:debuggerAddress"));
|
||||
caps = fromJSON({ "moz:debuggerAddress": true });
|
||||
equal(null, caps.get("moz:debuggerAddress"));
|
||||
|
||||
// Extension capabilities
|
||||
caps = fromJSON({ "webauthn:virtualAuthenticators": true });
|
||||
equal(true, caps.get("webauthn:virtualAuthenticators"));
|
||||
caps = fromJSON({ "webauthn:virtualAuthenticators": false });
|
||||
|
|
@ -505,31 +531,13 @@ add_task(function test_Capabilities_fromJSON() {
|
|||
/InvalidArgumentError/
|
||||
);
|
||||
|
||||
caps = fromJSON({ "moz:accessibilityChecks": true });
|
||||
equal(true, caps.get("moz:accessibilityChecks"));
|
||||
caps = fromJSON({ "moz:accessibilityChecks": false });
|
||||
equal(false, caps.get("moz:accessibilityChecks"));
|
||||
|
||||
// capability is always populated with null if remote agent is not listening
|
||||
caps = fromJSON({});
|
||||
equal(null, caps.get("moz:debuggerAddress"));
|
||||
caps = fromJSON({ "moz:debuggerAddress": "foo" });
|
||||
equal(null, caps.get("moz:debuggerAddress"));
|
||||
caps = fromJSON({ "moz:debuggerAddress": true });
|
||||
equal(null, caps.get("moz:debuggerAddress"));
|
||||
|
||||
caps = fromJSON({ "moz:webdriverClick": true });
|
||||
equal(true, caps.get("moz:webdriverClick"));
|
||||
caps = fromJSON({ "moz:webdriverClick": false });
|
||||
equal(false, caps.get("moz:webdriverClick"));
|
||||
|
||||
// No longer supported capabilities
|
||||
Assert.throws(
|
||||
() => fromJSON({ "moz:useNonSpecCompliantPointerOrigin": false }),
|
||||
() => fromJSON({ "moz:useNonSpecCompliantPointerOrigin": false }, true),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
Assert.throws(
|
||||
() => fromJSON({ "moz:useNonSpecCompliantPointerOrigin": true }),
|
||||
() => fromJSON({ "moz:useNonSpecCompliantPointerOrigin": true }, true),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Capabilities, Timeouts } = ChromeUtils.importESModule(
|
||||
const { Timeouts } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/webdriver/Capabilities.sys.mjs"
|
||||
);
|
||||
const { getWebDriverSessionById, WebDriverSession } =
|
||||
|
|
@ -12,26 +12,79 @@ const { getWebDriverSessionById, WebDriverSession } =
|
|||
"chrome://remote/content/shared/webdriver/Session.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(function test_WebDriverSession_ctor() {
|
||||
const session = new WebDriverSession();
|
||||
function createSession(options = {}) {
|
||||
const { capabilities = {}, connection, isHttp = false } = options;
|
||||
|
||||
const flags = new Set();
|
||||
if (isHttp) {
|
||||
flags.add("http");
|
||||
}
|
||||
|
||||
return new WebDriverSession(capabilities, flags, connection);
|
||||
}
|
||||
|
||||
add_task(function test_WebDriverSession_ctor() {
|
||||
Assert.throws(() => new WebDriverSession({}), /TypeError/);
|
||||
|
||||
// Session id and path
|
||||
let session = createSession();
|
||||
equal(typeof session.id, "string");
|
||||
ok(session.capabilities instanceof Capabilities);
|
||||
equal(session.path, `/session/${session.id}`);
|
||||
|
||||
// Sets HTTP flag
|
||||
session = createSession({ isHttp: true });
|
||||
equal(session.http, true);
|
||||
session = createSession({ isHttp: false });
|
||||
equal(session.http, false);
|
||||
|
||||
// Sets capabilities based on session configuration flag.
|
||||
const capabilities = {
|
||||
acceptInsecureCerts: true,
|
||||
unhandledPromptBehavior: "ignore",
|
||||
|
||||
// HTTP only
|
||||
pageLoadStrategy: "eager",
|
||||
strictFileInteractability: true,
|
||||
timeouts: { script: 1000 },
|
||||
};
|
||||
|
||||
// HTTP session
|
||||
session = createSession({ isHttp: true, capabilities });
|
||||
equal(session.acceptInsecureCerts, true);
|
||||
equal(session.pageLoadStrategy, "eager");
|
||||
equal(session.strictFileInteractability, true);
|
||||
equal(session.timeouts.script, 1000);
|
||||
equal(session.userPromptHandler.toJSON(), "ignore");
|
||||
|
||||
// BiDi session (uses default values for HTTP only capabilities)
|
||||
session = createSession({ isHttp: false, capabilities });
|
||||
equal(session.acceptInsecureCerts, true);
|
||||
equal(session.pageLoadStrategy, "normal");
|
||||
equal(session.strictFileInteractability, false);
|
||||
equal(session.timeouts.script, 30000);
|
||||
equal(session.userPromptHandler.toJSON(), "dismiss and notify");
|
||||
});
|
||||
|
||||
add_task(function test_WebDriverSession_destroy() {
|
||||
const session = new WebDriverSession();
|
||||
const session = createSession();
|
||||
|
||||
session.destroy();
|
||||
|
||||
// Calling twice doesn't raise error.
|
||||
session.destroy();
|
||||
});
|
||||
|
||||
add_task(function test_WebDriverSession_getters() {
|
||||
const session = new WebDriverSession();
|
||||
const session = createSession();
|
||||
|
||||
equal(
|
||||
session.a11yChecks,
|
||||
session.capabilities.get("moz:accessibilityChecks")
|
||||
);
|
||||
equal(
|
||||
session.acceptInsecureCerts,
|
||||
session.capabilities.get("acceptInsecureCerts")
|
||||
);
|
||||
equal(session.pageLoadStrategy, session.capabilities.get("pageLoadStrategy"));
|
||||
equal(session.proxy, session.capabilities.get("proxy"));
|
||||
equal(
|
||||
|
|
@ -46,7 +99,7 @@ add_task(function test_WebDriverSession_getters() {
|
|||
});
|
||||
|
||||
add_task(function test_WebDriverSession_setters() {
|
||||
const session = new WebDriverSession();
|
||||
const session = createSession();
|
||||
|
||||
const timeouts = new Timeouts();
|
||||
timeouts.pageLoad = 45;
|
||||
|
|
@ -56,8 +109,8 @@ add_task(function test_WebDriverSession_setters() {
|
|||
});
|
||||
|
||||
add_task(function test_getWebDriverSessionById() {
|
||||
const session1 = new WebDriverSession();
|
||||
const session2 = new WebDriverSession();
|
||||
const session1 = createSession();
|
||||
const session2 = createSession();
|
||||
|
||||
equal(getWebDriverSessionById(session1.id), session1);
|
||||
equal(getWebDriverSessionById(session2.id), session2);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ export class WebDriverBiDi {
|
|||
* @param {Object<string, *>=} capabilities
|
||||
* JSON Object containing any of the recognised capabilities as listed
|
||||
* on the `WebDriverSession` class.
|
||||
*
|
||||
* @param {Set} flags
|
||||
* Session configuration flags.
|
||||
* @param {WebDriverBiDiConnection=} sessionlessConnection
|
||||
* Optional connection that is not yet accociated with a WebDriver
|
||||
* session, and has to be associated with the new WebDriver session.
|
||||
|
|
@ -80,7 +81,7 @@ export class WebDriverBiDi {
|
|||
* @throws {SessionNotCreatedError}
|
||||
* If, for whatever reason, a session could not be created.
|
||||
*/
|
||||
async createSession(capabilities, sessionlessConnection) {
|
||||
async createSession(capabilities, flags, sessionlessConnection) {
|
||||
if (this.session) {
|
||||
throw new lazy.error.SessionNotCreatedError(
|
||||
"Maximum number of active sessions"
|
||||
|
|
@ -89,6 +90,7 @@ export class WebDriverBiDi {
|
|||
|
||||
const session = new lazy.WebDriverSession(
|
||||
capabilities,
|
||||
flags,
|
||||
sessionlessConnection
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -180,13 +180,15 @@ export class WebDriverBiDiConnection extends WebSocketConnection {
|
|||
if (module === "session" && command === "new") {
|
||||
const processedCapabilities = lazy.processCapabilities(params);
|
||||
|
||||
const flags = new Set();
|
||||
result = await lazy.RemoteAgent.webDriverBiDi.createSession(
|
||||
processedCapabilities,
|
||||
flags,
|
||||
this
|
||||
);
|
||||
|
||||
// Since in Capabilities class we setup default values also for capabilities which are
|
||||
// not relevant for bidi, we want to remove them from the payload before returning to a client.
|
||||
// The Capabilities class sets up default values also for capabilities
|
||||
// which are not relevant for BiDi, we want to remove those from the payload.
|
||||
result.capabilities = Array.from(result.capabilities.entries()).reduce(
|
||||
(object, [key, value]) => {
|
||||
if (!lazy.WEBDRIVER_CLASSIC_CAPABILITIES.includes(key)) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue