mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-11 12:33:45 +03:00
chore: push dispatcher guid into object, reuse it in trace (#6250)
This commit is contained in:
parent
06b0619260
commit
85e2db2416
@ -131,7 +131,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||
return;
|
||||
}
|
||||
await this._context._setRequestInterceptor((route, request) => {
|
||||
this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
|
||||
this._dispatchEvent('route', { route: RouteDispatcher.from(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,14 +17,14 @@
|
||||
import { ConsoleMessage } from '../server/console';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { createHandle } from './elementHandlerDispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
|
||||
export class ConsoleMessageDispatcher extends Dispatcher<ConsoleMessage, channels.ConsoleMessageInitializer> implements channels.ConsoleMessageChannel {
|
||||
constructor(scope: DispatcherScope, message: ConsoleMessage) {
|
||||
super(scope, message, 'ConsoleMessage', {
|
||||
type: message.type(),
|
||||
text: message.text(),
|
||||
args: message.args().map(a => createHandle(scope, a)),
|
||||
args: message.args().map(a => ElementHandleDispatcher.fromJSHandle(scope, a)),
|
||||
location: message.location(),
|
||||
});
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { serializeError } from '../protocol/serializers';
|
||||
import { createScheme, Validator, ValidationError } from '../protocol/validator';
|
||||
import { assert, createGuid, debugAssert, isUnderTest, monotonicTime } from '../utils/utils';
|
||||
import { assert, debugAssert, isUnderTest, monotonicTime } from '../utils/utils';
|
||||
import { tOptional } from '../protocol/validatorPrimitives';
|
||||
import { kBrowserOrContextClosedError } from '../utils/errors';
|
||||
import { CallMetadata, SdkObject } from '../server/instrumentation';
|
||||
@ -41,7 +41,7 @@ export function lookupNullableDispatcher<DispatcherType>(object: any | null): Di
|
||||
return object ? lookupDispatcher(object) : undefined;
|
||||
}
|
||||
|
||||
export class Dispatcher<Type, Initializer> extends EventEmitter implements channels.Channel {
|
||||
export class Dispatcher<Type extends { guid: string }, Initializer> extends EventEmitter implements channels.Channel {
|
||||
private _connection: DispatcherConnection;
|
||||
private _isScope: boolean;
|
||||
// Parent is always "isScope".
|
||||
@ -55,7 +55,7 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
|
||||
readonly _scope: Dispatcher<any, any>;
|
||||
_object: Type;
|
||||
|
||||
constructor(parent: Dispatcher<any, any> | DispatcherConnection, object: Type, type: string, initializer: Initializer, isScope?: boolean, guid = type + '@' + createGuid()) {
|
||||
constructor(parent: Dispatcher<any, any> | DispatcherConnection, object: Type, type: string, initializer: Initializer, isScope?: boolean) {
|
||||
super();
|
||||
|
||||
this._connection = parent instanceof DispatcherConnection ? parent : parent._connection;
|
||||
@ -63,6 +63,7 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
|
||||
this._parent = parent instanceof DispatcherConnection ? undefined : parent;
|
||||
this._scope = isScope ? this : this._parent!;
|
||||
|
||||
const guid = object.guid;
|
||||
assert(!this._connection._dispatchers.has(guid));
|
||||
this._connection._dispatchers.set(guid, this);
|
||||
if (this._parent) {
|
||||
@ -120,9 +121,9 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
|
||||
}
|
||||
|
||||
export type DispatcherScope = Dispatcher<any, any>;
|
||||
class Root extends Dispatcher<{}, {}> {
|
||||
class Root extends Dispatcher<{ guid: '' }, {}> {
|
||||
constructor(connection: DispatcherConnection) {
|
||||
super(connection, {}, '', {}, true, '');
|
||||
super(connection, { guid: '' }, '', {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,8 +202,8 @@ export class DispatcherConnection {
|
||||
let callMetadata: CallMetadata = {
|
||||
id,
|
||||
...validMetadata,
|
||||
pageId: sdkObject?.attribution.page?.uniqueId,
|
||||
frameId: sdkObject?.attribution.frame?.uniqueId,
|
||||
pageId: sdkObject?.attribution.page?.guid,
|
||||
frameId: sdkObject?.attribution.frame?.guid,
|
||||
startTime: monotonicTime(),
|
||||
endTime: 0,
|
||||
type: dispatcher._type,
|
||||
|
@ -20,7 +20,7 @@ import * as channels from '../protocol/channels';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { PageDispatcher } from './pageDispatcher';
|
||||
import { parseArgument, serializeResult } from './jsHandleDispatcher';
|
||||
import { createHandle } from './elementHandlerDispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
|
||||
export class ElectronDispatcher extends Dispatcher<Electron, channels.ElectronInitializer> implements channels.ElectronChannel {
|
||||
constructor(scope: DispatcherScope, electron: Electron) {
|
||||
@ -44,7 +44,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
||||
electronApplication.on(ElectronApplication.Events.Window, (page: ElectronPage) => {
|
||||
this._dispatchEvent('window', {
|
||||
page: lookupDispatcher<PageDispatcher>(page),
|
||||
browserWindow: createHandle(this._scope, page.browserWindow),
|
||||
browserWindow: ElementHandleDispatcher.fromJSHandle(this._scope, page.browserWindow),
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -57,7 +57,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
||||
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
|
||||
const handle = this._object._nodeElectronHandle!;
|
||||
const result = await handle.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
return { handle: createHandle(this._scope, result) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, result) };
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
|
@ -17,25 +17,32 @@
|
||||
import { ElementHandle } from '../server/dom';
|
||||
import * as js from '../server/javascript';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { DispatcherScope, lookupNullableDispatcher } from './dispatcher';
|
||||
import { DispatcherScope, existingDispatcher, lookupNullableDispatcher } from './dispatcher';
|
||||
import { JSHandleDispatcher, serializeResult, parseArgument } from './jsHandleDispatcher';
|
||||
import { FrameDispatcher } from './frameDispatcher';
|
||||
import { CallMetadata } from '../server/instrumentation';
|
||||
|
||||
export function createHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
|
||||
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
|
||||
}
|
||||
|
||||
export class ElementHandleDispatcher extends JSHandleDispatcher implements channels.ElementHandleChannel {
|
||||
readonly _elementHandle: ElementHandle;
|
||||
|
||||
static createNullable(scope: DispatcherScope, handle: ElementHandle | null): ElementHandleDispatcher | undefined {
|
||||
if (!handle)
|
||||
return undefined;
|
||||
return new ElementHandleDispatcher(scope, handle);
|
||||
static from(scope: DispatcherScope, handle: ElementHandle): ElementHandleDispatcher {
|
||||
return existingDispatcher<ElementHandleDispatcher>(handle) || new ElementHandleDispatcher(scope, handle);
|
||||
}
|
||||
|
||||
constructor(scope: DispatcherScope, elementHandle: ElementHandle) {
|
||||
static fromNullable(scope: DispatcherScope, handle: ElementHandle | null): ElementHandleDispatcher | undefined {
|
||||
if (!handle)
|
||||
return undefined;
|
||||
return existingDispatcher<ElementHandleDispatcher>(handle) || new ElementHandleDispatcher(scope, handle);
|
||||
}
|
||||
|
||||
static fromJSHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
|
||||
const result = existingDispatcher<JSHandleDispatcher>(handle);
|
||||
if (result)
|
||||
return result;
|
||||
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
|
||||
}
|
||||
|
||||
private constructor(scope: DispatcherScope, elementHandle: ElementHandle) {
|
||||
super(scope, elementHandle);
|
||||
this._elementHandle = elementHandle;
|
||||
}
|
||||
@ -162,12 +169,12 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
|
||||
|
||||
async querySelector(params: channels.ElementHandleQuerySelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorResult> {
|
||||
const handle = await this._elementHandle.$(params.selector);
|
||||
return { element: handle ? new ElementHandleDispatcher(this._scope, handle) : undefined };
|
||||
return { element: ElementHandleDispatcher.fromNullable(this._scope, handle) };
|
||||
}
|
||||
|
||||
async querySelectorAll(params: channels.ElementHandleQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorAllResult> {
|
||||
const elements = await this._elementHandle.$$(params.selector);
|
||||
return { elements: elements.map(e => new ElementHandleDispatcher(this._scope, e)) };
|
||||
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) };
|
||||
}
|
||||
|
||||
async evalOnSelector(params: channels.ElementHandleEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorResult> {
|
||||
@ -183,6 +190,6 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
|
||||
}
|
||||
|
||||
async waitForSelector(params: channels.ElementHandleWaitForSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleWaitForSelectorResult> {
|
||||
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._elementHandle.waitForSelector(metadata, params.selector, params)) };
|
||||
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._elementHandle.waitForSelector(metadata, params.selector, params)) };
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { Frame, NavigationEvent } from '../server/frames';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Dispatcher, DispatcherScope, lookupNullableDispatcher, existingDispatcher } from './dispatcher';
|
||||
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { parseArgument, serializeResult } from './jsHandleDispatcher';
|
||||
import { ResponseDispatcher, RequestDispatcher } from './networkDispatchers';
|
||||
import { CallMetadata } from '../server/instrumentation';
|
||||
@ -57,7 +57,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
}
|
||||
|
||||
async frameElement(): Promise<channels.FrameFrameElementResult> {
|
||||
return { element: new ElementHandleDispatcher(this._scope, await this._frame.frameElement()) };
|
||||
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.frameElement()) };
|
||||
}
|
||||
|
||||
async evaluateExpression(params: channels.FrameEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionResult> {
|
||||
@ -65,11 +65,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> {
|
||||
return { handle: createHandle(this._scope, await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
|
||||
}
|
||||
|
||||
async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> {
|
||||
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._frame.waitForSelector(metadata, params.selector, params)) };
|
||||
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._frame.waitForSelector(metadata, params.selector, params)) };
|
||||
}
|
||||
|
||||
async dispatchEvent(params: channels.FrameDispatchEventParams, metadata: CallMetadata): Promise<void> {
|
||||
@ -85,12 +85,12 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
}
|
||||
|
||||
async querySelector(params: channels.FrameQuerySelectorParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorResult> {
|
||||
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._frame.$(params.selector)) };
|
||||
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._frame.$(params.selector)) };
|
||||
}
|
||||
|
||||
async querySelectorAll(params: channels.FrameQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorAllResult> {
|
||||
const elements = await this._frame.$$(params.selector);
|
||||
return { elements: elements.map(e => new ElementHandleDispatcher(this._scope, e)) };
|
||||
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) };
|
||||
}
|
||||
|
||||
async content(): Promise<channels.FrameContentResult> {
|
||||
@ -102,11 +102,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
}
|
||||
|
||||
async addScriptTag(params: channels.FrameAddScriptTagParams, metadata: CallMetadata): Promise<channels.FrameAddScriptTagResult> {
|
||||
return { element: new ElementHandleDispatcher(this._scope, await this._frame.addScriptTag(params)) };
|
||||
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.addScriptTag(params)) };
|
||||
}
|
||||
|
||||
async addStyleTag(params: channels.FrameAddStyleTagParams, metadata: CallMetadata): Promise<channels.FrameAddStyleTagResult> {
|
||||
return { element: new ElementHandleDispatcher(this._scope, await this._frame.addStyleTag(params)) };
|
||||
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.addStyleTag(params)) };
|
||||
}
|
||||
|
||||
async click(params: channels.FrameClickParams, metadata: CallMetadata): Promise<void> {
|
||||
@ -201,7 +201,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
}
|
||||
|
||||
async waitForFunction(params: channels.FrameWaitForFunctionParams, metadata: CallMetadata): Promise<channels.FrameWaitForFunctionResult> {
|
||||
return { handle: createHandle(this._scope, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
|
||||
}
|
||||
|
||||
async title(params: channels.FrameTitleParams, metadata: CallMetadata): Promise<channels.FrameTitleResult> {
|
||||
|
@ -17,12 +17,12 @@
|
||||
import * as js from '../server/javascript';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { createHandle } from './elementHandlerDispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { parseSerializedValue, serializeValue } from '../protocol/serializers';
|
||||
|
||||
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandleInitializer> implements channels.JSHandleChannel {
|
||||
|
||||
constructor(scope: DispatcherScope, jsHandle: js.JSHandle) {
|
||||
protected constructor(scope: DispatcherScope, jsHandle: js.JSHandle) {
|
||||
// Do not call this directly, use createHandle() instead.
|
||||
super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', {
|
||||
preview: jsHandle.toString(),
|
||||
@ -36,19 +36,19 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
|
||||
|
||||
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
|
||||
const jsHandle = await this._object.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
return { handle: createHandle(this._scope, jsHandle) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, jsHandle) };
|
||||
}
|
||||
|
||||
async getProperty(params: channels.JSHandleGetPropertyParams): Promise<channels.JSHandleGetPropertyResult> {
|
||||
const jsHandle = await this._object.getProperty(params.name);
|
||||
return { handle: createHandle(this._scope, jsHandle) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, jsHandle) };
|
||||
}
|
||||
|
||||
async getPropertyList(): Promise<channels.JSHandleGetPropertyListResult> {
|
||||
const map = await this._object.getProperties();
|
||||
const properties = [];
|
||||
for (const [name, value] of map)
|
||||
properties.push({ name, value: createHandle(this._scope, value) });
|
||||
properties.push({ name, value: ElementHandleDispatcher.fromJSHandle(this._scope, value) });
|
||||
return { properties };
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,16 @@ export class RequestDispatcher extends Dispatcher<Request, channels.RequestIniti
|
||||
|
||||
export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseInitializer> implements channels.ResponseChannel {
|
||||
|
||||
constructor(scope: DispatcherScope, response: Response) {
|
||||
static from(scope: DispatcherScope, response: Response): ResponseDispatcher {
|
||||
const result = existingDispatcher<ResponseDispatcher>(response);
|
||||
return result || new ResponseDispatcher(scope, response);
|
||||
}
|
||||
|
||||
static fromNullable(scope: DispatcherScope, response: Response | null): ResponseDispatcher | undefined {
|
||||
return response ? ResponseDispatcher.from(scope, response) : undefined;
|
||||
}
|
||||
|
||||
private constructor(scope: DispatcherScope, response: Response) {
|
||||
super(scope, response, 'Response', {
|
||||
// TODO: responses in popups can point to non-reported requests.
|
||||
request: RequestDispatcher.from(scope, response.request()),
|
||||
@ -75,7 +84,16 @@ export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseIn
|
||||
|
||||
export class RouteDispatcher extends Dispatcher<Route, channels.RouteInitializer> implements channels.RouteChannel {
|
||||
|
||||
constructor(scope: DispatcherScope, route: Route) {
|
||||
static from(scope: DispatcherScope, route: Route): RouteDispatcher {
|
||||
const result = existingDispatcher<RouteDispatcher>(route);
|
||||
return result || new RouteDispatcher(scope, route);
|
||||
}
|
||||
|
||||
static fromNullable(scope: DispatcherScope, route: Route | null): RouteDispatcher | undefined {
|
||||
return route ? RouteDispatcher.from(scope, route) : undefined;
|
||||
}
|
||||
|
||||
private constructor(scope: DispatcherScope, route: Route) {
|
||||
super(scope, route, 'Route', {
|
||||
// Context route can point to a non-reported request.
|
||||
request: RequestDispatcher.from(scope, route.request())
|
||||
|
@ -26,7 +26,7 @@ import { DialogDispatcher } from './dialogDispatcher';
|
||||
import { FrameDispatcher } from './frameDispatcher';
|
||||
import { RequestDispatcher, ResponseDispatcher, RouteDispatcher, WebSocketDispatcher } from './networkDispatchers';
|
||||
import { serializeResult, parseArgument } from './jsHandleDispatcher';
|
||||
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { FileChooser } from '../server/fileChooser';
|
||||
import { CRCoverage } from '../server/chromium/crCoverage';
|
||||
import { JSHandle } from '../server/javascript';
|
||||
@ -34,6 +34,7 @@ import { CallMetadata } from '../server/instrumentation';
|
||||
import { Artifact } from '../server/artifact';
|
||||
import { ArtifactDispatcher } from './artifactDispatcher';
|
||||
import { Download } from '../server/download';
|
||||
import { createGuid } from '../utils/utils';
|
||||
|
||||
export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> implements channels.PageChannel {
|
||||
private _page: Page;
|
||||
@ -67,7 +68,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
|
||||
this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: new ArtifactDispatcher(scope, download.artifact) });
|
||||
});
|
||||
this._page.on(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
|
||||
element: new ElementHandleDispatcher(this._scope, fileChooser.element()),
|
||||
element: ElementHandleDispatcher.from(this._scope, fileChooser.element()),
|
||||
isMultiple: fileChooser.isMultiple()
|
||||
}));
|
||||
page.on(Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
|
||||
@ -84,7 +85,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
|
||||
request: RequestDispatcher.from(scope, request),
|
||||
responseEndTiming: request._responseEndTiming
|
||||
}));
|
||||
page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: new ResponseDispatcher(this._scope, response) }));
|
||||
page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: ResponseDispatcher.from(this._scope, response) }));
|
||||
page.on(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) }));
|
||||
page.on(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
|
||||
page.on(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));
|
||||
@ -145,7 +146,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
|
||||
return;
|
||||
}
|
||||
await this._page._setClientRequestInterceptor((route, request) => {
|
||||
this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
|
||||
this._dispatchEvent('route', { route: RouteDispatcher.from(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
|
||||
});
|
||||
}
|
||||
|
||||
@ -263,21 +264,21 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerInitiali
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: channels.WorkerEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionHandleResult> {
|
||||
return { handle: createHandle(this._scope, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
}
|
||||
|
||||
export class BindingCallDispatcher extends Dispatcher<{}, channels.BindingCallInitializer> implements channels.BindingCallChannel {
|
||||
export class BindingCallDispatcher extends Dispatcher<{ guid: string }, channels.BindingCallInitializer> implements channels.BindingCallChannel {
|
||||
private _resolve: ((arg: any) => void) | undefined;
|
||||
private _reject: ((error: any) => void) | undefined;
|
||||
private _promise: Promise<any>;
|
||||
|
||||
constructor(scope: DispatcherScope, name: string, needsHandle: boolean, source: { context: BrowserContext, page: Page, frame: Frame }, args: any[]) {
|
||||
super(scope, {}, 'BindingCall', {
|
||||
super(scope, { guid: createGuid() }, 'BindingCall', {
|
||||
frame: lookupDispatcher<FrameDispatcher>(source.frame),
|
||||
name,
|
||||
args: needsHandle ? undefined : args.map(serializeResult),
|
||||
handle: needsHandle ? createHandle(scope, args[0] as JSHandle) : undefined,
|
||||
handle: needsHandle ? ElementHandleDispatcher.fromJSHandle(scope, args[0] as JSHandle) : undefined,
|
||||
});
|
||||
this._promise = new Promise((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
|
@ -38,6 +38,6 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
|
||||
deviceDescriptors,
|
||||
selectors: customSelectors || new SelectorsDispatcher(scope, playwright.selectors),
|
||||
preLaunchedBrowser,
|
||||
}, false, 'Playwright');
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
@ -17,18 +17,19 @@
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import * as stream from 'stream';
|
||||
import { createGuid } from '../utils/utils';
|
||||
|
||||
export class StreamDispatcher extends Dispatcher<stream.Readable, channels.StreamInitializer> implements channels.StreamChannel {
|
||||
export class StreamDispatcher extends Dispatcher<{ guid: string, stream: stream.Readable }, channels.StreamInitializer> implements channels.StreamChannel {
|
||||
constructor(scope: DispatcherScope, stream: stream.Readable) {
|
||||
super(scope, stream, 'Stream', {});
|
||||
super(scope, { guid: createGuid(), stream }, 'Stream', {});
|
||||
}
|
||||
|
||||
async read(params: channels.StreamReadParams): Promise<channels.StreamReadResult> {
|
||||
const buffer = this._object.read(Math.min(this._object.readableLength, params.size || this._object.readableLength));
|
||||
const buffer = this._object.stream.read(Math.min(this._object.stream.readableLength, params.size || this._object.stream.readableLength));
|
||||
return { binary: buffer ? buffer.toString('base64') : '' };
|
||||
}
|
||||
|
||||
async close() {
|
||||
this._object.destroy();
|
||||
this._object.stream.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ export interface DeviceBackend {
|
||||
}
|
||||
|
||||
export interface SocketBackend extends EventEmitter {
|
||||
guid: string;
|
||||
write(data: Buffer): Promise<void>;
|
||||
close(): void;
|
||||
}
|
||||
@ -61,7 +62,7 @@ export class Android extends SdkObject {
|
||||
readonly _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(backend: Backend, playwrightOptions: PlaywrightOptions) {
|
||||
super(playwrightOptions.rootSdkObject);
|
||||
super(playwrightOptions.rootSdkObject, 'android');
|
||||
this._backend = backend;
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
this._timeoutSettings = new TimeoutSettings();
|
||||
@ -115,7 +116,7 @@ export class AndroidDevice extends SdkObject {
|
||||
private _isClosed = false;
|
||||
|
||||
constructor(android: Android, backend: DeviceBackend, model: string) {
|
||||
super(android);
|
||||
super(android, 'android-device');
|
||||
this._android = android;
|
||||
this._backend = backend;
|
||||
this.model = model;
|
||||
|
@ -19,6 +19,7 @@ import debug from 'debug';
|
||||
import * as net from 'net';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Backend, DeviceBackend, SocketBackend } from './android';
|
||||
import { createGuid } from '../../utils/utils';
|
||||
|
||||
export class AdbBackend implements Backend {
|
||||
async devices(): Promise<DeviceBackend[]> {
|
||||
@ -99,6 +100,7 @@ function encodeMessage(message: string): Buffer {
|
||||
}
|
||||
|
||||
class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
|
||||
readonly guid = createGuid();
|
||||
private _socket: net.Socket;
|
||||
private _buffer = Buffer.from([]);
|
||||
private _isSocket = false;
|
||||
@ -149,7 +151,7 @@ class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
|
||||
await this._connectPromise;
|
||||
assert(!this._isSocket, 'Can not read by length in socket mode');
|
||||
while (this._buffer.length < length)
|
||||
await new Promise(f => this._notifyReader = f);
|
||||
await new Promise<void>(f => this._notifyReader = f);
|
||||
const result = this._buffer.slice(0, length);
|
||||
this._buffer = this._buffer.slice(length);
|
||||
debug('pw:adb:recv')(result.toString().substring(0, 100) + '...');
|
||||
@ -158,7 +160,7 @@ class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
|
||||
|
||||
async readAll(): Promise<Buffer> {
|
||||
while (!this._isClosed)
|
||||
await new Promise(f => this._notifyReader = f);
|
||||
await new Promise<void>(f => this._notifyReader = f);
|
||||
return this._buffer;
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,11 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { SdkObject } from './instrumentation';
|
||||
|
||||
type SaveCallback = (localPath: string, error?: string) => Promise<void>;
|
||||
|
||||
export class Artifact {
|
||||
export class Artifact extends SdkObject {
|
||||
private _localPath: string;
|
||||
private _unaccessibleErrorMessage: string | undefined;
|
||||
private _finishedCallback: () => void;
|
||||
@ -29,7 +30,8 @@ export class Artifact {
|
||||
private _deleted = false;
|
||||
private _failureError: string | null = null;
|
||||
|
||||
constructor(localPath: string, unaccessibleErrorMessage?: string) {
|
||||
constructor(parent: SdkObject, localPath: string, unaccessibleErrorMessage?: string) {
|
||||
super(parent, 'artifact');
|
||||
this._localPath = localPath;
|
||||
this._unaccessibleErrorMessage = unaccessibleErrorMessage;
|
||||
this._finishedCallback = () => {};
|
||||
|
@ -66,7 +66,7 @@ export abstract class Browser extends SdkObject {
|
||||
readonly _idToVideo = new Map<string, { context: BrowserContext, artifact: Artifact }>();
|
||||
|
||||
constructor(options: BrowserOptions) {
|
||||
super(options.rootSdkObject);
|
||||
super(options.rootSdkObject, 'browser');
|
||||
this.attribution.browser = this;
|
||||
this.options = options;
|
||||
}
|
||||
@ -97,7 +97,7 @@ export abstract class Browser extends SdkObject {
|
||||
}
|
||||
|
||||
_videoStarted(context: BrowserContext, videoId: string, path: string, pageOrError: Promise<Page | Error>) {
|
||||
const artifact = new Artifact(path);
|
||||
const artifact = new Artifact(context, path);
|
||||
this._idToVideo.set(videoId, { context, artifact });
|
||||
context.emit(BrowserContext.Events.VideoStarted, artifact);
|
||||
pageOrError.then(page => {
|
||||
|
@ -53,7 +53,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||
private _origins = new Set<string>();
|
||||
|
||||
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
|
||||
super(browser);
|
||||
super(browser, 'browser-context');
|
||||
this.attribution.context = this;
|
||||
this._browser = browser;
|
||||
this._options = options;
|
||||
|
@ -44,7 +44,7 @@ export abstract class BrowserType extends SdkObject {
|
||||
readonly _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(browserName: registry.BrowserName, playwrightOptions: PlaywrightOptions) {
|
||||
super(playwrightOptions.rootSdkObject);
|
||||
super(playwrightOptions.rootSdkObject, 'browser-type');
|
||||
this.attribution.browserType = this;
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
this._name = browserName;
|
||||
|
@ -137,9 +137,11 @@ export class CRSession extends EventEmitter {
|
||||
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
once: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
readonly guid: string;
|
||||
|
||||
constructor(connection: CRConnection, rootSessionId: string, targetType: string, sessionId: string) {
|
||||
super();
|
||||
this.guid = `cdp-session@${sessionId}`;
|
||||
this.setMaxListeners(0);
|
||||
this._connection = connection;
|
||||
this._rootSessionId = rootSessionId;
|
||||
|
@ -799,7 +799,7 @@ class FrameSession {
|
||||
lineNumber: lineNumber || 0,
|
||||
columnNumber: 0,
|
||||
};
|
||||
this._page.emit(Page.Events.Console, new ConsoleMessage(level, text, [], location));
|
||||
this._page.emit(Page.Events.Console, new ConsoleMessage(this._page, level, text, [], location));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,16 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SdkObject } from './instrumentation';
|
||||
import * as js from './javascript';
|
||||
import { ConsoleMessageLocation } from './types';
|
||||
|
||||
export class ConsoleMessage {
|
||||
export class ConsoleMessage extends SdkObject {
|
||||
private _type: string;
|
||||
private _text?: string;
|
||||
private _args: js.JSHandle[];
|
||||
private _location: ConsoleMessageLocation;
|
||||
|
||||
constructor(type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
|
||||
constructor(parent: SdkObject, type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
|
||||
super(parent, 'console-message');
|
||||
this._type = type;
|
||||
this._text = text;
|
||||
this._args = args;
|
||||
|
@ -32,7 +32,7 @@ export class Dialog extends SdkObject {
|
||||
private _defaultValue: string;
|
||||
|
||||
constructor(page: Page, type: string, message: string, onHandle: OnHandle, defaultValue?: string) {
|
||||
super(page);
|
||||
super(page, 'dialog');
|
||||
this._page = page;
|
||||
this._type = type;
|
||||
this._message = message;
|
||||
|
@ -27,7 +27,7 @@ export class Download {
|
||||
|
||||
constructor(page: Page, downloadsPath: string, uuid: string, url: string, suggestedFilename?: string) {
|
||||
const unaccessibleErrorMessage = !page._browserContext._options.acceptDownloads ? 'Pass { acceptDownloads: true } when you are creating your browser context.' : undefined;
|
||||
this.artifact = new Artifact(path.join(downloadsPath, uuid), unaccessibleErrorMessage);
|
||||
this.artifact = new Artifact(page, path.join(downloadsPath, uuid), unaccessibleErrorMessage);
|
||||
this._page = page;
|
||||
this.url = url;
|
||||
this._suggestedFilename = suggestedFilename;
|
||||
|
@ -64,7 +64,7 @@ export class ElectronApplication extends SdkObject {
|
||||
readonly _timeoutSettings = new TimeoutSettings();
|
||||
|
||||
constructor(parent: SdkObject, browser: CRBrowser, nodeConnection: CRConnection) {
|
||||
super(parent);
|
||||
super(parent, 'electron-app');
|
||||
this._browserContext = browser._defaultContext as CRBrowserContext;
|
||||
this._browserContext.on(BrowserContext.Events.Close, () => {
|
||||
// Emit application closed after context closed.
|
||||
@ -122,7 +122,7 @@ export class Electron extends SdkObject {
|
||||
private _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(playwrightOptions: PlaywrightOptions) {
|
||||
super(playwrightOptions.rootSdkObject);
|
||||
super(playwrightOptions.rootSdkObject, 'electron');
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ import { Page } from './page';
|
||||
import * as types from './types';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { Progress, ProgressController } from './progress';
|
||||
import { assert, createGuid, makeWaitForNextTask } from '../utils/utils';
|
||||
import { assert, makeWaitForNextTask } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { CallMetadata, internalCallMetadata, SdkObject } from './instrumentation';
|
||||
import { ElementStateWithoutStable } from './injected/injectedScript';
|
||||
@ -411,11 +411,9 @@ export class Frame extends SdkObject {
|
||||
private _setContentCounter = 0;
|
||||
readonly _detachedPromise: Promise<void>;
|
||||
private _detachedCallback = () => {};
|
||||
readonly uniqueId: string;
|
||||
|
||||
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
||||
super(page);
|
||||
this.uniqueId = parentFrame ? `frame@${createGuid()}` : page.uniqueId;
|
||||
super(page, 'frame');
|
||||
this.attribution.frame = this;
|
||||
this._id = id;
|
||||
this._page = page;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { Point, StackFrame } from '../common/types';
|
||||
import { createGuid } from '../utils/utils';
|
||||
import type { Browser } from './browser';
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import type { BrowserType } from './browserType';
|
||||
@ -50,11 +51,13 @@ export type CallMetadata = {
|
||||
};
|
||||
|
||||
export class SdkObject extends EventEmitter {
|
||||
guid: string;
|
||||
attribution: Attribution;
|
||||
instrumentation: Instrumentation;
|
||||
|
||||
protected constructor(parent: SdkObject) {
|
||||
protected constructor(parent: SdkObject, guidPrefix?: string, guid?: string) {
|
||||
super();
|
||||
this.guid = guid || `${guidPrefix || ''}@${createGuid()}`;
|
||||
this.setMaxListeners(0);
|
||||
this.attribution = { ...parent.attribution };
|
||||
this.instrumentation = parent.instrumentation;
|
||||
|
@ -56,7 +56,7 @@ export class ExecutionContext extends SdkObject {
|
||||
private _utilityScriptPromise: Promise<JSHandle> | undefined;
|
||||
|
||||
constructor(parent: SdkObject, delegate: ExecutionContextDelegate) {
|
||||
super(parent);
|
||||
super(parent, 'execution-context');
|
||||
this._delegate = delegate;
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ export class JSHandle<T = any> extends SdkObject {
|
||||
private _previewCallback: ((preview: string) => void) | undefined;
|
||||
|
||||
constructor(context: ExecutionContext, type: string, objectId?: ObjectId, value?: any) {
|
||||
super(context);
|
||||
super(context, 'handle');
|
||||
this._context = context;
|
||||
this._objectId = objectId;
|
||||
this._value = value;
|
||||
|
@ -99,7 +99,7 @@ export class Request extends SdkObject {
|
||||
|
||||
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
|
||||
url: string, resourceType: string, method: string, postData: Buffer | null, headers: types.HeadersArray) {
|
||||
super(frame);
|
||||
super(frame, 'request');
|
||||
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
||||
assert(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects');
|
||||
this._routeDelegate = routeDelegate;
|
||||
@ -210,7 +210,7 @@ export class Route extends SdkObject {
|
||||
private _handled = false;
|
||||
|
||||
constructor(request: Request, delegate: RouteDelegate) {
|
||||
super(request.frame());
|
||||
super(request.frame(), 'route');
|
||||
this._request = request;
|
||||
this._delegate = delegate;
|
||||
}
|
||||
@ -277,7 +277,7 @@ export class Response extends SdkObject {
|
||||
private _timing: ResourceTiming;
|
||||
|
||||
constructor(request: Request, status: number, statusText: string, headers: types.HeadersArray, timing: ResourceTiming, getResponseBodyCallback: GetResponseBodyCallback) {
|
||||
super(request.frame());
|
||||
super(request.frame(), 'response');
|
||||
this._request = request;
|
||||
this._timing = timing;
|
||||
this._status = status;
|
||||
@ -357,7 +357,7 @@ export class WebSocket extends SdkObject {
|
||||
};
|
||||
|
||||
constructor(parent: SdkObject, url: string) {
|
||||
super(parent);
|
||||
super(parent, 'ws');
|
||||
this._url = url;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import { ConsoleMessage } from './console';
|
||||
import * as accessibility from './accessibility';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { Progress, ProgressController } from './progress';
|
||||
import { assert, createGuid, isError } from '../utils/utils';
|
||||
import { assert, isError } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { Selectors } from './selectors';
|
||||
import { CallMetadata, SdkObject } from './instrumentation';
|
||||
@ -145,14 +145,12 @@ export class Page extends SdkObject {
|
||||
private _serverRequestInterceptor: network.RouteHandler | undefined;
|
||||
_ownedContext: BrowserContext | undefined;
|
||||
readonly selectors: Selectors;
|
||||
readonly uniqueId: string;
|
||||
_pageIsError: Error | undefined;
|
||||
_video: Artifact | null = null;
|
||||
_opener: Page | undefined;
|
||||
|
||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||
super(browserContext);
|
||||
this.uniqueId = 'page@' + createGuid();
|
||||
super(browserContext, 'page');
|
||||
this.attribution.page = this;
|
||||
this._delegate = delegate;
|
||||
this._closedCallback = () => {};
|
||||
@ -296,7 +294,7 @@ export class Page extends SdkObject {
|
||||
}
|
||||
|
||||
_addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) {
|
||||
const message = new ConsoleMessage(type, text, args, location);
|
||||
const message = new ConsoleMessage(this, type, text, args, location);
|
||||
const intercepted = this._frameManager.interceptConsoleMessage(message);
|
||||
if (intercepted || !this.listenerCount(Page.Events.Console))
|
||||
args.forEach(arg => arg.dispose());
|
||||
@ -519,7 +517,7 @@ export class Worker extends SdkObject {
|
||||
_existingExecutionContext: js.ExecutionContext | null = null;
|
||||
|
||||
constructor(parent: SdkObject, url: string) {
|
||||
super(parent);
|
||||
super(parent, 'worker');
|
||||
this._url = url;
|
||||
this._executionContextCallback = () => {};
|
||||
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
||||
|
@ -46,7 +46,7 @@ export class Playwright extends SdkObject {
|
||||
listeners.push(new InspectorController());
|
||||
}
|
||||
const instrumentation = multiplexInstrumentation(listeners);
|
||||
super({ attribution: {}, instrumentation } as any);
|
||||
super({ attribution: {}, instrumentation } as any, undefined, 'Playwright');
|
||||
this.options = {
|
||||
registry: new Registry(path.join(__dirname, '..', '..')),
|
||||
rootSdkObject: this,
|
||||
|
@ -19,6 +19,7 @@ import * as frames from './frames';
|
||||
import * as js from './javascript';
|
||||
import * as types from './types';
|
||||
import { ParsedSelector, parseSelector } from './common/selectorParser';
|
||||
import { createGuid } from '../utils/utils';
|
||||
|
||||
export type SelectorInfo = {
|
||||
parsed: ParsedSelector,
|
||||
@ -29,6 +30,7 @@ export type SelectorInfo = {
|
||||
export class Selectors {
|
||||
readonly _builtinEngines: Set<string>;
|
||||
readonly _engines: Map<string, { source: string, contentScript: boolean }>;
|
||||
readonly guid = `selectors@${createGuid()}`;
|
||||
|
||||
constructor() {
|
||||
// Note: keep in sync with InjectedScript class.
|
||||
|
@ -156,10 +156,9 @@ export class SnapshotServer {
|
||||
response.statusCode = 200;
|
||||
response.setHeader('Cache-Control', 'public, max-age=31536000');
|
||||
response.setHeader('Content-Type', 'application/json');
|
||||
const [ pageId, query ] = request.url!.substring('/snapshot/'.length).split('?');
|
||||
const [ pageOrFrameId, query ] = request.url!.substring('/snapshot/'.length).split('?');
|
||||
const parsed: any = querystring.parse(query);
|
||||
|
||||
const snapshot = parsed.name ? this._snapshotStorage.snapshotByName(pageId, parsed.name) : this._snapshotStorage.snapshotByTime(pageId, parsed.time);
|
||||
const snapshot = this._snapshotStorage.snapshotByName(pageOrFrameId, parsed.name);
|
||||
const snapshotData: any = snapshot ? snapshot.render() : { html: '' };
|
||||
response.end(JSON.stringify(snapshotData));
|
||||
return true;
|
||||
|
@ -25,8 +25,7 @@ export interface SnapshotStorage {
|
||||
resources(): ResourceSnapshot[];
|
||||
resourceContent(sha1: string): Buffer | undefined;
|
||||
resourceById(resourceId: string): ResourceSnapshot | undefined;
|
||||
snapshotByName(frameId: string, snapshotName: string): SnapshotRenderer | undefined;
|
||||
snapshotByTime(frameId: string, timestamp: number): SnapshotRenderer | undefined;
|
||||
snapshotByName(pageOrFrameId: string, snapshotName: string): SnapshotRenderer | undefined;
|
||||
}
|
||||
|
||||
export abstract class BaseSnapshotStorage extends EventEmitter implements SnapshotStorage {
|
||||
@ -64,6 +63,8 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
|
||||
renderer: [],
|
||||
};
|
||||
this._frameSnapshots.set(snapshot.frameId, frameSnapshots);
|
||||
if (snapshot.isMainFrame)
|
||||
this._frameSnapshots.set(snapshot.pageId, frameSnapshots);
|
||||
}
|
||||
frameSnapshots.raw.push(snapshot);
|
||||
const renderer = new SnapshotRenderer(new Map(this._contextResources), frameSnapshots.raw, frameSnapshots.raw.length - 1);
|
||||
@ -81,17 +82,9 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
|
||||
return this._resources.slice();
|
||||
}
|
||||
|
||||
snapshotByName(frameId: string, snapshotName: string): SnapshotRenderer | undefined {
|
||||
return this._frameSnapshots.get(frameId)?.renderer.find(r => r.snapshotName === snapshotName);
|
||||
}
|
||||
|
||||
snapshotByTime(frameId: string, timestamp: number): SnapshotRenderer | undefined {
|
||||
let result: SnapshotRenderer | undefined = undefined;
|
||||
for (const snapshot of this._frameSnapshots.get(frameId)?.renderer.values() || []) {
|
||||
if (timestamp && snapshot.snapshot().timestamp <= timestamp)
|
||||
result = snapshot;
|
||||
}
|
||||
return result;
|
||||
snapshotByName(pageOrFrameId: string, snapshotName: string): SnapshotRenderer | undefined {
|
||||
const snapshot = this._frameSnapshots.get(pageOrFrameId);
|
||||
return snapshot?.renderer.find(r => r.snapshotName === snapshotName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@ export type FrameSnapshot = {
|
||||
html: NodeSnapshot,
|
||||
resourceOverrides: ResourceOverride[],
|
||||
viewport: { width: number, height: number },
|
||||
isMainFrame: boolean,
|
||||
};
|
||||
|
||||
export type ContextResources = Map<string, { resourceId: string, frameId: string }[]>;
|
||||
|
@ -61,8 +61,8 @@ export class Snapshotter {
|
||||
await this._context.exposeBinding(this._snapshotBinding, false, (source, data: SnapshotData) => {
|
||||
const snapshot: FrameSnapshot = {
|
||||
snapshotName: data.snapshotName,
|
||||
pageId: source.page.uniqueId,
|
||||
frameId: source.frame.uniqueId,
|
||||
pageId: source.page.guid,
|
||||
frameId: source.frame.guid,
|
||||
frameUrl: data.url,
|
||||
doctype: data.doctype,
|
||||
html: data.html,
|
||||
@ -71,6 +71,7 @@ export class Snapshotter {
|
||||
pageTimestamp: data.timestamp,
|
||||
collectionTime: data.collectionTime,
|
||||
resourceOverrides: [],
|
||||
isMainFrame: source.page.mainFrame() === source.frame
|
||||
};
|
||||
for (const { url, content } of data.resourceOverrides) {
|
||||
if (typeof content === 'string') {
|
||||
@ -167,8 +168,8 @@ export class Snapshotter {
|
||||
const body = await response.body().catch(e => debugLogger.log('error', e));
|
||||
const responseSha1 = body ? calculateSha1(body) : 'none';
|
||||
const resource: ResourceSnapshot = {
|
||||
pageId: page.uniqueId,
|
||||
frameId: response.frame().uniqueId,
|
||||
pageId: page.guid,
|
||||
frameId: response.frame().guid,
|
||||
resourceId: 'resource@' + createGuid(),
|
||||
url,
|
||||
contentType,
|
||||
@ -203,7 +204,7 @@ export class Snapshotter {
|
||||
const context = await parent._mainContext();
|
||||
await context?.evaluate(({ snapshotStreamer, frameElement, frameId }) => {
|
||||
(window as any)[snapshotStreamer].markIframe(frameElement, frameId);
|
||||
}, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.uniqueId });
|
||||
}, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.guid });
|
||||
frameElement.dispose();
|
||||
} catch (e) {
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class ContextTracer {
|
||||
}
|
||||
|
||||
private _onPage(page: Page) {
|
||||
const pageId = page.uniqueId;
|
||||
const pageId = page.guid;
|
||||
|
||||
const event: trace.PageCreatedTraceEvent = {
|
||||
timestamp: monotonicTime(),
|
||||
@ -197,7 +197,7 @@ class ContextTracer {
|
||||
const sha1 = calculateSha1(params.buffer);
|
||||
const event: trace.ScreencastFrameTraceEvent = {
|
||||
type: 'page-screencast-frame',
|
||||
pageId: page.uniqueId,
|
||||
pageId: page.guid,
|
||||
contextId: this._contextId,
|
||||
sha1,
|
||||
pageTimestamp: params.timestamp,
|
||||
|
@ -23,15 +23,15 @@ it('should scope context handles', async ({browserType, browserOptions, server})
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'Android', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
{ _guid: 'android', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [
|
||||
{ _guid: 'browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
{ _guid: 'electron', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'selectors', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browser, GOLDEN_PRECONDITION);
|
||||
@ -42,23 +42,23 @@ it('should scope context handles', async ({browserType, browserOptions, server})
|
||||
await expectScopeState(browser, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'Android', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'BrowserContext', objects: [
|
||||
{ _guid: 'Frame', objects: [] },
|
||||
{ _guid: 'Page', objects: [
|
||||
{ _guid: 'Request', objects: [] },
|
||||
{ _guid: 'Response', objects: [] },
|
||||
{ _guid: 'android', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [
|
||||
{ _guid: 'browser', objects: [
|
||||
{ _guid: 'browser-context', objects: [
|
||||
{ _guid: 'frame', objects: [] },
|
||||
{ _guid: 'page', objects: [
|
||||
{ _guid: 'request', objects: [] },
|
||||
{ _guid: 'response', objects: [] },
|
||||
]},
|
||||
]},
|
||||
] },
|
||||
] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
{ _guid: 'electron', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'selectors', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
@ -74,15 +74,15 @@ it('should scope CDPSession handles', async ({browserType, browserOptions, brows
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'Android', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
{ _guid: 'android', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [
|
||||
{ _guid: 'browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
{ _guid: 'electron', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'selectors', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
@ -91,17 +91,17 @@ it('should scope CDPSession handles', async ({browserType, browserOptions, brows
|
||||
await expectScopeState(browserType, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'Android', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'CDPSession', objects: [] },
|
||||
{ _guid: 'android', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [
|
||||
{ _guid: 'browser', objects: [
|
||||
{ _guid: 'cdp-session', objects: [] },
|
||||
] },
|
||||
] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
{ _guid: 'electron', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'selectors', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
@ -115,13 +115,13 @@ it('should scope browser handles', async ({browserType, browserOptions}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'Android', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
{ _guid: 'android', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'electron', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'selectors', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
@ -131,20 +131,20 @@ it('should scope browser handles', async ({browserType, browserOptions}) => {
|
||||
await expectScopeState(browserType, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'Android', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'android', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [] },
|
||||
{ _guid: 'browser-type', objects: [
|
||||
{
|
||||
_guid: 'Browser', objects: [
|
||||
{ _guid: 'BrowserContext', objects: [] }
|
||||
_guid: 'browser', objects: [
|
||||
{ _guid: 'browser-context', objects: [] }
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
{ _guid: 'electron', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'selectors', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user