chore: add waitForSelector for BiDi (#10383)

This commit is contained in:
Nikolay Vitkov 2023-06-16 09:16:04 +02:00 committed by GitHub
parent 866addd132
commit d560299aa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 795 additions and 391 deletions

16
package-lock.json generated
View File

@ -3085,9 +3085,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/chromium-bidi": { "node_modules/chromium-bidi": {
"version": "0.4.11", "version": "0.4.12",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.11.tgz", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.12.tgz",
"integrity": "sha512-p03ajLhlQ5gebw3cmbDBFmBc2wnJM5dnXS8Phu6mblGn/KQd76yOVL5VwE0VAisa7oazNfKGTaXlIZ8Q5Bb9OA==", "integrity": "sha512-yl0ngMHtYUGJa2G0lkcbPvbnUZ9WMQyMNSfYmlrGD1nHRNyI9KOGw3dOaofFugXHHToneUaSmF9iUdgCBamCjA==",
"dependencies": { "dependencies": {
"mitt": "3.0.0" "mitt": "3.0.0"
}, },
@ -10164,7 +10164,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@puppeteer/browsers": "1.4.1", "@puppeteer/browsers": "1.4.1",
"chromium-bidi": "0.4.11", "chromium-bidi": "0.4.12",
"cross-fetch": "3.1.6", "cross-fetch": "3.1.6",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1135028", "devtools-protocol": "0.0.1135028",
@ -12317,9 +12317,9 @@
"version": "1.1.4" "version": "1.1.4"
}, },
"chromium-bidi": { "chromium-bidi": {
"version": "0.4.11", "version": "0.4.12",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.11.tgz", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.12.tgz",
"integrity": "sha512-p03ajLhlQ5gebw3cmbDBFmBc2wnJM5dnXS8Phu6mblGn/KQd76yOVL5VwE0VAisa7oazNfKGTaXlIZ8Q5Bb9OA==", "integrity": "sha512-yl0ngMHtYUGJa2G0lkcbPvbnUZ9WMQyMNSfYmlrGD1nHRNyI9KOGw3dOaofFugXHHToneUaSmF9iUdgCBamCjA==",
"requires": { "requires": {
"mitt": "3.0.0" "mitt": "3.0.0"
} }
@ -15738,7 +15738,7 @@
"version": "file:packages/puppeteer-core", "version": "file:packages/puppeteer-core",
"requires": { "requires": {
"@puppeteer/browsers": "1.4.1", "@puppeteer/browsers": "1.4.1",
"chromium-bidi": "0.4.11", "chromium-bidi": "0.4.12",
"cross-fetch": "3.1.6", "cross-fetch": "3.1.6",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1135028", "devtools-protocol": "0.0.1135028",

View File

@ -132,7 +132,7 @@
"author": "The Chromium Authors", "author": "The Chromium Authors",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"chromium-bidi": "0.4.11", "chromium-bidi": "0.4.12",
"cross-fetch": "3.1.6", "cross-fetch": "3.1.6",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1135028", "devtools-protocol": "0.0.1135028",

View File

@ -16,11 +16,13 @@
import {Protocol} from 'devtools-protocol'; import {Protocol} from 'devtools-protocol';
import {Frame} from '../api/Frame.js';
import {CDPSession} from '../common/Connection.js'; import {CDPSession} from '../common/Connection.js';
import {ExecutionContext} from '../common/ExecutionContext.js'; import {ExecutionContext} from '../common/ExecutionContext.js';
import {getQueryHandlerAndSelector} from '../common/GetQueryHandler.js'; import {getQueryHandlerAndSelector} from '../common/GetQueryHandler.js';
import {MouseClickOptions} from '../common/Input.js'; import {MouseClickOptions} from '../common/Input.js';
import {WaitForSelectorOptions} from '../common/IsolatedWorld.js'; import {WaitForSelectorOptions} from '../common/IsolatedWorld.js';
import {LazyArg} from '../common/LazyArg.js';
import { import {
ElementFor, ElementFor,
EvaluateFuncWith, EvaluateFuncWith,
@ -33,7 +35,6 @@ import {isString, withSourcePuppeteerURLIfNone} from '../common/util.js';
import {assert} from '../util/assert.js'; import {assert} from '../util/assert.js';
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js'; import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
import {Frame} from './Frame.js';
import {JSHandle} from './JSHandle.js'; import {JSHandle} from './JSHandle.js';
import {ScreenshotOptions} from './Page.js'; import {ScreenshotOptions} from './Page.js';
@ -485,12 +486,30 @@ export class ElementHandle<
)) as ElementHandle<NodeFor<Selector>> | null; )) as ElementHandle<NodeFor<Selector>> | null;
} }
async #checkVisibility(visibility: boolean): Promise<boolean> {
const element = await this.frame.isolatedRealm().adoptHandle(this);
try {
return await this.frame.isolatedRealm().evaluate(
async (PuppeteerUtil, element, visibility) => {
return Boolean(PuppeteerUtil.checkVisibility(element, visibility));
},
LazyArg.create(context => {
return context.puppeteerUtil;
}),
element,
visibility
);
} finally {
await element.dispose();
}
}
/** /**
* Checks if an element is visible using the same mechanism as * Checks if an element is visible using the same mechanism as
* {@link ElementHandle.waitForSelector}. * {@link ElementHandle.waitForSelector}.
*/ */
async isVisible(): Promise<boolean> { async isVisible(): Promise<boolean> {
throw new Error('Not implemented.'); return this.#checkVisibility(true);
} }
/** /**
@ -498,7 +517,7 @@ export class ElementHandle<
* {@link ElementHandle.waitForSelector}. * {@link ElementHandle.waitForSelector}.
*/ */
async isHidden(): Promise<boolean> { async isHidden(): Promise<boolean> {
throw new Error('Not implemented.'); return this.#checkVisibility(false);
} }
/** /**
@ -565,14 +584,16 @@ export class ElementHandle<
*/ */
async waitForXPath( async waitForXPath(
xpath: string, xpath: string,
options?: { options: {
visible?: boolean; visible?: boolean;
hidden?: boolean; hidden?: boolean;
timeout?: number; timeout?: number;
} = {}
): Promise<ElementHandle<Node> | null> {
if (xpath.startsWith('//')) {
xpath = `.${xpath}`;
} }
): Promise<ElementHandle<Node> | null>; return this.waitForSelector(`xpath/${xpath}`, options);
async waitForXPath(): Promise<ElementHandle<Node> | null> {
throw new Error('Not implemented');
} }
/** /**

View File

@ -20,6 +20,7 @@ import {Page, WaitTimeoutOptions} from '../api/Page.js';
import {CDPSession} from '../common/Connection.js'; import {CDPSession} from '../common/Connection.js';
import {DeviceRequestPrompt} from '../common/DeviceRequestPrompt.js'; import {DeviceRequestPrompt} from '../common/DeviceRequestPrompt.js';
import {ExecutionContext} from '../common/ExecutionContext.js'; import {ExecutionContext} from '../common/ExecutionContext.js';
import {getQueryHandlerAndSelector} from '../common/GetQueryHandler.js';
import { import {
IsolatedWorldChart, IsolatedWorldChart,
WaitForSelectorOptions, WaitForSelectorOptions,
@ -29,11 +30,52 @@ import {
EvaluateFunc, EvaluateFunc,
EvaluateFuncWith, EvaluateFuncWith,
HandleFor, HandleFor,
InnerLazyParams,
NodeFor, NodeFor,
} from '../common/types.js'; } from '../common/types.js';
import {TaskManager} from '../common/WaitTask.js';
import {JSHandle} from './JSHandle.js';
import {Locator} from './Locator.js'; import {Locator} from './Locator.js';
/**
* @internal
*/
export interface Realm {
taskManager: TaskManager;
waitForFunction<
Params extends unknown[],
Func extends EvaluateFunc<InnerLazyParams<Params>> = EvaluateFunc<
InnerLazyParams<Params>
>
>(
pageFunction: Func | string,
options: {
polling?: 'raf' | 'mutation' | number;
timeout?: number;
root?: ElementHandle<Node>;
signal?: AbortSignal;
},
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
adoptHandle<T extends JSHandle<Node>>(handle: T): Promise<T>;
transferHandle<T extends JSHandle<Node>>(handle: T): Promise<T>;
evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
}
/** /**
* @public * @public
*/ */
@ -307,6 +349,20 @@ export class Frame {
throw new Error('Not implemented'); throw new Error('Not implemented');
} }
/**
* @internal
*/
mainRealm(): Realm {
throw new Error('Not implemented');
}
/**
* @internal
*/
isolatedRealm(): Realm {
throw new Error('Not implemented');
}
/** /**
* Behaves identically to {@link Page.evaluateHandle} except it's run within * Behaves identically to {@link Page.evaluateHandle} except it's run within
* the context of this frame. * the context of this frame.
@ -529,12 +585,15 @@ export class Frame {
*/ */
async waitForSelector<Selector extends string>( async waitForSelector<Selector extends string>(
selector: Selector, selector: Selector,
options?: WaitForSelectorOptions options: WaitForSelectorOptions = {}
): Promise<ElementHandle<NodeFor<Selector>> | null>; ): Promise<ElementHandle<NodeFor<Selector>> | null> {
async waitForSelector<Selector extends string>(): Promise<ElementHandle< const {updatedSelector, QueryHandler} =
NodeFor<Selector> getQueryHandlerAndSelector(selector);
> | null> { return (await QueryHandler.waitFor(
throw new Error('Not implemented'); this,
updatedSelector,
options
)) as ElementHandle<NodeFor<Selector>> | null;
} }
/** /**
@ -561,10 +620,12 @@ export class Frame {
*/ */
async waitForXPath( async waitForXPath(
xpath: string, xpath: string,
options?: WaitForSelectorOptions options: WaitForSelectorOptions = {}
): Promise<ElementHandle<Node> | null>; ): Promise<ElementHandle<Node> | null> {
async waitForXPath(): Promise<ElementHandle<Node> | null> { if (xpath.startsWith('//')) {
throw new Error('Not implemented'); xpath = `.${xpath}`;
}
return this.waitForSelector(`xpath/${xpath}`, options);
} }
/** /**
@ -605,16 +666,15 @@ export class Frame {
Func extends EvaluateFunc<Params> = EvaluateFunc<Params> Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>( >(
pageFunction: Func | string, pageFunction: Func | string,
options?: FrameWaitForFunctionOptions, options: FrameWaitForFunctionOptions = {},
...args: Params ...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>>; ): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
waitForFunction< return this.mainRealm().waitForFunction(
Params extends unknown[], pageFunction,
Func extends EvaluateFunc<Params> = EvaluateFunc<Params> options,
>(): Promise<HandleFor<Awaited<ReturnType<Func>>>> { ...args
throw new Error('Not implemented'); ) as Promise<HandleFor<Awaited<ReturnType<Func>>>>;
} }
/** /**
* The full HTML contents of the frame, including the DOCTYPE. * The full HTML contents of the frame, including the DOCTYPE.
*/ */

