diff --git a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts index ec0cb681a0793..aea4672196f81 100644 --- a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +++ b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts @@ -16,6 +16,7 @@ import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers'; import * as js from '../javascript'; +import * as dom from '../dom'; import { BidiDeserializer } from './third_party/bidiDeserializer'; import * as bidi from './third_party/bidiProtocol'; import { BidiSerializer } from './third_party/bidiSerializer'; @@ -137,7 +138,45 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { }); } - async rawCallFunction(functionDeclaration: string, arg: bidi.Script.LocalValue): Promise { + + async nodeIdForElementHandle(handle: dom.ElementHandle): Promise { + const shared = await this._remoteValueForReference({ handle: handle._objectId }); + // TODO: store sharedId in the handle. + if (!('sharedId' in shared)) + throw new Error('Element is not a node'); + return { + sharedId: shared.sharedId!, + }; + } + + async remoteObjectForNodeId(nodeId: bidi.Script.SharedReference): Promise { + const result = await this._remoteValueForReference(nodeId); + if ('handle' in result) + return { objectId: result.handle!, ...result }; + throw new Error('Can\'t get remote object for nodeId'); + } + + async contentFrameIdForFrame(handle: dom.ElementHandle) { + const contentWindow = await this._rawCallFunction('e => e.contentWindow', { handle: handle._objectId }); + if (contentWindow?.type === 'window') + return contentWindow.value.context; + return null; + } + + async frameIdForWindowHandle(handle: js.JSHandle): Promise { + if (!handle._objectId) + throw new Error('JSHandle is not a DOM node handle'); + const contentWindow = await this._remoteValueForReference({ handle: handle._objectId }); + if (contentWindow.type === 'window') + return contentWindow.value.context; + return null; + } + + private async _remoteValueForReference(reference: bidi.Script.RemoteReference) { + return await this._rawCallFunction('e => e', reference); + } + + private async _rawCallFunction(functionDeclaration: string, arg: bidi.Script.LocalValue): Promise { const response = await this._session.send('script.callFunction', { functionDeclaration, target: this._target, diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index 0ee5822f933a3..badd68d1a16ff 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -413,13 +413,10 @@ export class BidiPage implements PageDelegate { async getContentFrame(handle: dom.ElementHandle): Promise { const executionContext = toBidiExecutionContext(handle._context); - const contentWindow = await executionContext.rawCallFunction('e => e.contentWindow', { handle: handle._objectId }); - if (contentWindow.type === 'window') { - const frameId = contentWindow.value.context; - const result = this._page._frameManager.frame(frameId); - return result; - } - return null; + const frameId = await executionContext.contentFrameIdForFrame(handle); + if (!frameId) + return null; + return this._page._frameManager.frame(frameId); } async getOwnerFrame(handle: dom.ElementHandle): Promise { @@ -430,15 +427,8 @@ export class BidiPage implements PageDelegate { }); if (!windowHandle) return null; - if (!windowHandle._objectId) - return null; - const executionContext = toBidiExecutionContext(windowHandle._context as dom.FrameExecutionContext); - const contentWindow = await executionContext.rawCallFunction('e => e', { handle: windowHandle._objectId }); - if (contentWindow.type === 'window') { - const frameId = contentWindow.value.context; - return frameId; - } - return null; + const executionContext = toBidiExecutionContext(handle._context); + return executionContext.frameIdForWindowHandle(windowHandle); } isElementHandle(remoteObject: bidi.Script.RemoteValue): boolean { @@ -535,29 +525,20 @@ export class BidiPage implements PageDelegate { async setInputFilePaths(handle: dom.ElementHandle, paths: string[]): Promise { const fromContext = toBidiExecutionContext(handle._context); - const shared = await fromContext.rawCallFunction('x => x', { handle: handle._objectId }); - // TODO: store sharedId in the handle. - if (!('sharedId' in shared)) - throw new Error('Element is not a node'); - const sharedId = shared.sharedId!; await this._session.send('input.setFiles', { context: this._session.sessionId, - element: { sharedId }, + element: await fromContext.nodeIdForElementHandle(handle), files: paths, }); } async adoptElementHandle(handle: dom.ElementHandle, to: dom.FrameExecutionContext): Promise> { const fromContext = toBidiExecutionContext(handle._context); - const shared = await fromContext.rawCallFunction('x => x', { handle: handle._objectId }); - // TODO: store sharedId in the handle. - if (!('sharedId' in shared)) - throw new Error('Element is not a node'); - const sharedId = shared.sharedId!; + const nodeId = await fromContext.nodeIdForElementHandle(handle); const executionContext = toBidiExecutionContext(to); - const result = await executionContext.rawCallFunction('x => x', { sharedId }); - if ('handle' in result) - return to.createHandle({ objectId: result.handle!, ...result }) as dom.ElementHandle; + const objectId = await executionContext.remoteObjectForNodeId(nodeId); + if (objectId) + return to.createHandle(objectId) as dom.ElementHandle; throw new Error('Failed to adopt element handle.'); }