Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/src/api/class-frame.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,9 @@ Optional argument to pass to [`param: expression`].
### option: Frame.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Frame.evaluate.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

### option: Frame.evaluate.world = %%-js-evaluate-world-%%
* since: v1.64

Expand Down Expand Up @@ -872,6 +875,9 @@ Optional argument to pass to [`param: expression`].
### option: Frame.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Frame.evaluateHandle.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

## async method: Frame.fill
* since: v1.8
* discouraged: Use locator-based [`method: Locator.fill`] instead. Read more about [locators](../locators.md).
Expand Down
6 changes: 6 additions & 0 deletions docs/src/api/class-jshandle.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ Optional argument to pass to [`param: expression`].
### option: JSHandle.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: JSHandle.evaluate.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

## async method: JSHandle.evaluateHandle
* since: v1.8
- returns: <[JSHandle]>
Expand Down Expand Up @@ -129,6 +132,9 @@ Optional argument to pass to [`param: expression`].
### option: JSHandle.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: JSHandle.evaluateHandle.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

## async method: JSHandle.getProperties
* since: v1.8
- returns: <[Map]<[string], [JSHandle]>>
Expand Down
6 changes: 6 additions & 0 deletions docs/src/api/class-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,9 @@ Optional argument to pass to [`param: expression`].
### option: Locator.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Locator.evaluate.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

### option: Locator.evaluate.world = %%-js-evaluate-world-%%
* since: v1.64

Expand Down Expand Up @@ -1289,6 +1292,9 @@ Optional argument to pass to [`param: expression`].
### option: Locator.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Locator.evaluateHandle.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

### option: Locator.evaluateHandle.timeout
* since: v1.14
* langs: python, java, csharp
Expand Down
6 changes: 6 additions & 0 deletions docs/src/api/class-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -1605,6 +1605,9 @@ Optional argument to pass to [`param: expression`].
### option: Page.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Page.evaluate.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

### option: Page.evaluate.world = %%-js-evaluate-world-%%
* since: v1.64

Expand Down Expand Up @@ -1720,6 +1723,9 @@ Optional argument to pass to [`param: expression`].
### option: Page.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Page.evaluateHandle.serialize = %%-js-evaluate-serialize-%%
* since: v1.64

## async method: Page.exposeBinding
* since: v1.8
- returns: <[Disposable]>
Expand Down
6 changes: 6 additions & 0 deletions docs/src/api/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,12 @@ to a function, the function is automatically invoked.

Function to be evaluated in the page context.

## js-evaluate-serialize
* langs: js
- `serialize` <[Array]<[SerializationType]<"Map"|"Set">>>

Additional built-in types to preserve in the evaluation argument and return value, including nested collections and circular references. Supports `"Map"` and `"Set"`. For example, `serialize: ['Map', 'Set']` preserves both [Map] and [Set] instances. Defaults to an empty array, in which case these types are serialized as plain objects.

## js-evaluate-expose-functions
* langs: js
- `exposeFunctions` <[boolean]>
Expand Down
2 changes: 1 addition & 1 deletion packages/injected/src/storageScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class StorageScript {
trivial = false;

return { fallThrough: v };
}, { extendedSerialization: true });
}, { serialize: ['Map', 'Set'] });
if (trivial)
return { trivial: value };
return { encoded };
Expand Down
12 changes: 6 additions & 6 deletions packages/injected/src/utilityScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class UtilityScript {
(global as any).builtins = this.builtins;
}

