From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:14:29 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- remote/shared/AppInfo.sys.mjs | 2 +- remote/shared/RecommendedPreferences.sys.mjs | 14 +- remote/shared/WebSocketConnection.sys.mjs | 7 +- .../NavigationListenerChild.sys.mjs | 4 +- .../listeners/ContextualIdentityListener.sys.mjs | 2 +- remote/shared/listeners/NetworkEventRecord.sys.mjs | 31 +-- .../test/browser/browser_NetworkListener.js | 8 +- .../test/browser/browser_PromptListener.js | 4 +- .../shared/messagehandler/MessageHandler.sys.mjs | 13 +- remote/shared/messagehandler/Module.sys.mjs | 4 +- .../messagehandler/RootMessageHandler.sys.mjs | 2 +- .../sessiondata/SessionDataReader.sys.mjs | 2 +- .../resources/modules/windowglobal/retry.sys.mjs | 8 +- .../windowglobal/windowglobaltoroot.sys.mjs | 4 +- .../test/browser/browser_UserContextManager.js | 5 +- .../test/xpcshell/test_RecommendedPreferences.js | 5 + remote/shared/test/xpcshell/test_Sync.js | 10 +- remote/shared/webdriver/Actions.sys.mjs | 57 +--- remote/shared/webdriver/Capabilities.sys.mjs | 6 + remote/shared/webdriver/Errors.sys.mjs | 17 ++ remote/shared/webdriver/Event.sys.mjs | 295 +++++++++++++++++++++ .../WebDriverProcessDataChild.sys.mjs | 2 +- .../shared/webdriver/test/xpcshell/test_Errors.js | 9 + 23 files changed, 393 insertions(+), 118 deletions(-) create mode 100644 remote/shared/webdriver/Event.sys.mjs (limited to 'remote/shared') diff --git a/remote/shared/AppInfo.sys.mjs b/remote/shared/AppInfo.sys.mjs index 9e354503ef..16db0191de 100644 --- a/remote/shared/AppInfo.sys.mjs +++ b/remote/shared/AppInfo.sys.mjs @@ -24,7 +24,7 @@ const ID_THUNDERBIRD = "{3550f703-e582-4d05-9a08-453d09bdfdc6}"; export const AppInfo = new Proxy( {}, { - get(target, prop, receiver) { + get(target, prop) { if (target.hasOwnProperty(prop)) { return target[prop]; } diff --git a/remote/shared/RecommendedPreferences.sys.mjs b/remote/shared/RecommendedPreferences.sys.mjs index d0a7739e52..b52057ee2b 100644 --- a/remote/shared/RecommendedPreferences.sys.mjs +++ b/remote/shared/RecommendedPreferences.sys.mjs @@ -145,6 +145,9 @@ const COMMON_PREFERENCES = new Map([ // Do not redirect user when a milstone upgrade of Firefox is detected ["browser.startup.homepage_override.mstone", "ignore"], + // Unload the previously selected tab immediately + ["browser.tabs.remote.unloadDelayMs", 0], + // Don't unload tabs when available memory is running low ["browser.tabs.unloadOnLowMemory", false], @@ -310,6 +313,9 @@ const COMMON_PREFERENCES = new Map([ // Privacy and Tracking Protection ["privacy.trackingprotection.enabled", false], + // Used to check if recommended preferences are applied + ["remote.prefs.recommended.applied", true], + // Don't do network connections for mitm priming ["security.certerrors.mitm.priming.enabled", false], @@ -362,7 +368,7 @@ export const RecommendedPreferences = { * @param {Map=} preferences * Map of preference name to preference value. */ - applyPreferences(preferences) { + applyPreferences(preferences = new Map()) { if (!lazy.useRecommendedPrefs) { // If remote.prefs.recommended is set to false, do not set any preference // here. Needed for our Firefox CI. @@ -374,11 +380,7 @@ export const RecommendedPreferences = { if (!this.isInitialized) { // Merge common preferences and optionally provided preferences in a // single map. Hereby the extra preferences have higher priority. - if (preferences) { - preferences = new Map([...COMMON_PREFERENCES, ...preferences]); - } else { - preferences = COMMON_PREFERENCES; - } + preferences = new Map([...COMMON_PREFERENCES, ...preferences]); Services.obs.addObserver(this, "quit-application"); this.isInitialized = true; diff --git a/remote/shared/WebSocketConnection.sys.mjs b/remote/shared/WebSocketConnection.sys.mjs index c9ef050dc5..57b533fffb 100644 --- a/remote/shared/WebSocketConnection.sys.mjs +++ b/remote/shared/WebSocketConnection.sys.mjs @@ -85,11 +85,8 @@ export class WebSocketConnection { * Register a new Session to forward the messages to. * * Needs to be implemented in the sub class. - * - * @param {Session} session - * The session to register. */ - registerSession(session) { + registerSession() { throw new Error("Not implemented"); } @@ -140,7 +137,7 @@ export class WebSocketConnection { /** * Called by the `transport` when the connection is closed. */ - onConnectionClose(status) { + onConnectionClose() { lazy.logger.debug(`${this.constructor.name} ${this.id} closed`); } diff --git a/remote/shared/js-window-actors/NavigationListenerChild.sys.mjs b/remote/shared/js-window-actors/NavigationListenerChild.sys.mjs index a2cd8ccf10..728d9b6e8c 100644 --- a/remote/shared/js-window-actors/NavigationListenerChild.sys.mjs +++ b/remote/shared/js-window-actors/NavigationListenerChild.sys.mjs @@ -56,12 +56,12 @@ export class NavigationListenerChild extends JSWindowActorChild { /** * See note above */ - handleEvent(event) {} + handleEvent() {} /** * See note above */ - receiveMessage(message) {} + receiveMessage() {} /** * A browsing context might be replaced before reaching the parent process, diff --git a/remote/shared/listeners/ContextualIdentityListener.sys.mjs b/remote/shared/listeners/ContextualIdentityListener.sys.mjs index d93b44ed77..42954d223c 100644 --- a/remote/shared/listeners/ContextualIdentityListener.sys.mjs +++ b/remote/shared/listeners/ContextualIdentityListener.sys.mjs @@ -49,7 +49,7 @@ export class ContextualIdentityListener { this.stopListening(); } - observe(subject, topic, data) { + observe(subject, topic) { switch (topic) { case OBSERVER_TOPIC_CREATED: this.emit("created", { identity: subject.wrappedJSObject }); diff --git a/remote/shared/listeners/NetworkEventRecord.sys.mjs b/remote/shared/listeners/NetworkEventRecord.sys.mjs index a41f3edd7d..72b43e3de1 100644 --- a/remote/shared/listeners/NetworkEventRecord.sys.mjs +++ b/remote/shared/listeners/NetworkEventRecord.sys.mjs @@ -163,13 +163,8 @@ export class NetworkEventRecord { * Required API for a NetworkObserver event owner. * * Not used for RemoteAgent. - * - * @param {object} info - * The object containing security information. - * @param {boolean} isRacing - * True if the corresponding channel raced the cache and network requests. */ - addSecurityInfo(info, isRacing) {} + addSecurityInfo() {} /** * Add network event timings. @@ -177,15 +172,8 @@ export class NetworkEventRecord { * Required API for a NetworkObserver event owner. * * Not used for RemoteAgent. - * - * @param {number} total - * The total time for the request. - * @param {object} timings - * The har-like timings. - * @param {object} offsets - * The har-like timings, but as offset from the request start. */ - addEventTimings(total, timings, offsets) {} + addEventTimings() {} /** * Add response cache entry. @@ -193,11 +181,8 @@ export class NetworkEventRecord { * Required API for a NetworkObserver event owner. * * Not used for RemoteAgent. - * - * @param {object} options - * An object which contains a single responseCache property. */ - addResponseCache(options) {} + addResponseCache() {} /** * Add response content. @@ -233,11 +218,8 @@ export class NetworkEventRecord { * Required API for a NetworkObserver event owner. * * Not used for RemoteAgent. - * - * @param {Array} serverTimings - * The server timings. */ - addServerTimings(serverTimings) {} + addServerTimings() {} /** * Add service worker timings. @@ -245,11 +227,8 @@ export class NetworkEventRecord { * Required API for a NetworkObserver event owner. * * Not used for RemoteAgent. - * - * @param {object} serviceWorkerTimings - * The server timings. */ - addServiceWorkerTimings(serviceWorkerTimings) {} + addServiceWorkerTimings() {} onAuthPrompt(authDetails, authCallbacks) { this.#emitAuthRequired(authCallbacks); diff --git a/remote/shared/listeners/test/browser/browser_NetworkListener.js b/remote/shared/listeners/test/browser/browser_NetworkListener.js index 78865f6b80..cc1b42f2fc 100644 --- a/remote/shared/listeners/test/browser/browser_NetworkListener.js +++ b/remote/shared/listeners/test/browser/browser_NetworkListener.js @@ -32,22 +32,22 @@ add_task(async function test_beforeRequestSent() { listener.startListening(); await fetch(tab1.linkedBrowser, "https://example.com/?1"); - ok(events.length == 1, "One event was received"); + Assert.equal(events.length, 1, "One event was received"); assertNetworkEvent(events[0], contextId1, "https://example.com/?1"); info("Check that events are no longer emitted after calling stopListening"); listener.stopListening(); await fetch(tab1.linkedBrowser, "https://example.com/?2"); - ok(events.length == 1, "No new event was received"); + Assert.equal(events.length, 1, "No new event was received"); listener.startListening(); await fetch(tab1.linkedBrowser, "https://example.com/?3"); - ok(events.length == 2, "A new event was received"); + Assert.equal(events.length, 2, "A new event was received"); assertNetworkEvent(events[1], contextId1, "https://example.com/?3"); info("Check network event from the new tab"); await fetch(tab2.linkedBrowser, "https://example.com/?4"); - ok(events.length == 3, "A new event was received"); + Assert.equal(events.length, 3, "A new event was received"); assertNetworkEvent(events[2], contextId2, "https://example.com/?4"); gBrowser.removeTab(tab1); diff --git a/remote/shared/listeners/test/browser/browser_PromptListener.js b/remote/shared/listeners/test/browser/browser_PromptListener.js index 0d3f23db3f..30503ef4fa 100644 --- a/remote/shared/listeners/test/browser/browser_PromptListener.js +++ b/remote/shared/listeners/test/browser/browser_PromptListener.js @@ -156,7 +156,7 @@ add_task(async function test_events_in_another_browser() { await createScriptNode(`setTimeout(() => window.confirm('test'))`); const dialogWin = await dialogPromise; - ok(events.length === 0, "No event was received"); + Assert.strictEqual(events.length, 0, "No event was received"); dialogWin.document.querySelector("dialog").acceptDialog(); @@ -166,7 +166,7 @@ add_task(async function test_events_in_another_browser() { setTimeout(resolve, 500); }); - ok(events.length === 0, "No event was received"); + Assert.strictEqual(events.length, 0, "No event was received"); listener.destroy(); await BrowserTestUtils.closeWindow(win); diff --git a/remote/shared/messagehandler/MessageHandler.sys.mjs b/remote/shared/messagehandler/MessageHandler.sys.mjs index 18ec6b820c..61c8b5c40e 100644 --- a/remote/shared/messagehandler/MessageHandler.sys.mjs +++ b/remote/shared/messagehandler/MessageHandler.sys.mjs @@ -215,7 +215,7 @@ export class MessageHandler extends EventEmitter { /** * Retrieve all module classes matching the moduleName and destination. - * See `getAllModuleClasses` (ModuleCache.jsm) for more details. + * See `getAllModuleClasses` (ModuleCache.sys.mjs) for more details. * * @param {string} moduleName * The name of the module. @@ -267,11 +267,8 @@ export class MessageHandler extends EventEmitter { * provided to this MessageHandler on startup. Implementation is specific to each MessageHandler class. * * By default the implementation is a no-op. - * - * @param {Array} sessionDataItems - * Initial session data items for this MessageHandler. */ - async initialize(sessionDataItems) {} + async initialize() {} /** * Returns the module path corresponding to this MessageHandler class. @@ -297,7 +294,7 @@ export class MessageHandler extends EventEmitter { * * Needs to be implemented in the sub class. */ - static getIdFromContext(context) { + static getIdFromContext() { throw new Error("Not implemented"); } @@ -306,7 +303,7 @@ export class MessageHandler extends EventEmitter { * * Needs to be implemented in the sub class. */ - forwardCommand(command) { + forwardCommand() { throw new Error("Not implemented"); } @@ -316,7 +313,7 @@ export class MessageHandler extends EventEmitter { * * Needs to be implemented in the sub class. */ - matchesContext(contextDescriptor) { + matchesContext() { throw new Error("Not implemented"); } diff --git a/remote/shared/messagehandler/Module.sys.mjs b/remote/shared/messagehandler/Module.sys.mjs index 30b26938e2..c526c7ce6c 100644 --- a/remote/shared/messagehandler/Module.sys.mjs +++ b/remote/shared/messagehandler/Module.sys.mjs @@ -61,12 +61,12 @@ export class Module { * * @param {string} name * Name of the event. - * @param {object} payload + * @param {object} _payload * The event's payload. * @returns {object} * The modified event payload. */ - interceptEvent(name, payload) { + interceptEvent(name, _payload) { throw new Error( `Could not intercept event ${name}, interceptEvent is not implemented in windowglobal-in-root module` ); diff --git a/remote/shared/messagehandler/RootMessageHandler.sys.mjs b/remote/shared/messagehandler/RootMessageHandler.sys.mjs index 06a8cd6f18..ce571c3c12 100644 --- a/remote/shared/messagehandler/RootMessageHandler.sys.mjs +++ b/remote/shared/messagehandler/RootMessageHandler.sys.mjs @@ -51,7 +51,7 @@ export class RootMessageHandler extends MessageHandler { * The ROOT MessageHandler is unique for a given MessageHandler network * (ie for a given sessionId). Reuse the type as context id here. */ - static getIdFromContext(context) { + static getIdFromContext() { return RootMessageHandler.type; } diff --git a/remote/shared/messagehandler/sessiondata/SessionDataReader.sys.mjs b/remote/shared/messagehandler/sessiondata/SessionDataReader.sys.mjs index 6d5ea08e59..f7c91e6bbf 100644 --- a/remote/shared/messagehandler/sessiondata/SessionDataReader.sys.mjs +++ b/remote/shared/messagehandler/sessiondata/SessionDataReader.sys.mjs @@ -18,7 +18,7 @@ ChromeUtils.defineLazyGetter(lazy, "sharedData", () => { /** * Returns a snapshot of the session data map, which is cloned from the - * sessionDataMap singleton of SessionData.jsm. + * sessionDataMap singleton of SessionData.sys.mjs. * * @returns {Map.>} * Map of session id to arrays of SessionDataItems. diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs index f7b2279018..3022744e7c 100644 --- a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs +++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs @@ -27,7 +27,7 @@ class RetryModule extends Module { // processes. const uri = this.messageHandler.window.document.baseURI; if (!uri.includes("example.net")) { - await new Promise(r => {}); + await new Promise(() => {}); } return { ...params }; @@ -37,7 +37,7 @@ class RetryModule extends Module { async blockedOneTime(params) { callsToBlockedOneTime++; if (callsToBlockedOneTime < 2) { - await new Promise(r => {}); + await new Promise(() => {}); } // Return: @@ -51,7 +51,7 @@ class RetryModule extends Module { async blockedTenTimes(params) { callsToBlockedTenTimes++; if (callsToBlockedTenTimes < 11) { - await new Promise(r => {}); + await new Promise(() => {}); } // Return: @@ -65,7 +65,7 @@ class RetryModule extends Module { async blockedElevenTimes(params) { callsToBlockedElevenTimes++; if (callsToBlockedElevenTimes < 12) { - await new Promise(r => {}); + await new Promise(() => {}); } // Return: diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs index 815a836d9c..1d9238f1bc 100644 --- a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs +++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs @@ -17,7 +17,7 @@ class WindowGlobalToRootModule extends Module { * Commands */ - testHandleCommandToRoot(params, destination) { + testHandleCommandToRoot() { return this.messageHandler.handleCommand({ moduleName: "windowglobaltoroot", commandName: "getValueFromRoot", @@ -27,7 +27,7 @@ class WindowGlobalToRootModule extends Module { }); } - testSendRootCommand(params, destination) { + testSendRootCommand() { return this.messageHandler.sendRootCommand({ moduleName: "windowglobaltoroot", commandName: "getValueFromRoot", diff --git a/remote/shared/test/browser/browser_UserContextManager.js b/remote/shared/test/browser/browser_UserContextManager.js index 2060c2bacd..cfae75dbe2 100644 --- a/remote/shared/test/browser/browser_UserContextManager.js +++ b/remote/shared/test/browser/browser_UserContextManager.js @@ -159,8 +159,9 @@ add_task(async function test_several_managers() { "manager2 has a valid id for the user context created by manager 1" ); - ok( - contextId1 != contextId2, + Assert.notEqual( + contextId1, + contextId2, "manager1 and manager2 have different ids for the same internal context id" ); diff --git a/remote/shared/test/xpcshell/test_RecommendedPreferences.js b/remote/shared/test/xpcshell/test_RecommendedPreferences.js index 20de07a528..88dc717be8 100644 --- a/remote/shared/test/xpcshell/test_RecommendedPreferences.js +++ b/remote/shared/test/xpcshell/test_RecommendedPreferences.js @@ -76,6 +76,11 @@ add_task(async function test_disabled() { add_task(async function test_noCustomPreferences() { info("Applying preferences without any custom preference should not throw"); + + // First call invokes setting of default preferences + RecommendedPreferences.applyPreferences(); + + // Second call does nothing RecommendedPreferences.applyPreferences(); cleanup(); diff --git a/remote/shared/test/xpcshell/test_Sync.js b/remote/shared/test/xpcshell/test_Sync.js index de4a4d30fe..a85c47adc2 100644 --- a/remote/shared/test/xpcshell/test_Sync.js +++ b/remote/shared/test/xpcshell/test_Sync.js @@ -50,14 +50,14 @@ class MockElement { } } - dispatchEvent(event) { + dispatchEvent() { if (this.wantUntrusted) { this.untrusted = true; } this.click(); } - removeEventListener(name, func) { + removeEventListener() { this.capture = false; this.eventName = null; this.func = null; @@ -213,12 +213,12 @@ add_task(async function test_EventPromise_checkFnCallback() { { checkFn: null, expected_count: 0 }, { checkFn: undefined, expected_count: 0 }, { - checkFn: event => { + checkFn: () => { throw new Error("foo"); }, expected_count: 0, }, - { checkFn: event => count++ > 0, expected_count: 2 }, + { checkFn: () => count++ > 0, expected_count: 2 }, ]; for (const { checkFn, expected_count } of data) { @@ -417,7 +417,7 @@ add_task(async function test_PollPromise_resolve() { const timeout = 100; await new PollPromise( - (resolve, reject) => { + resolve => { resolve(); }, { timeout, errorMessage } diff --git a/remote/shared/webdriver/Actions.sys.mjs b/remote/shared/webdriver/Actions.sys.mjs index 4f5a41a421..2639c4dc9f 100644 --- a/remote/shared/webdriver/Actions.sys.mjs +++ b/remote/shared/webdriver/Actions.sys.mjs @@ -13,7 +13,7 @@ ChromeUtils.defineESModuleGetters(lazy, { clearTimeout: "resource://gre/modules/Timer.sys.mjs", dom: "chrome://remote/content/shared/DOM.sys.mjs", error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", - event: "chrome://remote/content/marionette/event.sys.mjs", + event: "chrome://remote/content/shared/webdriver/Event.sys.mjs", keyData: "chrome://remote/content/shared/webdriver/KeyData.sys.mjs", Log: "chrome://remote/content/shared/Log.sys.mjs", pprint: "chrome://remote/content/shared/Format.sys.mjs", @@ -508,11 +508,8 @@ class Origin { * Viewport coordinates of the origin of this coordinate system. * * This is overridden in subclasses to provide a class-specific origin. - * - * @param {InputSource} inputSource - State of current input device. - * @param {WindowProxy} win - Current window global */ - getOriginCoordinates(inputSource, win) { + getOriginCoordinates() { throw new Error( `originCoordinates not defined for ${this.constructor.name}` ); @@ -559,13 +556,13 @@ class Origin { } class ViewportOrigin extends Origin { - getOriginCoordinates(inputSource, win) { + getOriginCoordinates() { return { x: 0, y: 0 }; } } class PointerOrigin extends Origin { - getOriginCoordinates(inputSource, win) { + getOriginCoordinates(inputSource) { return { x: inputSource.x, y: inputSource.y }; } } @@ -624,13 +621,9 @@ class Action { * This is overridden by subclasses to implement the type-specific * dispatch of the action. * - * @param {State} state - Actions state. - * @param {InputSource} inputSource - State of the current input device. - * @param {number} tickDuration - Length of the current tick, in ms. - * @param {WindowProxy} win - Current window global. * @returns {Promise} - Promise that is resolved once the action is complete. */ - dispatch(state, inputSource, tickDuration, win) { + dispatch() { throw new Error( `Action subclass ${this.constructor.name} must override dispatch()` ); @@ -708,10 +701,9 @@ class PauseAction extends NullAction { * @param {State} state - Actions state. * @param {InputSource} inputSource - State of the current input device. * @param {number} tickDuration - Length of the current tick, in ms. - * @param {WindowProxy} win - Current window global. * @returns {Promise} - Promise that is resolved once the action is complete. */ - dispatch(state, inputSource, tickDuration, win) { + dispatch(state, inputSource, tickDuration) { const ms = this.duration ?? tickDuration; lazy.logger.trace( @@ -1416,15 +1408,9 @@ class TouchActionGroup { * This is overridden by subclasses to implement the type-specific * dispatch of the action. * - * @param {State} state - Actions state. - * @param {null} inputSource - * This is always null; the argument only exists for compatibility - * with {@link Action.dispatch}. - * @param {number} tickDuration - Length of the current tick, in ms. - * @param {WindowProxy} win - Current window global. * @returns {Promise} - Promise that is resolved once the action is complete. */ - dispatch(state, inputSource, tickDuration, win) { + dispatch() { throw new Error( "TouchActionGroup subclass missing dispatch implementation" ); @@ -1622,7 +1608,7 @@ class PointerMoveTouchActionGroup extends TouchActionGroup { } ); const reachedTarget = perPointerData.every( - ([inputSource, action, target]) => + ([inputSource, , target]) => target[0] === inputSource.x && target[1] === inputSource.y ); @@ -1778,38 +1764,22 @@ class Pointer { /** * Implementation of depressing the pointer. - * - * @param {State} state - Actions state. - * @param {InputSource} inputSource - State of the current input device. - * @param {Action} action - The Action object invoking the pointer - * @param {WindowProxy} win - Current window global. */ - pointerDown(state, inputSource, action, win) { + pointerDown() { throw new Error(`Unimplemented pointerDown for pointerType ${this.type}`); } /** * Implementation of releasing the pointer. - * - * @param {State} state - Actions state. - * @param {InputSource} inputSource - State of the current input device. - * @param {Action} action - The Action object invoking the pointer - * @param {WindowProxy} win - Current window global. */ - pointerUp(state, inputSource, action, win) { + pointerUp() { throw new Error(`Unimplemented pointerUp for pointerType ${this.type}`); } /** * Implementation of moving the pointer. - * - * @param {State} state - Actions state. - * @param {InputSource} inputSource - State of the current input device. - * @param {number} targetX - Target X coordinate of the pointer move - * @param {number} targetY - Target Y coordinate of the pointer move - * @param {WindowProxy} win - Current window global. */ - pointerMove(state, inputSource, targetX, targetY, win) { + pointerMove() { throw new Error(`Unimplemented pointerMove for pointerType ${this.type}`); } @@ -2138,11 +2108,8 @@ class InputEventData { /** * Update the input data based on global and input state - * - * @param {State} state - Actions state. - * @param {InputSource} inputSource - State of the current input device. */ - update(state, inputSource) {} + update() {} toString() { return `${this.constructor.name} ${JSON.stringify(this)}`; diff --git a/remote/shared/webdriver/Capabilities.sys.mjs b/remote/shared/webdriver/Capabilities.sys.mjs index e3761315f2..3c30ea0789 100644 --- a/remote/shared/webdriver/Capabilities.sys.mjs +++ b/remote/shared/webdriver/Capabilities.sys.mjs @@ -445,6 +445,12 @@ export class Capabilities extends Map { ["timeouts", new Timeouts()], ["strictFileInteractability", false], ["unhandledPromptBehavior", UnhandledPromptBehavior.DismissAndNotify], + [ + "userAgent", + Cc["@mozilla.org/network/protocol;1?name=http"].getService( + Ci.nsIHttpProtocolHandler + ).userAgent, + ], ["webSocketUrl", null], // proprietary diff --git a/remote/shared/webdriver/Errors.sys.mjs b/remote/shared/webdriver/Errors.sys.mjs index 53b9d4426b..7060131075 100644 --- a/remote/shared/webdriver/Errors.sys.mjs +++ b/remote/shared/webdriver/Errors.sys.mjs @@ -41,6 +41,7 @@ const ERRORS = new Set([ "TimeoutError", "UnableToCaptureScreen", "UnableToSetCookieError", + "UnableToSetFileInputError", "UnexpectedAlertOpenError", "UnknownCommandError", "UnknownError", @@ -756,6 +757,21 @@ class UnableToSetCookieError extends WebDriverError { } } +/** + * A command to set a file could not be satisfied. + * + * @param {string=} message + * Optional string describing error situation. + * @param {object=} data + * Additional error data helpful in diagnosing the error. + */ +class UnableToSetFileInputError extends WebDriverError { + constructor(message, data = {}) { + super(message, data); + this.status = "unable to set file input"; + } +} + /** * A command to capture a screenshot could not be satisfied. * @@ -865,6 +881,7 @@ const STATUSES = new Map([ ["timeout", TimeoutError], ["unable to capture screen", UnableToCaptureScreen], ["unable to set cookie", UnableToSetCookieError], + ["unable to set file input", UnableToSetFileInputError], ["unexpected alert open", UnexpectedAlertOpenError], ["unknown command", UnknownCommandError], ["unknown error", UnknownError], diff --git a/remote/shared/webdriver/Event.sys.mjs b/remote/shared/webdriver/Event.sys.mjs new file mode 100644 index 0000000000..2240f1f2b2 --- /dev/null +++ b/remote/shared/webdriver/Event.sys.mjs @@ -0,0 +1,295 @@ +/* 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/. */ + +/* eslint-disable no-restricted-globals */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + keyData: "chrome://remote/content/shared/webdriver/KeyData.sys.mjs", +}); + +/** Provides functionality for creating and sending DOM events. */ +export const event = {}; + +const _eventUtils = new WeakMap(); + +function _getEventUtils(win) { + if (!_eventUtils.has(win)) { + const eventUtilsObject = { + window: win, + parent: win, + _EU_Ci: Ci, + _EU_Cc: Cc, + }; + Services.scriptloader.loadSubScript( + "chrome://remote/content/external/EventUtils.js", + eventUtilsObject + ); + _eventUtils.set(win, eventUtilsObject); + } + return _eventUtils.get(win); +} + +event.MouseEvents = { + click: 0, + dblclick: 1, + mousedown: 2, + mouseup: 3, + mouseover: 4, + mouseout: 5, +}; + +event.Modifiers = { + shiftKey: 0, + ctrlKey: 1, + altKey: 2, + metaKey: 3, +}; + +event.MouseButton = { + isPrimary(button) { + return button === 0; + }, + isAuxiliary(button) { + return button === 1; + }, + isSecondary(button) { + return button === 2; + }, +}; + +/** + * Synthesise a mouse event at a point. + * + * If the type is specified in opts, an mouse event of that type is + * fired. Otherwise, a mousedown followed by a mouseup is performed. + * + * @param {number} left + * Offset from viewport left, in CSS pixels + * @param {number} top + * Offset from viewport top, in CSS pixels + * @param {object} opts + * Object which may contain the properties "shiftKey", "ctrlKey", + * "altKey", "metaKey", "accessKey", "clickCount", "button", and + * "type". + * @param {Window} win + * Window object. + * + * @returns {boolean} defaultPrevented + */ +event.synthesizeMouseAtPoint = function (left, top, opts, win) { + return _getEventUtils(win).synthesizeMouseAtPoint(left, top, opts, win); +}; + +/** + * Synthesise a touch event at a point. + * + * If the type is specified in opts, a touch event of that type is + * fired. Otherwise, a touchstart followed by a touchend is performed. + * + * @param {number} left + * Offset from viewport left, in CSS pixels + * @param {number} top + * Offset from viewport top, in CSS pixels + * @param {object} opts + * Object which may contain the properties "id", "rx", "ry", "angle", + * "force", "shiftKey", "ctrlKey", "altKey", "metaKey", "accessKey", + * "type". + * @param {Window} win + * Window object. + * + * @returns {boolean} defaultPrevented + */ +event.synthesizeTouchAtPoint = function (left, top, opts, win) { + return _getEventUtils(win).synthesizeTouchAtPoint(left, top, opts, win); +}; + +/** + * Synthesise a wheel scroll event at a point. + * + * @param {number} left + * Offset from viewport left, in CSS pixels + * @param {number} top + * Offset from viewport top, in CSS pixels + * @param {object} opts + * Object which may contain the properties "shiftKey", "ctrlKey", + * "altKey", "metaKey", "accessKey", "deltaX", "deltaY", "deltaZ", + * "deltaMode", "lineOrPageDeltaX", "lineOrPageDeltaY", "isMomentum", + * "isNoLineOrPageDelta", "isCustomizedByPrefs", "expectedOverflowDeltaX", + * "expectedOverflowDeltaY" + * @param {Window} win + * Window object. + */ +event.synthesizeWheelAtPoint = function (left, top, opts, win) { + const dpr = win.devicePixelRatio; + + // All delta properties expect the value in device pixels while the + // WebDriver specification uses CSS pixels. + if (typeof opts.deltaX !== "undefined") { + opts.deltaX *= dpr; + } + if (typeof opts.deltaY !== "undefined") { + opts.deltaY *= dpr; + } + if (typeof opts.deltaZ !== "undefined") { + opts.deltaZ *= dpr; + } + + return _getEventUtils(win).synthesizeWheelAtPoint(left, top, opts, win); +}; + +event.synthesizeMultiTouch = function (opts, win) { + const modifiers = _getEventUtils(win)._parseModifiers(opts); + win.windowUtils.sendTouchEvent( + opts.type, + opts.id, + opts.x, + opts.y, + opts.rx, + opts.ry, + opts.angle, + opts.force, + opts.tiltx, + opts.tilty, + opts.twist, + modifiers + ); +}; + +/** + * Synthesize a keydown event for a single key. + * + * @param {object} key + * Key data as returned by keyData.getData + * @param {Window} win + * Window object. + */ +event.sendKeyDown = function (key, win) { + event.sendSingleKey(key, win, "keydown"); +}; + +/** + * Synthesize a keyup event for a single key. + * + * @param {object} key + * Key data as returned by keyData.getData + * @param {Window} win + * Window object. + */ +event.sendKeyUp = function (key, win) { + event.sendSingleKey(key, win, "keyup"); +}; + +/** + * Synthesize a key event for a single key. + * + * @param {object} key + * Key data as returned by keyData.getData + * @param {Window} win + * Window object. + * @param {string=} type + * Event to emit. By default the full keydown/keypressed/keyup event + * sequence is emitted. + */ +event.sendSingleKey = function (key, win, type = null) { + let keyValue = key.key; + if (!key.printable) { + keyValue = `KEY_${keyValue}`; + } + const event = { + code: key.code, + location: key.location, + altKey: key.altKey ?? false, + shiftKey: key.shiftKey ?? false, + ctrlKey: key.ctrlKey ?? false, + metaKey: key.metaKey ?? false, + repeat: key.repeat ?? false, + }; + if (type) { + event.type = type; + } + _getEventUtils(win).synthesizeKey(keyValue, event, win); +}; + +/** + * Send a string as a series of keypresses. + * + * @param {string} keyString + * Sequence of characters to send as key presses + * @param {Window} win + * Window object + */ +event.sendKeys = function (keyString, win) { + const modifiers = {}; + for (let modifier in event.Modifiers) { + modifiers[modifier] = false; + } + + for (let keyValue of keyString) { + // keyValue will contain enough to represent the UTF-16 encoding of a single abstract character + // i.e. either a single scalar value, or a surrogate pair + if (modifiers.shiftKey) { + keyValue = lazy.keyData.getShiftedKey(keyValue); + } + const data = lazy.keyData.getData(keyValue); + const key = { ...data, ...modifiers }; + if (data.modifier) { + // Negating the state of the modifier here is not spec compliant but + // makes us compatible to Chrome's behavior for now. That's fine unless + // we know the correct behavior. + // + // @see: https://github.com/w3c/webdriver/issues/1734 + modifiers[data.modifier] = !modifiers[data.modifier]; + } + event.sendSingleKey(key, win); + } +}; + +event.sendEvent = function (eventType, el, modifiers = {}, opts = {}) { + opts.canBubble = opts.canBubble || true; + + let doc = el.ownerDocument || el.document; + let ev = doc.createEvent("Event"); + + ev.shiftKey = modifiers.shift; + ev.metaKey = modifiers.meta; + ev.altKey = modifiers.alt; + ev.ctrlKey = modifiers.ctrl; + + ev.initEvent(eventType, opts.canBubble, true); + el.dispatchEvent(ev); +}; + +event.mouseover = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("mouseover", el, modifiers, opts); +}; + +event.mousemove = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("mousemove", el, modifiers, opts); +}; + +event.mousedown = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("mousedown", el, modifiers, opts); +}; + +event.mouseup = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("mouseup", el, modifiers, opts); +}; + +event.cancel = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("cancel", el, modifiers, opts); +}; + +event.click = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("click", el, modifiers, opts); +}; + +event.change = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("change", el, modifiers, opts); +}; + +event.input = function (el, modifiers = {}, opts = {}) { + return event.sendEvent("input", el, modifiers, opts); +}; diff --git a/remote/shared/webdriver/process-actors/WebDriverProcessDataChild.sys.mjs b/remote/shared/webdriver/process-actors/WebDriverProcessDataChild.sys.mjs index 39db9d939e..8101240abe 100644 --- a/remote/shared/webdriver/process-actors/WebDriverProcessDataChild.sys.mjs +++ b/remote/shared/webdriver/process-actors/WebDriverProcessDataChild.sys.mjs @@ -17,7 +17,7 @@ class BrowsingContextObserver { this.actor = actor; } - async observe(subject, topic, data) { + async observe(subject, topic) { if (topic === "browsing-context-discarded") { this.actor.cleanUp({ browsingContext: subject }); } diff --git a/remote/shared/webdriver/test/xpcshell/test_Errors.js b/remote/shared/webdriver/test/xpcshell/test_Errors.js index 22e3526039..d4803dee87 100644 --- a/remote/shared/webdriver/test/xpcshell/test_Errors.js +++ b/remote/shared/webdriver/test/xpcshell/test_Errors.js @@ -36,6 +36,7 @@ const errors = [ error.StaleElementReferenceError, error.TimeoutError, error.UnableToSetCookieError, + error.UnableToSetFileInputError, error.UnexpectedAlertOpenError, error.UnknownCommandError, error.UnknownError, @@ -510,6 +511,14 @@ add_task(function test_UnableToSetCookieError() { ok(err instanceof error.WebDriverError); }); +add_task(function test_UnableToSetFileInputError() { + let err = new error.UnableToSetFileInputError("foo"); + equal("UnableToSetFileInputError", err.name); + equal("foo", err.message); + equal("unable to set file input", err.status); + ok(err instanceof error.WebDriverError); +}); + add_task(function test_UnexpectedAlertOpenError() { let err = new error.UnexpectedAlertOpenError("foo"); equal("UnexpectedAlertOpenError", err.name); -- cgit v1.2.3