Bug 1954992 - [webdriver-bidi] Add "emulation.setGeolocationOverride" command. r=webdriver-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D243435
This commit is contained in:
@@ -19,6 +19,7 @@ remote.jar:
|
||||
# WebDriver BiDi root modules
|
||||
content/webdriver-bidi/modules/root/browser.sys.mjs (modules/root/browser.sys.mjs)
|
||||
content/webdriver-bidi/modules/root/browsingContext.sys.mjs (modules/root/browsingContext.sys.mjs)
|
||||
content/webdriver-bidi/modules/root/emulation.sys.mjs (modules/root/emulation.sys.mjs)
|
||||
content/webdriver-bidi/modules/root/input.sys.mjs (modules/root/input.sys.mjs)
|
||||
content/webdriver-bidi/modules/root/log.sys.mjs (modules/root/log.sys.mjs)
|
||||
content/webdriver-bidi/modules/root/network.sys.mjs (modules/root/network.sys.mjs)
|
||||
@@ -31,6 +32,7 @@ remote.jar:
|
||||
# WebDriver BiDi windowglobal modules
|
||||
content/webdriver-bidi/modules/windowglobal/_configuration.sys.mjs (modules/windowglobal/_configuration.sys.mjs)
|
||||
content/webdriver-bidi/modules/windowglobal/browsingContext.sys.mjs (modules/windowglobal/browsingContext.sys.mjs)
|
||||
content/webdriver-bidi/modules/windowglobal/emulation.sys.mjs (modules/windowglobal/emulation.sys.mjs)
|
||||
content/webdriver-bidi/modules/windowglobal/input.sys.mjs (modules/windowglobal/input.sys.mjs)
|
||||
content/webdriver-bidi/modules/windowglobal/log.sys.mjs (modules/windowglobal/log.sys.mjs)
|
||||
content/webdriver-bidi/modules/windowglobal/network.sys.mjs (modules/windowglobal/network.sys.mjs)
|
||||
|
||||
@@ -20,6 +20,8 @@ ChromeUtils.defineESModuleGetters(modules.root, {
|
||||
"chrome://remote/content/webdriver-bidi/modules/root/browser.sys.mjs",
|
||||
browsingContext:
|
||||
"chrome://remote/content/webdriver-bidi/modules/root/browsingContext.sys.mjs",
|
||||
emulation:
|
||||
"chrome://remote/content/webdriver-bidi/modules/root/emulation.sys.mjs",
|
||||
input: "chrome://remote/content/webdriver-bidi/modules/root/input.sys.mjs",
|
||||
log: "chrome://remote/content/webdriver-bidi/modules/root/log.sys.mjs",
|
||||
network:
|
||||
@@ -52,6 +54,8 @@ ChromeUtils.defineESModuleGetters(modules.windowglobal, {
|
||||
"chrome://remote/content/webdriver-bidi/modules/windowglobal/_configuration.sys.mjs",
|
||||
browsingContext:
|
||||
"chrome://remote/content/webdriver-bidi/modules/windowglobal/browsingContext.sys.mjs",
|
||||
emulation:
|
||||
"chrome://remote/content/webdriver-bidi/modules/windowglobal/emulation.sys.mjs",
|
||||
input:
|
||||
"chrome://remote/content/webdriver-bidi/modules/windowglobal/input.sys.mjs",
|
||||
log: "chrome://remote/content/webdriver-bidi/modules/windowglobal/log.sys.mjs",
|
||||
|
||||
@@ -1472,7 +1472,7 @@ class BrowsingContextModule extends RootBiDiModule {
|
||||
|
||||
if (contextId !== null && userContextIds !== null) {
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
`Providing both "contexts" and "userContexts" arguments is not supported`
|
||||
`Providing both "context" and "userContexts" arguments is not supported`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
291
remote/webdriver-bidi/modules/root/emulation.sys.mjs
Normal file
291
remote/webdriver-bidi/modules/root/emulation.sys.mjs
Normal file
@@ -0,0 +1,291 @@
|
||||
/* 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/. */
|
||||
|
||||
import { RootBiDiModule } from "chrome://remote/content/webdriver-bidi/modules/RootBiDiModule.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
|
||||
ContextDescriptorType:
|
||||
"chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs",
|
||||
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
|
||||
pprint: "chrome://remote/content/shared/Format.sys.mjs",
|
||||
SessionDataMethod:
|
||||
"chrome://remote/content/shared/messagehandler/sessiondata/SessionData.sys.mjs",
|
||||
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
|
||||
UserContextManager:
|
||||
"chrome://remote/content/shared/UserContextManager.sys.mjs",
|
||||
});
|
||||
|
||||
class EmulationModule extends RootBiDiModule {
|
||||
/**
|
||||
* Create a new module instance.
|
||||
*
|
||||
* @param {MessageHandler} messageHandler
|
||||
* The MessageHandler instance which owns this Module instance.
|
||||
*/
|
||||
constructor(messageHandler) {
|
||||
super(messageHandler);
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
/**
|
||||
* Used as an argument for emulation.setGeolocationOverride command
|
||||
* to represent an object which holds geolocation coordinates which
|
||||
* should override the return result of geolocation APIs.
|
||||
*
|
||||
* @typedef {object} GeolocationCoordinates
|
||||
*
|
||||
* @property {number} latitude
|
||||
* @property {number} longitude
|
||||
* @property {number=} accuracy
|
||||
* Defaults to 1.
|
||||
* @property {number=} altitude
|
||||
* Defaults to null.
|
||||
* @property {number=} altitudeAccuracy
|
||||
* Defaults to null.
|
||||
* @property {number=} heading
|
||||
* Defaults to null.
|
||||
* @property {number=} speed
|
||||
* Defaults to null.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the geolocation override to the list of top-level navigables
|
||||
* or user contexts.
|
||||
*
|
||||
* @param {object=} options
|
||||
* @param {Array<string>=} options.contexts
|
||||
* Optional list of browsing context ids.
|
||||
* @param {(GeolocationCoordinates|null)} options.coordinates
|
||||
* Geolocation coordinates which have to override
|
||||
* the return result of geolocation APIs.
|
||||
* Null value resets the override.
|
||||
* @param {Array<string>=} options.userContexts
|
||||
* Optional list of user context ids.
|
||||
*
|
||||
* @throws {InvalidArgumentError}
|
||||
* Raised if an argument is of an invalid type or value.
|
||||
* @throws {NoSuchFrameError}
|
||||
* If the browsing context cannot be found.
|
||||
* @throws {NoSuchUserContextError}
|
||||
* Raised if the user context id could not be found.
|
||||
*/
|
||||
async setGeolocationOverride(options = {}) {
|
||||
let { coordinates } = options;
|
||||
const { contexts: contextIds = null, userContexts: userContextIds = null } =
|
||||
options;
|
||||
|
||||
if (coordinates !== null) {
|
||||
lazy.assert.object(
|
||||
coordinates,
|
||||
lazy.pprint`Expected "coordinates" to be an object, got ${coordinates}`
|
||||
);
|
||||
|
||||
const {
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy = 1,
|
||||
// For platform API if we want to set values to null
|
||||
// we have to set them to NaN.
|
||||
altitude = NaN,
|
||||
altitudeAccuracy = NaN,
|
||||
heading = NaN,
|
||||
speed = NaN,
|
||||
} = coordinates;
|
||||
|
||||
lazy.assert.number(
|
||||
latitude,
|
||||
lazy.pprint`Expected "latitude" to be a number, got ${latitude}`
|
||||
);
|
||||
|
||||
lazy.assert.number(
|
||||
longitude,
|
||||
lazy.pprint`Expected "longitude" to be a number, got ${longitude}`
|
||||
);
|
||||
|
||||
lazy.assert.number(
|
||||
accuracy,
|
||||
lazy.pprint`Expected "accuracy" to be a number, got ${accuracy}`
|
||||
);
|
||||
|
||||
if (!Number.isNaN(altitude)) {
|
||||
lazy.assert.number(
|
||||
altitude,
|
||||
lazy.pprint`Expected "altitude" to be a number, got ${altitude}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!Number.isNaN(altitudeAccuracy)) {
|
||||
lazy.assert.number(
|
||||
altitudeAccuracy,
|
||||
lazy.pprint`Expected "altitudeAccuracy" to be a number, got ${altitudeAccuracy}`
|
||||
);
|
||||
|
||||
if (Number.isNaN(altitude)) {
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
`When "altitudeAccuracy" is provided it's required to provide "altitude" as well`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Number.isNaN(heading)) {
|
||||
lazy.assert.number(
|
||||
heading,
|
||||
lazy.pprint`Expected "heading" to be a number, got ${heading}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!Number.isNaN(speed)) {
|
||||
lazy.assert.number(
|
||||
speed,
|
||||
lazy.pprint`Expected "speed" to be a number, got ${speed}`
|
||||
);
|
||||
}
|
||||
|
||||
coordinates = {
|
||||
...coordinates,
|
||||
accuracy,
|
||||
altitude,
|
||||
altitudeAccuracy,
|
||||
heading,
|
||||
speed,
|
||||
};
|
||||
}
|
||||
|
||||
const navigables = new Set();
|
||||
const userContexts = new Set();
|
||||
if (contextIds !== null) {
|
||||
lazy.assert.isNonEmptyArray(
|
||||
contextIds,
|
||||
lazy.pprint`Expected "contexts" to be a non-empty array, got ${contextIds}`
|
||||
);
|
||||
|
||||
for (const contextId of contextIds) {
|
||||
lazy.assert.string(
|
||||
contextId,
|
||||
lazy.pprint`Expected elements of "contexts" to be a string, got ${contextId}`
|
||||
);
|
||||
|
||||
const context = this.#getBrowsingContext(contextId);
|
||||
|
||||
lazy.assert.topLevel(
|
||||
context,
|
||||
`Browsing context with id ${contextId} is not top-level`
|
||||
);
|
||||
|
||||
navigables.add(context);
|
||||
}
|
||||
} else if (userContextIds !== null) {
|
||||
lazy.assert.isNonEmptyArray(
|
||||
userContextIds,
|
||||
lazy.pprint`Expected "userContexts" to be a non-empty array, got ${userContextIds}`
|
||||
);
|
||||
|
||||
for (const userContextId of userContextIds) {
|
||||
lazy.assert.string(
|
||||
userContextId,
|
||||
lazy.pprint`Expected elements of "userContexts" to be a string, got ${userContextId}`
|
||||
);
|
||||
|
||||
const internalId =
|
||||
lazy.UserContextManager.getInternalIdById(userContextId);
|
||||
|
||||
if (internalId === null) {
|
||||
throw new lazy.error.NoSuchUserContextError(
|
||||
`User context with id: ${userContextId} doesn't exist`
|
||||
);
|
||||
}
|
||||
|
||||
userContexts.add(internalId);
|
||||
|
||||
// Prepare the list of navigables to update.
|
||||
lazy.UserContextManager.getTabsForUserContext(internalId).forEach(
|
||||
tab => {
|
||||
const contentBrowser = lazy.TabManager.getBrowserForTab(tab);
|
||||
navigables.add(contentBrowser.browsingContext);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
`At least one of "contexts" or "userContexts" arguments should be provided`
|
||||
);
|
||||
}
|
||||
|
||||
if (contextIds !== null && userContextIds !== null) {
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
`Providing both "contexts" and "userContexts" arguments is not supported`
|
||||
);
|
||||
}
|
||||
|
||||
const sessionDataItems = [];
|
||||
if (userContextIds !== null) {
|
||||
for (const userContext of userContexts) {
|
||||
sessionDataItems.push({
|
||||
category: "geolocation-override",
|
||||
moduleName: "_configuration",
|
||||
values: [coordinates],
|
||||
contextDescriptor: {
|
||||
type: lazy.ContextDescriptorType.UserContext,
|
||||
id: userContext,
|
||||
},
|
||||
method: lazy.SessionDataMethod.Add,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const navigable of navigables) {
|
||||
sessionDataItems.push({
|
||||
category: "geolocation-override",
|
||||
moduleName: "_configuration",
|
||||
values: [coordinates],
|
||||
contextDescriptor: {
|
||||
type: lazy.ContextDescriptorType.TopBrowsingContext,
|
||||
id: navigable.browserId,
|
||||
},
|
||||
method: lazy.SessionDataMethod.Add,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sessionDataItems.length) {
|
||||
// TODO: Bug 1953079. Saving the geolocation override in the session data works fine
|
||||
// with one session, but when we start supporting multiple BiDi session, we will
|
||||
// have to rethink this approach.
|
||||
await this.messageHandler.updateSessionData(sessionDataItems);
|
||||
}
|
||||
|
||||
const commands = [];
|
||||
|
||||
for (const navigable of navigables) {
|
||||
commands.push(
|
||||
this._forwardToWindowGlobal(
|
||||
"_setGeolocationOverride",
|
||||
navigable.id,
|
||||
{
|
||||
coordinates,
|
||||
},
|
||||
{ retryOnAbort: true }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(commands);
|
||||
}
|
||||
|
||||
#getBrowsingContext(contextId) {
|
||||
const context = lazy.TabManager.getBrowsingContextById(contextId);
|
||||
if (context === null) {
|
||||
throw new lazy.error.NoSuchFrameError(
|
||||
`Browsing Context with id ${contextId} not found`
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
export const emulation = EmulationModule;
|
||||
@@ -42,15 +42,14 @@ class PermissionsModule extends RootBiDiModule {
|
||||
* The state which will be set to the permission.
|
||||
* @param {string} options.origin
|
||||
* The origin which is used as a target for permission update.
|
||||
* @param {string=} options.userContext [unsupported]
|
||||
* @param {string=} options.userContext
|
||||
* The id of the user context which should be used as a target
|
||||
* for permission update.
|
||||
*
|
||||
* @throws {InvalidArgumentError}
|
||||
* Raised if an argument is of an invalid type or value.
|
||||
* @throws {UnsupportedOperationError}
|
||||
* Raised when unsupported permissions are set or <var>userContext</var>
|
||||
* argument is used.
|
||||
* Raised when unsupported permissions are set.
|
||||
*/
|
||||
async setPermission(options = {}) {
|
||||
const {
|
||||
|
||||
@@ -17,6 +17,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
* Internal module to set the configuration on the newly created navigables.
|
||||
*/
|
||||
class _ConfigurationModule extends WindowGlobalBiDiModule {
|
||||
#geolocationConfiguration;
|
||||
#preloadScripts;
|
||||
#resolveBlockerPromise;
|
||||
#viewportConfiguration;
|
||||
@@ -24,6 +25,7 @@ class _ConfigurationModule extends WindowGlobalBiDiModule {
|
||||
constructor(messageHandler) {
|
||||
super(messageHandler);
|
||||
|
||||
this.#geolocationConfiguration = null;
|
||||
this.#preloadScripts = new Set();
|
||||
this.#viewportConfiguration = new Map();
|
||||
|
||||
@@ -53,7 +55,8 @@ class _ConfigurationModule extends WindowGlobalBiDiModule {
|
||||
// Do nothing if there is no configuration to apply.
|
||||
if (
|
||||
this.#preloadScripts.size === 0 &&
|
||||
this.#viewportConfiguration.size === 0
|
||||
this.#viewportConfiguration.size === 0 &&
|
||||
this.#geolocationConfiguration === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -64,6 +67,20 @@ class _ConfigurationModule extends WindowGlobalBiDiModule {
|
||||
});
|
||||
this.messageHandler.window.document.blockParsing(blockerPromise);
|
||||
|
||||
if (this.#geolocationConfiguration !== null) {
|
||||
await this.messageHandler.handleCommand({
|
||||
moduleName: "emulation",
|
||||
commandName: "_setGeolocationOverride",
|
||||
destination: {
|
||||
type: lazy.WindowGlobalMessageHandler.type,
|
||||
id: this.messageHandler.context.id,
|
||||
},
|
||||
params: {
|
||||
coordinates: this.#geolocationConfiguration,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (this.#viewportConfiguration.size !== 0) {
|
||||
await this.messageHandler.forwardCommand({
|
||||
moduleName: "browsingContext",
|
||||
@@ -116,9 +133,10 @@ class _ConfigurationModule extends WindowGlobalBiDiModule {
|
||||
}
|
||||
}
|
||||
|
||||
// Viewport overrides apply only to top-level traversables.
|
||||
// Geolocation and viewport overrides apply only to top-level traversables.
|
||||
if (
|
||||
category === "viewport-overrides" &&
|
||||
(category === "geolocation-override" ||
|
||||
category === "viewport-overrides") &&
|
||||
!this.messageHandler.context.parent
|
||||
) {
|
||||
for (const { contextDescriptor, value } of sessionData) {
|
||||
@@ -126,15 +144,19 @@ class _ConfigurationModule extends WindowGlobalBiDiModule {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value.viewport !== undefined) {
|
||||
this.#viewportConfiguration.set("viewport", value.viewport);
|
||||
}
|
||||
if (category === "geolocation-override") {
|
||||
this.#geolocationConfiguration = value;
|
||||
} else {
|
||||
if (value.viewport !== undefined) {
|
||||
this.#viewportConfiguration.set("viewport", value.viewport);
|
||||
}
|
||||
|
||||
if (value.devicePixelRatio !== undefined) {
|
||||
this.#viewportConfiguration.set(
|
||||
"devicePixelRatio",
|
||||
value.devicePixelRatio
|
||||
);
|
||||
if (value.devicePixelRatio !== undefined) {
|
||||
this.#viewportConfiguration.set(
|
||||
"devicePixelRatio",
|
||||
value.devicePixelRatio
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
remote/webdriver-bidi/modules/windowglobal/emulation.sys.mjs
Normal file
45
remote/webdriver-bidi/modules/windowglobal/emulation.sys.mjs
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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/. */
|
||||
|
||||
import { WindowGlobalBiDiModule } from "chrome://remote/content/webdriver-bidi/modules/WindowGlobalBiDiModule.sys.mjs";
|
||||
|
||||
class EmulationModule extends WindowGlobalBiDiModule {
|
||||
constructor(messageHandler) {
|
||||
super(messageHandler);
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
/**
|
||||
* Internal commands
|
||||
*/
|
||||
|
||||
_applySessionData() {}
|
||||
|
||||
/**
|
||||
* Set the geolocation override to the navigable.
|
||||
*
|
||||
* @param {object=} params
|
||||
* @param {(GeolocationCoordinates|null)} params.coordinates
|
||||
* Geolocation coordinates which have to override
|
||||
* the return result of geolocation APIs.
|
||||
* Null value resets the override.
|
||||
*/
|
||||
async _setGeolocationOverride(params = {}) {
|
||||
const { coordinates } = params;
|
||||
|
||||
if (coordinates === null) {
|
||||
this.messageHandler.context.setGeolocationServiceOverride();
|
||||
} else {
|
||||
this.messageHandler.context.setGeolocationServiceOverride({
|
||||
coords: coordinates,
|
||||
// The timestamp attribute represents the time
|
||||
// when the geographic position of the device was acquired.
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const emulation = EmulationModule;
|
||||
Reference in New Issue
Block a user