chore(evaluate): explicitly annotate methods that wait for signals (#5859)

This commit is contained in:
Pavel Feldman 2021-03-18 01:47:07 +08:00 committed by GitHub
parent c55000812b
commit 7011e5737a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 119 additions and 113 deletions

View file

@ -51,12 +51,12 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> {
const handle = this._object._nodeElectronHandle!;
return { value: serializeResult(await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
return { value: serializeResult(await handle.evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
}
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
const handle = this._object._nodeElectronHandle!;
const result = await handle._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
const result = await handle.evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return { handle: createHandle(this._scope, result) };
}

View file

@ -171,11 +171,11 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
}
async evalOnSelector(params: channels.ElementHandleEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorResult> {
return { value: serializeResult(await this._elementHandle._$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
return { value: serializeResult(await this._elementHandle.$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
}
async evalOnSelectorAll(params: channels.ElementHandleEvalOnSelectorAllParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorAllResult> {
return { value: serializeResult(await this._elementHandle._$$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
return { value: serializeResult(await this._elementHandle.$$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
}
async waitForElementState(params: channels.ElementHandleWaitForElementStateParams, metadata: CallMetadata): Promise<void> {

View file

@ -61,11 +61,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
}
async evaluateExpression(params: channels.FrameEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionResult> {
return { value: serializeResult(await this._frame._evaluateExpression(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
return { value: serializeResult(await this._frame.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
}
async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> {
return { handle: createHandle(this._scope, await this._frame._evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
return { handle: createHandle(this._scope, await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
}
async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> {

View file

@ -31,11 +31,11 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
}
async evaluateExpression(params: channels.JSHandleEvaluateExpressionParams): Promise<channels.JSHandleEvaluateExpressionResult> {
return { value: serializeResult(await this._object._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
return { value: serializeResult(await this._object.evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
}
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
const jsHandle = await this._object._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
const jsHandle = await this._object.evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return { handle: createHandle(this._scope, jsHandle) };
}

View file

@ -316,7 +316,7 @@ export abstract class BrowserContext extends SdkObject {
const originStorage: types.OriginStorage = { origin, localStorage: [] };
const frame = page.mainFrame();
await frame.goto(internalMetadata, origin);
const storage = await frame._evaluateExpression(`({
const storage = await frame.evaluateExpression(`({
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
})`, false, undefined, 'utility');
originStorage.localStorage = storage.localStorage;
@ -340,7 +340,7 @@ export abstract class BrowserContext extends SdkObject {
for (const originState of state.origins) {
const frame = page.mainFrame();
await frame.goto(metadata, originState.origin);
await frame._evaluateExpression(`
await frame.evaluateExpression(`
originState => {
for (const { name, value } of (originState.localStorage || []))
localStorage.setItem(name, value);

View file

@ -143,7 +143,7 @@ export class CRPage implements PageDelegate {
async exposeBinding(binding: PageBinding) {
await this._forAllFrameSessions(frame => frame._initBinding(binding));
await Promise.all(this._page.frames().map(frame => frame._evaluateExpression(binding.source, false, {}, binding.world).catch(e => {})));
await Promise.all(this._page.frames().map(frame => frame.evaluateExpression(binding.source, false, {}, binding.world).catch(e => {})));
}
async updateExtraHTTPHeaders(): Promise<void> {
@ -284,7 +284,7 @@ export class CRPage implements PageDelegate {
}
async setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void> {
await handle._evaluateInUtility(([injected, node, files]) =>
await handle.evaluateInUtility(([injected, node, files]) =>
injected.setInputFiles(node, files), files);
}
@ -421,9 +421,9 @@ class FrameSession {
worldName: UTILITY_WORLD_NAME,
});
for (const binding of this._crPage._browserContext._pageBindings.values())
frame._evaluateExpression(binding.source, false, undefined, binding.world).catch(e => {});
frame.evaluateExpression(binding.source, false, undefined, binding.world).catch(e => {});
for (const source of this._crPage._browserContext._evaluateOnNewDocumentSources)
frame._evaluateExpression(source, false, undefined, 'main').catch(e => {});
frame.evaluateExpression(source, false, undefined, 'main').catch(e => {});
}
const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ':';
if (isInitialEmptyPage) {

View file

@ -43,31 +43,33 @@ export class FrameExecutionContext extends js.ExecutionContext {
return null;
}
async evaluateInternal<R>(pageFunction: js.Func0<R>): Promise<R>;
async evaluateInternal<Arg, R>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R>;
async evaluateInternal(pageFunction: never, ...args: never[]): Promise<any> {
async evaluate<Arg, R>(pageFunction: js.Func1<Arg, R>, arg?: Arg): Promise<R> {
return js.evaluate(this, true /* returnByValue */, pageFunction, arg);
}
async evaluateHandle<Arg, R>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>> {
return js.evaluate(this, false /* returnByValue */, pageFunction, arg);
}
async evaluateExpression(expression: string, isFunction: boolean | undefined, arg?: any): Promise<any> {
return js.evaluateExpression(this, true /* returnByValue */, expression, isFunction, arg);
}
async evaluateAndWaitForSignals<Arg, R>(pageFunction: js.Func1<Arg, R>, arg?: Arg): Promise<R> {
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
return js.evaluate(this, true /* returnByValue */, pageFunction, ...args);
return this.evaluate(pageFunction, arg);
});
}
async evaluateExpressionInternal(expression: string, isFunction: boolean | undefined, ...args: any[]): Promise<any> {
async evaluateExpressionAndWaitForSignals(expression: string, isFunction: boolean | undefined, arg?: any): Promise<any> {
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
return js.evaluateExpression(this, true /* returnByValue */, expression, isFunction, ...args);
return this.evaluateExpression(expression, isFunction, arg);
});
}
async evaluateHandleInternal<R>(pageFunction: js.Func0<R>): Promise<js.SmartHandle<R>>;
async evaluateHandleInternal<Arg, R>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
async evaluateHandleInternal(pageFunction: never, ...args: never[]): Promise<any> {
async evaluateExpressionHandleAndWaitForSignals(expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
return js.evaluate(this, false /* returnByValue */, pageFunction, ...args);
});
}
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);
return js.evaluateExpression(this, false /* returnByValue */, expression, isFunction, arg);
});
}
@ -124,19 +126,19 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this;
}
async _evaluateInMain<R, Arg>(pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], R>, arg: Arg): Promise<R> {
private async _evaluateInMainAndWaitForSignals<R, Arg>(pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], R>, arg: Arg): Promise<R> {
const main = await this._context.frame._mainContext();
return main.evaluateInternal(pageFunction, [await main.injectedScript(), this, arg]);
return main.evaluateAndWaitForSignals(pageFunction, [await main.injectedScript(), this, arg]);
}
async _evaluateInUtility<R, Arg>(pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], R>, arg: Arg): Promise<R> {
async evaluateInUtility<R, Arg>(pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], R>, arg: Arg): Promise<R> {
const utility = await this._context.frame._utilityContext();
return utility.evaluateInternal(pageFunction, [await utility.injectedScript(), this, arg]);
return utility.evaluate(pageFunction, [await utility.injectedScript(), this, arg]);
}
async _evaluateHandleInUtility<R, Arg>(pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], R>, arg: Arg): Promise<js.JSHandle<R>> {
async evaluateHandleInUtility<R, Arg>(pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], R>, arg: Arg): Promise<js.JSHandle<R>> {
const utility = await this._context.frame._utilityContext();
return utility.evaluateHandleInternal(pageFunction, [await utility.injectedScript(), this, arg]);
return utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]);
}
async ownerFrame(): Promise<frames.Frame | null> {
@ -155,14 +157,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
async contentFrame(): Promise<frames.Frame | null> {
const isFrameElement = await this._evaluateInUtility(([injected, node]) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {});
const isFrameElement = await this.evaluateInUtility(([injected, node]) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {});
if (!isFrameElement)
return null;
return this._page._delegate.getContentFrame(this);
}
async getAttribute(name: string): Promise<string | null> {
return throwFatalDOMError(await this._evaluateInUtility(([injeced, node, name]) => {
return throwFatalDOMError(await this.evaluateInUtility(([injeced, node, name]) => {
if (node.nodeType !== Node.ELEMENT_NODE)
return 'error:notelement';
const element = node as unknown as Element;
@ -171,11 +173,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
async textContent(): Promise<string | null> {
return this._evaluateInUtility(([injected, node]) => node.textContent, {});
return this.evaluateInUtility(([injected, node]) => node.textContent, {});
}
async innerText(): Promise<string> {
return throwFatalDOMError(await this._evaluateInUtility(([injected, node]) => {
return throwFatalDOMError(await this.evaluateInUtility(([injected, node]) => {
if (node.nodeType !== Node.ELEMENT_NODE)
return 'error:notelement';
if (node.namespaceURI !== 'http://www.w3.org/1999/xhtml')
@ -186,7 +188,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
async innerHTML(): Promise<string> {
return throwFatalDOMError(await this._evaluateInUtility(([injected, node]) => {
return throwFatalDOMError(await this.evaluateInUtility(([injected, node]) => {
if (node.nodeType !== Node.ELEMENT_NODE)
return 'error:notelement';
const element = node as unknown as Element;
@ -195,7 +197,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
async dispatchEvent(type: string, eventInit: Object = {}) {
await this._evaluateInMain(([injected, node, { type, eventInit }]) =>
await this._evaluateInMainAndWaitForSignals(([injected, node, { type, eventInit }]) =>
injected.dispatchEvent(node, type, eventInit), { type, eventInit });
await this._page._doSlowMo();
}
@ -246,7 +248,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const [quads, metrics] = await Promise.all([
this._page._delegate.getContentQuads(this),
this._page.mainFrame()._utilityContext().then(utility => utility.evaluateInternal(() => ({width: innerWidth, height: innerHeight}))),
this._page.mainFrame()._utilityContext().then(utility => utility.evaluate(() => ({width: innerWidth, height: innerHeight}))),
] as const);
if (!quads || !quads.length)
return 'error:notvisible';
@ -268,7 +270,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
private async _offsetPoint(offset: types.Point): Promise<types.Point | 'error:notvisible'> {
const [box, border] = await Promise.all([
this.boundingBox(),
this._evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch(e => {}),
this.evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch(e => {}),
]);
if (!box || !border)
return 'error:notvisible';
@ -302,7 +304,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const timeout = waitTime[Math.min(retry - 1, waitTime.length - 1)];
if (timeout) {
progress.log(` waiting ${timeout}ms`);
await this._evaluateInUtility(([injected, node, timeout]) => new Promise(f => setTimeout(f, timeout)), timeout);
await this.evaluateInUtility(([injected, node, timeout]) => new Promise(f => setTimeout(f, timeout)), timeout);
}
} else {
progress.log(`attempting ${actionName} action`);
@ -348,7 +350,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
progress.log(' scrolling into view if needed');
progress.throwIfAborted(); // Avoid action that has side-effects.
if (forceScrollOptions) {
await this._evaluateInUtility(([injected, node, options]) => {
await this.evaluateInUtility(([injected, node, options]) => {
if (node.nodeType === 1 /* Node.ELEMENT_NODE */)
(node as Node as Element).scrollIntoView(options);
}, forceScrollOptions);
@ -460,7 +462,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
progress.throwIfAborted(); // Avoid action that has side-effects.
progress.log(' selecting specified option(s)');
const poll = await this._evaluateHandleInUtility(([injected, node, optionsToSelect]) => {
const poll = await this.evaluateHandleInUtility(([injected, node, optionsToSelect]) => {
return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled'], injected.selectOptions.bind(injected, optionsToSelect));
}, optionsToSelect);
const pollHandler = new InjectedScriptPollHandler(progress, poll);
@ -483,7 +485,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
await progress.beforeInputAction(this);
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
progress.log(' waiting for element to be visible, enabled and editable');
const poll = await this._evaluateHandleInUtility(([injected, node, value]) => {
const poll = await this.evaluateHandleInUtility(([injected, node, value]) => {
return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled', 'editable'], injected.fill.bind(injected, value));
}, value);
const pollHandler = new InjectedScriptPollHandler(progress, poll);
@ -509,7 +511,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const controller = new ProgressController(metadata, this);
return controller.run(async progress => {
progress.throwIfAborted(); // Avoid action that has side-effects.
const poll = await this._evaluateHandleInUtility(([injected, node]) => {
const poll = await this.evaluateHandleInUtility(([injected, node]) => {
return injected.waitForElementStatesAndPerformAction(node, ['visible'], injected.selectText.bind(injected));
}, {});
const pollHandler = new InjectedScriptPollHandler(progress, poll);
@ -527,7 +529,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
async _setInputFiles(progress: Progress, files: types.FilePayload[], options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
const multiple = throwFatalDOMError(await this._evaluateInUtility(([injected, node]): 'error:notinput' | 'error:notconnected' | boolean => {
const multiple = throwFatalDOMError(await this.evaluateInUtility(([injected, node]): 'error:notinput' | 'error:notconnected' | boolean => {
if (node.nodeType !== Node.ELEMENT_NODE || (node as Node as Element).tagName !== 'INPUT')
return 'error:notinput';
const input = node as Node as HTMLInputElement;
@ -556,7 +558,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async _focus(progress: Progress, resetSelectionIfNotFocused?: boolean): Promise<'error:notconnected' | 'done'> {
progress.throwIfAborted(); // Avoid action that has side-effects.
const result = await this._evaluateInUtility(([injected, node, resetSelectionIfNotFocused]) => injected.focusNode(node, resetSelectionIfNotFocused), resetSelectionIfNotFocused);
const result = await this.evaluateInUtility(([injected, node, resetSelectionIfNotFocused]) => injected.focusNode(node, resetSelectionIfNotFocused), resetSelectionIfNotFocused);
return throwFatalDOMError(result);
}
@ -620,7 +622,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async _setChecked(progress: Progress, state: boolean, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
const isChecked = async () => {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'checked'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'checked'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
};
if (await isChecked() === state)
@ -652,49 +654,49 @@ 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 | undefined, 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}"`);
const result = await handle._evaluateExpression(expression, isFunction, true, arg);
const result = await handle.evaluateExpression(expression, isFunction, true, arg);
handle.dispose();
return result;
}
async _$$evalExpression(selector: string, expression: string, isFunction: boolean | undefined, 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);
const result = await arrayHandle.evaluateExpression(expression, isFunction, true, arg);
arrayHandle.dispose();
return result;
}
async isVisible(): Promise<boolean> {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'visible'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'visible'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
}
async isHidden(): Promise<boolean> {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'hidden'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'hidden'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
}
async isEnabled(): Promise<boolean> {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'enabled'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'enabled'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
}
async isDisabled(): Promise<boolean> {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'disabled'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'disabled'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
}
async isEditable(): Promise<boolean> {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'editable'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'editable'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
}
async isChecked(): Promise<boolean> {
const result = await this._evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'checked'), {});
const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'checked'), {});
return throwRetargetableDOMError(throwFatalDOMError(result));
}
@ -702,7 +704,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const controller = new ProgressController(metadata, this);
return controller.run(async progress => {
progress.log(` waiting for element to be ${state}`);
const poll = await this._evaluateHandleInUtility(([injected, node, state]) => {
const poll = await this.evaluateHandleInUtility(([injected, node, state]) => {
return injected.waitForElementStatesAndPerformAction(node, [state], () => 'done' as const);
}, state);
const pollHandler = new InjectedScriptPollHandler(progress, poll);
@ -746,7 +748,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
progress.log(` waiting for element to be visible, enabled and stable`);
else
progress.log(` waiting for element to be visible and stable`);
const poll = this._evaluateHandleInUtility(([injected, node, waitForEnabled]) => {
const poll = this.evaluateHandleInUtility(([injected, node, waitForEnabled]) => {
return injected.waitForElementStatesAndPerformAction(node,
waitForEnabled ? ['visible', 'stable', 'enabled'] : ['visible', 'stable'], () => 'done' as const);
}, waitForEnabled);
@ -769,7 +771,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
// Translate from viewport coordinates to frame coordinates.
point = { x: point.x - box.x, y: point.y - box.y };
}
return this._evaluateInUtility(([injected, node, point]) => injected.checkHitTargetAt(node, point), point);
return this.evaluateInUtility(([injected, node, point]) => injected.checkHitTargetAt(node, point), point);
}
}

View file

@ -389,7 +389,7 @@ export class FFPage implements PageDelegate {
async takeScreenshot(format: 'png' | 'jpeg', documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined): Promise<Buffer> {
if (!documentRect) {
const context = await this._page.mainFrame()._utilityContext();
const scrollOffset = await context.evaluateInternal(() => ({ x: window.scrollX, y: window.scrollY }));
const scrollOffset = await context.evaluate(() => ({ x: window.scrollX, y: window.scrollY }));
documentRect = {
x: viewportRect!.x + scrollOffset.x,
y: viewportRect!.y + scrollOffset.y,
@ -484,7 +484,7 @@ export class FFPage implements PageDelegate {
}
async setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void> {
await handle._evaluateInUtility(([injected, node, files]) =>
await handle.evaluateInUtility(([injected, node, files]) =>
injected.setInputFiles(node, files), files);
}

View file

@ -593,17 +593,25 @@ export class Frame extends SdkObject {
return this._context('utility');
}
async _evaluateExpressionHandle(expression: string, isFunction: boolean | undefined, arg: any, world: types.World = 'main'): Promise<any> {
async evaluateExpressionHandleAndWaitForSignals(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);
const handle = await context.evaluateExpressionHandleAndWaitForSignals(expression, isFunction, arg);
if (world === 'main')
await this._page._doSlowMo();
return handle;
}
async _evaluateExpression(expression: string, isFunction: boolean | undefined, 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);
const value = await context.evaluateExpression(expression, isFunction, arg);
if (world === 'main')
await this._page._doSlowMo();
return value;
}
async evaluateExpressionAndWaitForSignals(expression: string, isFunction: boolean | undefined, arg: any, world: types.World = 'main'): Promise<any> {
const context = await this._context(world);
const value = await context.evaluateExpressionAndWaitForSignals(expression, isFunction, arg);
if (world === 'main')
await this._page._doSlowMo();
return value;
@ -652,14 +660,14 @@ export class Frame extends SdkObject {
const handle = await this.$(selector);
if (!handle)
throw new Error(`Error: failed to find element matching selector "${selector}"`);
const result = await handle._evaluateExpression(expression, isFunction, true, arg);
const result = await handle.evaluateExpression(expression, isFunction, true, arg);
handle.dispose();
return result;
}
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);
const result = await arrayHandle.evaluateExpression(expression, isFunction, true, arg);
arrayHandle.dispose();
return result;
}
@ -670,7 +678,7 @@ export class Frame extends SdkObject {
async content(): Promise<string> {
const context = await this._utilityContext();
return context.evaluateInternal(() => {
return context.evaluate(() => {
let retVal = '';
if (document.doctype)
retVal = new XMLSerializer().serializeToString(document.doctype);
@ -694,7 +702,7 @@ export class Frame extends SdkObject {
this._waitForLoadState(progress, waitUntil).then(resolve).catch(reject);
});
});
const contentPromise = context.evaluateInternal(({ html, tag }) => {
const contentPromise = context.evaluate(({ html, tag }) => {
window.stop();
document.open();
console.debug(tag); // eslint-disable-line no-console
@ -738,12 +746,12 @@ export class Frame extends SdkObject {
const context = await this._mainContext();
return this._raceWithCSPError(async () => {
if (url !== null)
return (await context.evaluateHandleInternal(addScriptUrl, { url, type })).asElement()!;
const result = (await context.evaluateHandleInternal(addScriptContent, { content: content!, type })).asElement()!;
return (await context.evaluateHandle(addScriptUrl, { url, type })).asElement()!;
const result = (await context.evaluateHandle(addScriptContent, { content: content!, type })).asElement()!;
// Another round trip to the browser to ensure that we receive CSP error messages
// (if any) logged asynchronously in a separate task on the content main thread.
if (this._page._delegate.cspErrorsAsynchronousForInlineScipts)
await context.evaluateInternal(() => true);
await context.evaluate(() => true);
return result;
});
@ -785,8 +793,8 @@ export class Frame extends SdkObject {
const context = await this._mainContext();
return this._raceWithCSPError(async () => {
if (url !== null)
return (await context.evaluateHandleInternal(addStyleUrl, url)).asElement()!;
return (await context.evaluateHandleInternal(addStyleContent, content!)).asElement()!;
return (await context.evaluateHandle(addStyleUrl, url)).asElement()!;
return (await context.evaluateHandle(addStyleContent, content!)).asElement()!;
});
async function addStyleUrl(url: string): Promise<HTMLElement> {
@ -1075,7 +1083,7 @@ export class Frame extends SdkObject {
async title(): Promise<string> {
const context = await this._utilityContext();
return context.evaluateInternal(() => document.title);
return context.evaluate(() => document.title);
}
_onDetached() {

View file

@ -114,19 +114,15 @@ export class JSHandle<T = any> extends SdkObject {
this._context._delegate.rawCallFunctionNoReply(func, this, arg);
}
async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: FuncOn<T, void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<R> {
async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg?: Arg): Promise<R> {
return evaluate(this._context, true /* returnByValue */, pageFunction, this, arg);
}
async evaluateHandle<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: FuncOn<T, void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async evaluateHandle<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg?: Arg): Promise<SmartHandle<R>> {
return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
}
async _evaluateExpression(expression: string, isFunction: boolean | undefined, 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;

View file

@ -563,17 +563,17 @@ export class PageBinding {
const binding = page.getBinding(name, context.world)!;
let result: any;
if (binding.needsHandle) {
const handle = await context.evaluateHandleInternal(takeHandle, { name, seq }).catch(e => null);
const handle = await context.evaluateHandle(takeHandle, { name, seq }).catch(e => null);
result = await binding.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, handle);
} else {
result = await binding.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, ...args);
}
context.evaluateInternal(deliverResult, { name, seq, result }).catch(e => debugLogger.log('error', e));
context.evaluate(deliverResult, { name, seq, result }).catch(e => debugLogger.log('error', e));
} catch (error) {
if (isError(error))
context.evaluateInternal(deliverError, { name, seq, message: error.message, stack: error.stack }).catch(e => debugLogger.log('error', e));
context.evaluate(deliverError, { name, seq, message: error.message, stack: error.stack }).catch(e => debugLogger.log('error', e));
else
context.evaluateInternal(deliverErrorValue, { name, seq, error }).catch(e => debugLogger.log('error', e));
context.evaluate(deliverErrorValue, { name, seq, error }).catch(e => debugLogger.log('error', e));
}
function takeHandle(arg: { name: string, seq: number }) {

View file

@ -37,14 +37,14 @@ export class Screenshotter {
let viewportSize = originalViewportSize;
if (!viewportSize) {
const context = await this._page.mainFrame()._utilityContext();
viewportSize = await context.evaluateInternal(() => ({ width: window.innerWidth, height: window.innerHeight }));
viewportSize = await context.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight }));
}
return { viewportSize, originalViewportSize };
}
private async _fullPageSize(): Promise<types.Size> {
const context = await this._page.mainFrame()._utilityContext();
const fullPageSize = await context.evaluateInternal(() => {
const fullPageSize = await context.evaluate(() => {
if (!document.body || !document.documentElement)
return null;
return {
@ -125,7 +125,7 @@ export class Screenshotter {
}
const context = await this._page.mainFrame()._utilityContext();
const scrollOffset = await context.evaluateInternal(() => ({ x: window.scrollX, y: window.scrollY }));
const scrollOffset = await context.evaluate(() => ({ x: window.scrollX, y: window.scrollY }));
const documentRect = { ...boundingBox };
documentRect.x += scrollOffset.x;
documentRect.y += scrollOffset.y;

View file

@ -185,7 +185,7 @@ export class Snapshotter {
async function setIntervalInFrame(frame: Frame, interval: number) {
const context = frame._existingMainContext();
await context?.evaluateInternal(({ kSnapshotStreamer, interval }) => {
await context?.evaluate(({ kSnapshotStreamer, interval }) => {
(window as any)[kSnapshotStreamer].setSnapshotInterval(interval);
}, { kSnapshotStreamer, interval }).catch(debugExceptionHandler);
}
@ -197,7 +197,7 @@ async function annotateFrameHierarchy(frame: Frame) {
if (!parent)
return;
const context = await parent._mainContext();
await context?.evaluateInternal(({ kSnapshotStreamer, frameElement, frameId }) => {
await context?.evaluate(({ kSnapshotStreamer, frameElement, frameId }) => {
(window as any)[kSnapshotStreamer].markIframe(frameElement, frameId);
}, { kSnapshotStreamer, frameElement, frameId: frame.uniqueId });
frameElement.dispose();

View file

@ -91,7 +91,7 @@ class HarContextTracer {
page.on(Page.Events.Response, (response: network.Response) => this._onResponse(page, response));
page.on(Page.Events.DOMContentLoaded, () => {
const promise = page.mainFrame()._evaluateExpression(String(() => {
const promise = page.mainFrame().evaluateExpression(String(() => {
return {
title: document.title,
domContentLoaded: performance.timing.domContentLoadedEventStart,
@ -103,7 +103,7 @@ class HarContextTracer {
this._addBarrier(page, promise);
});
page.on(Page.Events.Load, () => {
const promise = page.mainFrame()._evaluateExpression(String(() => {
const promise = page.mainFrame().evaluateExpression(String(() => {
return {
title: document.title,
loaded: performance.timing.loadEventStart,
@ -118,7 +118,7 @@ class HarContextTracer {
private _addBarrier(page: Page, promise: Promise<void>) {
const race = Promise.race([
new Promise(f => page.on('close', () => {
new Promise<void>(f => page.on('close', () => {
this._barrierPromises.delete(race);
f();
})),

View file

@ -121,25 +121,25 @@ export class RecorderApp extends EventEmitter {
}
async setMode(mode: 'none' | 'recording' | 'inspecting'): Promise<void> {
await this._page.mainFrame()._evaluateExpression(((mode: Mode) => {
await this._page.mainFrame().evaluateExpression(((mode: Mode) => {
window.playwrightSetMode(mode);
}).toString(), true, mode, 'main').catch(() => {});
}
async setFile(file: string): Promise<void> {
await this._page.mainFrame()._evaluateExpression(((file: string) => {
await this._page.mainFrame().evaluateExpression(((file: string) => {
window.playwrightSetFile(file);
}).toString(), true, file, 'main').catch(() => {});
}
async setPaused(paused: boolean): Promise<void> {
await this._page.mainFrame()._evaluateExpression(((paused: boolean) => {
await this._page.mainFrame().evaluateExpression(((paused: boolean) => {
window.playwrightSetPaused(paused);
}).toString(), true, paused, 'main').catch(() => {});
}
async setSources(sources: Source[]): Promise<void> {
await this._page.mainFrame()._evaluateExpression(((sources: Source[]) => {
await this._page.mainFrame().evaluateExpression(((sources: Source[]) => {
window.playwrightSetSources(sources);
}).toString(), true, sources, 'main').catch(() => {});
@ -154,13 +154,13 @@ export class RecorderApp extends EventEmitter {
}
async setSelector(selector: string, focus?: boolean): Promise<void> {
await this._page.mainFrame()._evaluateExpression(((arg: any) => {
await this._page.mainFrame().evaluateExpression(((arg: any) => {
window.playwrightSetSelector(arg.selector, arg.focus);
}).toString(), true, { selector, focus }, 'main').catch(() => {});
}
async updateCallLogs(callLogs: CallLog[]): Promise<void> {
await this._page.mainFrame()._evaluateExpression(((callLogs: CallLog[]) => {
await this._page.mainFrame().evaluateExpression(((callLogs: CallLog[]) => {
window.playwrightUpdateLogs(callLogs);
}).toString(), true, callLogs, 'main').catch(() => {});
}

View file

@ -284,7 +284,7 @@ export class RecorderSupplement {
private _refreshOverlay() {
for (const page of this._context.pages())
page.mainFrame()._evaluateExpression('window._playwrightRefreshOverlay()', false, undefined, 'main').catch(() => {});
page.mainFrame().evaluateExpression('window._playwrightRefreshOverlay()', false, undefined, 'main').catch(() => {});
}
private async _onPage(page: Page) {

View file

@ -178,7 +178,7 @@ export class WKPage implements PageDelegate {
promises.push(WKPage._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme));
const bootstrapScript = this._calculateBootstrapScript();
promises.push(session.send('Page.setBootstrapScript', { source: bootstrapScript }));
this._page.frames().map(frame => frame._evaluateExpression(bootstrapScript, false, undefined, 'main').catch(e => {}));
this._page.frames().map(frame => frame.evaluateExpression(bootstrapScript, false, undefined, 'main').catch(e => {}));
if (contextOptions.bypassCSP)
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
if (this._page._state.viewportSize) {
@ -699,7 +699,7 @@ export class WKPage implements PageDelegate {
if (binding.world !== 'main')
throw new Error('Only main context bindings are supported in WebKit.');
const script = this._bindingToScript(binding);
await Promise.all(this._page.frames().map(frame => frame._evaluateExpression(script, false, {}).catch(e => {})));
await Promise.all(this._page.frames().map(frame => frame.evaluateExpression(script, false, {}).catch(e => {})));
}
async evaluateOnNewDocument(script: string): Promise<void> {

View file

@ -246,22 +246,22 @@ it('should work with internal bindings', (test, { mode, browserName }) => {
foo = arg;
}, 'utility');
expect(await page.evaluate('!!window.foo')).toBe(false);
expect(await implPage.mainFrame()._evaluateExpression('!!window.foo', false, {}, 'utility')).toBe(true);
expect(await implPage.mainFrame().evaluateExpression('!!window.foo', false, {}, 'utility')).toBe(true);
expect(foo).toBe(undefined);
await implPage.mainFrame()._evaluateExpression('window.foo(123)', false, {}, 'utility');
await implPage.mainFrame().evaluateExpression('window.foo(123)', false, {}, 'utility');
expect(foo).toBe(123);
// should work after reload
await page.goto(server.EMPTY_PAGE);
expect(await page.evaluate('!!window.foo')).toBe(false);
await implPage.mainFrame()._evaluateExpression('window.foo(456)', false, {}, 'utility');
await implPage.mainFrame().evaluateExpression('window.foo(456)', false, {}, 'utility');
expect(foo).toBe(456);
// should work inside frames
const frame = await attachFrame(page, 'myframe', server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(await frame.evaluate('!!window.foo')).toBe(false);
const implFrame: import('../src/server/frames').Frame = toImpl(frame);
await implFrame._evaluateExpression('window.foo(789)', false, {}, 'utility');
await implFrame.evaluateExpression('window.foo(789)', false, {}, 'utility');
expect(foo).toBe(789);
});