mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-15 06:02:57 +03:00
feat: auto-detect expression/function in js server (#5284)
This commit is contained in:
parent
fa1cf4108b
commit
3d253c4e5c
@ -1261,11 +1261,11 @@ export type FrameNavigatedEvent = {
|
||||
export type FrameEvalOnSelectorParams = {
|
||||
selector: string,
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type FrameEvalOnSelectorOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type FrameEvalOnSelectorResult = {
|
||||
value: SerializedValue,
|
||||
@ -1273,11 +1273,11 @@ export type FrameEvalOnSelectorResult = {
|
||||
export type FrameEvalOnSelectorAllParams = {
|
||||
selector: string,
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type FrameEvalOnSelectorAllOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type FrameEvalOnSelectorAllResult = {
|
||||
value: SerializedValue,
|
||||
@ -1377,11 +1377,12 @@ export type FrameDispatchEventOptions = {
|
||||
export type FrameDispatchEventResult = void;
|
||||
export type FrameEvaluateExpressionParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
world?: 'main' | 'utility',
|
||||
};
|
||||
export type FrameEvaluateExpressionOptions = {
|
||||
isFunction?: boolean,
|
||||
world?: 'main' | 'utility',
|
||||
};
|
||||
export type FrameEvaluateExpressionResult = {
|
||||
@ -1389,11 +1390,12 @@ export type FrameEvaluateExpressionResult = {
|
||||
};
|
||||
export type FrameEvaluateExpressionHandleParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
world?: 'main' | 'utility',
|
||||
};
|
||||
export type FrameEvaluateExpressionHandleOptions = {
|
||||
isFunction?: boolean,
|
||||
world?: 'main' | 'utility',
|
||||
};
|
||||
export type FrameEvaluateExpressionHandleResult = {
|
||||
@ -1680,12 +1682,13 @@ export type FrameUncheckOptions = {
|
||||
export type FrameUncheckResult = void;
|
||||
export type FrameWaitForFunctionParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
timeout?: number,
|
||||
pollingInterval?: number,
|
||||
};
|
||||
export type FrameWaitForFunctionOptions = {
|
||||
isFunction?: boolean,
|
||||
timeout?: number,
|
||||
pollingInterval?: number,
|
||||
};
|
||||
@ -1717,22 +1720,22 @@ export interface WorkerChannel extends Channel {
|
||||
export type WorkerCloseEvent = {};
|
||||
export type WorkerEvaluateExpressionParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type WorkerEvaluateExpressionOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type WorkerEvaluateExpressionResult = {
|
||||
value: SerializedValue,
|
||||
};
|
||||
export type WorkerEvaluateExpressionHandleParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type WorkerEvaluateExpressionHandleOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type WorkerEvaluateExpressionHandleResult = {
|
||||
handle: JSHandleChannel,
|
||||
@ -1759,22 +1762,22 @@ export type JSHandleDisposeOptions = {};
|
||||
export type JSHandleDisposeResult = void;
|
||||
export type JSHandleEvaluateExpressionParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type JSHandleEvaluateExpressionOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type JSHandleEvaluateExpressionResult = {
|
||||
value: SerializedValue,
|
||||
};
|
||||
export type JSHandleEvaluateExpressionHandleParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type JSHandleEvaluateExpressionHandleOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type JSHandleEvaluateExpressionHandleResult = {
|
||||
handle: JSHandleChannel,
|
||||
@ -1844,11 +1847,11 @@ export interface ElementHandleChannel extends JSHandleChannel {
|
||||
export type ElementHandleEvalOnSelectorParams = {
|
||||
selector: string,
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type ElementHandleEvalOnSelectorOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type ElementHandleEvalOnSelectorResult = {
|
||||
value: SerializedValue,
|
||||
@ -1856,11 +1859,11 @@ export type ElementHandleEvalOnSelectorResult = {
|
||||
export type ElementHandleEvalOnSelectorAllParams = {
|
||||
selector: string,
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type ElementHandleEvalOnSelectorAllOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type ElementHandleEvalOnSelectorAllResult = {
|
||||
value: SerializedValue,
|
||||
@ -2499,22 +2502,22 @@ export type ElectronApplicationWindowEvent = {
|
||||
};
|
||||
export type ElectronApplicationEvaluateExpressionParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type ElectronApplicationEvaluateExpressionOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type ElectronApplicationEvaluateExpressionResult = {
|
||||
value: SerializedValue,
|
||||
};
|
||||
export type ElectronApplicationEvaluateExpressionHandleParams = {
|
||||
expression: string,
|
||||
isFunction: boolean,
|
||||
isFunction?: boolean,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type ElectronApplicationEvaluateExpressionHandleOptions = {
|
||||
|
||||
isFunction?: boolean,
|
||||
};
|
||||
export type ElectronApplicationEvaluateExpressionHandleResult = {
|
||||
handle: JSHandleChannel,
|
||||
|
@ -1050,7 +1050,7 @@ Frame:
|
||||
parameters:
|
||||
selector: string
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -1059,7 +1059,7 @@ Frame:
|
||||
parameters:
|
||||
selector: string
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -1149,7 +1149,7 @@ Frame:
|
||||
evaluateExpression:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
world:
|
||||
type: enum?
|
||||
@ -1162,7 +1162,7 @@ Frame:
|
||||
evaluateExpressionHandle:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
world:
|
||||
type: enum?
|
||||
@ -1396,7 +1396,7 @@ Frame:
|
||||
waitForFunction:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
timeout: number?
|
||||
# When present, polls on interval. Otherwise, polls on raf.
|
||||
@ -1458,7 +1458,7 @@ Worker:
|
||||
evaluateExpression:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -1466,7 +1466,7 @@ Worker:
|
||||
evaluateExpressionHandle:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
handle: JSHandle
|
||||
@ -1489,7 +1489,7 @@ JSHandle:
|
||||
evaluateExpression:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -1497,7 +1497,7 @@ JSHandle:
|
||||
evaluateExpressionHandle:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
handle: JSHandle
|
||||
@ -1541,7 +1541,7 @@ ElementHandle:
|
||||
parameters:
|
||||
selector: string
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -1550,7 +1550,7 @@ ElementHandle:
|
||||
parameters:
|
||||
selector: string
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -2113,7 +2113,7 @@ ElectronApplication:
|
||||
evaluateExpression:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
value: SerializedValue
|
||||
@ -2121,7 +2121,7 @@ ElectronApplication:
|
||||
evaluateExpressionHandle:
|
||||
parameters:
|
||||
expression: string
|
||||
isFunction: boolean
|
||||
isFunction: boolean?
|
||||
arg: SerializedArgument
|
||||
returns:
|
||||
handle: JSHandle
|
||||
|
@ -488,13 +488,13 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
scheme.FrameEvalOnSelectorParams = tObject({
|
||||
selector: tString,
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.FrameEvalOnSelectorAllParams = tObject({
|
||||
selector: tString,
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.FrameAddScriptTagParams = tObject({
|
||||
@ -542,13 +542,13 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
});
|
||||
scheme.FrameEvaluateExpressionParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
world: tOptional(tEnum(['main', 'utility'])),
|
||||
});
|
||||
scheme.FrameEvaluateExpressionHandleParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
world: tOptional(tEnum(['main', 'utility'])),
|
||||
});
|
||||
@ -680,7 +680,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
});
|
||||
scheme.FrameWaitForFunctionParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
timeout: tOptional(tNumber),
|
||||
pollingInterval: tOptional(tNumber),
|
||||
@ -692,25 +692,25 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
});
|
||||
scheme.WorkerEvaluateExpressionParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.WorkerEvaluateExpressionHandleParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.JSHandleDisposeParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleDisposeParams = tType('JSHandleDisposeParams');
|
||||
scheme.JSHandleEvaluateExpressionParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElementHandleEvaluateExpressionParams = tType('JSHandleEvaluateExpressionParams');
|
||||
scheme.JSHandleEvaluateExpressionHandleParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElementHandleEvaluateExpressionHandleParams = tType('JSHandleEvaluateExpressionHandleParams');
|
||||
@ -725,13 +725,13 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
scheme.ElementHandleEvalOnSelectorParams = tObject({
|
||||
selector: tString,
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElementHandleEvalOnSelectorAllParams = tObject({
|
||||
selector: tString,
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElementHandleBoundingBoxParams = tOptional(tObject({}));
|
||||
@ -923,12 +923,12 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
});
|
||||
scheme.ElectronApplicationEvaluateExpressionParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElectronApplicationEvaluateExpressionHandleParams = tObject({
|
||||
expression: tString,
|
||||
isFunction: tBoolean,
|
||||
isFunction: tOptional(tBoolean),
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElectronApplicationCloseParams = tOptional(tObject({}));
|
||||
|
@ -50,7 +50,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
});
|
||||
}
|
||||
|
||||
async evaluateExpressionInternal(expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||
async evaluateExpressionInternal(expression: string, isFunction: boolean | undefined, ...args: any[]): Promise<any> {
|
||||
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
|
||||
return js.evaluateExpression(this, true /* returnByValue */, expression, isFunction, ...args);
|
||||
});
|
||||
@ -64,7 +64,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
});
|
||||
}
|
||||
|
||||
async evaluateExpressionHandleInternal(expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||
async evaluateExpressionHandleInternal(expression: string, isFunction: boolean | undefined, ...args: any[]): Promise<any> {
|
||||
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
|
||||
return js.evaluateExpression(this, false /* returnByValue */, expression, isFunction, ...args);
|
||||
});
|
||||
@ -628,7 +628,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
return this._page.selectors._queryAll(this._context.frame, selector, this, true /* adoptToMain */);
|
||||
}
|
||||
|
||||
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
async _$evalExpression(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const handle = await this._page.selectors._query(this._context.frame, selector, this);
|
||||
if (!handle)
|
||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||
@ -637,7 +637,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
async _$$evalExpression(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const arrayHandle = await this._page.selectors._queryArray(this._context.frame, selector, this);
|
||||
const result = await arrayHandle._evaluateExpression(expression, isFunction, true, arg);
|
||||
arrayHandle.dispose();
|
||||
|
@ -139,7 +139,7 @@ export class FrameManager {
|
||||
await barrier.waitFor();
|
||||
this._signalBarriers.delete(barrier);
|
||||
// Resolve in the next task, after all waitForNavigations.
|
||||
await new Promise(makeWaitForNextTask());
|
||||
await new Promise<void>(makeWaitForNextTask());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -579,7 +579,7 @@ export class Frame extends EventEmitter {
|
||||
return this._context('utility');
|
||||
}
|
||||
|
||||
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
async _evaluateExpressionHandle(expression: string, isFunction: boolean | undefined, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
const context = await this._context(world);
|
||||
const handle = await context.evaluateExpressionHandleInternal(expression, isFunction, arg);
|
||||
if (world === 'main')
|
||||
@ -587,7 +587,7 @@ export class Frame extends EventEmitter {
|
||||
return handle;
|
||||
}
|
||||
|
||||
async _evaluateExpression(expression: string, isFunction: boolean, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
async _evaluateExpression(expression: string, isFunction: boolean | undefined, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
const context = await this._context(world);
|
||||
const value = await context.evaluateExpressionInternal(expression, isFunction, arg);
|
||||
if (world === 'main')
|
||||
@ -632,7 +632,7 @@ export class Frame extends EventEmitter {
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
async _$evalExpression(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const handle = await this.$(selector);
|
||||
if (!handle)
|
||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||
@ -641,7 +641,7 @@ export class Frame extends EventEmitter {
|
||||
return result;
|
||||
}
|
||||
|
||||
async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
async _$$evalExpression(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const arrayHandle = await this._page.selectors._queryArray(this, selector);
|
||||
const result = await arrayHandle._evaluateExpression(expression, isFunction, true, arg);
|
||||
arrayHandle.dispose();
|
||||
@ -805,7 +805,7 @@ export class Frame extends EventEmitter {
|
||||
let result: dom.ElementHandle;
|
||||
let error: Error | undefined;
|
||||
let cspMessage: ConsoleMessage | undefined;
|
||||
const actionPromise = new Promise<dom.ElementHandle>(async resolve => {
|
||||
const actionPromise = new Promise<void>(async resolve => {
|
||||
try {
|
||||
result = await func();
|
||||
} catch (e) {
|
||||
@ -813,7 +813,7 @@ export class Frame extends EventEmitter {
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
const errorPromise = new Promise(resolve => {
|
||||
const errorPromise = new Promise<void>(resolve => {
|
||||
listeners.push(helper.addEventListener(this._page, Page.Events.Console, (message: ConsoleMessage) => {
|
||||
if (message.type() === 'error' && message.text().includes('Content Security Policy')) {
|
||||
cspMessage = message;
|
||||
@ -1016,7 +1016,7 @@ export class Frame extends EventEmitter {
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _waitForFunctionExpression<R>(expression: string, isFunction: boolean, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
|
||||
async _waitForFunctionExpression<R>(expression: string, isFunction: boolean | undefined, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
|
||||
if (typeof options.pollingInterval === 'number')
|
||||
assert(options.pollingInterval > 0, 'Cannot poll with non-positive interval: ' + options.pollingInterval);
|
||||
const predicateBody = isFunction ? 'return (' + expression + ')(arg)' : 'return (' + expression + ')';
|
||||
|
@ -17,17 +17,23 @@
|
||||
import { serializeAsCallArgument, parseEvaluationResultValue } from '../common/utilityScriptSerializers';
|
||||
|
||||
export default class UtilityScript {
|
||||
evaluate(returnByValue: boolean, expression: string) {
|
||||
const result = global.eval(expression);
|
||||
return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;
|
||||
}
|
||||
|
||||
callFunction(returnByValue: boolean, functionText: string, argCount: number, ...argsAndHandles: any[]) {
|
||||
evaluate(isFunction: boolean | undefined, returnByValue: boolean, expression: string, argCount: number, ...argsAndHandles: any[]) {
|
||||
const args = argsAndHandles.slice(0, argCount);
|
||||
const handles = argsAndHandles.slice(argCount);
|
||||
const parameters = args.map(a => parseEvaluationResultValue(a, handles));
|
||||
const func = global.eval('(' + functionText + ')');
|
||||
const result = func(...parameters);
|
||||
expression = expression.trim();
|
||||
if (expression.startsWith('function ') || expression.startsWith('async function '))
|
||||
expression = '(' + expression + ')';
|
||||
let result = global.eval(expression);
|
||||
if (isFunction === true) {
|
||||
result = result(...parameters);
|
||||
} else if (isFunction === false) {
|
||||
result = result;
|
||||
} else {
|
||||
// auto detect.
|
||||
if (typeof result === 'function')
|
||||
result = result(...parameters);
|
||||
}
|
||||
return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import * as dom from './dom';
|
||||
import * as utilityScriptSource from '../generated/utilityScriptSource';
|
||||
import * as sourceMap from '../utils/sourceMap';
|
||||
import { serializeAsCallArgument } from './common/utilityScriptSerializers';
|
||||
import type UtilityScript from './injected/utilityScript';
|
||||
|
||||
@ -114,7 +113,7 @@ export class JSHandle<T = any> {
|
||||
return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
|
||||
}
|
||||
|
||||
async _evaluateExpression(expression: string, isFunction: boolean, returnByValue: boolean, arg: any) {
|
||||
async _evaluateExpression(expression: string, isFunction: boolean | undefined, returnByValue: boolean, arg: any) {
|
||||
const value = await evaluateExpression(this._context, returnByValue, expression, isFunction, this, arg);
|
||||
await this._context.doSlowMo();
|
||||
return value;
|
||||
@ -174,28 +173,25 @@ export async function evaluate(context: ExecutionContext, returnByValue: boolean
|
||||
return evaluateExpression(context, returnByValue, String(pageFunction), typeof pageFunction === 'function', ...args);
|
||||
}
|
||||
|
||||
export async function evaluateExpression(context: ExecutionContext, returnByValue: boolean, expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||
export async function evaluateExpression(context: ExecutionContext, returnByValue: boolean, expression: string, isFunction: boolean | undefined, ...args: any[]): Promise<any> {
|
||||
const utilityScript = await context.utilityScript();
|
||||
if (!isFunction) {
|
||||
const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)`;
|
||||
return context._delegate.evaluateWithArguments(script, returnByValue, utilityScript, [returnByValue, expression], []);
|
||||
}
|
||||
|
||||
let functionText = expression;
|
||||
try {
|
||||
new Function('(' + functionText + ')');
|
||||
} catch (e1) {
|
||||
// This means we might have a function shorthand. Try another
|
||||
// time prefixing 'function '.
|
||||
if (functionText.startsWith('async '))
|
||||
functionText = 'async function ' + functionText.substring('async '.length);
|
||||
else
|
||||
functionText = 'function ' + functionText;
|
||||
if (isFunction) {
|
||||
try {
|
||||
new Function('(' + functionText + ')');
|
||||
} catch (e2) {
|
||||
// We tried hard to serialize, but there's a weird beast here.
|
||||
throw new Error('Passed function is not well-serializable!');
|
||||
new Function('(' + expression + ')');
|
||||
} catch (e1) {
|
||||
// This means we might have a function shorthand. Try another
|
||||
// time prefixing 'function '.
|
||||
if (expression.startsWith('async '))
|
||||
expression = 'async function ' + expression.substring('async '.length);
|
||||
else
|
||||
expression = 'function ' + expression;
|
||||
try {
|
||||
new Function('(' + expression + ')');
|
||||
} catch (e2) {
|
||||
// We tried hard to serialize, but there's a weird beast here.
|
||||
throw new Error('Passed function is not well-serializable!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,11 +224,10 @@ export async function evaluateExpression(context: ExecutionContext, returnByValu
|
||||
utilityScriptObjectIds.push(handle._objectId!);
|
||||
}
|
||||
|
||||
functionText += await sourceMap.generateSourceMapUrl(expression, functionText);
|
||||
// See UtilityScript for arguments.
|
||||
const utilityScriptValues = [returnByValue, functionText, args.length, ...args];
|
||||
const utilityScriptValues = [isFunction, returnByValue, expression, args.length, ...args];
|
||||
|
||||
const script = `(utilityScript, ...args) => utilityScript.callFunction(...args)`;
|
||||
const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)`;
|
||||
try {
|
||||
return await context._delegate.evaluateWithArguments(script, returnByValue, utilityScript, utilityScriptValues, utilityScriptObjectIds);
|
||||
} finally {
|
||||
|
@ -503,7 +503,7 @@ export class Worker extends EventEmitter {
|
||||
|
||||
private _url: string;
|
||||
private _executionContextPromise: Promise<js.ExecutionContext>;
|
||||
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
||||
private _executionContextCallback: (value: js.ExecutionContext) => void;
|
||||
_existingExecutionContext: js.ExecutionContext | null = null;
|
||||
|
||||
constructor(url: string) {
|
||||
@ -522,11 +522,11 @@ export class Worker extends EventEmitter {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
async _evaluateExpression(expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
return js.evaluateExpression(await this._executionContextPromise, true /* returnByValue */, expression, isFunction, arg);
|
||||
}
|
||||
|
||||
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
async _evaluateExpressionHandle(expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
return js.evaluateExpression(await this._executionContextPromise, false /* returnByValue */, expression, isFunction, arg);
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { getCallerFilePath } from './stackTrace';
|
||||
import { isDebugMode } from './utils';
|
||||
|
||||
type Position = {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
|
||||
export async function generateSourceMapUrl(functionText: string, generatedText: string): Promise<string> {
|
||||
if (!isDebugMode())
|
||||
return '';
|
||||
const sourceMapUrl = await innerGenerateSourceMapUrl(functionText, generatedText);
|
||||
return sourceMapUrl || '';
|
||||
}
|
||||
|
||||
async function innerGenerateSourceMapUrl(functionText: string, generatedText: string): Promise<string | undefined> {
|
||||
const filePath = getCallerFilePath();
|
||||
if (!filePath)
|
||||
return;
|
||||
try {
|
||||
const generatedIndex = generatedText.indexOf(functionText);
|
||||
if (generatedIndex === -1)
|
||||
return;
|
||||
const compiledPosition = findPosition(generatedText, generatedIndex);
|
||||
const source = await util.promisify(fs.readFile)(filePath, 'utf8');
|
||||
const sourceIndex = source.indexOf(functionText);
|
||||
if (sourceIndex === -1)
|
||||
return;
|
||||
const sourcePosition = findPosition(source, sourceIndex);
|
||||
const delta = findPosition(functionText, functionText.length);
|
||||
const sourceMap = generateSourceMap(filePath, sourcePosition, compiledPosition, delta);
|
||||
return `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(sourceMap).toString('base64')}\n`;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
const VLQ_BASE_SHIFT = 5;
|
||||
const VLQ_BASE = 1 << VLQ_BASE_SHIFT;
|
||||
const VLQ_BASE_MASK = VLQ_BASE - 1;
|
||||
const VLQ_CONTINUATION_BIT = VLQ_BASE;
|
||||
const BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
function base64VLQ(value: number): string {
|
||||
if (value < 0)
|
||||
value = ((-value) << 1) | 1;
|
||||
else
|
||||
value <<= 1;
|
||||
let result = '';
|
||||
do {
|
||||
let digit = value & VLQ_BASE_MASK;
|
||||
value >>>= VLQ_BASE_SHIFT;
|
||||
if (value > 0)
|
||||
digit |= VLQ_CONTINUATION_BIT;
|
||||
result += BASE64_DIGITS[digit];
|
||||
} while (value > 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
function generateSourceMap(filePath: string, sourcePosition: Position, compiledPosition: Position, delta: Position): any {
|
||||
const mappings = [];
|
||||
let lastCompiled = { line: 0, column: 0 };
|
||||
let lastSource = { line: 0, column: 0 };
|
||||
for (let line = 0; line < delta.line; line++) {
|
||||
// We need at least a mapping per line. This will yield an execution line at the start of each line.
|
||||
// Note: for more granular mapping, we can do word-by-word.
|
||||
const source = advancePosition(sourcePosition, { line, column: 0 });
|
||||
const compiled = advancePosition(compiledPosition, { line, column: 0 });
|
||||
while (lastCompiled.line < compiled.line) {
|
||||
mappings.push(';');
|
||||
lastCompiled.line++;
|
||||
lastCompiled.column = 0;
|
||||
}
|
||||
mappings.push(base64VLQ(compiled.column - lastCompiled.column));
|
||||
mappings.push(base64VLQ(0)); // Source index.
|
||||
mappings.push(base64VLQ(source.line - lastSource.line));
|
||||
mappings.push(base64VLQ(source.column - lastSource.column));
|
||||
lastCompiled = compiled;
|
||||
lastSource = source;
|
||||
}
|
||||
return JSON.stringify({
|
||||
version: 3,
|
||||
sources: ['file://' + filePath],
|
||||
names: [],
|
||||
mappings: mappings.join(''),
|
||||
});
|
||||
}
|
||||
|
||||
function findPosition(source: string, offset: number): Position {
|
||||
const result: Position = { line: 0, column: 0 };
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const newline = source.indexOf('\n', index);
|
||||
if (newline === -1 || newline >= offset)
|
||||
break;
|
||||
result.line++;
|
||||
index = newline + 1;
|
||||
}
|
||||
result.column = offset - index;
|
||||
return result;
|
||||
}
|
||||
|
||||
function advancePosition(position: Position, delta: Position): Position {
|
||||
return {
|
||||
line: position.line + delta.line,
|
||||
column: delta.column + (delta.line ? 0 : position.column),
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user