diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index 5c1cb400d1..1f376c820d 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -20,7 +20,7 @@ import { BrowserContext, assertBrowserContextIsNotOwned } from '../browserContex import * as network from '../network'; import { BidiConnection } from './bidiConnection'; import { bidiBytesValueToString } from './bidiNetworkManager'; -import { BidiPage } from './bidiPage'; +import { addMainBinding, BidiPage, kPlaywrightBindingChannel } from './bidiPage'; import * as bidi from './third_party/bidiProtocol'; import type { RegisteredListener } from '../utils/eventsHelper'; @@ -203,6 +203,7 @@ export class BidiBrowser extends Browser { export class BidiBrowserContext extends BrowserContext { declare readonly _browser: BidiBrowser; + private _initScriptIds: bidi.Script.PreloadScript[] = []; constructor(browser: BidiBrowser, browserContextId: string | undefined, options: types.BrowserContextOptions) { super(browser, options, browserContextId); @@ -213,6 +214,45 @@ export class BidiBrowserContext extends BrowserContext { return [...this._browser._bidiPages.values()].filter(bidiPage => bidiPage._browserContext === this); } + override async _initialize() { + const promises: Promise[] = [ + super._initialize(), + this._installMainBinding(), + ]; + // FIXME: Persistent context doesn't have an id, so we can't set the command for it. + if (this._options.viewport && this._browserContextId) { + promises.push(this._browser._browserSession.send('browsingContext.setViewport', { + viewport: { + width: this._options.viewport.width, + height: this._options.viewport.height + }, + devicePixelRatio: this._options.deviceScaleFactor || 1, + userContexts: [this._browserContextId], + })); + } + await Promise.all(promises); + } + + // TODO: consider calling this only when bindings are added. + private async _installMainBinding() { + // TODO: Cannot install main binding for persistent context as it doesn't have an id. + if (!this._browserContextId) + return; + const functionDeclaration = addMainBinding.toString(); + const args: bidi.Script.ChannelValue[] = [{ + type: 'channel', + value: { + channel: kPlaywrightBindingChannel, + ownership: bidi.Script.ResultOwnership.Root, + } + }]; + await this._browser._browserSession.send('script.addPreloadScript', { + functionDeclaration, + arguments: args, + userContexts: [this._browserContextId], + }); + } + override possiblyUninitializedPages(): Page[] { return this._bidiPages().map(bidiPage => bidiPage._page); } @@ -293,10 +333,22 @@ export class BidiBrowserContext extends BrowserContext { } async doAddInitScript(initScript: InitScript) { - await Promise.all(this.pages().map(page => (page._delegate as BidiPage).addInitScript(initScript))); + // TODO: support persistent context. + if (!this._browserContextId) + return; + const { script } = await this._browser._browserSession.send('script.addPreloadScript', { + // TODO: remove function call from the source. + functionDeclaration: `() => { return ${initScript.source} }`, + userContexts: [this._browserContextId], + }); + if (!initScript.internal) + this._initScriptIds.push(script); } async doRemoveNonInternalInitScripts() { + const promise = Promise.all(this._initScriptIds.map(script => this._browser._browserSession.send('script.removePreloadScript', { script }))); + this._initScriptIds = []; + await promise; } async doUpdateRequestInterception(): Promise { diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index 13cb21e36f..76267ab89c 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -37,7 +37,7 @@ import type { BidiSession } from './bidiConnection'; import type * as channels from '@protocol/channels'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; -const kPlaywrightBindingChannel = 'playwrightChannel'; +export const kPlaywrightBindingChannel = 'playwrightChannel'; export class BidiPage implements PageDelegate { readonly rawMouse: RawMouseImpl; @@ -51,7 +51,7 @@ export class BidiPage implements PageDelegate { readonly _browserContext: BidiBrowserContext; readonly _networkManager: BidiNetworkManager; private readonly _pdf: BidiPDF; - private _initScriptIds: string[] = []; + private _initScriptIds: bidi.Script.PreloadScript[] = []; constructor(browserContext: BidiBrowserContext, bidiSession: BidiSession, opener: BidiPage | null) { this._session = bidiSession; @@ -92,7 +92,6 @@ export class BidiPage implements PageDelegate { await Promise.all([ this.updateHttpCredentials(), this.updateRequestInterception(), - this._updateViewport(), this._installMainBinding(), this._addAllInitScripts(), ]); @@ -258,10 +257,6 @@ export class BidiPage implements PageDelegate { async updateEmulateMedia(): Promise { } - async updateEmulatedViewportSize(): Promise { - await this._updateViewport(); - } - async updateUserAgent(): Promise { } @@ -271,7 +266,7 @@ export class BidiPage implements PageDelegate { }); } - private async _updateViewport(): Promise { + async updateEmulatedViewportSize(): Promise { const options = this._browserContext._options; const deviceSize = this._page.emulatedSize(); if (deviceSize === null) @@ -328,7 +323,11 @@ export class BidiPage implements PageDelegate { } // TODO: consider calling this only when bindings are added. + // TODO: delete this method once we can add preload script for persistent context. private async _installMainBinding() { + // For non-persistent context, the main binding is installed during context creation. + if (this._browserContext._browserContextId) + return; const functionDeclaration = addMainBinding.toString(); const args: bidi.Script.ChannelValue[] = [{ type: 'channel', @@ -573,7 +572,7 @@ export class BidiPage implements PageDelegate { } } -function addMainBinding(callback: (arg: any) => void) { +export function addMainBinding(callback: (arg: any) => void) { (globalThis as any)['__playwright__binding__'] = callback; }