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,
|
||||
EvaluateFuncWith,
|
||||
HandleFor,
|
||||
InnerLazyParams,
|
||||
NodeFor,
|
||||
} from '../common/types.js';
|
||||
import {importFSPromises} from '../common/util.js';
|
||||
import {TaskManager} from '../common/WaitTask.js';
|
||||
|
||||
import {KeyboardTypeOptions} from './Input.js';
|
||||
import {JSHandle} from './JSHandle.js';
|
||||
import {FunctionLocator, Locator, NodeLocator} from './locators/locators.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>;
|
||||
}
|
||||
import {Realm} from './Realm.js';
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
this.#detached = true;
|
||||
this.worlds[MAIN_WORLD]._detach();
|
||||
this.worlds[PUPPETEER_WORLD]._detach();
|
||||
this.worlds[MAIN_WORLD][Symbol.dispose]();
|
||||
this.worlds[PUPPETEER_WORLD][Symbol.dispose]();
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,8 @@
|
||||
|
||||
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 {assert} from '../util/assert.js';
|
||||
import {Realm} from '../api/Realm.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {Binding} from './Binding.js';
|
||||
@ -31,24 +28,14 @@ import {FrameManager} from './FrameManager.js';
|
||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||
import {CDPJSHandle} from './JSHandle.js';
|
||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||
import {
|
||||
BindingPayload,
|
||||
EvaluateFunc,
|
||||
EvaluateFuncWith,
|
||||
HandleFor,
|
||||
InnerLazyParams,
|
||||
NodeFor,
|
||||
} from './types.js';
|
||||
import {BindingPayload, EvaluateFunc, HandleFor} from './types.js';
|
||||
import {
|
||||
addPageBinding,
|
||||
createJSHandle,
|
||||
debugError,
|
||||
getPageContent,
|
||||
setPageContent,
|
||||
withSourcePuppeteerURLIfNone,
|
||||
} from './util.js';
|
||||
import {TaskManager, WaitTask} from './WaitTask.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -102,27 +89,22 @@ export interface IsolatedWorldChart {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class IsolatedWorld implements Realm {
|
||||
export class IsolatedWorld extends Realm {
|
||||
#frame: Frame;
|
||||
#context = Deferred.create<ExecutionContext>();
|
||||
#detached = false;
|
||||
|
||||
// Set of bindings that have been registered in the current context.
|
||||
#contextBindings = new Set<string>();
|
||||
|
||||
// Contains mapping from functions that should be bound to Puppeteer functions.
|
||||
#bindings = new Map<string, Binding>();
|
||||
#taskManager = new TaskManager();
|
||||
|
||||
get taskManager(): TaskManager {
|
||||
return this.#taskManager;
|
||||
}
|
||||
|
||||
get _bindings(): Map<string, Binding> {
|
||||
return this.#bindings;
|
||||
}
|
||||
|
||||
constructor(frame: Frame) {
|
||||
super(frame._frameManager.timeoutSettings);
|
||||
this.#frame = frame;
|
||||
this.frameUpdated();
|
||||
}
|
||||
@ -139,10 +121,6 @@ export class IsolatedWorld implements Realm {
|
||||
return this.#frame._frameManager;
|
||||
}
|
||||
|
||||
get #timeoutSettings(): TimeoutSettings {
|
||||
return this.#frameManager.timeoutSettings;
|
||||
}
|
||||
|
||||
frame(): Frame {
|
||||
return this.#frame;
|
||||
}
|
||||
@ -154,23 +132,15 @@ export class IsolatedWorld implements Realm {
|
||||
setContext(context: ExecutionContext): void {
|
||||
this.#contextBindings.clear();
|
||||
this.#context.resolve(context);
|
||||
void this.#taskManager.rerunAll();
|
||||
void this.taskManager.rerunAll();
|
||||
}
|
||||
|
||||
hasContext(): boolean {
|
||||
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> {
|
||||
if (this.#detached) {
|
||||
if (this.disposed) {
|
||||
throw new Error(
|
||||
`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);
|
||||
}
|
||||
|
||||
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(
|
||||
html: string,
|
||||
options: {
|
||||
@ -284,7 +190,7 @@ export class IsolatedWorld implements Realm {
|
||||
): Promise<void> {
|
||||
const {
|
||||
waitUntil = ['load'],
|
||||
timeout = this.#timeoutSettings.navigationTimeout(),
|
||||
timeout = this.timeoutSettings.navigationTimeout(),
|
||||
} = options;
|
||||
|
||||
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
|
||||
// first one to set up the binding in the page before running the others.
|
||||
#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> {
|
||||
return this.evaluate(() => {
|
||||
return document.title;
|
||||
@ -522,6 +344,11 @@ export class IsolatedWorld implements Realm {
|
||||
await handle.dispose();
|
||||
return newHandle;
|
||||
}
|
||||
|
||||
[Symbol.dispose](): void {
|
||||
super[Symbol.dispose]();
|
||||
this.#client.off('Runtime.bindingCalled', this.#onBindingCalled);
|
||||
}
|
||||
}
|
||||
|
||||
class Mutex {
|
||||
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import {ElementHandle} from '../api/ElementHandle.js';
|
||||
import {Realm} from '../api/Frame.js';
|
||||
import {JSHandle} from '../api/JSHandle.js';
|
||||
import {Realm} from '../api/Realm.js';
|
||||
import type {Poller} from '../injected/Poller.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
|
@ -279,7 +279,7 @@ export class Frame extends BaseFrame {
|
||||
this.#detached = true;
|
||||
this.#abortDeferred.reject(new Error('Frame detached'));
|
||||
this.#context.dispose();
|
||||
this.sandboxes[MAIN_SANDBOX].dispose();
|
||||
this.sandboxes[PUPPETEER_SANDBOX].dispose();
|
||||
this.sandboxes[MAIN_SANDBOX][Symbol.dispose]();
|
||||
this.sandboxes[PUPPETEER_SANDBOX][Symbol.dispose]();
|
||||
}
|
||||
}
|
||||
|
@ -16,21 +16,11 @@
|
||||
|
||||
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 {assert} from '../../util/assert.js';
|
||||
import {Realm as RealmApi} from '../../api/Realm.js';
|
||||
import {TimeoutSettings} from '../TimeoutSettings.js';
|
||||
import {
|
||||
EvaluateFunc,
|
||||
EvaluateFuncWith,
|
||||
HandleFor,
|
||||
InnerLazyParams,
|
||||
NodeFor,
|
||||
} from '../types.js';
|
||||
import {EvaluateFunc, HandleFor} from '../types.js';
|
||||
import {withSourcePuppeteerURLIfNone} from '../util.js';
|
||||
import {TaskManager, WaitTask} from '../WaitTask.js';
|
||||
|
||||
import {BrowsingContext} from './BrowsingContext.js';
|
||||
import {JSHandle} from './JSHandle.js';
|
||||
@ -62,99 +52,26 @@ export interface SandboxChart {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class Sandbox implements RealmBase {
|
||||
export class Sandbox extends RealmApi {
|
||||
#realm: Realm;
|
||||
|
||||
#timeoutSettings: TimeoutSettings;
|
||||
#taskManager = new TaskManager();
|
||||
|
||||
constructor(
|
||||
// TODO: We should split the Realm and BrowsingContext
|
||||
realm: Realm | BrowsingContext,
|
||||
timeoutSettings: TimeoutSettings
|
||||
) {
|
||||
super(timeoutSettings);
|
||||
this.#realm = realm;
|
||||
this.#timeoutSettings = timeoutSettings;
|
||||
|
||||
// TODO: Tack correct realm similar to BrowsingContexts
|
||||
this.#realm.connection.on(
|
||||
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<
|
||||
Params extends unknown[],
|
||||
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
||||
@ -200,91 +117,4 @@ export class Sandbox implements RealmBase {
|
||||
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;
|
||||
}
|
||||
|
||||
// ///////////////////
|
||||
// // 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