Skip to content

Commit 1f643a3

Browse files
committed
cherry-pick(#36214): fix: ensure ElementHandlerDispatcher has FrameDispatcher parent
1 parent 88e4873 commit 1f643a3

File tree

7 files changed

+42
-20
lines changed

7 files changed

+42
-20
lines changed

packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { CDPSessionDispatcher } from './cdpSessionDispatcher';
2323
import { DialogDispatcher } from './dialogDispatcher';
2424
import { Dispatcher } from './dispatcher';
2525
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
26+
import { FrameDispatcher } from './frameDispatcher';
27+
import { JSHandleDispatcher } from './jsHandleDispatcher';
2628
import { APIRequestContextDispatcher, RequestDispatcher, ResponseDispatcher, RouteDispatcher } from './networkDispatchers';
2729
import { BindingCallDispatcher, PageDispatcher, WorkerDispatcher } from './pageDispatcher';
2830
import { CRBrowserContext } from '../chromium/crBrowser';
@@ -42,7 +44,6 @@ import type { CallMetadata } from '../instrumentation';
4244
import type { Request, Response, RouteHandler } from '../network';
4345
import type { InitScript, Page, PageBinding } from '../page';
4446
import type { DispatcherScope } from './dispatcher';
45-
import type { FrameDispatcher } from './frameDispatcher';
4647
import type * as channels from '@protocol/channels';
4748

4849
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel, DispatcherScope> implements channels.BrowserContextChannel {
@@ -126,7 +127,12 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
126127
page: pageDispatcher,
127128
type: message.type(),
128129
text: message.text(),
129-
args: message.args().map(a => ElementHandleDispatcher.fromJSHandle(pageDispatcher, a)),
130+
args: message.args().map(a => {
131+
const elementHandle = a.asElement();
132+
if (elementHandle)
133+
return ElementHandleDispatcher.from(FrameDispatcher.from(this, elementHandle._frame), elementHandle);
134+
return JSHandleDispatcher.fromJSHandle(pageDispatcher, a);
135+
}),
130136
location: message.location(),
131137
});
132138
}

packages/playwright-core/src/server/dispatchers/electronDispatcher.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616

1717
import { BrowserContextDispatcher } from './browserContextDispatcher';
1818
import { Dispatcher } from './dispatcher';
19-
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
20-
import { parseArgument, serializeResult } from './jsHandleDispatcher';
19+
import { JSHandleDispatcher, parseArgument, serializeResult } from './jsHandleDispatcher';
2120
import { ElectronApplication } from '../electron/electron';
2221

2322
import type { RootDispatcher } from './dispatcher';
@@ -59,15 +58,15 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
5958
this._dispatchEvent('console', {
6059
type: message.type(),
6160
text: message.text(),
62-
args: message.args().map(a => ElementHandleDispatcher.fromJSHandle(this, a)),
61+
args: message.args().map(a => JSHandleDispatcher.fromJSHandle(this, a)),
6362
location: message.location()
6463
});
6564
});
6665
}
6766

6867
async browserWindow(params: channels.ElectronApplicationBrowserWindowParams): Promise<channels.ElectronApplicationBrowserWindowResult> {
6968
const handle = await this._object.browserWindow((params.page as PageDispatcher).page());
70-
return { handle: ElementHandleDispatcher.fromJSHandle(this, handle) };
69+
return { handle: JSHandleDispatcher.fromJSHandle(this, handle) };
7170
}
7271

7372
async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> {
@@ -78,7 +77,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
7877
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
7978
const handle = await this._object._nodeElectronHandlePromise;
8079
const result = await handle.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg));
81-
return { handle: ElementHandleDispatcher.fromJSHandle(this, result) };
80+
return { handle: JSHandleDispatcher.fromJSHandle(this, result) };
8281
}
8382

8483
async updateSubscription(params: channels.ElectronApplicationUpdateSubscriptionParams): Promise<void> {

packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type { ElementHandle } from '../dom';
2222
import type { Frame } from '../frames';
2323
import type { CallMetadata } from '../instrumentation';
2424
import type * as js from '../javascript';
25-
import type { JSHandleDispatcherParentScope } from './jsHandleDispatcher';
2625
import type * as channels from '@protocol/channels';
2726

2827

@@ -41,15 +40,13 @@ export class ElementHandleDispatcher extends JSHandleDispatcher<FrameDispatcher>
4140
return scope.connection.existingDispatcher<ElementHandleDispatcher>(handle) || new ElementHandleDispatcher(scope, handle);
4241
}
4342

44-
static fromJSHandle(scope: JSHandleDispatcherParentScope, handle: js.JSHandle): JSHandleDispatcher {
43+
static fromJSOrElementHandle(scope: FrameDispatcher, handle: js.JSHandle): JSHandleDispatcher {
4544
const result = scope.connection.existingDispatcher<JSHandleDispatcher>(handle);
4645
if (result)
4746
return result;
4847
const elementHandle = handle.asElement();
4948
if (!elementHandle)
5049
return new JSHandleDispatcher(scope, handle);
51-
if (!(scope instanceof FrameDispatcher))
52-
throw new Error('ElementHandle can only be created from FrameDispatcher');
5350
return new ElementHandleDispatcher(scope, elementHandle);
5451
}
5552

packages/playwright-core/src/server/dispatchers/frameDispatcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Br
8888
}
8989

