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 @@ -745,6 +745,9 @@ await bodyHandle.DisposeAsync();

Optional argument to pass to [`param: expression`].

### option: Frame.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

## async method: Frame.evaluateHandle
* since: v1.8
- returns: <[JSHandle]>
Expand Down Expand Up @@ -857,6 +860,9 @@ await resultHandle.DisposeAsync();

Optional argument to pass to [`param: expression`].

### option: Frame.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

## 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 @@ -96,6 +96,9 @@ Assert.AreEqual("10 retweets", await tweetHandle.EvaluateAsync("node => node.inn

Optional argument to pass to [`param: expression`].

### option: JSHandle.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

## async method: JSHandle.evaluateHandle
* since: v1.8
- returns: <[JSHandle]>
Expand Down Expand Up @@ -123,6 +126,9 @@ See [`method: Page.evaluateHandle`] for more details.

Optional argument to pass to [`param: expression`].

### option: JSHandle.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

## 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 @@ -1123,6 +1123,9 @@ Console.WriteLine(result); // prints "myId text 56"

Optional argument to pass to [`param: expression`].

### option: Locator.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### option: Locator.evaluate.timeout
* since: v1.14
* langs: python, java, csharp
Expand Down Expand Up @@ -1222,6 +1225,9 @@ See [`method: Page.evaluateHandle`] for more details.

Optional argument to pass to [`param: expression`].

### option: Locator.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

### 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 @@ -1587,6 +1587,9 @@ await bodyHandle.DisposeAsync();

Optional argument to pass to [`param: expression`].

### option: Page.evaluate.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

## async method: Page.evaluateHandle
* since: v1.8
- returns: <[JSHandle]>
Expand Down Expand Up @@ -1696,6 +1699,9 @@ await resultHandle.DisposeAsync();

Optional argument to pass to [`param: expression`].

### option: Page.evaluateHandle.exposeFunctions = %%-js-evaluate-expose-functions-%%
* since: v1.62

## 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-expose-functions
* langs: js
- `exposeFunctions` <[boolean]>

When set to `true`, functions passed inside [`param: arg`] are exposed in the page and can be called from the page function. Calling one returns a [Promise] of its result. The page-side functions are scoped to the execution context they were passed to and disappear when the page navigates. Defaults to `false`, in which case functions are not serializable and passing one throws an error, as before.

## js-evalonselector-pagefunction
* langs: js
- `pageFunction` <[function]\([Element]\)|[string]>
Expand Down
36 changes: 20 additions & 16 deletions packages/injected/src/bindingsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,32 @@ export class BindingsController {
this._globalBindingName = globalBindingName;
}

addBinding(bindingName: string) {
addBinding(bindingName: string, noGlobal?: boolean) {
const data: BindingData = {
callbacks: new Map(),
lastSeq: 0,
removed: false,
};
this._bindings.set(bindingName, data);
(this._global as any)[bindingName] = (...args: any[]) => {
if (data.removed)
throw new Error(`binding "${bindingName}" has been removed`);
const seq = ++data.lastSeq;
const promise = new Promise((resolve, reject) => data.callbacks.set(seq, { resolve, reject }));
const serializedArgs = [];
for (let i = 0; i < args.length; i++) {
serializedArgs[i] = serializeAsCallArgument(args[i], v => {
return { fallThrough: v };
});
}
const payload: BindingPayload = { name: bindingName, seq, serializedArgs };
(this._global as any)[this._globalBindingName](JSON.stringify(payload));
return promise;
};
if (!noGlobal)
(this._global as any)[bindingName] = (...args: any[]) => this.callBinding(bindingName, ...args);
}

callBinding(bindingName: string, ...args: any[]): Promise<any> {
const data = this._bindings.get(bindingName);
if (!data || data.removed)
throw new Error(`binding "${bindingName}" has been removed`);
const seq = ++data.lastSeq;
const promise = new Promise((resolve, reject) => data.callbacks.set(seq, { resolve, reject }));
const serializedArgs = [];
for (let i = 0; i < args.length; i++) {
serializedArgs[i] = serializeAsCallArgument(args[i], v => {
return { fallThrough: v };
});
}
const payload: BindingPayload = { name: bindingName, seq, serializedArgs };
(this._global as any)[this._globalBindingName](JSON.stringify(payload));
return promise;
}

removeBinding(bindingName: string) {
Expand Down
16 changes: 16 additions & 0 deletions packages/isomorphic/utilityScriptSerializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

type TypedArrayKind = 'i8' | 'ui8' | 'ui8c' | 'i16' | 'ui16' | 'i32' | 'ui32' | 'f32' | 'f64' | 'bi64' | 'bui64';

// Name prefix of the page bindings backing the functions passed to evaluate()
// as arguments. Only functions carrying this prefix serialize as { fn },
// arbitrary functions are dropped as before.
export const kFunctionBindingPrefix = '__pw_fn_';

export const kBindingsControllerProperty = '__playwright__binding__controller__';

export type SerializedValue =
undefined | boolean | number | string |
{ v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } |
Expand All @@ -28,6 +35,7 @@ export type SerializedValue =
{ o: { k: string, v: SerializedValue }[], id: number } |
{ ref: number } |
{ h: number } |
{ fn: string } |
{ ta: { b: string, k: TypedArrayKind } } |
{ ab: { b: string } };

Expand Down Expand Up @@ -177,6 +185,11 @@ export function parseEvaluationResultValue(value: SerializedValue, handles: any[
}
if ('h' in value)
return handles[value.h];
if ('fn' in value) {
const name = value.fn;
// eslint-disable-next-line no-restricted-globals
return (...args: any[]) => (globalThis as any)[kBindingsControllerProperty].callBinding(name, ...args);
}
if ('ta' in value)
return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);
if ('ab' in value)
Expand Down Expand Up @@ -300,4 +313,7 @@ function innerSerialize(value: any, handleSerializer: (value: any) => HandleOrVa

return { o, id };
}

if (typeof value === 'function' && value.name.startsWith(kFunctionBindingPrefix))
return { fn: value.name };
}
Loading
Loading