View File

@ -33,9 +33,7 @@ import {ExecutionContext} from './ExecutionContext.js';
import {Frame} from './Frame.js'; import {Frame} from './Frame.js';
import {FrameManager} from './FrameManager.js'; import {FrameManager} from './FrameManager.js';
import {WaitForSelectorOptions} from './IsolatedWorld.js'; import {WaitForSelectorOptions} from './IsolatedWorld.js';
import {PUPPETEER_WORLD} from './IsolatedWorlds.js';
import {CDPJSHandle} from './JSHandle.js'; import {CDPJSHandle} from './JSHandle.js';
import {LazyArg} from './LazyArg.js';
import {CDPPage} from './Page.js'; import {CDPPage} from './Page.js';
import {NodeFor} from './types.js'; import {NodeFor} from './types.js';
import {KeyInput} from './USKeyboardLayout.js'; import {KeyInput} from './USKeyboardLayout.js';
@ -128,46 +126,6 @@ export class CDPElementHandle<
> | null; > | null;
} }
override async waitForXPath(
xpath: string,
options: {
visible?: boolean;
hidden?: boolean;
timeout?: number;
} = {}
): Promise<CDPElementHandle<Node> | null> {
if (xpath.startsWith('//')) {
xpath = `.${xpath}`;
}
return this.waitForSelector(`xpath/${xpath}`, options);
}
async #checkVisibility(visibility: boolean): Promise<boolean> {
const element = await this.frame.worlds[PUPPETEER_WORLD].adoptHandle(this);
try {
return await this.frame.worlds[PUPPETEER_WORLD].evaluate(
async (PuppeteerUtil, element, visibility) => {
return Boolean(PuppeteerUtil.checkVisibility(element, visibility));
},
LazyArg.create(context => {
return context.puppeteerUtil;
}),
element,
visibility
);
} finally {
await element.dispose();
}
}
override async isVisible(): Promise<boolean> {
return this.#checkVisibility(true);
}
override async isHidden(): Promise<boolean> {
return this.#checkVisibility(false);
}
override async contentFrame(): Promise<Frame | null> { override async contentFrame(): Promise<Frame | null> {
const nodeInfo = await this.client.send('DOM.describeNode', { const nodeInfo = await this.client.send('DOM.describeNode', {
objectId: this.id, objectId: this.id,

View File

@ -21,7 +21,6 @@ import {
Frame as BaseFrame, Frame as BaseFrame,
FrameAddScriptTagOptions, FrameAddScriptTagOptions,
FrameAddStyleTagOptions, FrameAddStyleTagOptions,
FrameWaitForFunctionOptions,
} from '../api/Frame.js'; } from '../api/Frame.js';
import {HTTPResponse} from '../api/HTTPResponse.js'; import {HTTPResponse} from '../api/HTTPResponse.js';
import {Page, WaitTimeoutOptions} from '../api/Page.js'; import {Page, WaitTimeoutOptions} from '../api/Page.js';
@ -36,12 +35,7 @@ import {
} from './DeviceRequestPrompt.js'; } from './DeviceRequestPrompt.js';
import {ExecutionContext} from './ExecutionContext.js'; import {ExecutionContext} from './ExecutionContext.js';
import {FrameManager} from './FrameManager.js'; import {FrameManager} from './FrameManager.js';
import {getQueryHandlerAndSelector} from './GetQueryHandler.js'; import {IsolatedWorld, IsolatedWorldChart} from './IsolatedWorld.js';
import {
IsolatedWorld,
IsolatedWorldChart,
WaitForSelectorOptions,
} from './IsolatedWorld.js';
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js'; import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
import {LazyArg} from './LazyArg.js'; import {LazyArg} from './LazyArg.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js'; import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
@ -221,6 +215,20 @@ export class Frame extends BaseFrame {
return this.worlds[MAIN_WORLD].executionContext(); return this.worlds[MAIN_WORLD].executionContext();
} }
/**
* @internal
*/
override mainRealm(): IsolatedWorld {
return this.worlds[MAIN_WORLD];
}
/**
* @internal
*/
override isolatedRealm(): IsolatedWorld {
return this.worlds[PUPPETEER_WORLD];
}
override async evaluateHandle< override async evaluateHandle<
Params extends unknown[], Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params> Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
@ -232,7 +240,7 @@ export class Frame extends BaseFrame {
this.evaluateHandle.name, this.evaluateHandle.name,
pageFunction pageFunction
); );
return this.worlds[MAIN_WORLD].evaluateHandle(pageFunction, ...args); return this.mainRealm().evaluateHandle(pageFunction, ...args);
} }
override async evaluate< override async evaluate<
@ -246,19 +254,19 @@ export class Frame extends BaseFrame {
this.evaluate.name, this.evaluate.name,
pageFunction pageFunction
); );
return this.worlds[MAIN_WORLD].evaluate(pageFunction, ...args); return this.mainRealm().evaluate(pageFunction, ...args);
} }
override async $<Selector extends string>( override async $<Selector extends string>(
selector: Selector selector: Selector
): Promise<ElementHandle<NodeFor<Selector>> | null> { ): Promise<ElementHandle<NodeFor<Selector>> | null> {
return this.worlds[MAIN_WORLD].$(selector); return this.mainRealm().$(selector);
} }
override async $$<Selector extends string>( override async $$<Selector extends string>(
selector: Selector selector: Selector
): Promise<Array<ElementHandle<NodeFor<Selector>>>> { ): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
return this.worlds[MAIN_WORLD].$$(selector); return this.mainRealm().$$(selector);
} }
override async $eval< override async $eval<
@ -274,7 +282,7 @@ export class Frame extends BaseFrame {
...args: Params ...args: Params
): Promise<Awaited<ReturnType<Func>>> { ): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction); pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
return this.worlds[MAIN_WORLD].$eval(selector, pageFunction, ...args); return this.mainRealm().$eval(selector, pageFunction, ...args);
} }
override async $$eval< override async $$eval<
@ -290,53 +298,15 @@ export class Frame extends BaseFrame {
...args: Params ...args: Params
): Promise<Awaited<ReturnType<Func>>> { ): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction); pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
return this.worlds[MAIN_WORLD].$$eval(selector, pageFunction, ...args); return this.mainRealm().$$eval(selector, pageFunction, ...args);
} }
override async $x(expression: string): Promise<Array<ElementHandle<Node>>> { override async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
return this.worlds[MAIN_WORLD].$x(expression); return this.mainRealm().$x(expression);
}
override async waitForSelector<Selector extends string>(
selector: Selector,
options: WaitForSelectorOptions = {}
): Promise<ElementHandle<NodeFor<Selector>> | null> {
const {updatedSelector, QueryHandler} =
getQueryHandlerAndSelector(selector);
return (await QueryHandler.waitFor(
this,
updatedSelector,
options
)) as ElementHandle<NodeFor<Selector>> | null;
}
override async waitForXPath(
xpath: string,
options: WaitForSelectorOptions = {}
): Promise<ElementHandle<Node> | null> {
if (xpath.startsWith('//')) {
xpath = `.${xpath}`;
}
return this.waitForSelector(`xpath/${xpath}`, options);
}
override waitForFunction<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
options: FrameWaitForFunctionOptions = {},
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
return this.worlds[MAIN_WORLD].waitForFunction(
pageFunction,
options,
...args
) as Promise<HandleFor<Awaited<ReturnType<Func>>>>;
} }
override async content(): Promise<string> { override async content(): Promise<string> {
return this.worlds[PUPPETEER_WORLD].content(); return this.isolatedRealm().content();
} }
override async setContent( override async setContent(
@ -346,7 +316,7 @@ export class Frame extends BaseFrame {
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
} = {} } = {}
): Promise<void> { ): Promise<void> {
return this.worlds[PUPPETEER_WORLD].setContent(html, options); return this.isolatedRealm().setContent(html, options);
} }
override name(): string { override name(): string {
@ -388,8 +358,8 @@ export class Frame extends BaseFrame {
type = type ?? 'text/javascript'; type = type ?? 'text/javascript';
return this.worlds[MAIN_WORLD].transferHandle( return this.mainRealm().transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle( await this.isolatedRealm().evaluateHandle(
async ({Deferred}, {url, id, type, content}) => { async ({Deferred}, {url, id, type, content}) => {
const deferred = Deferred.create<void>(); const deferred = Deferred.create<void>();
const script = document.createElement('script'); const script = document.createElement('script');
@ -456,8 +426,8 @@ export class Frame extends BaseFrame {
options.content = content; options.content = content;
} }
return this.worlds[MAIN_WORLD].transferHandle( return this.mainRealm().transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle( await this.isolatedRealm().evaluateHandle(
async ({Deferred}, {url, content}) => { async ({Deferred}, {url, content}) => {
const deferred = Deferred.create<void>(); const deferred = Deferred.create<void>();
let element: HTMLStyleElement | HTMLLinkElement; let element: HTMLStyleElement | HTMLLinkElement;
@ -504,23 +474,23 @@ export class Frame extends BaseFrame {
selector: string, selector: string,
options: Readonly<ClickOptions> = {} options: Readonly<ClickOptions> = {}
): Promise<void> { ): Promise<void> {
return this.worlds[PUPPETEER_WORLD].click(selector, options); return this.isolatedRealm().click(selector, options);
} }
override async focus(selector: string): Promise<void> { override async focus(selector: string): Promise<void> {
return this.worlds[PUPPETEER_WORLD].focus(selector); return this.isolatedRealm().focus(selector);
} }
override async hover(selector: string): Promise<void> { override async hover(selector: string): Promise<void> {
return this.worlds[PUPPETEER_WORLD].hover(selector); return this.isolatedRealm().hover(selector);
} }
override select(selector: string, ...values: string[]): Promise<string[]> { override select(selector: string, ...values: string[]): Promise<string[]> {
return this.worlds[PUPPETEER_WORLD].select(selector, ...values); return this.isolatedRealm().select(selector, ...values);
} }
override async tap(selector: string): Promise<void> { override async tap(selector: string): Promise<void> {
return this.worlds[PUPPETEER_WORLD].tap(selector); return this.isolatedRealm().tap(selector);
} }
override async type( override async type(
@ -528,11 +498,11 @@ export class Frame extends BaseFrame {
text: string, text: string,
options?: {delay: number} options?: {delay: number}
): Promise<void> { ): Promise<void> {
return this.worlds[PUPPETEER_WORLD].type(selector, text, options); return this.isolatedRealm().type(selector, text, options);
} }
override async title(): Promise<string> { override async title(): Promise<string> {
return this.worlds[PUPPETEER_WORLD].title(); return this.isolatedRealm().title();
} }
_deviceRequestPromptManager(): DeviceRequestPromptManager { _deviceRequestPromptManager(): DeviceRequestPromptManager {

View File

@ -34,7 +34,10 @@ import {Target} from './Target.js';
import {TimeoutSettings} from './TimeoutSettings.js'; import {TimeoutSettings} from './TimeoutSettings.js';
import {debugError, PuppeteerURL} from './util.js'; import {debugError, PuppeteerURL} from './util.js';
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; /**
* @internal
*/
export const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
/** /**
* We use symbols to prevent external parties listening to these events. * We use symbols to prevent external parties listening to these events.

View File

@ -17,6 +17,7 @@
import {Protocol} from 'devtools-protocol'; import {Protocol} from 'devtools-protocol';
import type {ClickOptions, ElementHandle} from '../api/ElementHandle.js'; import type {ClickOptions, ElementHandle} from '../api/ElementHandle.js';
import {Realm} from '../api/Frame.js';
import {JSHandle} from '../api/JSHandle.js'; import {JSHandle} from '../api/JSHandle.js';
import {assert} from '../util/assert.js'; import {assert} from '../util/assert.js';
import {Deferred} from '../util/Deferred.js'; import {Deferred} from '../util/Deferred.js';
@ -99,7 +100,7 @@ export interface IsolatedWorldChart {
/** /**
* @internal * @internal
*/ */
export class IsolatedWorld { export class IsolatedWorld implements Realm {
#frame: Frame; #frame: Frame;
#document?: ElementHandle<Document>; #document?: ElementHandle<Document>;
#context = Deferred.create<ExecutionContext>(); #context = Deferred.create<ExecutionContext>();

View File

@ -22,7 +22,6 @@ import {interpolateFunction, stringifyFunction} from '../util/Function.js';
import {transposeIterableHandle} from './HandleIterator.js'; import {transposeIterableHandle} from './HandleIterator.js';
import type {WaitForSelectorOptions} from './IsolatedWorld.js'; import type {WaitForSelectorOptions} from './IsolatedWorld.js';
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
import {LazyArg} from './LazyArg.js'; import {LazyArg} from './LazyArg.js';
import type {Awaitable, AwaitableIterable} from './types.js'; import type {Awaitable, AwaitableIterable} from './types.js';
@ -160,7 +159,7 @@ export class QueryHandler {
frame = elementOrFrame; frame = elementOrFrame;
} else { } else {
frame = elementOrFrame.frame; frame = elementOrFrame.frame;
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame); element = await frame.isolatedRealm().adoptHandle(elementOrFrame);
} }
const {visible = false, hidden = false, timeout, signal} = options; const {visible = false, hidden = false, timeout, signal} = options;
@ -168,7 +167,7 @@ export class QueryHandler {
try { try {
signal?.throwIfAborted(); signal?.throwIfAborted();
const handle = await frame.worlds[PUPPETEER_WORLD].waitForFunction( const handle = await frame.isolatedRealm().waitForFunction(
async (PuppeteerUtil, query, selector, root, visible) => { async (PuppeteerUtil, query, selector, root, visible) => {
const querySelector = PuppeteerUtil.createFunction( const querySelector = PuppeteerUtil.createFunction(
query query
@ -204,7 +203,7 @@ export class QueryHandler {
await handle.dispose(); await handle.dispose();
return null; return null;
} }
return frame.worlds[MAIN_WORLD].transferHandle(handle); return frame.mainRealm().transferHandle(handle);
} catch (error) { } catch (error) {
if (!isErrorLike(error)) { if (!isErrorLike(error)) {
throw error; throw error;

View File

@ -15,6 +15,7 @@
*/ */
import {ElementHandle} from '../api/ElementHandle.js'; import {ElementHandle} from '../api/ElementHandle.js';
import {Realm} from '../api/Frame.js';
import {JSHandle} from '../api/JSHandle.js'; import {JSHandle} from '../api/JSHandle.js';
import type {Poller} from '../injected/Poller.js'; import type {Poller} from '../injected/Poller.js';
import {Deferred} from '../util/Deferred.js'; import {Deferred} from '../util/Deferred.js';
@ -22,7 +23,6 @@ import {isErrorLike} from '../util/ErrorLike.js';
import {stringifyFunction} from '../util/Function.js'; import {stringifyFunction} from '../util/Function.js';
import {TimeoutError} from './Errors.js'; import {TimeoutError} from './Errors.js';
import {IsolatedWorld} from './IsolatedWorld.js';
import {LazyArg} from './LazyArg.js'; import {LazyArg} from './LazyArg.js';
import {HandleFor} from './types.js'; import {HandleFor} from './types.js';
@ -40,7 +40,7 @@ export interface WaitTaskOptions {
* @internal * @internal
*/ */
export class WaitTask<T = unknown> { export class WaitTask<T = unknown> {
#world: IsolatedWorld; #world: Realm;
#polling: 'raf' | 'mutation' | number; #polling: 'raf' | 'mutation' | number;
#root?: ElementHandle<Node>; #root?: ElementHandle<Node>;
@ -55,7 +55,7 @@ export class WaitTask<T = unknown> {
#signal?: AbortSignal; #signal?: AbortSignal;
constructor( constructor(
world: IsolatedWorld, world: Realm,
options: WaitTaskOptions, options: WaitTaskOptions,
fn: ((...args: unknown[]) => Promise<T>) | string, fn: ((...args: unknown[]) => Promise<T>) | string,
...args: unknown[] ...args: unknown[]

View File

@ -2,37 +2,17 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import ProtocolMapping from 'devtools-protocol/types/protocol-mapping.js'; import ProtocolMapping from 'devtools-protocol/types/protocol-mapping.js';
import {WaitForOptions} from '../../api/Page.js'; import {WaitForOptions} from '../../api/Page.js';
import PuppeteerUtil from '../../injected/injected.js';
import {assert} from '../../util/assert.js'; import {assert} from '../../util/assert.js';
import {Deferred} from '../../util/Deferred.js'; import {Deferred} from '../../util/Deferred.js';
import {stringifyFunction} from '../../util/Function.js';
import type {CDPSession, Connection as CDPConnection} from '../Connection.js'; import type {CDPSession, Connection as CDPConnection} from '../Connection.js';
import {ProtocolError, TimeoutError} from '../Errors.js'; import {ProtocolError, TimeoutError} from '../Errors.js';
import {EventEmitter} from '../EventEmitter.js'; import {EventEmitter} from '../EventEmitter.js';
import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js'; import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js';
import {scriptInjector} from '../ScriptInjector.js';
import {TimeoutSettings} from '../TimeoutSettings.js'; import {TimeoutSettings} from '../TimeoutSettings.js';
import {EvaluateFunc, HandleFor} from '../types.js'; import {getPageContent, setPageContent, waitWithTimeout} from '../util.js';
import {
PuppeteerURL,
getPageContent,
getSourcePuppeteerURLIfAvailable,
isString,
setPageContent,
waitWithTimeout,
} from '../util.js';
import {Connection} from './Connection.js'; import {Connection} from './Connection.js';
import {ElementHandle} from './ElementHandle.js'; import {Realm} from './Realm.js';
import {JSHandle} from './JSHandle.js';
import {BidiSerializer} from './Serializer.js';
import {createEvaluationError} from './utils.js';
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
const getSourceUrlComment = (url: string) => {
return `//# sourceURL=${url}`;
};
/** /**
* @internal * @internal
@ -104,8 +84,7 @@ export class CDPSessionWrapper extends EventEmitter implements CDPSession {
/** /**
* @internal * @internal
*/ */
export class BrowsingContext extends EventEmitter { export class BrowsingContext extends Realm {
connection: Connection;
#timeoutSettings: TimeoutSettings; #timeoutSettings: TimeoutSettings;
#id: string; #id: string;
#url = 'about:blank'; #url = 'about:blank';
@ -116,27 +95,15 @@ export class BrowsingContext extends EventEmitter {
timeoutSettings: TimeoutSettings, timeoutSettings: TimeoutSettings,
info: Bidi.BrowsingContext.Info info: Bidi.BrowsingContext.Info
) { ) {
super(); super(connection, info.context);
this.connection = connection; this.connection = connection;
this.#timeoutSettings = timeoutSettings; this.#timeoutSettings = timeoutSettings;
this.#id = info.context; this.#id = info.context;
this.#cdpSession = new CDPSessionWrapper(this); this.#cdpSession = new CDPSessionWrapper(this);
} }
#puppeteerUtil?: Promise<JSHandle<PuppeteerUtil>>; createSandboxRealm(sandbox: string): Realm {
get puppeteerUtil(): Promise<JSHandle<PuppeteerUtil>> { return new Realm(this.connection, this.#id, sandbox);
const promise = Promise.resolve() as Promise<unknown>;
scriptInjector.inject(script => {
if (this.#puppeteerUtil) {
void this.#puppeteerUtil.then(handle => {
void handle.dispose();
});
}
this.#puppeteerUtil = promise.then(() => {
return this.evaluateHandle(script) as Promise<JSHandle<PuppeteerUtil>>;
});
}, !this.#puppeteerUtil);
return this.#puppeteerUtil as Promise<JSHandle<PuppeteerUtil>>;
} }
get url(): string { get url(): string {
@ -212,97 +179,6 @@ export class BrowsingContext extends EventEmitter {
); );
} }
async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
return this.#evaluate(false, pageFunction, ...args);
}
async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
return this.#evaluate(true, pageFunction, ...args);
}
async #evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
returnByValue: true,
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
async #evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
returnByValue: false,
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
async #evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
returnByValue: boolean,
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>> | Awaited<ReturnType<Func>>> {
const sourceUrlComment = getSourceUrlComment(
getSourcePuppeteerURLIfAvailable(pageFunction)?.toString() ??
PuppeteerURL.INTERNAL_URL
);
let responsePromise;
const resultOwnership = returnByValue ? 'none' : 'root';
if (isString(pageFunction)) {
const expression = SOURCE_URL_REGEX.test(pageFunction)
? pageFunction
: `${pageFunction}\n${sourceUrlComment}\n`;
responsePromise = this.connection.send('script.evaluate', {
expression,
target: {context: this.#id},
resultOwnership,
awaitPromise: true,
});
} else {
let functionDeclaration = stringifyFunction(pageFunction);
functionDeclaration = SOURCE_URL_REGEX.test(functionDeclaration)
? functionDeclaration
: `${functionDeclaration}\n${sourceUrlComment}\n`;
responsePromise = this.connection.send('script.callFunction', {
functionDeclaration,
arguments: await Promise.all(
args.map(arg => {
return BidiSerializer.serialize(arg, this);
})
),
target: {context: this.#id},
resultOwnership,
awaitPromise: true,
});
}
const {result} = await responsePromise;
if ('type' in result && result.type === 'exception') {
throw createEvaluationError(result.exceptionDetails);
}
return returnByValue
? BidiSerializer.deserialize(result.result)
: getBidiHandle(this, result.result);
}
async setContent( async setContent(
html: string, html: string,
options: { options: {
@ -344,29 +220,16 @@ export class BrowsingContext extends EventEmitter {
return this.#cdpSession.send(method, ...paramArgs); return this.#cdpSession.send(method, ...paramArgs);
} }
dispose(): void {
this.removeAllListeners();
this.connection.unregisterBrowsingContexts(this.#id);
}
title(): Promise<string> { title(): Promise<string> {
return this.evaluate(() => { return this.evaluate(() => {
return document.title; return document.title;
}); });
} }
}
/** dispose(): void {
* @internal this.removeAllListeners();
*/ this.connection.unregisterBrowsingContexts(this.#id);
export function getBidiHandle(
context: BrowsingContext,
result: Bidi.CommonDataTypes.RemoteValue
): JSHandle | ElementHandle<Node> {
if (result.type === 'node' || result.type === 'window') {
return new ElementHandle(context, result);
} }
return new JSHandle(context, result);
} }
/** /**

View File

@ -65,6 +65,10 @@ interface Commands {
params: Bidi.Script.DisownParameters; params: Bidi.Script.DisownParameters;
returnType: Bidi.Script.DisownResult; returnType: Bidi.Script.DisownResult;
}; };
'script.addPreloadScript': {
params: Bidi.Script.AddPreloadScriptParameters;
returnType: Bidi.Script.AddPreloadScriptResult;
};
'browsingContext.create': { 'browsingContext.create': {
params: Bidi.BrowsingContext.CreateParameters; params: Bidi.BrowsingContext.CreateParameters;
@ -116,11 +120,11 @@ interface Commands {
}; };
'session.subscribe': { 'session.subscribe': {
params: Bidi.Session.SubscriptionRequest; params: Bidi.Session.SubscriptionRequest;
returnType: Bidi.Session.SubscribeResult; returnType: Bidi.Message.EmptyResult;
}; };
'session.unsubscribe': { 'session.unsubscribe': {
params: Bidi.Session.SubscriptionRequest; params: Bidi.Session.SubscriptionRequest;
returnType: Bidi.Session.UnsubscribeResult; returnType: Bidi.Message.EmptyResult;
}; };
'cdp.sendCommand': { 'cdp.sendCommand': {
params: Bidi.CDP.SendCommandParams; params: Bidi.CDP.SendCommandParams;

View File

@ -18,8 +18,9 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {ElementHandle as BaseElementHandle} from '../../api/ElementHandle.js'; import {ElementHandle as BaseElementHandle} from '../../api/ElementHandle.js';
import {BrowsingContext} from './BrowsingContext.js'; import {Frame} from './Frame.js';
import {JSHandle} from './JSHandle.js'; import {JSHandle} from './JSHandle.js';
import {Realm} from './Realm.js';
/** /**
* @internal * @internal
@ -28,15 +29,22 @@ export class ElementHandle<
ElementType extends Node = Element ElementType extends Node = Element
> extends BaseElementHandle<ElementType> { > extends BaseElementHandle<ElementType> {
declare handle: JSHandle<ElementType>; declare handle: JSHandle<ElementType>;
#frame: Frame;
constructor( constructor(
context: BrowsingContext, realm: Realm,
remoteValue: Bidi.CommonDataTypes.RemoteValue remoteValue: Bidi.CommonDataTypes.RemoteValue,
frame: Frame
) { ) {
super(new JSHandle(context, remoteValue)); super(new JSHandle(realm, remoteValue));
this.#frame = frame;
} }
context(): BrowsingContext { override get frame(): Frame {
return this.#frame;
}
context(): Realm {
return this.handle.context(); return this.handle.context();
} }

View File

@ -16,7 +16,9 @@
import {ElementHandle} from '../../api/ElementHandle.js'; import {ElementHandle} from '../../api/ElementHandle.js';
import {Frame as BaseFrame} from '../../api/Frame.js'; import {Frame as BaseFrame} from '../../api/Frame.js';
import {UTILITY_WORLD_NAME} from '../FrameManager.js';
import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js'; import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js';
import {TimeoutSettings} from '../TimeoutSettings.js';
import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from '../types.js'; import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from '../types.js';
import {withSourcePuppeteerURLIfNone} from '../util.js'; import {withSourcePuppeteerURLIfNone} from '../util.js';
@ -40,7 +42,12 @@ export class Frame extends BaseFrame {
sandboxes: SandboxChart; sandboxes: SandboxChart;
override _id: string; override _id: string;
constructor(page: Page, context: BrowsingContext, parentId?: string | null) { constructor(
page: Page,
context: BrowsingContext,
timeoutSettings: TimeoutSettings,
parentId?: string | null
) {
super(); super();
this.#page = page; this.#page = page;
this.#context = context; this.#context = context;
@ -48,11 +55,22 @@ export class Frame extends BaseFrame {
this._parentId = parentId ?? undefined; this._parentId = parentId ?? undefined;
this.sandboxes = { this.sandboxes = {
[MAIN_SANDBOX]: new Sandbox(context), [MAIN_SANDBOX]: new Sandbox(context, timeoutSettings),
[PUPPETEER_SANDBOX]: new Sandbox(context), [PUPPETEER_SANDBOX]: new Sandbox(
context.createSandboxRealm(UTILITY_WORLD_NAME),
timeoutSettings
),
}; };
} }
override mainRealm(): Sandbox {
return this.sandboxes[MAIN_SANDBOX];
}
override isolatedRealm(): Sandbox {
return this.sandboxes[PUPPETEER_SANDBOX];
}
override page(): Page { override page(): Page {
return this.#page; return this.#page;
} }

View File

@ -21,26 +21,23 @@ import {JSHandle as BaseJSHandle} from '../../api/JSHandle.js';
import {EvaluateFuncWith, HandleFor, HandleOr} from '../../common/types.js'; import {EvaluateFuncWith, HandleFor, HandleOr} from '../../common/types.js';
import {withSourcePuppeteerURLIfNone} from '../util.js'; import {withSourcePuppeteerURLIfNone} from '../util.js';
import {BrowsingContext} from './BrowsingContext.js'; import {Realm} from './Realm.js';
import {BidiSerializer} from './Serializer.js'; import {BidiSerializer} from './Serializer.js';
import {releaseReference} from './utils.js'; import {releaseReference} from './utils.js';
export class JSHandle<T = unknown> extends BaseJSHandle<T> { export class JSHandle<T = unknown> extends BaseJSHandle<T> {
#disposed = false; #disposed = false;
#context; #realm: Realm;
#remoteValue; #remoteValue;
constructor( constructor(realm: Realm, remoteValue: Bidi.CommonDataTypes.RemoteValue) {
context: BrowsingContext,
remoteValue: Bidi.CommonDataTypes.RemoteValue
) {
super(); super();
this.#context = context; this.#realm = realm;
this.#remoteValue = remoteValue; this.#remoteValue = remoteValue;
} }
context(): BrowsingContext { context(): Realm {
return this.#context; return this.#realm;
} }
override get disposed(): boolean { override get disposed(): boolean {
@ -136,7 +133,7 @@ export class JSHandle<T = unknown> extends BaseJSHandle<T> {
} }
this.#disposed = true; this.#disposed = true;
if ('handle' in this.#remoteValue) { if ('handle' in this.#remoteValue) {
await releaseReference(this.#context, this.#remoteValue); await releaseReference(this.#realm, this.#remoteValue);
} }
} }

View File

@ -53,12 +53,13 @@ import {
import {Browser} from './Browser.js'; import {Browser} from './Browser.js';
import {BrowserContext} from './BrowserContext.js'; import {BrowserContext} from './BrowserContext.js';
import {BrowsingContext, getBidiHandle} from './BrowsingContext.js'; import {BrowsingContext} from './BrowsingContext.js';
import {Connection} from './Connection.js'; import {Connection} from './Connection.js';
import {Frame} from './Frame.js'; import {Frame} from './Frame.js';
import {HTTPRequest} from './HTTPRequest.js'; import {HTTPRequest} from './HTTPRequest.js';
import {HTTPResponse} from './HTTPResponse.js'; import {HTTPResponse} from './HTTPResponse.js';
import {NetworkManager} from './NetworkManager.js'; import {NetworkManager} from './NetworkManager.js';
import {getBidiHandle} from './Realm.js';
import {BidiSerializer} from './Serializer.js'; import {BidiSerializer} from './Serializer.js';
/** /**
@ -208,7 +209,13 @@ export class Page extends PageBase {
); );
this.#connection.registerBrowsingContexts(context); this.#connection.registerBrowsingContexts(context);
const frame = new Frame(this, context, info.parent); const frame = new Frame(
this,
context,
this.#timeoutSettings,
info.parent
);
context.setFrame(frame);
this.#frameTree.addFrame(frame); this.#frameTree.addFrame(frame);
this.emit(FrameManagerEmittedEvents.FrameAttached, frame); this.emit(FrameManagerEmittedEvents.FrameAttached, frame);
@ -250,12 +257,13 @@ export class Page extends PageBase {
} }
#onLogEntryAdded(event: Bidi.Log.LogEntry): void { #onLogEntryAdded(event: Bidi.Log.LogEntry): void {
if (!this.frame(event.source.context)) { const frame = this.frame(event.source.context);
if (!frame) {
return; return;
} }
if (isConsoleLogEntry(event)) { if (isConsoleLogEntry(event)) {
const args = event.args.map(arg => { const args = event.args.map(arg => {
return getBidiHandle(this.mainFrame().context(), arg); return getBidiHandle(frame.context(), arg, frame);
}); });
const text = args const text = args

View File

@ -0,0 +1,171 @@
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import PuppeteerUtil from '../../injected/injected.js';
import {stringifyFunction} from '../../util/Function.js';
import {EventEmitter} from '../EventEmitter.js';
import {scriptInjector} from '../ScriptInjector.js';
import {EvaluateFunc, HandleFor} from '../types.js';
import {
PuppeteerURL,
getSourcePuppeteerURLIfAvailable,
isString,
} from '../util.js';
import {Connection} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
import {Frame} from './Frame.js';
import {JSHandle} from './JSHandle.js';
import {BidiSerializer} from './Serializer.js';
import {createEvaluationError} from './utils.js';
export const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
export const getSourceUrlComment = (url: string): string => {
return `//# sourceURL=${url}`;
};
export class Realm extends EventEmitter {
connection: Connection;
#frame!: Frame;
#id: string;
#sandbox?: string;
constructor(connection: Connection, id: string, sandbox?: string) {
super();
this.connection = connection;
this.#id = id;
this.#sandbox = sandbox;
}
get target(): Bidi.Script.Target {
return {
context: this.#id,
sandbox: this.#sandbox,
};
}
setFrame(frame: Frame): void {
this.#frame = frame;
}
protected internalPuppeteerUtil?: Promise<JSHandle<PuppeteerUtil>>;
get puppeteerUtil(): Promise<JSHandle<PuppeteerUtil>> {
const promise = Promise.resolve() as Promise<unknown>;
scriptInjector.inject(script => {
if (this.internalPuppeteerUtil) {
void this.internalPuppeteerUtil.then(handle => {
void handle.dispose();
});
}
this.internalPuppeteerUtil = promise.then(() => {
return this.evaluateHandle(script) as Promise<JSHandle<PuppeteerUtil>>;
});
}, !this.internalPuppeteerUtil);
return this.internalPuppeteerUtil as Promise<JSHandle<PuppeteerUtil>>;
}
async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
return this.#evaluate(false, pageFunction, ...args);
}
async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
return this.#evaluate(true, pageFunction, ...args);
}
async #evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
returnByValue: true,
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
async #evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
returnByValue: false,
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
async #evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
returnByValue: boolean,
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>> | Awaited<ReturnType<Func>>> {
const sourceUrlComment = getSourceUrlComment(
getSourcePuppeteerURLIfAvailable(pageFunction)?.toString() ??
PuppeteerURL.INTERNAL_URL
);
let responsePromise;
const resultOwnership = returnByValue ? 'none' : 'root';
if (isString(pageFunction)) {
const expression = SOURCE_URL_REGEX.test(pageFunction)
? pageFunction
: `${pageFunction}\n${sourceUrlComment}\n`;
responsePromise = this.connection.send('script.evaluate', {
expression,
target: this.target,
resultOwnership,
awaitPromise: true,
});
} else {
let functionDeclaration = stringifyFunction(pageFunction);
functionDeclaration = SOURCE_URL_REGEX.test(functionDeclaration)
? functionDeclaration
: `${functionDeclaration}\n${sourceUrlComment}\n`;
responsePromise = this.connection.send('script.callFunction', {
functionDeclaration,
arguments: await Promise.all(
args.map(arg => {
return BidiSerializer.serialize(arg, this as any);
})
),
target: this.target,
resultOwnership,
awaitPromise: true,
});
}
const {result} = await responsePromise;
if ('type' in result && result.type === 'exception') {
throw createEvaluationError(result.exceptionDetails);
}
return returnByValue
? BidiSerializer.deserialize(result.result)
: getBidiHandle(this as any, result.result, this.#frame);
}
}
/**
* @internal
*/
export function getBidiHandle(
realmOrContext: Realm,
result: Bidi.CommonDataTypes.RemoteValue,
frame: Frame
): JSHandle | ElementHandle<Node> {
if (result.type === 'node' || result.type === 'window') {
return new ElementHandle(realmOrContext, result, frame);
}
return new JSHandle(realmOrContext, result);
}

View File

@ -15,11 +15,21 @@
*/ */
import {ElementHandle} from '../../api/ElementHandle.js'; import {ElementHandle} from '../../api/ElementHandle.js';
import {withSourcePuppeteerURLIfNone} from '../common.js'; import {Realm as RealmBase} from '../../api/Frame.js';
import {EvaluateFuncWith, NodeFor} from '../types.js'; import {JSHandle as BaseJSHandle} from '../../api/JSHandle.js';
import {TimeoutSettings} from '../TimeoutSettings.js';
import {BrowsingContext} from './BrowsingContext.js'; import {
EvaluateFunc,
EvaluateFuncWith,
HandleFor,
InnerLazyParams,
NodeFor,
} from '../types.js';
import {withSourcePuppeteerURLIfNone} from '../util.js';
import {TaskManager, WaitTask} from '../WaitTask.js';
import {JSHandle} from './JSHandle.js';
import {Realm} from './Realm.js';
/** /**
* A unique key for {@link SandboxChart} to denote the default world. * A unique key for {@link SandboxChart} to denote the default world.
* Realms are automatically created in the default sandbox. * Realms are automatically created in the default sandbox.
@ -47,19 +57,27 @@ export interface SandboxChart {
/** /**
* @internal * @internal
*/ */
export class Sandbox { export class Sandbox implements RealmBase {
#document?: ElementHandle<Document>; #document?: ElementHandle<Document>;
#context: BrowsingContext; #realm: Realm;
constructor(context: BrowsingContext) { #timeoutSettings: TimeoutSettings;
this.#context = context; #taskManager = new TaskManager();
constructor(context: Realm, timeoutSettings: TimeoutSettings) {
this.#realm = context;
this.#timeoutSettings = timeoutSettings;
}
get taskManager(): TaskManager {
return this.#taskManager;
} }
async document(): Promise<ElementHandle<Document>> { async document(): Promise<ElementHandle<Document>> {
if (this.#document) { if (this.#document) {
return this.#document; return this.#document;
} }
this.#document = await this.#context.evaluateHandle(() => { this.#document = await this.#realm.evaluateHandle(() => {
return document; return document;
}); });
return this.#document; return this.#document;
@ -117,4 +135,90 @@ export class Sandbox {
const document = await this.document(); const document = await this.document();
return document.$x(expression); return document.$x(expression);
} }
async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
pageFunction = withSourcePuppeteerURLIfNone(
this.evaluateHandle.name,
pageFunction
);
return this.#realm.evaluateHandle(pageFunction, ...args);
}
async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(
this.evaluate.name,
pageFunction
);
return this.#realm.evaluate(pageFunction, ...args);
}
async adoptHandle<T extends BaseJSHandle<Node>>(handle: T): Promise<T> {
return (await this.evaluateHandle(node => {
return node;
}, handle)) as unknown as T;
}
async transferHandle<T extends BaseJSHandle<Node>>(handle: T): Promise<T> {
if ((handle as unknown as JSHandle).context() === this.#realm) {
return handle;
}
const transferredHandle = await this.evaluateHandle(node => {
return node;
}, handle);
await handle.dispose();
return transferredHandle as unknown as T;
}
waitForFunction<
Params extends unknown[],
Func extends EvaluateFunc<InnerLazyParams<Params>> = EvaluateFunc<
InnerLazyParams<Params>
>
>(
pageFunction: Func | string,
options: {
polling?: 'raf' | 'mutation' | number;
timeout?: number;
root?: ElementHandle<Node>;
signal?: AbortSignal;
} = {},
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
const {
polling = 'raf',
timeout = this.#timeoutSettings.timeout(),
root,
signal,
} = options;
if (typeof polling === 'number' && polling < 0) {
throw new Error('Cannot poll with non-positive interval');
}
const waitTask = new WaitTask(
this,
{
polling,
root,
timeout,
signal,
},
pageFunction as unknown as
| ((...args: unknown[]) => Promise<Awaited<ReturnType<Func>>>)
| string,
...args
);
return waitTask.result;
}
} }

View File

@ -156,7 +156,10 @@ export class BidiSerializer {
? arg ? arg
: null; : null;
if (objectHandle) { if (objectHandle) {
if (objectHandle.context() !== context) { if (
objectHandle.context() !== context &&
!('sharedId' in objectHandle.remoteValue())
) {
throw new Error( throw new Error(
'JSHandles can be evaluated only in the context they were created!' 'JSHandles can be evaluated only in the context they were created!'
); );

View File

@ -19,7 +19,7 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {debug} from '../Debug.js'; import {debug} from '../Debug.js';
import {PuppeteerURL} from '../util.js'; import {PuppeteerURL} from '../util.js';
import {BrowsingContext} from './BrowsingContext.js'; import {Realm} from './Realm.js';
import {BidiSerializer} from './Serializer.js'; import {BidiSerializer} from './Serializer.js';
/** /**
@ -30,7 +30,7 @@ export const debugError = debug('puppeteer:error');
* @internal * @internal
*/ */
export async function releaseReference( export async function releaseReference(
client: BrowsingContext, client: Realm,
remoteReference: Bidi.CommonDataTypes.RemoteReference remoteReference: Bidi.CommonDataTypes.RemoteReference
): Promise<void> { ): Promise<void> {
if (!remoteReference.handle) { if (!remoteReference.handle) {
@ -38,7 +38,7 @@ export async function releaseReference(
} }
await client.connection await client.connection
.send('script.disown', { .send('script.disown', {
target: {context: client.id}, target: client.target,
handles: [remoteReference.handle], handles: [remoteReference.handle],
}) })
.catch((error: any) => { .catch((error: any) => {

View File

@ -263,6 +263,24 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["SKIP"] "expectations": ["SKIP"]
}, },
{
"testIdPattern": "[ariaqueryhandler.spec] AriaQueryHandler waitForSelector (aria) should have an error message specifically for awaiting an element to be hidden",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[ariaqueryhandler.spec] AriaQueryHandler waitForSelector (aria) should have correct stack trace for timeout",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[ariaqueryhandler.spec] AriaQueryHandler waitForSelector (aria) should respect timeout",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[browser.spec] Browser specs Browser.process should return child_process instance", "testIdPattern": "[browser.spec] Browser specs Browser.process should return child_process instance",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -275,6 +293,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["SKIP"] "expectations": ["SKIP"]
}, },
{
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests Puppeteer.launch |pipe| option should fire \"disconnected\" when closing with pipe",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[Connection.spec] WebDriver BiDi Connection should work", "testIdPattern": "[Connection.spec] WebDriver BiDi Connection should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -311,35 +335,35 @@
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs Custom queries should wait correctly with waitFor",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs Custom queries should wait correctly with waitForSelector",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs Custom queries should wait correctly with waitForSelector on an element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[elementhandle.spec] ElementHandle specs Element.toElement should work", "testIdPattern": "[elementhandle.spec] ElementHandle specs Element.toElement should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs Element.waitForSelector should wait correctly with waitForSelector on an element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs Element.waitForXPath should wait correctly with waitForXPath on an element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.isVisible and ElementHandle.isHidden should work", "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.isVisible and ElementHandle.isHidden should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["PASS"]
},
{
"testIdPattern": "[emulation.spec] *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
}, },
{ {
"testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have different execution contexts", "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have different execution contexts",
@ -773,6 +797,12 @@
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[page.spec] Page Page.url should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[page.spec] Page Page.waitForNetworkIdle should work with aborted requests", "testIdPattern": "[page.spec] Page Page.waitForNetworkIdle should work with aborted requests",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -807,7 +837,7 @@
"testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work for ARIA selectors in multiple isolated worlds", "testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work for ARIA selectors in multiple isolated worlds",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL", "TIMEOUT"]
}, },
{ {
"testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work with :hover", "testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work with :hover",
@ -875,6 +905,120 @@
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should be cancellable",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should have an error message specifically for awaiting an element to be hidden",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should have correct stack trace for timeout",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should immediately resolve promise if node exists",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should resolve promise when node is added",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should respect timeout",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should respond to node attribute mutation",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should return null if waiting to hide non-existing element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should return the element handle",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be hidden (bounding box)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be hidden (display)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be hidden (removal)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be hidden (visibility)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible (bounding box)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible (display)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible (visibility)",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible recursively",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should work when node is added through innerHTML",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should work with removed MutationObserver",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForTimeout waits for the given timeout before resolving", "testIdPattern": "[waittask.spec] waittask specs Frame.waitForTimeout waits for the given timeout before resolving",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -971,12 +1115,30 @@
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests Puppeteer.launch |browserURL| option should be able to connect using browserUrl, with and without trailing slash",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests Puppeteer.launch |browserURL| option should throw when trying to connect to non-existing browser", "testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests Puppeteer.launch |browserURL| option should throw when trying to connect to non-existing browser",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests Puppeteer.launch |browserURL| option should throw when trying to connect to non-existing browser",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests Puppeteer.launch |pipe| option should fire \"disconnected\" when closing with pipe",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"]
},
{ {
"testIdPattern": "[click.spec] Page.click should click on checkbox label and toggle", "testIdPattern": "[click.spec] Page.click should click on checkbox label and toggle",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1175,18 +1337,42 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["SKIP"] "expectations": ["SKIP"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[emulation.spec] Emulation Page.emulate should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.emulateCPUThrottling should change the CPU throttling rate successfully", "testIdPattern": "[emulation.spec] Emulation Page.emulateCPUThrottling should change the CPU throttling rate successfully",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.emulateMediaFeatures should throw in case of bad argument",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.emulateMediaFeatures should work", "testIdPattern": "[emulation.spec] Emulation Page.emulateMediaFeatures should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.emulateMediaType should throw in case of bad argument",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.emulateMediaType should work", "testIdPattern": "[emulation.spec] Emulation Page.emulateMediaType should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1199,6 +1385,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.emulateNetworkConditions should change navigator.connection.effectiveType",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.emulateTimezone should throw for invalid timezone IDs", "testIdPattern": "[emulation.spec] Emulation Page.emulateTimezone should throw for invalid timezone IDs",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1211,12 +1403,24 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.emulateVisionDeficiency should throw for invalid vision deficiencies",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.emulateVisionDeficiency should work", "testIdPattern": "[emulation.spec] Emulation Page.emulateVisionDeficiency should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.viewport should detect touch when applying viewport with touches",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.viewport should get the proper viewport size", "testIdPattern": "[emulation.spec] Emulation Page.viewport should get the proper viewport size",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1229,42 +1433,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.viewport should support touch emulation", "testIdPattern": "[emulation.spec] Emulation Page.viewport should support touch emulation",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[emulation.spec] Emulation Page.viewport should detect touch when applying viewport with touches",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[emulation.spec] Emulation Page.emulate should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[emulation.spec] Emulation Page.emulateNetworkConditions should change navigator.connection.effectiveType",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[evaluation.spec] Evaluation specs \"after each\" hook for \"should transfer 100Mb of data from page to node.js\"", "testIdPattern": "[evaluation.spec] Evaluation specs \"after each\" hook for \"should transfer 100Mb of data from page to node.js\"",
"platforms": ["darwin"], "platforms": ["darwin"],
@ -1367,6 +1541,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with mixed content",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception", "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1553,12 +1733,36 @@
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[locator.spec] Locator Locator.click can be aborted",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[locator.spec] Locator Locator.click should retry clicks on errors",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[locator.spec] Locator Locator.click should time out",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[locator.spec] Locator Locator.race can be aborted", "testIdPattern": "[locator.spec] Locator Locator.race can be aborted",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[locator.spec] Locator Locator.scroll should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[mouse.spec] Mouse should reset properly", "testIdPattern": "[mouse.spec] Mouse should reset properly",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2177,6 +2381,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[page.spec] Page Page.addScriptTag should throw an error if loading from url fail",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[page.spec] Page Page.addScriptTag should throw when added with content to the CSP page", "testIdPattern": "[page.spec] Page Page.addScriptTag should throw when added with content to the CSP page",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2189,6 +2399,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["SKIP"] "expectations": ["SKIP"]
}, },
{
"testIdPattern": "[page.spec] Page Page.addStyleTag should throw an error if loading from url fail",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[page.spec] Page Page.addStyleTag should throw when added with content to the CSP page", "testIdPattern": "[page.spec] Page Page.addStyleTag should throw when added with content to the CSP page",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2459,12 +2675,6 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[page.spec] Page Page.url should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[page.spec] Page Page.waitForNetworkIdle should work", "testIdPattern": "[page.spec] Page Page.waitForNetworkIdle should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2783,6 +2993,12 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["SKIP"] "expectations": ["SKIP"]
}, },
{
"testIdPattern": "[worker.spec] Workers should report errors",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{ {
"testIdPattern": "[CDPSession.spec] Target.createCDPSession should send events", "testIdPattern": "[CDPSession.spec] Target.createCDPSession should send events",
"platforms": ["win32"], "platforms": ["win32"],

View File

@ -39,7 +39,7 @@ describe('headful tests', function () {
/* These tests fire up an actual browser so let's /* These tests fire up an actual browser so let's
* allow a higher timeout * allow a higher timeout
*/ */
this.timeout(20 * 1000); this.timeout(20_000);
let headfulOptions: PuppeteerLaunchOptions | undefined; let headfulOptions: PuppeteerLaunchOptions | undefined;
let headlessOptions: PuppeteerLaunchOptions & {headless: boolean}; let headlessOptions: PuppeteerLaunchOptions & {headless: boolean};

View File

@ -35,7 +35,7 @@ import {
import {dumpFrames, waitEvent} from './utils.js'; import {dumpFrames, waitEvent} from './utils.js';
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
const FIREFOX_TIMEOUT = 30 * 1000; const FIREFOX_TIMEOUT = 30_000;
describe('Launcher specs', function () { describe('Launcher specs', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();