9090
async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> {
91-
return { handle: ElementHandleDispatcher.fromJSHandle(this, await this._frame.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg))) };
91+
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg))) };
9292
}
9393

9494
async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> {
@@ -246,7 +246,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Br
246246
}
247247

248248
async waitForFunction(params: channels.FrameWaitForFunctionParams, metadata: CallMetadata): Promise<channels.FrameWaitForFunctionResult> {
249-
return { handle: ElementHandleDispatcher.fromJSHandle(this, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
249+
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
250250
}
251251

252252
async title(params: channels.FrameTitleParams, metadata: CallMetadata): Promise<channels.FrameTitleResult> {

packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ export type JSHandleDispatcherParentScope = PageDispatcher | FrameDispatcher | W
3030
export class JSHandleDispatcher<ParentScope extends JSHandleDispatcherParentScope = JSHandleDispatcherParentScope> extends Dispatcher<js.JSHandle, channels.JSHandleChannel, ParentScope> implements channels.JSHandleChannel {
3131
_type_JSHandle = true;
3232

33+
static fromJSHandle(scope: JSHandleDispatcherParentScope, handle: js.JSHandle): JSHandleDispatcher {
34+
return scope.connection.existingDispatcher<JSHandleDispatcher>(handle) || new JSHandleDispatcher(scope, handle);
35+
}
36+
3337
protected constructor(scope: ParentScope, jsHandle: js.JSHandle) {
3438
// Do not call this directly, use createHandle() instead.
3539
super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', {
@@ -44,19 +48,23 @@ export class JSHandleDispatcher<ParentScope extends JSHandleDispatcherParentScop
4448

4549
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
4650
const jsHandle = await this._object.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg));
47-
return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle) };
51+
// If "jsHandle" is an ElementHandle, it belongs to the same frame as "this".
52+
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope() as FrameDispatcher, jsHandle) };
4853
}
4954

5055
async getProperty(params: channels.JSHandleGetPropertyParams): Promise<channels.JSHandleGetPropertyResult> {
5156
const jsHandle = await this._object.getProperty(params.name);
52-
return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle) };
57+
// If "jsHandle" is an ElementHandle, it belongs to the same frame as "this".
58+
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope() as FrameDispatcher, jsHandle) };
5359
}
5460

5561
async getPropertyList(): Promise<channels.JSHandleGetPropertyListResult> {
5662
const map = await this._object.getProperties();
5763
const properties = [];
58-
for (const [name, value] of map)
59-
properties.push({ name, value: ElementHandleDispatcher.fromJSHandle(this.parentScope(), value) });
64+
for (const [name, value] of map) {
65+
// If "jsHandle" is an ElementHandle, it belongs to the same frame as "this".
66+
properties.push({ name, value: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope() as FrameDispatcher, value) });
67+
}
6068
return { properties };
6169
}
6270

packages/playwright-core/src/server/dispatchers/pageDispatcher.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { parseError } from '../errors';
2020
import { ArtifactDispatcher } from './artifactDispatcher';
2121
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
2222
import { FrameDispatcher } from './frameDispatcher';
23-
import { parseArgument, serializeResult } from './jsHandleDispatcher';
23+
import { JSHandleDispatcher, parseArgument, serializeResult } from './jsHandleDispatcher';
2424
import { RequestDispatcher } from './networkDispatchers';
2525
import { ResponseDispatcher } from './networkDispatchers';
2626
import { RouteDispatcher, WebSocketDispatcher } from './networkDispatchers';
@@ -399,7 +399,7 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel,
399399
}
400400

401401
async evaluateExpressionHandle(params: channels.WorkerEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionHandleResult> {
402-
return { handle: ElementHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
402+
return { handle: JSHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
403403
}
404404
}
405405

@@ -415,7 +415,7 @@ export class BindingCallDispatcher extends Dispatcher<{ guid: string }, channels
415415
frame: frameDispatcher,
416416
name,
417417
args: needsHandle ? undefined : args.map(serializeResult),
418-
handle: needsHandle ? ElementHandleDispatcher.fromJSHandle(frameDispatcher, args[0] as JSHandle) : undefined,
418+
handle: needsHandle ? ElementHandleDispatcher.fromJSOrElementHandle(frameDispatcher, args[0] as JSHandle) : undefined,
419419
});
420420
this._promise = new Promise((resolve, reject) => {
421421
this._resolve = resolve;

tests/library/browsercontext-events.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { browserTest as test, expect } from '../config/browserTest';
18+
import type { ElementHandle } from 'playwright-core';
1819

1920
test('console event should work @smoke', async ({ page }) => {
2021
const [, message] = await Promise.all([
@@ -26,6 +27,17 @@ test('console event should work @smoke', async ({ page }) => {
2627
expect(message.page()).toBe(page);
2728
});
2829

30+
test('console event should work with element handles', async ({ page }) => {
31+
await page.setContent('<body>hello</body>');
32+
const [, message] = await Promise.all([
33+
page.evaluate(() => console.log(document.body)),
34+
page.context().waitForEvent('console'),
35+
]);
36+
const body = message.args()[0];
37+
expect(await body.evaluate(x => x.nodeName)).toBe('BODY');
38+
await (body as ElementHandle).click();
39+
});
40+
2941
test('console event should work in popup', async ({ page }) => {
3042
const [, message, popup] = await Promise.all([
3143
page.evaluate(() => {

0 commit comments

Comments
 (0)