diff --git a/remote/marionette/driver.sys.mjs b/remote/marionette/driver.sys.mjs index 4dfc2a65c603..4c5c1971cd44 100644 --- a/remote/marionette/driver.sys.mjs +++ b/remote/marionette/driver.sys.mjs @@ -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"); } diff --git a/remote/shared/messagehandler/test/browser/webdriver/browser_session_execute_command_errors.js b/remote/shared/messagehandler/test/browser/webdriver/browser_session_execute_command_errors.js index 36a510bb29dc..a85bf476ec4a 100644 --- a/remote/shared/messagehandler/test/browser/webdriver/browser_session_execute_command_errors.js +++ b/remote/shared/messagehandler/test/browser/webdriver/browser_session_execute_command_errors.js @@ -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" diff --git a/remote/shared/webdriver/Capabilities.sys.mjs b/remote/shared/webdriver/Capabilities.sys.mjs index f0993e5c8f15..873723952478 100644 --- a/remote/shared/webdriver/Capabilities.sys.mjs +++ b/remote/shared/webdriver/Capabilities.sys.mjs @@ -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} */ @@ -501,11 +512,13 @@ export class Capabilities extends Map { * * @param {Object=} 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( diff --git a/remote/shared/webdriver/Session.sys.mjs b/remote/shared/webdriver/Session.sys.mjs index 6d78ed45221b..ee40a8cca247 100644 --- a/remote/shared/webdriver/Session.sys.mjs +++ b/remote/shared/webdriver/Session.sys.mjs @@ -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. + * + *
+ *
"http" (string) + *
Flag indicating a WebDriver classic (HTTP) session. + *
+ */ + /** * 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. * *
pageLoadStrategy (string) - *
The page load strategy to use for the current session. Must be + *
(HTTP only) The page load strategy to use for the current session. Must be * one of "none", "eager", and "normal". * *
proxy (Proxy object) *
Defines the proxy configuration. * *
setWindowRect (boolean) - *
Indicates whether the remote end supports all of the resizing + *
(HTTP only) Indicates whether the remote end supports all of the resizing * and repositioning commands. * *
strictFileInteractability (boolean) - *
Defines the current session’s strict file interactability. + *
(HTTP only) Defines the current session’s strict file interactability. * *
timeouts (Timeouts object) - *
Describes the timeouts imposed on certian session operations. + *
(HTTP only) Describes the timeouts imposed on certain session operations. * - * TODO: update for WebDriver BiDi type *
unhandledPromptBehavior (string) *
Describes the current session’s user prompt handler. Must be one of * "accept", "accept and notify", "dismiss", @@ -76,13 +96,13 @@ export class WebDriverSession { * "dismiss and notify" state. * *
moz:accessibilityChecks (boolean) - *
Run a11y checks when clicking elements. + *
(HTTP only) Run a11y checks when clicking elements. * *
moz:debuggerAddress (boolean) *
Indicate that the Chrome DevTools Protocol (CDP) has to be enabled. * *
moz:webdriverClick (boolean) - *
Use a WebDriver conforming WebDriver::ElementClick. + *
(HTTP only) Use a WebDriver conforming WebDriver::ElementClick. * * *

WebAuthn

@@ -140,7 +160,7 @@ export class WebDriverSession { * proxyType is "manual". * *
noProxy (string) - *
Lists the adress for which the proxy should be bypassed when + *
Lists the address for which the proxy should be bypassed when * the proxyType is "manual". Must be a JSON * List containing any number of any of domains, IPv4 addresses, or IPv6 * addresses. @@ -168,9 +188,10 @@ export class WebDriverSession { * * * @param {Object=} 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 diff --git a/remote/shared/webdriver/test/xpcshell/test_Capabilities.js b/remote/shared/webdriver/test/xpcshell/test_Capabilities.js index dc1e370261ef..0bb1d386143c 100644 --- a/remote/shared/webdriver/test/xpcshell/test_Capabilities.js +++ b/remote/shared/webdriver/test/xpcshell/test_Capabilities.js @@ -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/ ); }); diff --git a/remote/shared/webdriver/test/xpcshell/test_Session.js b/remote/shared/webdriver/test/xpcshell/test_Session.js index 780a28295bb1..931f77eff50b 100644 --- a/remote/shared/webdriver/test/xpcshell/test_Session.js +++ b/remote/shared/webdriver/test/xpcshell/test_Session.js @@ -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); diff --git a/remote/webdriver-bidi/WebDriverBiDi.sys.mjs b/remote/webdriver-bidi/WebDriverBiDi.sys.mjs index 33e877d670a5..b5eb75731749 100644 --- a/remote/webdriver-bidi/WebDriverBiDi.sys.mjs +++ b/remote/webdriver-bidi/WebDriverBiDi.sys.mjs @@ -69,7 +69,8 @@ export class WebDriverBiDi { * @param {Object=} 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 ); diff --git a/remote/webdriver-bidi/WebDriverBiDiConnection.sys.mjs b/remote/webdriver-bidi/WebDriverBiDiConnection.sys.mjs index 5ec7ff9a06bf..62abeb7688cf 100644 --- a/remote/webdriver-bidi/WebDriverBiDiConnection.sys.mjs +++ b/remote/webdriver-bidi/WebDriverBiDiConnection.sys.mjs @@ -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)) {