chore: reuse ExecutionContext between browsers (#102)
This commit is contained in:
parent
dfc5592910
commit
06ba0f7a7f
|
|
@ -16,45 +16,28 @@
|
|||
*/
|
||||
|
||||
import { CDPSession } from './Connection';
|
||||
import { Frame } from './FrameManager';
|
||||
import { helper } from '../helper';
|
||||
import { valueFromRemoteObject, getExceptionMessage } from './protocolHelper';
|
||||
import { createJSHandle, ElementHandle, JSHandle } from './JSHandle';
|
||||
import { createJSHandle, JSHandle, ElementHandle } from './JSHandle';
|
||||
import { Protocol } from './protocol';
|
||||
import * as injectedSource from '../generated/injectedSource';
|
||||
import * as cssSelectorEngineSource from '../generated/cssSelectorEngineSource';
|
||||
import * as xpathSelectorEngineSource from '../generated/xpathSelectorEngineSource';
|
||||
import * as types from '../types';
|
||||
import { Response } from './NetworkManager';
|
||||
import * as js from '../javascript';
|
||||
|
||||
export const EVALUATION_SCRIPT_URL = '__playwright_evaluation_script__';
|
||||
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
|
||||
|
||||
export class ExecutionContext {
|
||||
_client: CDPSession;
|
||||
private _frame: Frame;
|
||||
private _injectedPromise: Promise<JSHandle> | null = null;
|
||||
private _documentPromise: Promise<ElementHandle> | null = null;
|
||||
private _contextId: number;
|
||||
export type ExecutionContext = js.ExecutionContext<JSHandle, ElementHandle, Response>;
|
||||
|
||||
constructor(client: CDPSession, contextPayload: Protocol.Runtime.ExecutionContextDescription, frame: Frame | null) {
|
||||
export class ExecutionContextDelegate implements js.ExecutionContextDelegate<JSHandle, ElementHandle, Response> {
|
||||
_client: CDPSession;
|
||||
_contextId: number;
|
||||
|
||||
constructor(client: CDPSession, contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
||||
this._client = client;
|
||||
this._frame = frame;
|
||||
this._contextId = contextPayload.id;
|
||||
}
|
||||
|
||||
frame(): Frame | null {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
evaluate: types.Evaluate<JSHandle> = (pageFunction, ...args) => {
|
||||
return this._evaluateInternal(true /* returnByValue */, pageFunction, ...args);
|
||||
}
|
||||
|
||||
evaluateHandle: types.EvaluateHandle<JSHandle> = (pageFunction, ...args) => {
|
||||
return this._evaluateInternal(false /* returnByValue */, pageFunction, ...args);
|
||||
}
|
||||
|
||||
async _evaluateInternal(returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||
async evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||
const suffix = `//# sourceURL=${EVALUATION_SCRIPT_URL}`;
|
||||
|
||||
if (helper.isString(pageFunction)) {
|
||||
|
|
@ -70,7 +53,7 @@ export class ExecutionContext {
|
|||
}).catch(rewriteError);
|
||||
if (exceptionDetails)
|
||||
throw new Error('Evaluation failed: ' + getExceptionMessage(exceptionDetails));
|
||||
return returnByValue ? valueFromRemoteObject(remoteObject) : createJSHandle(this, remoteObject);
|
||||
return returnByValue ? valueFromRemoteObject(remoteObject) : createJSHandle(context, remoteObject);
|
||||
}
|
||||
|
||||
if (typeof pageFunction !== 'function')
|
||||
|
|
@ -111,7 +94,7 @@ export class ExecutionContext {
|
|||
const { exceptionDetails, result: remoteObject } = await callFunctionOnPromise.catch(rewriteError);
|
||||
if (exceptionDetails)
|
||||
throw new Error('Evaluation failed: ' + getExceptionMessage(exceptionDetails));
|
||||
return returnByValue ? valueFromRemoteObject(remoteObject) : createJSHandle(this, remoteObject);
|
||||
return returnByValue ? valueFromRemoteObject(remoteObject) : createJSHandle(context, remoteObject);
|
||||
|
||||
function convertArgument(arg: any): any {
|
||||
if (typeof arg === 'bigint') // eslint-disable-line valid-typeof
|
||||
|
|
@ -126,7 +109,7 @@ export class ExecutionContext {
|
|||
return { unserializableValue: 'NaN' };
|
||||
const objectHandle = arg && (arg instanceof JSHandle) ? arg : null;
|
||||
if (objectHandle) {
|
||||
if (objectHandle._context !== this)
|
||||
if (objectHandle._context !== context)
|
||||
throw new Error('JSHandles can be evaluated only in the context they were created!');
|
||||
if (objectHandle._disposed)
|
||||
throw new Error('JSHandle is disposed!');
|
||||
|
|
@ -151,30 +134,11 @@ export class ExecutionContext {
|
|||
}
|
||||
}
|
||||
|
||||
async _adoptBackendNodeId(backendNodeId: Protocol.DOM.BackendNodeId): Promise<ElementHandle> {
|
||||
async adoptBackendNodeId(context: ExecutionContext, backendNodeId: Protocol.DOM.BackendNodeId) {
|
||||
const {object} = await this._client.send('DOM.resolveNode', {
|
||||
backendNodeId,
|
||||
executionContextId: this._contextId,
|
||||
});
|
||||
return createJSHandle(this, object) as ElementHandle;
|
||||
}
|
||||
|
||||
_injected(): Promise<JSHandle> {
|
||||
if (!this._injectedPromise) {
|
||||
const engineSources = [cssSelectorEngineSource.source, xpathSelectorEngineSource.source];
|
||||
const source = `
|
||||
new (${injectedSource.source})([
|
||||
${engineSources.join(',\n')}
|
||||
])
|
||||
`;
|
||||
this._injectedPromise = this.evaluateHandle(source);
|
||||
}
|
||||
return this._injectedPromise;
|
||||
}
|
||||
|
||||
_document(): Promise<ElementHandle> {
|
||||
if (!this._documentPromise)
|
||||
this._documentPromise = this.evaluateHandle('document').then(handle => handle.asElement()!);
|
||||
return this._documentPromise;
|
||||
return createJSHandle(context, object) as ElementHandle;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@ import { EventEmitter } from 'events';
|
|||
import { assert, debugError } from '../helper';
|
||||
import { TimeoutSettings } from '../TimeoutSettings';
|
||||
import { CDPSession } from './Connection';
|
||||
import { EVALUATION_SCRIPT_URL, ExecutionContext } from './ExecutionContext';
|
||||
import { EVALUATION_SCRIPT_URL, ExecutionContextDelegate, ExecutionContext } from './ExecutionContext';
|
||||
import * as frames from '../frames';
|
||||
import * as js from '../javascript';
|
||||
import { LifecycleWatcher } from './LifecycleWatcher';
|
||||
import { NetworkManager, Response } from './NetworkManager';
|
||||
import { Page } from './Page';
|
||||
import { Protocol } from './protocol';
|
||||
import { ElementHandle, JSHandle } from './JSHandle';
|
||||
import { ElementHandle, JSHandle, createJSHandle } from './JSHandle';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
||||
|
|
@ -44,9 +45,9 @@ type FrameData = {
|
|||
lifecycleEvents: Set<string>,
|
||||
};
|
||||
|
||||
export type Frame = frames.Frame<JSHandle, ElementHandle, ExecutionContext, Response>;
|
||||
export type Frame = frames.Frame<JSHandle, ElementHandle, Response>;
|
||||
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate<JSHandle, ElementHandle, ExecutionContext, Response> {
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate<JSHandle, ElementHandle, Response> {
|
||||
_client: CDPSession;
|
||||
private _page: Page;
|
||||
private _networkManager: NetworkManager;
|
||||
|
|
@ -186,7 +187,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate<J
|
|||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||
objectId: elementHandle._remoteObject.objectId,
|
||||
});
|
||||
return context._adoptBackendNodeId(nodeInfo.node.backendNodeId);
|
||||
return (context._delegate as ExecutionContextDelegate).adoptBackendNodeId(context, nodeInfo.node.backendNodeId);
|
||||
}
|
||||
|
||||
_onLifecycleEvent(event: Protocol.Page.lifecycleEventPayload) {
|
||||
|
|
@ -328,7 +329,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate<J
|
|||
const frame = this._frames.get(frameId) || null;
|
||||
if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated')
|
||||
this._isolatedWorlds.add(contextPayload.name);
|
||||
const context: ExecutionContext = new ExecutionContext(this._client, contextPayload, frame);
|
||||
const context: ExecutionContext = new js.ExecutionContext(new ExecutionContextDelegate(this._client, contextPayload), frame);
|
||||
if (frame) {
|
||||
if (contextPayload.auxData && !!contextPayload.auxData['isDefault'])
|
||||
frame._contextCreated('main', context);
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import Injected from '../injected/injected';
|
|||
import * as input from '../input';
|
||||
import * as types from '../types';
|
||||
import { CDPSession } from './Connection';
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { Frame } from './FrameManager';
|
||||
import { FrameManager } from './FrameManager';
|
||||
import { Page } from './Page';
|
||||
import { Protocol } from './protocol';
|
||||
import { releaseObject, valueFromRemoteObject } from './protocolHelper';
|
||||
import { ExecutionContext, ExecutionContextDelegate } from './ExecutionContext';
|
||||
|
||||
type SelectorRoot = Element | ShadowRoot | Document;
|
||||
|
||||
|
|
@ -35,12 +35,13 @@ type Point = {
|
|||
};
|
||||
|
||||
export function createJSHandle(context: ExecutionContext, remoteObject: Protocol.Runtime.RemoteObject): JSHandle {
|
||||
const delegate = context._delegate as ExecutionContextDelegate;
|
||||
const frame = context.frame();
|
||||
if (remoteObject.subtype === 'node' && frame) {
|
||||
const frameManager = frame._delegate as FrameManager;
|
||||
return new ElementHandle(context, context._client, remoteObject, frameManager.page(), frameManager);
|
||||
return new ElementHandle(context, delegate._client, remoteObject, frameManager.page(), frameManager);
|
||||
}
|
||||
return new JSHandle(context, context._client, remoteObject);
|
||||
return new JSHandle(context, delegate._client, remoteObject);
|
||||
}
|
||||
|
||||
export class JSHandle {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import { Target } from './Target';
|
|||
import { TaskQueue } from './TaskQueue';
|
||||
import * as input from '../input';
|
||||
import * as types from '../types';
|
||||
import { ExecutionContextDelegate } from './ExecutionContext';
|
||||
|
||||
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||
|
||||
|
|
@ -155,7 +156,7 @@ export class Page extends EventEmitter {
|
|||
return;
|
||||
const frame = this._frameManager.frame(event.frameId);
|
||||
const context = await frame._utilityContext();
|
||||
const handle = await context._adoptBackendNodeId(event.backendNodeId);
|
||||
const handle = await (context._delegate as ExecutionContextDelegate).adoptBackendNodeId(context, event.backendNodeId);
|
||||
const interceptors = Array.from(this._fileChooserInterceptors);
|
||||
this._fileChooserInterceptors.clear();
|
||||
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export { BrowserFetcher } from './BrowserFetcher';
|
|||
export { Chromium } from './features/chromium';
|
||||
export { CDPSession } from './Connection';
|
||||
export { Dialog } from './Dialog';
|
||||
export { ExecutionContext } from './ExecutionContext';
|
||||
export { ExecutionContext } from '../javascript';
|
||||
export { Accessibility } from './features/accessibility';
|
||||
export { Coverage } from './features/coverage';
|
||||
export { Overrides } from './features/overrides';
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@
|
|||
*/
|
||||
import { EventEmitter } from 'events';
|
||||
import { CDPSession, Connection } from '../Connection';
|
||||
import { ExecutionContext } from '../ExecutionContext';
|
||||
import { debugError } from '../../helper';
|
||||
import { JSHandle } from '../JSHandle';
|
||||
import { Protocol } from '../protocol';
|
||||
import { Events } from '../events';
|
||||
import * as types from '../../types';
|
||||
import * as js from '../../javascript';
|
||||
import { ExecutionContext, ExecutionContextDelegate } from '../ExecutionContext';
|
||||
|
||||
type AddToConsoleCallback = (type: string, args: JSHandle[], stackTrace: Protocol.Runtime.StackTrace | undefined) => void;
|
||||
type HandleExceptionCallback = (exceptionDetails: Protocol.Runtime.ExceptionDetails) => void;
|
||||
|
|
@ -68,7 +69,7 @@ export class Worker extends EventEmitter {
|
|||
let jsHandleFactory: (o: Protocol.Runtime.RemoteObject) => JSHandle;
|
||||
this._client.once('Runtime.executionContextCreated', async event => {
|
||||
jsHandleFactory = remoteObject => new JSHandle(executionContext, client, remoteObject);
|
||||
const executionContext = new ExecutionContext(client, event.context, null);
|
||||
const executionContext = new js.ExecutionContext(new ExecutionContextDelegate(client, event.context), null);
|
||||
this._executionContextCallback(executionContext);
|
||||
});
|
||||
// This might fail if the target is closed before we recieve all execution contexts.
|
||||
|
|
|
|||
|
|
@ -16,33 +16,42 @@
|
|||
*/
|
||||
|
||||
import {helper} from '../helper';
|
||||
import {JSHandle, createHandle, ElementHandle} from './JSHandle';
|
||||
import { Frame } from './FrameManager';
|
||||
import * as injectedSource from '../generated/injectedSource';
|
||||
import * as cssSelectorEngineSource from '../generated/cssSelectorEngineSource';
|
||||
import * as xpathSelectorEngineSource from '../generated/xpathSelectorEngineSource';
|
||||
import * as types from '../types';
|
||||
import { JSHandle, createHandle, ElementHandle } from './JSHandle';
|
||||
import { Response } from './NetworkManager';
|
||||
import * as js from '../javascript';
|
||||
import { JugglerSession } from './Connection';
|
||||
|
||||
export class ExecutionContext {
|
||||
_session: any;
|
||||
_frame: Frame;
|
||||
export type ExecutionContext = js.ExecutionContext<JSHandle, ElementHandle, Response>;
|
||||
|
||||
export class ExecutionContextDelegate implements js.ExecutionContextDelegate<JSHandle, ElementHandle, Response> {
|
||||
_session: JugglerSession;
|
||||
_executionContextId: string;
|
||||
private _injectedPromise: Promise<JSHandle> | null = null;
|
||||
private _documentPromise: Promise<ElementHandle> | null = null;
|
||||
|
||||
constructor(session: any, frame: Frame | null, executionContextId: string) {
|
||||
constructor(session: JugglerSession, executionContextId: string) {
|
||||
this._session = session;
|
||||
this._frame = frame;
|
||||
this._executionContextId = executionContextId;
|
||||
}
|
||||
|
||||
evaluateHandle: types.EvaluateHandle<JSHandle> = async (pageFunction, ...args) => {
|
||||
async evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||
if (returnByValue) {
|
||||
try {
|
||||
const handle = await this.evaluate(context, false /* returnByValue */, pageFunction, ...args as any);
|
||||
const result = await handle.jsonValue();
|
||||
await handle.dispose();
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (e.message.includes('cyclic object value') || e.message.includes('Object is not serializable'))
|
||||
return undefined;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (helper.isString(pageFunction)) {
|
||||
const payload = await this._session.send('Runtime.evaluate', {
|
||||
expression: pageFunction.trim(),
|
||||
executionContextId: this._executionContextId,
|
||||
}).catch(rewriteError);
|
||||
return createHandle(this, payload.result, payload.exceptionDetails);
|
||||
return createHandle(context, payload.result, payload.exceptionDetails);
|
||||
}
|
||||
if (typeof pageFunction !== 'function')
|
||||
throw new Error(`Expected to get |string| or |function| as the first argument, but got "${pageFunction}" instead.`);
|
||||
|
|
@ -66,7 +75,7 @@ export class ExecutionContext {
|
|||
}
|
||||
const protocolArgs = args.map(arg => {
|
||||
if (arg instanceof JSHandle) {
|
||||
if (arg._context !== this)
|
||||
if (arg._context !== context)
|
||||
throw new Error('JSHandles can be evaluated only in the context they were created!');
|
||||
if (arg._disposed)
|
||||
throw new Error('JSHandle is disposed!');
|
||||
|
|
@ -95,7 +104,7 @@ export class ExecutionContext {
|
|||
throw err;
|
||||
}
|
||||
const payload = await callFunctionPromise.catch(rewriteError);
|
||||
return createHandle(this, payload.result, payload.exceptionDetails);
|
||||
return createHandle(context, payload.result, payload.exceptionDetails);
|
||||
|
||||
function rewriteError(error) {
|
||||
if (error.message.includes('Failed to find execution context with id'))
|
||||
|
|
@ -103,40 +112,4 @@ export class ExecutionContext {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
frame() {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
evaluate: types.Evaluate<JSHandle> = async (pageFunction, ...args) => {
|
||||
try {
|
||||
const handle = await this.evaluateHandle(pageFunction, ...args as any);
|
||||
const result = await handle.jsonValue();
|
||||
await handle.dispose();
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (e.message.includes('cyclic object value') || e.message.includes('Object is not serializable'))
|
||||
return undefined;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
_injected(): Promise<JSHandle> {
|
||||
if (!this._injectedPromise) {
|
||||
const engineSources = [cssSelectorEngineSource.source, xpathSelectorEngineSource.source];
|
||||
const source = `
|
||||
new (${injectedSource.source})([
|
||||
${engineSources.join(',\n')}
|
||||
])
|
||||
`;
|
||||
this._injectedPromise = this.evaluateHandle(source);
|
||||
}
|
||||
return this._injectedPromise;
|
||||
}
|
||||
|
||||
_document(): Promise<ElementHandle> {
|
||||
if (!this._documentPromise)
|
||||
this._documentPromise = this.evaluateHandle('document').then(handle => handle.asElement()!);
|
||||
return this._documentPromise;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,13 @@ import { Page } from './Page';
|
|||
import {RegisteredListener, helper, assert} from '../helper';
|
||||
import {TimeoutError} from '../Errors';
|
||||
import {EventEmitter} from 'events';
|
||||
import {ExecutionContext} from './ExecutionContext';
|
||||
import { ExecutionContext, ExecutionContextDelegate } from './ExecutionContext';
|
||||
import {NavigationWatchdog, NextNavigationWatchdog} from './NavigationWatchdog';
|
||||
import { JSHandle, ElementHandle } from './JSHandle';
|
||||
import { TimeoutSettings } from '../TimeoutSettings';
|
||||
import { Response } from './NetworkManager';
|
||||
import * as frames from '../frames';
|
||||
import * as js from '../javascript';
|
||||
|
||||
export const FrameManagerEvents = {
|
||||
FrameNavigated: Symbol('FrameManagerEvents.FrameNavigated'),
|
||||
|
|
@ -41,9 +43,9 @@ type FrameData = {
|
|||
firedEvents: Set<string>,
|
||||
};
|
||||
|
||||
export type Frame = frames.Frame<JSHandle, ElementHandle, ExecutionContext, Response>;
|
||||
export type Frame = frames.Frame<JSHandle, ElementHandle, Response>;
|
||||
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate<JSHandle, ElementHandle, ExecutionContext, Response> {
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate<JSHandle, ElementHandle, Response> {
|
||||
_session: JugglerSession;
|
||||
_page: Page;
|
||||
_networkManager: any;
|
||||
|
|
@ -80,7 +82,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate<J
|
|||
_onExecutionContextCreated({executionContextId, auxData}) {
|
||||
const frameId = auxData ? auxData.frameId : null;
|
||||
const frame = this._frames.get(frameId) || null;
|
||||
const context = new ExecutionContext(this._session, frame, executionContextId);
|
||||
const context = new js.ExecutionContext(new ExecutionContextDelegate(this._session, executionContextId), frame);
|
||||
if (frame) {
|
||||
frame._contextCreated('main', context);
|
||||
frame._contextCreated('utility', context);
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import Injected from '../injected/injected';
|
|||
import * as input from '../input';
|
||||
import * as types from '../types';
|
||||
import { JugglerSession } from './Connection';
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { Frame, FrameManager } from './FrameManager';
|
||||
import { Page } from './Page';
|
||||
import { ExecutionContext, ExecutionContextDelegate } from './ExecutionContext';
|
||||
|
||||
type SelectorRoot = Element | ShadowRoot | Document;
|
||||
|
||||
|
|
@ -38,8 +38,9 @@ export class JSHandle {
|
|||
|
||||
constructor(context: ExecutionContext, payload: any) {
|
||||
this._context = context;
|
||||
this._session = this._context._session;
|
||||
this._executionContextId = this._context._executionContextId;
|
||||
const delegate = context._delegate as ExecutionContextDelegate;
|
||||
this._session = delegate._session;
|
||||
this._executionContextId = delegate._executionContextId;
|
||||
this._objectId = payload.objectId;
|
||||
this._type = payload.type;
|
||||
this._subtype = payload.subtype;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export { Keyboard, Mouse } from '../input';
|
|||
export { Browser, BrowserContext, Target } from './Browser';
|
||||
export { BrowserFetcher } from './BrowserFetcher';
|
||||
export { Dialog } from './Dialog';
|
||||
export { ExecutionContext } from './ExecutionContext';
|
||||
export { ExecutionContext } from '../javascript';
|
||||
export { Accessibility } from './features/accessibility';
|
||||
export { Interception } from './features/interception';
|
||||
export { Permissions } from './features/permissions';
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
import * as types from './types';
|
||||
import * as fs from 'fs';
|
||||
import * as js from './javascript';
|
||||
import { helper, assert } from './helper';
|
||||
import { ClickOptions, MultiClickOptions, PointerActionOptions, SelectOption } from './input';
|
||||
import { waitForSelectorOrXPath, WaitTaskParams, WaitTask } from './waitTask';
|
||||
|
|
@ -25,11 +26,11 @@ import { TimeoutSettings } from './TimeoutSettings';
|
|||
const readFileAsync = helper.promisify(fs.readFile);
|
||||
|
||||
type WorldType = 'main' | 'utility';
|
||||
type World<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle>, ExecutionContext> = {
|
||||
contextPromise: Promise<ExecutionContext>;
|
||||
contextResolveCallback: (c: ExecutionContext) => void;
|
||||
context: ExecutionContext | null;
|
||||
waitTasks: Set<WaitTask<JSHandle, ElementHandle>>;
|
||||
type World<JSHandle extends types.JSHandle<JSHandle, ElementHandle, Response>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle, Response>, Response> = {
|
||||
contextPromise: Promise<js.ExecutionContext<JSHandle, ElementHandle, Response>>;
|
||||
contextResolveCallback: (c: js.ExecutionContext<JSHandle, ElementHandle, Response>) => void;
|
||||
context: js.ExecutionContext<JSHandle, ElementHandle, Response> | null;
|
||||
waitTasks: Set<WaitTask<JSHandle, ElementHandle, Response>>;
|
||||
};
|
||||
|
||||
export type NavigateOptions = {
|
||||
|
|
@ -41,24 +42,24 @@ export type GotoOptions = NavigateOptions & {
|
|||
referer?: string,
|
||||
};
|
||||
|
||||
export interface FrameDelegate<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle>, ExecutionContext extends types.ExecutionContext<JSHandle, ElementHandle>, Response> {
|
||||
export interface FrameDelegate<JSHandle extends types.JSHandle<JSHandle, ElementHandle, Response>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle, Response>, Response> {
|
||||
timeoutSettings(): TimeoutSettings;
|
||||
navigateFrame(frame: Frame<JSHandle, ElementHandle, ExecutionContext, Response>, url: string, options?: GotoOptions): Promise<Response | null>;
|
||||
waitForFrameNavigation(frame: Frame<JSHandle, ElementHandle, ExecutionContext, Response>, options?: NavigateOptions): Promise<Response | null>;
|
||||
setFrameContent(frame: Frame<JSHandle, ElementHandle, ExecutionContext, Response>, html: string, options?: NavigateOptions): Promise<void>;
|
||||
adoptElementHandle(elementHandle: ElementHandle, context: ExecutionContext): Promise<ElementHandle>;
|
||||
navigateFrame(frame: Frame<JSHandle, ElementHandle, Response>, url: string, options?: GotoOptions): Promise<Response | null>;
|
||||
waitForFrameNavigation(frame: Frame<JSHandle, ElementHandle, Response>, options?: NavigateOptions): Promise<Response | null>;
|
||||
setFrameContent(frame: Frame<JSHandle, ElementHandle, Response>, html: string, options?: NavigateOptions): Promise<void>;
|
||||
adoptElementHandle(elementHandle: ElementHandle, context: js.ExecutionContext<JSHandle, ElementHandle, Response>): Promise<ElementHandle>;
|
||||
}
|
||||
|
||||
export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle>, ExecutionContext extends types.ExecutionContext<JSHandle, ElementHandle>, Response> {
|
||||
_delegate: FrameDelegate<JSHandle, ElementHandle, ExecutionContext, Response>;
|
||||
private _parentFrame: Frame<JSHandle, ElementHandle, ExecutionContext, Response>;
|
||||
export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle, Response>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle, Response>, Response> {
|
||||
_delegate: FrameDelegate<JSHandle, ElementHandle, Response>;
|
||||
private _parentFrame: Frame<JSHandle, ElementHandle, Response>;
|
||||
private _url = '';
|
||||
private _detached = false;
|
||||
private _worlds = new Map<WorldType, World<JSHandle, ElementHandle, ExecutionContext>>();
|
||||
private _childFrames = new Set<Frame<JSHandle, ElementHandle, ExecutionContext, Response>>();
|
||||
private _worlds = new Map<WorldType, World<JSHandle, ElementHandle, Response>>();
|
||||
private _childFrames = new Set<Frame<JSHandle, ElementHandle, Response>>();
|
||||
private _name: string;
|
||||
|
||||
constructor(delegate: FrameDelegate<JSHandle, ElementHandle, ExecutionContext, Response>, parentFrame: Frame<JSHandle, ElementHandle, ExecutionContext, Response> | null) {
|
||||
constructor(delegate: FrameDelegate<JSHandle, ElementHandle, Response>, parentFrame: Frame<JSHandle, ElementHandle, Response> | null) {
|
||||
this._delegate = delegate;
|
||||
this._parentFrame = parentFrame;
|
||||
|
||||
|
|
@ -79,19 +80,19 @@ export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, Ele
|
|||
return this._delegate.waitForFrameNavigation(this, options);
|
||||
}
|
||||
|
||||
_mainContext(): Promise<ExecutionContext> {
|
||||
_mainContext(): Promise<js.ExecutionContext<JSHandle, ElementHandle, Response>> {
|
||||
if (this._detached)
|
||||
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
||||
return this._worlds.get('main').contextPromise;
|
||||
}
|
||||
|
||||
_utilityContext(): Promise<ExecutionContext> {
|
||||
_utilityContext(): Promise<js.ExecutionContext<JSHandle, ElementHandle, Response>> {
|
||||
if (this._detached)
|
||||
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
||||
return this._worlds.get('utility').contextPromise;
|
||||
}
|
||||
|
||||
executionContext(): Promise<ExecutionContext> {
|
||||
executionContext(): Promise<js.ExecutionContext<JSHandle, ElementHandle, Response>> {
|
||||
return this._mainContext();
|
||||
}
|
||||
|
||||
|
|
@ -159,11 +160,11 @@ export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, Ele
|
|||
return this._url;
|
||||
}
|
||||
|
||||
parentFrame(): Frame<JSHandle, ElementHandle, ExecutionContext, Response> | null {
|
||||
parentFrame(): Frame<JSHandle, ElementHandle, Response> | null {
|
||||
return this._parentFrame;
|
||||
}
|
||||
|
||||
childFrames(): Frame<JSHandle, ElementHandle, ExecutionContext, Response>[] {
|
||||
childFrames(): Frame<JSHandle, ElementHandle, Response>[] {
|
||||
return Array.from(this._childFrames);
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +451,7 @@ export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, Ele
|
|||
this._parentFrame = null;
|
||||
}
|
||||
|
||||
private _scheduleWaitTask(params: WaitTaskParams, world: World<JSHandle, ElementHandle, ExecutionContext>): Promise<JSHandle> {
|
||||
private _scheduleWaitTask(params: WaitTaskParams, world: World<JSHandle, ElementHandle, Response>): Promise<JSHandle> {
|
||||
const task = new WaitTask(params, () => world.waitTasks.delete(task));
|
||||
world.waitTasks.add(task);
|
||||
if (world.context)
|
||||
|
|
@ -458,7 +459,7 @@ export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, Ele
|
|||
return task.promise;
|
||||
}
|
||||
|
||||
private _setContext(worldType: WorldType, context: ExecutionContext | null) {
|
||||
private _setContext(worldType: WorldType, context: js.ExecutionContext<JSHandle, ElementHandle, Response> | null) {
|
||||
const world = this._worlds.get(worldType);
|
||||
world.context = context;
|
||||
if (context) {
|
||||
|
|
@ -472,7 +473,7 @@ export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, Ele
|
|||
}
|
||||
}
|
||||
|
||||
_contextCreated(worldType: WorldType, context: ExecutionContext) {
|
||||
_contextCreated(worldType: WorldType, context: js.ExecutionContext<JSHandle, ElementHandle, Response>) {
|
||||
const world = this._worlds.get(worldType);
|
||||
// In case of multiple sessions to the same target, there's a race between
|
||||
// connections so we might end up creating multiple isolated worlds.
|
||||
|
|
@ -481,14 +482,14 @@ export class Frame<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, Ele
|
|||
this._setContext(worldType, context);
|
||||
}
|
||||
|
||||
_contextDestroyed(context: ExecutionContext) {
|
||||
_contextDestroyed(context: js.ExecutionContext<JSHandle, ElementHandle, Response>) {
|
||||
for (const [worldType, world] of this._worlds) {
|
||||
if (world.context === context)
|
||||
this._setContext(worldType, null);
|
||||
}
|
||||
}
|
||||
|
||||
private async _adoptElementHandle(elementHandle: ElementHandle, context: ExecutionContext, dispose: boolean): Promise<ElementHandle> {
|
||||
private async _adoptElementHandle(elementHandle: ElementHandle, context: js.ExecutionContext<JSHandle, ElementHandle, Response>, dispose: boolean): Promise<ElementHandle> {
|
||||
if (elementHandle.executionContext() === context)
|
||||
return elementHandle;
|
||||
const handle = this._delegate.adoptElementHandle(elementHandle, context);
|
||||
|
|
|
|||
56
src/javascript.ts
Normal file
56
src/javascript.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import * as injectedSource from './generated/injectedSource';
|
||||
import * as cssSelectorEngineSource from './generated/cssSelectorEngineSource';
|
||||
import * as xpathSelectorEngineSource from './generated/xpathSelectorEngineSource';
|
||||
|
||||
export interface ExecutionContextDelegate<JSHandle extends types.JSHandle<JSHandle, ElementHandle, Response>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle, Response>, Response> {
|
||||
evaluate(context: ExecutionContext<JSHandle, ElementHandle, Response>, returnByValue: boolean, pageFunction: string | Function, ...args: any[]): Promise<any>;
|
||||
}
|
||||
|
||||
export class ExecutionContext<JSHandle extends types.JSHandle<JSHandle, ElementHandle, Response>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle, Response>, Response> {
|
||||
_delegate: ExecutionContextDelegate<JSHandle, ElementHandle, Response>;
|
||||
private _frame: frames.Frame<JSHandle, ElementHandle, Response>;
|
||||
private _injectedPromise: Promise<JSHandle> | null = null;
|
||||
private _documentPromise: Promise<ElementHandle> | null = null;
|
||||
|
||||
constructor(delegate: ExecutionContextDelegate<JSHandle, ElementHandle, Response>, frame: frames.Frame<JSHandle, ElementHandle, Response> | null) {
|
||||
this._delegate = delegate;
|
||||
this._frame = frame;
|
||||
}
|
||||
|
||||
frame(): frames.Frame<JSHandle, ElementHandle, Response> | null {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
evaluate: types.Evaluate<JSHandle> = (pageFunction, ...args) => {
|
||||
return this._delegate.evaluate(this, true /* returnByValue */, pageFunction, ...args);
|
||||
}
|
||||
|
||||
evaluateHandle: types.EvaluateHandle<JSHandle> = (pageFunction, ...args) => {
|
||||
return this._delegate.evaluate(this, false /* returnByValue */, pageFunction, ...args);
|
||||
}
|
||||
|
||||
_injected(): Promise<JSHandle> {
|
||||
if (!this._injectedPromise) {
|
||||
const engineSources = [cssSelectorEngineSource.source, xpathSelectorEngineSource.source];
|
||||
const source = `
|
||||
new (${injectedSource.source})([
|
||||
${engineSources.join(',\n')}
|
||||
])
|
||||
`;
|
||||
this._injectedPromise = this.evaluateHandle(source);
|
||||
}
|
||||
return this._injectedPromise;
|
||||
}
|
||||
|
||||
_document(): Promise<ElementHandle> {
|
||||
if (!this._documentPromise)
|
||||
this._documentPromise = this.evaluateHandle('document').then(handle => handle.asElement()!);
|
||||
return this._documentPromise;
|
||||
}
|
||||
}
|
||||
|
||||
13
src/types.ts
13
src/types.ts
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import * as input from './input';
|
||||
import * as js from './javascript';
|
||||
|
||||
type Boxed<Args extends any[], Handle> = { [Index in keyof Args]: Args[Index] | Handle };
|
||||
type PageFunction<Args extends any[], R = any> = string | ((...args: Args) => R | Promise<R>);
|
||||
|
|
@ -14,19 +15,13 @@ export type $$Eval<Handle> = <Args extends any[], R>(selector: string, pageFunct
|
|||
export type EvaluateOn<Handle> = <Args extends any[], R>(pageFunction: PageFunctionOn<any, Args, R>, ...args: Boxed<Args, Handle>) => Promise<R>;
|
||||
export type EvaluateHandleOn<Handle> = <Args extends any[]>(pageFunction: PageFunctionOn<any, Args>, ...args: Boxed<Args, Handle>) => Promise<Handle>;
|
||||
|
||||
export interface ExecutionContext<Handle extends JSHandle<Handle, EHandle>, EHandle extends ElementHandle<Handle, EHandle>> {
|
||||
evaluate: Evaluate<Handle>;
|
||||
evaluateHandle: EvaluateHandle<Handle>;
|
||||
_document(): Promise<EHandle>;
|
||||
}
|
||||
|
||||
export interface JSHandle<Handle extends JSHandle<Handle, EHandle>, EHandle extends ElementHandle<Handle, EHandle>> {
|
||||
executionContext(): ExecutionContext<Handle, EHandle>;
|
||||
export interface JSHandle<Handle extends JSHandle<Handle, EHandle, Response>, EHandle extends ElementHandle<Handle, EHandle, Response>, Response> {
|
||||
executionContext(): js.ExecutionContext<Handle, EHandle, Response>;
|
||||
dispose(): Promise<void>;
|
||||
asElement(): EHandle | null;
|
||||
}
|
||||
|
||||
export interface ElementHandle<Handle extends JSHandle<Handle, EHandle>, EHandle extends ElementHandle<Handle, EHandle>> extends JSHandle<Handle, EHandle> {
|
||||
export interface ElementHandle<Handle extends JSHandle<Handle, EHandle, Response>, EHandle extends ElementHandle<Handle, EHandle, Response>, Response> extends JSHandle<Handle, EHandle, Response> {
|
||||
$(selector: string): Promise<EHandle | null>;
|
||||
$x(expression: string): Promise<EHandle[]>;
|
||||
$$(selector: string): Promise<EHandle[]>;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import { assert, helper } from './helper';
|
||||
import * as types from './types';
|
||||
import * as js from './javascript';
|
||||
import { TimeoutError } from './Errors';
|
||||
|
||||
export type WaitTaskParams = {
|
||||
|
|
@ -14,7 +15,7 @@ export type WaitTaskParams = {
|
|||
args: any[];
|
||||
};
|
||||
|
||||
export class WaitTask<JSHandle extends types.JSHandle<JSHandle, ElementHandle>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle>> {
|
||||
export class WaitTask<JSHandle extends types.JSHandle<JSHandle, ElementHandle, Response>, ElementHandle extends types.ElementHandle<JSHandle, ElementHandle, Response>, Response> {
|
||||
readonly promise: Promise<JSHandle>;
|
||||
private _cleanup: () => void;
|
||||
private _params: WaitTaskParams & { predicateBody: string };
|
||||
|
|
@ -56,7 +57,7 @@ export class WaitTask<JSHandle extends types.JSHandle<JSHandle, ElementHandle>,
|
|||
this._doCleanup();
|
||||
}
|
||||
|
||||
async rerun(context: types.ExecutionContext<JSHandle, ElementHandle>) {
|
||||
async rerun(context: js.ExecutionContext<JSHandle, ElementHandle, Response>) {
|
||||
const runCount = ++this._runCount;
|
||||
let success: JSHandle | null = null;
|
||||
let error = null;
|
||||
|
|
|
|||
|
|
@ -16,32 +16,27 @@
|
|||
*/
|
||||
|
||||
import { TargetSession } from './Connection';
|
||||
import { Frame } from './FrameManager';
|
||||
import { helper } from '../helper';
|
||||
import { valueFromRemoteObject } from './protocolHelper';
|
||||
import { createJSHandle, JSHandle, ElementHandle } from './JSHandle';
|
||||
import { Protocol } from './protocol';
|
||||
import * as injectedSource from '../generated/injectedSource';
|
||||
import * as cssSelectorEngineSource from '../generated/cssSelectorEngineSource';
|
||||
import * as xpathSelectorEngineSource from '../generated/xpathSelectorEngineSource';
|
||||
import * as types from '../types';
|
||||
import { Response } from './NetworkManager';
|
||||
import * as js from '../javascript';
|
||||
|
||||
export const EVALUATION_SCRIPT_URL = '__playwright_evaluation_script__';
|
||||
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
|
||||
|
||||
export class ExecutionContext {
|
||||
_globalObjectId?: string;
|
||||
_session: TargetSession;
|
||||
_frame: Frame;
|
||||
_contextId: number;
|
||||
private _contextDestroyedCallback: any;
|
||||
private _executionContextDestroyedPromise: Promise<unknown>;
|
||||
private _injectedPromise: Promise<JSHandle> | null = null;
|
||||
private _documentPromise: Promise<ElementHandle> | null = null;
|
||||
export type ExecutionContext = js.ExecutionContext<JSHandle, ElementHandle, Response>;
|
||||
|
||||
constructor(client: TargetSession, contextPayload: Protocol.Runtime.ExecutionContextDescription, frame: Frame | null) {
|
||||
export class ExecutionContextDelegate implements js.ExecutionContextDelegate<JSHandle, ElementHandle, Response> {
|
||||
private _globalObjectId?: string;
|
||||
_session: TargetSession;
|
||||
private _contextId: number;
|
||||
private _contextDestroyedCallback: () => void;
|
||||
private _executionContextDestroyedPromise: Promise<unknown>;
|
||||
|
||||
constructor(client: TargetSession, contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
||||
this._session = client;
|
||||
this._frame = frame;
|
||||
this._contextId = contextPayload.id;
|
||||
this._contextDestroyedCallback = null;
|
||||
this._executionContextDestroyedPromise = new Promise((resolve, reject) => {
|
||||
|
|
@ -53,19 +48,7 @@ export class ExecutionContext {
|
|||
this._contextDestroyedCallback();
|
||||
}
|
||||
|
||||
frame(): Frame | null {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
evaluate: types.Evaluate<JSHandle> = (pageFunction, ...args) => {
|
||||
return this._evaluateInternal(true /* returnByValue */, pageFunction, ...args);
|
||||
}
|
||||
|
||||
evaluateHandle: types.EvaluateHandle<JSHandle> = (pageFunction, ...args) => {
|
||||
return this._evaluateInternal(false /* returnByValue */, pageFunction, ...args);
|
||||
}
|
||||
|
||||
async _evaluateInternal(returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||
async evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||
const suffix = `//# sourceURL=${EVALUATION_SCRIPT_URL}`;
|
||||
|
||||
if (helper.isString(pageFunction)) {
|
||||
|
|
@ -98,7 +81,7 @@ export class ExecutionContext {
|
|||
if (response.wasThrown)
|
||||
throw new Error('Evaluation failed: ' + response.result.description);
|
||||
if (!returnByValue)
|
||||
return createJSHandle(this, response.result);
|
||||
return createJSHandle(context, response.result);
|
||||
if (response.result.objectId) {
|
||||
const serializeFunction = function() {
|
||||
try {
|
||||
|
|
@ -204,7 +187,7 @@ export class ExecutionContext {
|
|||
if (response.wasThrown)
|
||||
throw new Error('Evaluation failed: ' + response.result.description);
|
||||
if (!returnByValue)
|
||||
return createJSHandle(this, response.result);
|
||||
return createJSHandle(context, response.result);
|
||||
if (response.result.objectId) {
|
||||
const serializeFunction = function() {
|
||||
try {
|
||||
|
|
@ -305,23 +288,4 @@ export class ExecutionContext {
|
|||
}
|
||||
return this._globalObjectId;
|
||||
}
|
||||
|
||||
_injected(): Promise<JSHandle> {
|
||||
if (!this._injectedPromise) {
|
||||
const engineSources = [cssSelectorEngineSource.source, xpathSelectorEngineSource.source];
|
||||
const source = `
|
||||
new (${injectedSource.source})([
|
||||
${engineSources.join(',\n')}
|
||||
])
|
||||
`;
|
||||
this._injectedPromise = this.evaluateHandle(source);
|
||||
}
|
||||
return this._injectedPromise;
|
||||
}
|
||||
|
||||
_document(): Promise<ElementHandle> {
|
||||
if (!this._documentPromise)
|
||||
this._documentPromise = this.evaluateHandle('document').then(handle => handle.asElement()!);
|
||||
return this._documentPromise;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@ import { Events } from './events';
|
|||
import { assert, debugError, helper, RegisteredListener } from '../helper';
|
||||
import { TimeoutSettings } from '../TimeoutSettings';
|
||||
import { TargetSession } from './Connection';
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { ExecutionContext, ExecutionContextDelegate } from './ExecutionContext';
|
||||
import { ElementHandle, JSHandle } from './JSHandle';
|
||||
import { NetworkManager, NetworkManagerEvents, Request, Response } from './NetworkManager';
|
||||
import { Page } from './Page';
|
||||
import { Protocol } from './protocol';
|
||||
import * as frames from '../frames';
|
||||
import * as js from '../javascript';
|
||||
|
||||
export const FrameManagerEvents = {
|
||||
FrameNavigatedWithinDocument: Symbol('FrameNavigatedWithinDocument'),
|
||||
|
|
@ -41,9 +42,9 @@ type FrameData = {
|
|||
id: string,
|
||||
};
|
||||
|
||||
export type Frame = frames.Frame<JSHandle, ElementHandle, ExecutionContext, Response>;
|
||||
export type Frame = frames.Frame<JSHandle, ElementHandle, Response>;
|
||||
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate<JSHandle, ElementHandle, ExecutionContext, Response> {
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate<JSHandle, ElementHandle, Response> {
|
||||
_session: TargetSession;
|
||||
_page: Page;
|
||||
_networkManager: NetworkManager;
|
||||
|
|
@ -102,7 +103,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate<J
|
|||
|
||||
disconnectFromTarget() {
|
||||
for (const context of this._contextIdToContext.values()) {
|
||||
context._dispose();
|
||||
(context._delegate as ExecutionContextDelegate)._dispose();
|
||||
context.frame()._contextDestroyed(context);
|
||||
}
|
||||
// this._mainFrame = null;
|
||||
|
|
@ -198,7 +199,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate<J
|
|||
frame._navigated(framePayload.url, framePayload.name);
|
||||
for (const context of this._contextIdToContext.values()) {
|
||||
if (context.frame() === frame) {
|
||||
context._dispose();
|
||||
(context._delegate as ExecutionContextDelegate)._dispose();
|
||||
frame._contextDestroyed(context);
|
||||
}
|
||||
}
|
||||
|
|
@ -230,7 +231,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate<J
|
|||
const frame = this._frames.get(frameId) || null;
|
||||
if (!frame)
|
||||
return;
|
||||
const context: ExecutionContext = new ExecutionContext(this._session, contextPayload, frame);
|
||||
const context: ExecutionContext = new js.ExecutionContext(new ExecutionContextDelegate(this._session, contextPayload), frame);
|
||||
if (frame) {
|
||||
frame._contextCreated('main', context);
|
||||
frame._contextCreated('utility', context);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import * as fs from 'fs';
|
|||
import { assert, debugError, helper } from '../helper';
|
||||
import * as input from '../input';
|
||||
import { TargetSession } from './Connection';
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { ExecutionContext, ExecutionContextDelegate } from './ExecutionContext';
|
||||
import { FrameManager } from './FrameManager';
|
||||
import { Page } from './Page';
|
||||
import { Protocol } from './protocol';
|
||||
|
|
@ -32,12 +32,13 @@ type SelectorRoot = Element | ShadowRoot | Document;
|
|||
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||
|
||||
export function createJSHandle(context: ExecutionContext, remoteObject: Protocol.Runtime.RemoteObject) {
|
||||
const delegate = context._delegate as ExecutionContextDelegate;
|
||||
const frame = context.frame();
|
||||
if (remoteObject.subtype === 'node' && frame) {
|
||||
const frameManager = frame._delegate as FrameManager;
|
||||
return new ElementHandle(context, context._session, remoteObject, frameManager.page(), frameManager);
|
||||
return new ElementHandle(context, delegate._session, remoteObject, frameManager.page(), frameManager);
|
||||
}
|
||||
return new JSHandle(context, context._session, remoteObject);
|
||||
return new JSHandle(context, delegate._session, remoteObject);
|
||||
}
|
||||
|
||||
export class JSHandle {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
export { TimeoutError } from '../Errors';
|
||||
export { Browser, BrowserContext } from './Browser';
|
||||
export { BrowserFetcher } from './BrowserFetcher';
|
||||
export { ExecutionContext } from './ExecutionContext';
|
||||
export { ExecutionContext } from '../javascript';
|
||||
export { Frame } from './FrameManager';
|
||||
export { Mouse, Keyboard } from '../input';
|
||||
export { ElementHandle, JSHandle } from './JSHandle';
|
||||
|
|
|
|||
Loading…
Reference in a new issue