mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: move waitFor methods from DOMWorld to Frame (#87)
This almost removes the DOMWorld, so we can unify them across the browsers.
This commit is contained in:
parent
2eb653740a
commit
3decf1f996
@ -17,30 +17,23 @@
|
|||||||
|
|
||||||
import { ExecutionContext } from './ExecutionContext';
|
import { ExecutionContext } from './ExecutionContext';
|
||||||
import { Frame } from './Frame';
|
import { Frame } from './Frame';
|
||||||
import { ElementHandle, JSHandle } from './JSHandle';
|
import { JSHandle } from './JSHandle';
|
||||||
import { TimeoutSettings } from '../TimeoutSettings';
|
import { WaitTask, WaitTaskParams } from '../waitTask';
|
||||||
import { WaitTask, WaitTaskParams, waitForSelectorOrXPath } from '../waitTask';
|
|
||||||
|
|
||||||
export class DOMWorld {
|
export class DOMWorld {
|
||||||
private _frame: Frame;
|
private _frame: Frame;
|
||||||
private _timeoutSettings: TimeoutSettings;
|
|
||||||
private _contextPromise: Promise<ExecutionContext>;
|
private _contextPromise: Promise<ExecutionContext>;
|
||||||
private _contextResolveCallback: ((c: ExecutionContext) => void) | null;
|
private _contextResolveCallback: ((c: ExecutionContext) => void) | null;
|
||||||
private _context: ExecutionContext | null;
|
_context: ExecutionContext | null;
|
||||||
_waitTasks = new Set<WaitTask<JSHandle>>();
|
_waitTasks = new Set<WaitTask<JSHandle>>();
|
||||||
private _detached = false;
|
private _detached = false;
|
||||||
|
|
||||||
constructor(frame: Frame, timeoutSettings: TimeoutSettings) {
|
constructor(frame: Frame) {
|
||||||
this._frame = frame;
|
this._frame = frame;
|
||||||
this._timeoutSettings = timeoutSettings;
|
|
||||||
this._contextPromise;
|
this._contextPromise;
|
||||||
this._setContext(null);
|
this._setContext(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame(): Frame {
|
|
||||||
return this._frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setContext(context: ExecutionContext | null) {
|
_setContext(context: ExecutionContext | null) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
if (context) {
|
if (context) {
|
||||||
@ -55,10 +48,6 @@ export class DOMWorld {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasContext(): boolean {
|
|
||||||
return !this._contextResolveCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
_detach() {
|
_detach() {
|
||||||
this._detached = true;
|
this._detached = true;
|
||||||
for (const waitTask of this._waitTasks)
|
for (const waitTask of this._waitTasks)
|
||||||
@ -71,42 +60,7 @@ export class DOMWorld {
|
|||||||
return this._contextPromise;
|
return this._contextPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForSelector(selector: string, options: { visible?: boolean; hidden?: boolean; timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
scheduleWaitTask(params: WaitTaskParams): Promise<JSHandle> {
|
||||||
const params = waitForSelectorOrXPath(selector, false /* isXPath */, { timeout: this._timeoutSettings.timeout(), ...options });
|
|
||||||
const handle = await this._scheduleWaitTask(params);
|
|
||||||
if (!handle.asElement()) {
|
|
||||||
await handle.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return handle.asElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
async waitForXPath(xpath: string, options: { visible?: boolean, hidden?: boolean, timeout?: number } = {}): Promise<ElementHandle | null> {
|
|
||||||
const params = waitForSelectorOrXPath(xpath, true /* isXPath */, { timeout: this._timeoutSettings.timeout(), ...options });
|
|
||||||
const handle = await this._scheduleWaitTask(params);
|
|
||||||
if (!handle.asElement()) {
|
|
||||||
await handle.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return handle.asElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForFunction(pageFunction: Function | string, options: { polling?: string | number; timeout?: number; } = {}, ...args): Promise<JSHandle> {
|
|
||||||
const {
|
|
||||||
polling = 'raf',
|
|
||||||
timeout = this._timeoutSettings.timeout(),
|
|
||||||
} = options;
|
|
||||||
const params: WaitTaskParams = {
|
|
||||||
predicateBody: pageFunction,
|
|
||||||
title: 'function',
|
|
||||||
polling,
|
|
||||||
timeout,
|
|
||||||
args
|
|
||||||
};
|
|
||||||
return this._scheduleWaitTask(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _scheduleWaitTask(params: WaitTaskParams): Promise<JSHandle> {
|
|
||||||
const task = new WaitTask(params, () => this._waitTasks.delete(task));
|
const task = new WaitTask(params, () => this._waitTasks.delete(task));
|
||||||
this._waitTasks.add(task);
|
this._waitTasks.add(task);
|
||||||
if (this._context)
|
if (this._context)
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CDPSession } from './Connection';
|
import { CDPSession } from './Connection';
|
||||||
import { DOMWorld } from './DOMWorld';
|
|
||||||
import { Frame } from './Frame';
|
import { Frame } from './Frame';
|
||||||
import { assert, helper } from '../helper';
|
import { assert, helper } from '../helper';
|
||||||
import { valueFromRemoteObject, getExceptionMessage } from './protocolHelper';
|
import { valueFromRemoteObject, getExceptionMessage } from './protocolHelper';
|
||||||
@ -32,19 +31,19 @@ const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
|
|||||||
|
|
||||||
export class ExecutionContext implements types.EvaluationContext<JSHandle> {
|
export class ExecutionContext implements types.EvaluationContext<JSHandle> {
|
||||||
_client: CDPSession;
|
_client: CDPSession;
|
||||||
_world: DOMWorld;
|
private _frame: Frame;
|
||||||
private _injectedPromise: Promise<JSHandle> | null = null;
|
private _injectedPromise: Promise<JSHandle> | null = null;
|
||||||
private _documentPromise: Promise<ElementHandle> | null = null;
|
private _documentPromise: Promise<ElementHandle> | null = null;
|
||||||
private _contextId: number;
|
private _contextId: number;
|
||||||
|
|
||||||
constructor(client: CDPSession, contextPayload: Protocol.Runtime.ExecutionContextDescription, world: DOMWorld | null) {
|
constructor(client: CDPSession, contextPayload: Protocol.Runtime.ExecutionContextDescription, frame: Frame | null) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._world = world;
|
this._frame = frame;
|
||||||
this._contextId = contextPayload.id;
|
this._contextId = contextPayload.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return this._world ? this._world.frame() : null;
|
return this._frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate: types.Evaluate<JSHandle> = (pageFunction, ...args) => {
|
evaluate: types.Evaluate<JSHandle> = (pageFunction, ...args) => {
|
||||||
@ -154,7 +153,7 @@ export class ExecutionContext implements types.EvaluationContext<JSHandle> {
|
|||||||
|
|
||||||
async _adoptElementHandle(elementHandle: ElementHandle): Promise<ElementHandle> {
|
async _adoptElementHandle(elementHandle: ElementHandle): Promise<ElementHandle> {
|
||||||
assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
|
assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
|
||||||
assert(this._world, 'Cannot adopt handle without DOMWorld');
|
assert(this._frame, 'Cannot adopt handle without a Frame');
|
||||||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||||
objectId: elementHandle._remoteObject.objectId,
|
objectId: elementHandle._remoteObject.objectId,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import { ElementHandle, JSHandle } from './JSHandle';
|
|||||||
import { Response } from './NetworkManager';
|
import { Response } from './NetworkManager';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { LifecycleWatcher } from './LifecycleWatcher';
|
import { LifecycleWatcher } from './LifecycleWatcher';
|
||||||
|
import { waitForSelectorOrXPath, WaitTaskParams } from '../waitTask';
|
||||||
|
|
||||||
const readFileAsync = helper.promisify(fs.readFile);
|
const readFileAsync = helper.promisify(fs.readFile);
|
||||||
|
|
||||||
@ -51,8 +52,8 @@ export class Frame {
|
|||||||
this._parentFrame = parentFrame;
|
this._parentFrame = parentFrame;
|
||||||
this._id = frameId;
|
this._id = frameId;
|
||||||
|
|
||||||
this._mainWorld = new DOMWorld(this, frameManager._timeoutSettings);
|
this._mainWorld = new DOMWorld(this);
|
||||||
this._secondaryWorld = new DOMWorld(this, frameManager._timeoutSettings);
|
this._secondaryWorld = new DOMWorld(this);
|
||||||
|
|
||||||
if (this._parentFrame)
|
if (this._parentFrame)
|
||||||
this._parentFrame._childFrames.add(this);
|
this._parentFrame._childFrames.add(this);
|
||||||
@ -383,11 +384,13 @@ export class Frame {
|
|||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
||||||
const handle = await this._secondaryWorld.waitForSelector(selector, options);
|
const params = waitForSelectorOrXPath(selector, false /* isXPath */, { timeout: this._frameManager._timeoutSettings.timeout(), ...options });
|
||||||
if (!handle)
|
const handle = await this._secondaryWorld.scheduleWaitTask(params);
|
||||||
return null;
|
let result = null;
|
||||||
|
if (handle.asElement()) {
|
||||||
const mainExecutionContext = await this._mainWorld.executionContext();
|
const mainExecutionContext = await this._mainWorld.executionContext();
|
||||||
const result = await mainExecutionContext._adoptElementHandle(handle);
|
result = await mainExecutionContext._adoptElementHandle(handle.asElement());
|
||||||
|
}
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -396,11 +399,13 @@ export class Frame {
|
|||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
||||||
const handle = await this._secondaryWorld.waitForXPath(xpath, options);
|
const params = waitForSelectorOrXPath(xpath, true /* isXPath */, { timeout: this._frameManager._timeoutSettings.timeout(), ...options });
|
||||||
if (!handle)
|
const handle = await this._secondaryWorld.scheduleWaitTask(params);
|
||||||
return null;
|
let result = null;
|
||||||
|
if (handle.asElement()) {
|
||||||
const mainExecutionContext = await this._mainWorld.executionContext();
|
const mainExecutionContext = await this._mainWorld.executionContext();
|
||||||
const result = await mainExecutionContext._adoptElementHandle(handle);
|
result = await mainExecutionContext._adoptElementHandle(handle.asElement());
|
||||||
|
}
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -409,7 +414,18 @@ export class Frame {
|
|||||||
pageFunction: Function | string,
|
pageFunction: Function | string,
|
||||||
options: { polling?: string | number; timeout?: number; } = {},
|
options: { polling?: string | number; timeout?: number; } = {},
|
||||||
...args): Promise<JSHandle> {
|
...args): Promise<JSHandle> {
|
||||||
return this._mainWorld.waitForFunction(pageFunction, options, ...args);
|
const {
|
||||||
|
polling = 'raf',
|
||||||
|
timeout = this._frameManager._timeoutSettings.timeout(),
|
||||||
|
} = options;
|
||||||
|
const params: WaitTaskParams = {
|
||||||
|
predicateBody: pageFunction,
|
||||||
|
title: 'function',
|
||||||
|
polling,
|
||||||
|
timeout,
|
||||||
|
args
|
||||||
|
};
|
||||||
|
return this._mainWorld.scheduleWaitTask(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
async title(): Promise<string> {
|
async title(): Promise<string> {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import { LifecycleWatcher } from './LifecycleWatcher';
|
|||||||
import { NetworkManager, Response } from './NetworkManager';
|
import { NetworkManager, Response } from './NetworkManager';
|
||||||
import { Page } from './Page';
|
import { Page } from './Page';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
import { DOMWorld } from './DOMWorld';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
||||||
@ -258,11 +259,11 @@ export class FrameManager extends EventEmitter {
|
|||||||
_onExecutionContextCreated(contextPayload) {
|
_onExecutionContextCreated(contextPayload) {
|
||||||
const frameId = contextPayload.auxData ? contextPayload.auxData.frameId : null;
|
const frameId = contextPayload.auxData ? contextPayload.auxData.frameId : null;
|
||||||
const frame = this._frames.get(frameId) || null;
|
const frame = this._frames.get(frameId) || null;
|
||||||
let world = null;
|
let world: DOMWorld | null = null;
|
||||||
if (frame) {
|
if (frame) {
|
||||||
if (contextPayload.auxData && !!contextPayload.auxData['isDefault']) {
|
if (contextPayload.auxData && !!contextPayload.auxData['isDefault']) {
|
||||||
world = frame._mainWorld;
|
world = frame._mainWorld;
|
||||||
} else if (contextPayload.name === UTILITY_WORLD_NAME && !frame._secondaryWorld._hasContext()) {
|
} else if (contextPayload.name === UTILITY_WORLD_NAME && !frame._secondaryWorld._context) {
|
||||||
// In case of multiple sessions to the same target, there's a race between
|
// In case of multiple sessions to the same target, there's a race between
|
||||||
// connections so we might end up creating multiple isolated worlds.
|
// connections so we might end up creating multiple isolated worlds.
|
||||||
// We can use either.
|
// We can use either.
|
||||||
@ -271,7 +272,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated')
|
if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated')
|
||||||
this._isolatedWorlds.add(contextPayload.name);
|
this._isolatedWorlds.add(contextPayload.name);
|
||||||
const context: ExecutionContext = new ExecutionContext(this._client, contextPayload, world);
|
const context: ExecutionContext = new ExecutionContext(this._client, contextPayload, frame);
|
||||||
if (world)
|
if (world)
|
||||||
world._setContext(context);
|
world._setContext(context);
|
||||||
this._contextIdToContext.set(contextPayload.id, context);
|
this._contextIdToContext.set(contextPayload.id, context);
|
||||||
@ -282,16 +283,18 @@ export class FrameManager extends EventEmitter {
|
|||||||
if (!context)
|
if (!context)
|
||||||
return;
|
return;
|
||||||
this._contextIdToContext.delete(executionContextId);
|
this._contextIdToContext.delete(executionContextId);
|
||||||
if (context._world)
|
const frame = context.frame();
|
||||||
context._world._setContext(null);
|
if (frame) {
|
||||||
|
if (frame._mainWorld._context === context)
|
||||||
|
frame._mainWorld._setContext(null);
|
||||||
|
if (frame._secondaryWorld._context === context)
|
||||||
|
frame._secondaryWorld._setContext(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onExecutionContextsCleared() {
|
_onExecutionContextsCleared() {
|
||||||
for (const context of this._contextIdToContext.values()) {
|
for (const contextId of Array.from(this._contextIdToContext.keys()))
|
||||||
if (context._world)
|
this._onExecutionContextDestroyed(contextId);
|
||||||
context._world._setContext(null);
|
|
||||||
}
|
|
||||||
this._contextIdToContext.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executionContextById(contextId: number): ExecutionContext {
|
executionContextById(contextId: number): ExecutionContext {
|
||||||
|
|||||||
@ -15,9 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ElementHandle, JSHandle} from './JSHandle';
|
import { JSHandle } from './JSHandle';
|
||||||
import { ExecutionContext } from './ExecutionContext';
|
import { ExecutionContext } from './ExecutionContext';
|
||||||
import { WaitTaskParams, WaitTask, waitForSelectorOrXPath } from '../waitTask';
|
import { WaitTaskParams, WaitTask } from '../waitTask';
|
||||||
|
|
||||||
export class DOMWorld {
|
export class DOMWorld {
|
||||||
_frame: any;
|
_frame: any;
|
||||||
@ -69,42 +69,7 @@ export class DOMWorld {
|
|||||||
return this._contextPromise;
|
return this._contextPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForSelector(selector: string, options: { visible?: boolean; hidden?: boolean; timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
scheduleWaitTask(params: WaitTaskParams): Promise<JSHandle> {
|
||||||
const params = waitForSelectorOrXPath(selector, false /* isXPath */, { timeout: this._timeoutSettings.timeout(), ...options });
|
|
||||||
const handle = await this._scheduleWaitTask(params);
|
|
||||||
if (!handle.asElement()) {
|
|
||||||
await handle.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return handle.asElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
async waitForXPath(xpath: string, options: { visible?: boolean, hidden?: boolean, timeout?: number } = {}): Promise<ElementHandle | null> {
|
|
||||||
const params = waitForSelectorOrXPath(xpath, true /* isXPath */, { timeout: this._timeoutSettings.timeout(), ...options });
|
|
||||||
const handle = await this._scheduleWaitTask(params);
|
|
||||||
if (!handle.asElement()) {
|
|
||||||
await handle.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return handle.asElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForFunction(pageFunction: Function | string, options: { polling?: string | number; timeout?: number; } | undefined = {}, ...args): Promise<JSHandle> {
|
|
||||||
const {
|
|
||||||
polling = 'raf',
|
|
||||||
timeout = this._timeoutSettings.timeout(),
|
|
||||||
} = options;
|
|
||||||
const params: WaitTaskParams = {
|
|
||||||
predicateBody: pageFunction,
|
|
||||||
title: 'function',
|
|
||||||
polling,
|
|
||||||
timeout,
|
|
||||||
args
|
|
||||||
};
|
|
||||||
return this._scheduleWaitTask(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _scheduleWaitTask(params: WaitTaskParams): Promise<JSHandle> {
|
|
||||||
const task = new WaitTask(params, () => this._waitTasks.delete(task));
|
const task = new WaitTask(params, () => this._waitTasks.delete(task));
|
||||||
this._waitTasks.add(task);
|
this._waitTasks.add(task);
|
||||||
if (this._context)
|
if (this._context)
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import { TimeoutSettings } from '../TimeoutSettings';
|
|||||||
import { NetworkManager } from './NetworkManager';
|
import { NetworkManager } from './NetworkManager';
|
||||||
import { MultiClickOptions, ClickOptions, SelectOption } from '../input';
|
import { MultiClickOptions, ClickOptions, SelectOption } from '../input';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
|
import { waitForSelectorOrXPath, WaitTaskParams } from '../waitTask';
|
||||||
|
|
||||||
const readFileAsync = helper.promisify(fs.readFile);
|
const readFileAsync = helper.promisify(fs.readFile);
|
||||||
|
|
||||||
@ -375,16 +376,48 @@ export class Frame {
|
|||||||
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
|
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForFunction(pageFunction: Function | string, options: { polling?: string | number; timeout?: number; } | undefined = {}, ...args): Promise<JSHandle> {
|
waitForFunction(
|
||||||
return this._mainWorld.waitForFunction(pageFunction, options, ...args);
|
pageFunction: Function | string,
|
||||||
|
options: { polling?: string | number; timeout?: number; } = {},
|
||||||
|
...args): Promise<JSHandle> {
|
||||||
|
const {
|
||||||
|
polling = 'raf',
|
||||||
|
timeout = this._frameManager._timeoutSettings.timeout(),
|
||||||
|
} = options;
|
||||||
|
const params: WaitTaskParams = {
|
||||||
|
predicateBody: pageFunction,
|
||||||
|
title: 'function',
|
||||||
|
polling,
|
||||||
|
timeout,
|
||||||
|
args
|
||||||
|
};
|
||||||
|
return this._mainWorld.scheduleWaitTask(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForSelector(selector: string, options: { timeout?: number; visible?: boolean; hidden?: boolean; } | undefined): Promise<ElementHandle> {
|
async waitForSelector(selector: string, options: {
|
||||||
return this._mainWorld.waitForSelector(selector, options);
|
visible?: boolean;
|
||||||
|
hidden?: boolean;
|
||||||
|
timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
||||||
|
const params = waitForSelectorOrXPath(selector, false /* isXPath */, { timeout: this._frameManager._timeoutSettings.timeout(), ...options });
|
||||||
|
const handle = await this._mainWorld.scheduleWaitTask(params);
|
||||||
|
if (!handle.asElement()) {
|
||||||
|
await handle.dispose();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return handle.asElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForXPath(xpath: string, options: { timeout?: number; visible?: boolean; hidden?: boolean; } | undefined): Promise<ElementHandle> {
|
async waitForXPath(xpath: string, options: {
|
||||||
return this._mainWorld.waitForXPath(xpath, options);
|
visible?: boolean;
|
||||||
|
hidden?: boolean;
|
||||||
|
timeout?: number; } | undefined): Promise<ElementHandle | null> {
|
||||||
|
const params = waitForSelectorOrXPath(xpath, true /* isXPath */, { timeout: this._frameManager._timeoutSettings.timeout(), ...options });
|
||||||
|
const handle = await this._mainWorld.scheduleWaitTask(params);
|
||||||
|
if (!handle.asElement()) {
|
||||||
|
await handle.dispose();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return handle.asElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
async content(): Promise<string> {
|
async content(): Promise<string> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user