mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: refactor Sandbox and IsolatedWorld (#10807)
This commit is contained in:
parent
900a1f227d
commit
b9744b2c95
@ -34,63 +34,13 @@ import {
|
|||||||
EvaluateFunc,
|
EvaluateFunc,
|
||||||
EvaluateFuncWith,
|
EvaluateFuncWith,
|
||||||
HandleFor,
|
HandleFor,
|
||||||
InnerLazyParams,
|
|
||||||
NodeFor,
|
NodeFor,
|
||||||
} from '../common/types.js';
|
} from '../common/types.js';
|
||||||
import {importFSPromises} from '../common/util.js';
|
import {importFSPromises} from '../common/util.js';
|
||||||
import {TaskManager} from '../common/WaitTask.js';
|
|
||||||
|
|
||||||
import {KeyboardTypeOptions} from './Input.js';
|
import {KeyboardTypeOptions} from './Input.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
|
||||||
import {FunctionLocator, Locator, NodeLocator} from './locators/locators.js';
|
import {FunctionLocator, Locator, NodeLocator} from './locators/locators.js';
|
||||||
|
import {Realm} from './Realm.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>>>;
|
|
||||||
click(selector: string, options: Readonly<ClickOptions>): Promise<void>;
|
|
||||||
focus(selector: string): Promise<void>;
|
|
||||||
hover(selector: string): Promise<void>;
|
|
||||||
select(selector: string, ...values: string[]): Promise<string[]>;
|
|
||||||
tap(selector: string): Promise<void>;
|
|
||||||
type(
|
|
||||||
selector: string,
|
|
||||||
text: string,
|
|
||||||
options?: Readonly<KeyboardTypeOptions>
|
|
||||||
): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
228
packages/puppeteer-core/src/api/Realm.ts
Normal file
228
packages/puppeteer-core/src/api/Realm.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {TimeoutSettings} from '../common/TimeoutSettings.js';
|
||||||
|
import {
|
||||||
|
EvaluateFunc,
|
||||||
|
EvaluateFuncWith,
|
||||||
|
HandleFor,
|
||||||
|
InnerLazyParams,
|
||||||
|
NodeFor,
|
||||||
|
} from '../common/types.js';
|
||||||
|
import {getPageContent, withSourcePuppeteerURLIfNone} from '../common/util.js';
|
||||||
|
import {TaskManager, WaitTask} from '../common/WaitTask.js';
|
||||||
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
|
import {ClickOptions, ElementHandle} from './ElementHandle.js';
|
||||||
|
import {KeyboardTypeOptions} from './Input.js';
|
||||||
|
import {JSHandle} from './JSHandle.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export abstract class Realm implements Disposable {
|
||||||
|
#timeoutSettings: TimeoutSettings;
|
||||||
|
#taskManager = new TaskManager();
|
||||||
|
|
||||||
|
constructor(timeoutSettings: TimeoutSettings) {
|
||||||
|
this.#timeoutSettings = timeoutSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get timeoutSettings(): TimeoutSettings {
|
||||||
|
return this.#timeoutSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
get taskManager(): TaskManager {
|
||||||
|
return this.#taskManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract adoptHandle<T extends JSHandle<Node>>(handle: T): Promise<T>;
|
||||||
|
abstract transferHandle<T extends JSHandle<Node>>(handle: T): Promise<T>;
|
||||||
|
abstract evaluateHandle<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
|
||||||
|
abstract evaluate<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
|
||||||
|
async document(): Promise<ElementHandle<Document>> {
|
||||||
|
// TODO(#10813): Implement document caching.
|
||||||
|
return await this.evaluateHandle(() => {
|
||||||
|
return document;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async $<Selector extends string>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
||||||
|
using document = await this.document();
|
||||||
|
return await document.$(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $$<Selector extends string>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
|
||||||
|
using document = await this.document();
|
||||||
|
return await document.$$(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
|
||||||
|
using document = await this.document();
|
||||||
|
return await document.$x(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $eval<
|
||||||
|
Selector extends string,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
|
NodeFor<Selector>,
|
||||||
|
Params
|
||||||
|
>,
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
|
||||||
|
using document = await this.document();
|
||||||
|
return await document.$eval(selector, pageFunction, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $$eval<
|
||||||
|
Selector extends string,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<
|
||||||
|
Array<NodeFor<Selector>>,
|
||||||
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
|
||||||
|
using document = await this.document();
|
||||||
|
return await document.$$eval(selector, pageFunction, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async content(): Promise<string> {
|
||||||
|
return await this.evaluate(getPageContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
async click(
|
||||||
|
selector: string,
|
||||||
|
options?: Readonly<ClickOptions>
|
||||||
|
): Promise<void> {
|
||||||
|
using handle = await this.$(selector);
|
||||||
|
assert(handle, `No element found for selector: ${selector}`);
|
||||||
|
await handle.click(options);
|
||||||
|
await handle.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
async focus(selector: string): Promise<void> {
|
||||||
|
using handle = await this.$(selector);
|
||||||
|
assert(handle, `No element found for selector: ${selector}`);
|
||||||
|
await handle.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async hover(selector: string): Promise<void> {
|
||||||
|
using handle = await this.$(selector);
|
||||||
|
assert(handle, `No element found for selector: ${selector}`);
|
||||||
|
await handle.hover();
|
||||||
|
}
|
||||||
|
|
||||||
|
async select(selector: string, ...values: string[]): Promise<string[]> {
|
||||||
|
using handle = await this.$(selector);
|
||||||
|
assert(handle, `No element found for selector: ${selector}`);
|
||||||
|
return await handle.select(...values);
|
||||||
|
}
|
||||||
|
|
||||||
|
async tap(selector: string): Promise<void> {
|
||||||
|
using handle = await this.$(selector);
|
||||||
|
assert(handle, `No element found for selector: ${selector}`);
|
||||||
|
await handle.tap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async type(
|
||||||
|
selector: string,
|
||||||
|
text: string,
|
||||||
|
options?: Readonly<KeyboardTypeOptions>
|
||||||
|
): Promise<void> {
|
||||||
|
using handle = await this.$(selector);
|
||||||
|
assert(handle, `No element found for selector: ${selector}`);
|
||||||
|
await handle.type(text, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
get disposed(): boolean {
|
||||||
|
return this.#disposed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#disposed = false;
|
||||||
|
[Symbol.dispose](): void {
|
||||||
|
this.#disposed = true;
|
||||||
|
this.taskManager.terminateAll(
|
||||||
|
new Error('waitForFunction failed: frame got detached.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -412,7 +412,7 @@ export class Frame extends BaseFrame {
|
|||||||
|
|
||||||
_detach(): void {
|
_detach(): void {
|
||||||
this.#detached = true;
|
this.#detached = true;
|
||||||
this.worlds[MAIN_WORLD]._detach();
|
this.worlds[MAIN_WORLD][Symbol.dispose]();
|
||||||
this.worlds[PUPPETEER_WORLD]._detach();
|
this.worlds[PUPPETEER_WORLD][Symbol.dispose]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,8 @@
|
|||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import type {ClickOptions, ElementHandle} from '../api/ElementHandle.js';
|
|
||||||
import {Realm} from '../api/Frame.js';
|
|
||||||
import {KeyboardTypeOptions} from '../api/Input.js';
|
|
||||||
import {JSHandle} from '../api/JSHandle.js';
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {Realm} from '../api/Realm.js';
|
||||||
import {Deferred} from '../util/Deferred.js';
|
import {Deferred} from '../util/Deferred.js';
|
||||||
|
|
||||||
import {Binding} from './Binding.js';
|
import {Binding} from './Binding.js';
|
||||||
@ -31,24 +28,14 @@ import {FrameManager} from './FrameManager.js';
|
|||||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||||
import {CDPJSHandle} from './JSHandle.js';
|
import {CDPJSHandle} from './JSHandle.js';
|
||||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
import {BindingPayload, EvaluateFunc, HandleFor} from './types.js';
|
||||||
import {
|
|
||||||
BindingPayload,
|
|
||||||
EvaluateFunc,
|
|
||||||
EvaluateFuncWith,
|
|
||||||
HandleFor,
|
|
||||||
InnerLazyParams,
|
|
||||||
NodeFor,
|
|
||||||
} from './types.js';
|
|
||||||
import {
|
import {
|
||||||
addPageBinding,
|
addPageBinding,
|
||||||
createJSHandle,
|
createJSHandle,
|
||||||
debugError,
|
debugError,
|
||||||
getPageContent,
|
|
||||||
setPageContent,
|
setPageContent,
|
||||||
withSourcePuppeteerURLIfNone,
|
withSourcePuppeteerURLIfNone,
|
||||||
} from './util.js';
|
} from './util.js';
|
||||||
import {TaskManager, WaitTask} from './WaitTask.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -102,27 +89,22 @@ export interface IsolatedWorldChart {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class IsolatedWorld implements Realm {
|
export class IsolatedWorld extends Realm {
|
||||||
#frame: Frame;
|
#frame: Frame;
|
||||||
#context = Deferred.create<ExecutionContext>();
|
#context = Deferred.create<ExecutionContext>();
|
||||||
#detached = false;
|
|
||||||
|
|
||||||
// Set of bindings that have been registered in the current context.
|
// Set of bindings that have been registered in the current context.
|
||||||
#contextBindings = new Set<string>();
|
#contextBindings = new Set<string>();
|
||||||
|
|
||||||
// Contains mapping from functions that should be bound to Puppeteer functions.
|
// Contains mapping from functions that should be bound to Puppeteer functions.
|
||||||
#bindings = new Map<string, Binding>();
|
#bindings = new Map<string, Binding>();
|
||||||
#taskManager = new TaskManager();
|
|
||||||
|
|
||||||
get taskManager(): TaskManager {
|
|
||||||
return this.#taskManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _bindings(): Map<string, Binding> {
|
get _bindings(): Map<string, Binding> {
|
||||||
return this.#bindings;
|
return this.#bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(frame: Frame) {
|
constructor(frame: Frame) {
|
||||||
|
super(frame._frameManager.timeoutSettings);
|
||||||
this.#frame = frame;
|
this.#frame = frame;
|
||||||
this.frameUpdated();
|
this.frameUpdated();
|
||||||
}
|
}
|
||||||
@ -139,10 +121,6 @@ export class IsolatedWorld implements Realm {
|
|||||||
return this.#frame._frameManager;
|
return this.#frame._frameManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
get #timeoutSettings(): TimeoutSettings {
|
|
||||||
return this.#frameManager.timeoutSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame(): Frame {
|
frame(): Frame {
|
||||||
return this.#frame;
|
return this.#frame;
|
||||||
}
|
}
|
||||||
@ -154,23 +132,15 @@ export class IsolatedWorld implements Realm {
|
|||||||
setContext(context: ExecutionContext): void {
|
setContext(context: ExecutionContext): void {
|
||||||
this.#contextBindings.clear();
|
this.#contextBindings.clear();
|
||||||
this.#context.resolve(context);
|
this.#context.resolve(context);
|
||||||
void this.#taskManager.rerunAll();
|
void this.taskManager.rerunAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
hasContext(): boolean {
|
hasContext(): boolean {
|
||||||
return this.#context.resolved();
|
return this.#context.resolved();
|
||||||
}
|
}
|
||||||
|
|
||||||
_detach(): void {
|
|
||||||
this.#detached = true;
|
|
||||||
this.#client.off('Runtime.bindingCalled', this.#onBindingCalled);
|
|
||||||
this.#taskManager.terminateAll(
|
|
||||||
new Error('waitForFunction failed: frame got detached.')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
executionContext(): Promise<ExecutionContext> {
|
executionContext(): Promise<ExecutionContext> {
|
||||||
if (this.#detached) {
|
if (this.disposed) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Execution context is not available in detached frame "${this.#frame.url()}" (are you trying to evaluate?)`
|
`Execution context is not available in detached frame "${this.#frame.url()}" (are you trying to evaluate?)`
|
||||||
);
|
);
|
||||||
@ -211,70 +181,6 @@ export class IsolatedWorld implements Realm {
|
|||||||
return context.evaluate(pageFunction, ...args);
|
return context.evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $<Selector extends string>(
|
|
||||||
selector: Selector
|
|
||||||
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $$<Selector extends string>(
|
|
||||||
selector: Selector
|
|
||||||
): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$$(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
async document(): Promise<ElementHandle<Document>> {
|
|
||||||
// TODO(#10813): Implement document caching.
|
|
||||||
return await this.evaluateHandle(() => {
|
|
||||||
return document;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$x(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $eval<
|
|
||||||
Selector extends string,
|
|
||||||
Params extends unknown[],
|
|
||||||
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
|
||||||
NodeFor<Selector>,
|
|
||||||
Params
|
|
||||||
>,
|
|
||||||
>(
|
|
||||||
selector: Selector,
|
|
||||||
pageFunction: Func | string,
|
|
||||||
...args: Params
|
|
||||||
): Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$eval(selector, pageFunction, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $$eval<
|
|
||||||
Selector extends string,
|
|
||||||
Params extends unknown[],
|
|
||||||
Func extends EvaluateFuncWith<
|
|
||||||
Array<NodeFor<Selector>>,
|
|
||||||
Params
|
|
||||||
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
|
|
||||||
>(
|
|
||||||
selector: Selector,
|
|
||||||
pageFunction: Func | string,
|
|
||||||
...args: Params
|
|
||||||
): Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$$eval(selector, pageFunction, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async content(): Promise<string> {
|
|
||||||
return await this.evaluate(getPageContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setContent(
|
async setContent(
|
||||||
html: string,
|
html: string,
|
||||||
options: {
|
options: {
|
||||||
@ -284,7 +190,7 @@ export class IsolatedWorld implements Realm {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {
|
const {
|
||||||
waitUntil = ['load'],
|
waitUntil = ['load'],
|
||||||
timeout = this.#timeoutSettings.navigationTimeout(),
|
timeout = this.timeoutSettings.navigationTimeout(),
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
await setPageContent(this, html);
|
await setPageContent(this, html);
|
||||||
@ -305,50 +211,6 @@ export class IsolatedWorld implements Realm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async click(
|
|
||||||
selector: string,
|
|
||||||
options?: Readonly<ClickOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.click(options);
|
|
||||||
await handle.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
async focus(selector: string): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
async hover(selector: string): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.hover();
|
|
||||||
}
|
|
||||||
|
|
||||||
async select(selector: string, ...values: string[]): Promise<string[]> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
return await handle.select(...values);
|
|
||||||
}
|
|
||||||
|
|
||||||
async tap(selector: string): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.tap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async type(
|
|
||||||
selector: string,
|
|
||||||
text: string,
|
|
||||||
options?: Readonly<KeyboardTypeOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.type(text, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If multiple waitFor are set up asynchronously, we need to wait for the
|
// If multiple waitFor are set up asynchronously, we need to wait for the
|
||||||
// first one to set up the binding in the page before running the others.
|
// first one to set up the binding in the page before running the others.
|
||||||
#mutex = new Mutex();
|
#mutex = new Mutex();
|
||||||
@ -431,46 +293,6 @@ export class IsolatedWorld implements Realm {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
async title(): Promise<string> {
|
async title(): Promise<string> {
|
||||||
return this.evaluate(() => {
|
return this.evaluate(() => {
|
||||||
return document.title;
|
return document.title;
|
||||||
@ -522,6 +344,11 @@ export class IsolatedWorld implements Realm {
|
|||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return newHandle;
|
return newHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Symbol.dispose](): void {
|
||||||
|
super[Symbol.dispose]();
|
||||||
|
this.#client.off('Runtime.bindingCalled', this.#onBindingCalled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Mutex {
|
class Mutex {
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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 {Realm} from '../api/Realm.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';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
@ -279,7 +279,7 @@ export class Frame extends BaseFrame {
|
|||||||
this.#detached = true;
|
this.#detached = true;
|
||||||
this.#abortDeferred.reject(new Error('Frame detached'));
|
this.#abortDeferred.reject(new Error('Frame detached'));
|
||||||
this.#context.dispose();
|
this.#context.dispose();
|
||||||
this.sandboxes[MAIN_SANDBOX].dispose();
|
this.sandboxes[MAIN_SANDBOX][Symbol.dispose]();
|
||||||
this.sandboxes[PUPPETEER_SANDBOX].dispose();
|
this.sandboxes[PUPPETEER_SANDBOX][Symbol.dispose]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,21 +16,11 @@
|
|||||||
|
|
||||||
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||||
|
|
||||||
import {ClickOptions, ElementHandle} from '../../api/ElementHandle.js';
|
|
||||||
import {Realm as RealmBase} from '../../api/Frame.js';
|
|
||||||
import {KeyboardTypeOptions} from '../../api/Input.js';
|
|
||||||
import {JSHandle as BaseJSHandle} from '../../api/JSHandle.js';
|
import {JSHandle as BaseJSHandle} from '../../api/JSHandle.js';
|
||||||
import {assert} from '../../util/assert.js';
|
import {Realm as RealmApi} from '../../api/Realm.js';
|
||||||
import {TimeoutSettings} from '../TimeoutSettings.js';
|
import {TimeoutSettings} from '../TimeoutSettings.js';
|
||||||
import {
|
import {EvaluateFunc, HandleFor} from '../types.js';
|
||||||
EvaluateFunc,
|
|
||||||
EvaluateFuncWith,
|
|
||||||
HandleFor,
|
|
||||||
InnerLazyParams,
|
|
||||||
NodeFor,
|
|
||||||
} from '../types.js';
|
|
||||||
import {withSourcePuppeteerURLIfNone} from '../util.js';
|
import {withSourcePuppeteerURLIfNone} from '../util.js';
|
||||||
import {TaskManager, WaitTask} from '../WaitTask.js';
|
|
||||||
|
|
||||||
import {BrowsingContext} from './BrowsingContext.js';
|
import {BrowsingContext} from './BrowsingContext.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {JSHandle} from './JSHandle.js';
|
||||||
@ -62,99 +52,26 @@ export interface SandboxChart {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class Sandbox implements RealmBase {
|
export class Sandbox extends RealmApi {
|
||||||
#realm: Realm;
|
#realm: Realm;
|
||||||
|
|
||||||
#timeoutSettings: TimeoutSettings;
|
|
||||||
#taskManager = new TaskManager();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
// TODO: We should split the Realm and BrowsingContext
|
// TODO: We should split the Realm and BrowsingContext
|
||||||
realm: Realm | BrowsingContext,
|
realm: Realm | BrowsingContext,
|
||||||
timeoutSettings: TimeoutSettings
|
timeoutSettings: TimeoutSettings
|
||||||
) {
|
) {
|
||||||
|
super(timeoutSettings);
|
||||||
this.#realm = realm;
|
this.#realm = realm;
|
||||||
this.#timeoutSettings = timeoutSettings;
|
|
||||||
|
|
||||||
// TODO: Tack correct realm similar to BrowsingContexts
|
// TODO: Tack correct realm similar to BrowsingContexts
|
||||||
this.#realm.connection.on(
|
this.#realm.connection.on(
|
||||||
Bidi.ChromiumBidi.Script.EventNames.RealmCreated,
|
Bidi.ChromiumBidi.Script.EventNames.RealmCreated,
|
||||||
() => {
|
() => {
|
||||||
void this.#taskManager.rerunAll();
|
void this.taskManager.rerunAll();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
this.#taskManager.terminateAll(
|
|
||||||
new Error('waitForFunction failed: frame got detached.')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get taskManager(): TaskManager {
|
|
||||||
return this.#taskManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
async document(): Promise<ElementHandle<Document>> {
|
|
||||||
// TODO(#10813): Implement document caching.
|
|
||||||
return await this.#realm.evaluateHandle(() => {
|
|
||||||
return document;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async $<Selector extends string>(
|
|
||||||
selector: Selector
|
|
||||||
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $$<Selector extends string>(
|
|
||||||
selector: Selector
|
|
||||||
): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$$(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $eval<
|
|
||||||
Selector extends string,
|
|
||||||
Params extends unknown[],
|
|
||||||
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
|
||||||
NodeFor<Selector>,
|
|
||||||
Params
|
|
||||||
>,
|
|
||||||
>(
|
|
||||||
selector: Selector,
|
|
||||||
pageFunction: Func | string,
|
|
||||||
...args: Params
|
|
||||||
): Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$eval(selector, pageFunction, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $$eval<
|
|
||||||
Selector extends string,
|
|
||||||
Params extends unknown[],
|
|
||||||
Func extends EvaluateFuncWith<
|
|
||||||
Array<NodeFor<Selector>>,
|
|
||||||
Params
|
|
||||||
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
|
|
||||||
>(
|
|
||||||
selector: Selector,
|
|
||||||
pageFunction: Func | string,
|
|
||||||
...args: Params
|
|
||||||
): Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$$eval(selector, pageFunction, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
|
|
||||||
using document = await this.document();
|
|
||||||
return await document.$x(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
async evaluateHandle<
|
async evaluateHandle<
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
||||||
@ -200,91 +117,4 @@ export class Sandbox implements RealmBase {
|
|||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return transferredHandle as unknown as T;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ///////////////////
|
|
||||||
// // Input methods //
|
|
||||||
// ///////////////////
|
|
||||||
async click(
|
|
||||||
selector: string,
|
|
||||||
options?: Readonly<ClickOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.click(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
async focus(selector: string): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
async hover(selector: string): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.hover();
|
|
||||||
}
|
|
||||||
|
|
||||||
async select(selector: string, ...values: string[]): Promise<string[]> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
const result = await handle.select(...values);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async tap(selector: string): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.tap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async type(
|
|
||||||
selector: string,
|
|
||||||
text: string,
|
|
||||||
options?: Readonly<KeyboardTypeOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
using handle = await this.$(selector);
|
|
||||||
assert(handle, `No element found for selector: ${selector}`);
|
|
||||||
await handle.type(text, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user