evaluate(isFunction: boolean | undefined, returnByValue: boolean, expression: string, argCount: number, ...argsAndHandles: any[]) {
evaluate(isFunction: boolean | undefined, returnByValue: boolean, serialize: ('Map' | 'Set')[] | undefined, expression: string, argCount: number, ...argsAndHandles: any[]) {
const args = argsAndHandles.slice(0, argCount);
const handles = argsAndHandles.slice(argCount);
const parameters = [];
Expand All @@ -80,20 +80,20 @@ export class UtilityScript {
if (typeof result === 'function')
result = result(...parameters);
}
return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;
return returnByValue ? this._promiseAwareJsonValueNoThrow(result, serialize) : result;
}

jsonValue(returnByValue: true, value: any) {
jsonValue(returnByValue: true, value: any, serialize?: ('Map' | 'Set')[]) {
// Special handling of undefined to work-around multi-step returnByValue handling in WebKit.
if (value === undefined)
return undefined;
return serializeAsCallArgument(value, (value: any) => ({ fallThrough: value }));
return serializeAsCallArgument(value, (value: any) => ({ fallThrough: value }), { serialize });
}

private _promiseAwareJsonValueNoThrow(value: any) {
private _promiseAwareJsonValueNoThrow(value: any, serialize?: ('Map' | 'Set')[]) {
const safeJson = (value: any) => {
try {
return this.jsonValue(true, value);
return this.jsonValue(true, value, serialize);
} catch (e) {
return undefined;
}
Expand Down
58 changes: 28 additions & 30 deletions packages/isomorphic/utilityScriptSerializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type HandleOrValue = { h: number } | { fn: string } | { fallThrough: any };
type VisitorInfo = {
visited: Map<object, number>;
lastId: number;
extendedSerialization?: boolean;
serialize?: ('Map' | 'Set')[];
};

function isRegExp(obj: any): obj is RegExp {
Expand Down Expand Up @@ -231,8 +231,8 @@ export function parseEvaluationResultValue(value: SerializedValue, handles: any[
return value;
}

export function serializeAsCallArgument(value: any, handleSerializer: (value: any) => HandleOrValue, options: { extendedSerialization?: boolean } = {}): SerializedValue {
return serialize(value, handleSerializer, { visited: new Map(), lastId: 0, extendedSerialization: options.extendedSerialization });
export function serializeAsCallArgument(value: any, handleSerializer: (value: any) => HandleOrValue, options: { serialize?: ('Map' | 'Set')[] } = {}): SerializedValue {
return serialize(value, handleSerializer, { visited: new Map(), lastId: 0, serialize: options.serialize });
}

function serialize(value: any, handleSerializer: (value: any) => HandleOrValue, visitorInfo: VisitorInfo): SerializedValue {
Expand Down Expand Up @@ -308,35 +308,33 @@ function innerSerialize(value: any, handleSerializer: (value: any) => HandleOrVa
if (id)
return { ref: id };

if (visitorInfo.extendedSerialization) {
if (isMap(value)) {
const m: { k: SerializedValue, v: SerializedValue }[] = [];
const id = ++visitorInfo.lastId;
visitorInfo.visited.set(value, id);
const iterator = value.entries();
const next = new Map().entries().next;
while (true) {
const entry = next.call(iterator);
if (entry.done)
break;
m.push({ k: serialize(entry.value[0], handleSerializer, visitorInfo), v: serialize(entry.value[1], handleSerializer, visitorInfo) });
}
return { m, id };
if (visitorInfo.serialize?.includes('Map') && isMap(value)) {
const m: { k: SerializedValue, v: SerializedValue }[] = [];
const id = ++visitorInfo.lastId;
visitorInfo.visited.set(value, id);
const iterator = value.entries();
const next = new Map().entries().next;
while (true) {
const entry = next.call(iterator);
if (entry.done)
break;
m.push({ k: serialize(entry.value[0], handleSerializer, visitorInfo), v: serialize(entry.value[1], handleSerializer, visitorInfo) });
}
if (isSet(value)) {
const s: SerializedValue[] = [];
const id = ++visitorInfo.lastId;
visitorInfo.visited.set(value, id);
const iterator = value.values();
const next = new Set().values().next;
while (true) {
const entry = next.call(iterator);
if (entry.done)
break;
s.push(serialize(entry.value, handleSerializer, visitorInfo));
}
return { s, id };
return { m, id };
}
if (visitorInfo.serialize?.includes('Set') && isSet(value)) {
const s: SerializedValue[] = [];
const id = ++visitorInfo.lastId;
visitorInfo.visited.set(value, id);
const iterator = value.values();
const next = new Set().values().next;
while (true) {
const entry = next.call(iterator);
if (entry.done)
break;
s.push(serialize(entry.value, handleSerializer, visitorInfo));
}
return { s, id };
}

if (Array.isArray(value)) {
Expand Down
28 changes: 14 additions & 14 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export interface Page {
* [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression).
* @param options
*/
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, world?: 'main'|'utility' }): Promise<R>;
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[], world?: 'main'|'utility' }): Promise<R>;
/**
* Returns the value of the
* [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression) invocation.
Expand Down Expand Up @@ -187,7 +187,7 @@ export interface Page {
* [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression).
* @param options
*/
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean, world?: 'main'|'utility' }): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[], world?: 'main'|'utility' }): Promise<R>;

/**
* Returns the value of the
Expand Down Expand Up @@ -235,7 +235,7 @@ export interface Page {
* [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;
/**
* Returns the value of the
* [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression) invocation as a
Expand Down Expand Up @@ -282,7 +282,7 @@ export interface Page {
* [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;

/**
* Adds a script which would be evaluated in one of the following scenarios:
Expand Down Expand Up @@ -5963,7 +5963,7 @@ export interface Frame {
* [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression).
* @param options
*/
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, world?: 'main'|'utility' }): Promise<R>;
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[], world?: 'main'|'utility' }): Promise<R>;
/**
* Returns the return value of
* [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression).
Expand Down Expand Up @@ -6012,7 +6012,7 @@ export interface Frame {
* [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression).
* @param options
*/
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean, world?: 'main'|'utility' }): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[], world?: 'main'|'utility' }): Promise<R>;

