diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index c82c64928a..50ed90607c 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -16,7 +16,7 @@ import { eventsHelper } from '../utils/eventsHelper'; import { Browser } from '../browser'; -import { BrowserContext, assertBrowserContextIsNotOwned } from '../browserContext'; +import { BrowserContext, assertBrowserContextIsNotOwned, verifyGeolocation } from '../browserContext'; import * as network from '../network'; import { BidiConnection } from './bidiConnection'; import { bidiBytesValueToString } from './bidiNetworkManager'; @@ -230,6 +230,8 @@ export class BidiBrowserContext extends BrowserContext { userContexts: [this._userContextId()], })); } + if (this._options.geolocation) + promises.push(this.setGeolocation(this._options.geolocation)); await Promise.all(promises); } @@ -312,6 +314,31 @@ export class BidiBrowserContext extends BrowserContext { } async setGeolocation(geolocation?: types.Geolocation): Promise { + verifyGeolocation(geolocation); + this._options.geolocation = geolocation; + const promises: Promise[] = [ + this._browser._browserSession.send('emulation.setGeolocationOverride', { + coordinates: { + latitude: geolocation?.latitude, + longitude: geolocation?.longitude, + accuracy: geolocation?.accuracy, + }, + userContexts: [this._browserContextId || 'default'], + }), + ]; + const pageIds = this.pages().map(page => (page._delegate as BidiPage)._session.sessionId); + if (pageIds.length) { + // TODO: we can't specify userContexts and contexts at the same time in Firefox. + promises.push(this._browser._browserSession.send('emulation.setGeolocationOverride', { + coordinates: { + latitude: geolocation?.latitude, + longitude: geolocation?.longitude, + accuracy: geolocation?.accuracy, + }, + contexts: pageIds as [string, ...string[]], + })); + } + await Promise.all(promises); } async setExtraHTTPHeaders(headers: types.HeadersArray): Promise { diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts b/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts index 4732d9e14c..b598d7f0cd 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts @@ -113,6 +113,11 @@ export interface Commands { returnType: Bidi.EmptyResult; }; + 'emulation.setGeolocationOverride': { + params: Bidi.Emulation.SetGeolocationOverrideParameters; + returnType: Bidi.EmptyResult; + }; + 'session.end': { params: Bidi.EmptyParams; returnType: Bidi.EmptyResult; diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts b/packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts index 51ae3f6d0e..ca0a96c83e 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts @@ -35,6 +35,7 @@ export type EventData = export type CommandData = | BrowserCommand | BrowsingContextCommand + | EmulationCommand | InputCommand | NetworkCommand | ScriptCommand @@ -492,13 +493,16 @@ export namespace BrowsingContext { export type Navigation = string; } export namespace BrowsingContext { - export type NavigationInfo = { + export type BaseNavigationInfo = { context: BrowsingContext.BrowsingContext; navigation: BrowsingContext.Navigation | null; timestamp: JsUint; url: string; }; } +export namespace BrowsingContext { + export type NavigationInfo = BrowsingContext.BaseNavigationInfo; +} export namespace BrowsingContext { export const enum ReadinessState { None = 'none', @@ -874,9 +878,14 @@ export namespace BrowsingContext { export namespace BrowsingContext { export type DownloadWillBegin = { method: 'browsingContext.downloadWillBegin'; - params: BrowsingContext.NavigationInfo; + params: BrowsingContext.DownloadWillBeginParams; }; } +export namespace BrowsingContext { + export type DownloadWillBeginParams = { + suggestedFilename: string; + } & BrowsingContext.BaseNavigationInfo; +} export namespace BrowsingContext { export type NavigationAborted = { method: 'browsingContext.navigationAborted'; @@ -924,6 +933,63 @@ export namespace BrowsingContext { defaultValue?: string; }; } +export type EmulationCommand = Emulation.SetGeolocationOverride; +export namespace Emulation { + export type SetGeolocationOverride = { + method: 'emulation.setGeolocationOverride'; + params: Emulation.SetGeolocationOverrideParameters; + }; +} +export namespace Emulation { + export type SetGeolocationOverrideParameters = { + coordinates: Emulation.GeolocationCoordinates | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Emulation { + export type GeolocationCoordinates = { + /** + * Must be between `-90` and `90`, inclusive. + */ + latitude: number; + /** + * Must be between `-180` and `180`, inclusive. + */ + longitude: number; + /** + * Must be greater than or equal to `0`. + * + * @defaultValue `1` + */ + accuracy?: number; + /** + * @defaultValue `null` + */ + altitude?: number | null; + /** + * Must be greater than or equal to `0`. + * + * @defaultValue `null` + */ + altitudeAccuracy?: number | null; + /** + * Must be between `0` and `360`. + * + * @defaultValue `null` + */ + heading?: number | null; + /** + * Must be greater than or equal to `0`. + * + * @defaultValue `null` + */ + speed?: number | null; + }; +} export type NetworkCommand = | Network.AddIntercept | Network.ContinueRequest diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index e8e6549cd2..2b11dfad63 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -703,7 +703,7 @@ export function validateBrowserContextOptions(options: types.BrowserContextOptio verifyGeolocation(options.geolocation); } -export function verifyGeolocation(geolocation?: types.Geolocation) { +export function verifyGeolocation(geolocation?: types.Geolocation): asserts geolocation is types.Geolocation { if (!geolocation) return; geolocation.accuracy = geolocation.accuracy || 0;