/**
* Returns the return value of
Expand Down Expand Up @@ -6062,7 +6062,7 @@ export interface Frame {
* [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;
/**
* Returns the return value of
* [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression) as a
Expand Down Expand Up @@ -6111,7 +6111,7 @@ export interface Frame {
* [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;

/**
* **NOTE** Use locator-based [frame.locator(selector[, options])](https://playwright.dev/docs/api/class-frame#frame-locator)
Expand Down Expand Up @@ -12035,7 +12035,7 @@ export interface JSHandle<T = any> {
* [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression).
* @param options
*/
evaluate<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean }): Promise<R>;
evaluate<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<R>;
/**
* Returns the return value of
* [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression).
Expand All @@ -12058,7 +12058,7 @@ export interface JSHandle<T = any> {
* [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression).
* @param options
*/
evaluate<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any, options?: { exposeFunctions?: boolean }): Promise<R>;
evaluate<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<R>;

/**
* Returns the return value of
Expand All @@ -12082,7 +12082,7 @@ export interface JSHandle<T = any> {
* [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;
/**
* Returns the return value of
* [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression)
Expand All @@ -12105,7 +12105,7 @@ export interface JSHandle<T = any> {
* [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any, options?: { exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any, options?: { exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;

/**
* Returns a JSON representation of the object. If the object has a `toJSON` function, it **will not be called**.
Expand Down Expand Up @@ -14300,7 +14300,7 @@ export interface Locator {
* [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression).
* @param options
*/
evaluate<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg?: Arg, options?: { timeout?: number, signal?: AbortSignal, exposeFunctions?: boolean, world?: 'main'|'utility' }): Promise<R>;
evaluate<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg?: Arg, options?: { timeout?: number, signal?: AbortSignal, exposeFunctions?: boolean, serialize?: ('Map'|'Set')[], world?: 'main'|'utility' }): Promise<R>;
/**
* Execute JavaScript code in the page, taking the matching element as an argument, and return a
* [JSHandle](https://playwright.dev/docs/api/class-jshandle) with the result.
Expand Down Expand Up @@ -14334,7 +14334,7 @@ export interface Locator {
* [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression).
* @param options
*/
evaluateHandle<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg?: Arg, options?: { timeout?: number, signal?: AbortSignal, exposeFunctions?: boolean }): Promise<SmartHandle<R>>;
evaluateHandle<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg?: Arg, options?: { timeout?: number, signal?: AbortSignal, exposeFunctions?: boolean, serialize?: ('Map'|'Set')[] }): Promise<SmartHandle<R>>;
/**
* Execute JavaScript code in the page, taking all matching elements as an argument.
*
Expand Down
8 changes: 8 additions & 0 deletions packages/playwright-core/src/client/channels.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2479,10 +2479,12 @@ export type FrameEvaluateExpressionParams = {
expression: string,
isFunction?: boolean,
arg: SerializedArgument,
serialize?: ('Map' | 'Set')[],
world?: 'main' | 'utility',
};
export type FrameEvaluateExpressionOptions = {
isFunction?: boolean,
serialize?: ('Map' | 'Set')[],
world?: 'main' | 'utility',
};
export type FrameEvaluateExpressionResult = {
Expand All @@ -2492,9 +2494,11 @@ export type FrameEvaluateExpressionHandleParams = {
expression: string,
isFunction?: boolean,
arg: SerializedArgument,
serialize?: ('Map' | 'Set')[],
};
export type FrameEvaluateExpressionHandleOptions = {
isFunction?: boolean,
serialize?: ('Map' | 'Set')[],
};
export type FrameEvaluateExpressionHandleResult = {
handle: JSHandleChannel,
Expand Down Expand Up @@ -2938,10 +2942,12 @@ export type JSHandleEvaluateExpressionParams = {
expression: string,
isFunction?: boolean,
arg: SerializedArgument,
serialize?: ('Map' | 'Set')[],
world?: 'main' | 'utility',
};
export type JSHandleEvaluateExpressionOptions = {
isFunction?: boolean,
serialize?: ('Map' | 'Set')[],
world?: 'main' | 'utility',
};
export type JSHandleEvaluateExpressionResult = {
Expand All @@ -2951,9 +2957,11 @@ export type JSHandleEvaluateExpressionHandleParams = {
expression: string,
isFunction?: boolean,
arg: SerializedArgument,
serialize?: ('Map' | 'Set')[],
};
export type JSHandleEvaluateExpressionHandleOptions = {
isFunction?: boolean,
serialize?: ('Map' | 'Set')[],
};
export type JSHandleEvaluateExpressionHandleResult = {
handle: JSHandleChannel,
Expand Down
Loading
Loading