2020-06-25 16:05:36 -07:00
|
|
|
/**
|
|
|
|
* Copyright (c) Microsoft Corporation.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { EventEmitter } from 'events';
|
2022-09-20 18:41:51 -07:00
|
|
|
import type * as channels from '@protocol/channels';
|
2022-07-01 09:58:07 -07:00
|
|
|
import { findValidator, ValidationError, createMetadataValidator, type ValidatorContext } from '../../protocol/validator';
|
2023-11-01 16:36:39 -07:00
|
|
|
import { LongStandingScope, assert, isUnderTest, monotonicTime, rewriteErrorMessage } from '../../utils';
|
2023-10-17 21:34:02 -07:00
|
|
|
import { TargetClosedError, isTargetClosedError, serializeError } from '../errors';
|
2022-04-06 21:21:27 -08:00
|
|
|
import type { CallMetadata } from '../instrumentation';
|
|
|
|
import { SdkObject } from '../instrumentation';
|
2021-08-19 17:31:14 +02:00
|
|
|
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
2022-07-06 19:32:31 -08:00
|
|
|
import { eventsHelper } from '../..//utils/eventsHelper';
|
|
|
|
import type { RegisteredListener } from '../..//utils/eventsHelper';
|
2023-10-16 13:13:00 -07:00
|
|
|
import { isProtocolError } from '../protocolError';
|
2020-06-25 16:05:36 -07:00
|
|
|
|
2020-06-30 21:30:39 -07:00
|
|
|
export const dispatcherSymbol = Symbol('dispatcher');
|
2022-07-01 09:58:07 -07:00
|
|
|
const metadataValidator = createMetadataValidator();
|
2020-06-30 21:30:39 -07:00
|
|
|
|
2022-11-09 21:10:57 -08:00
|
|
|
export function existingDispatcher<DispatcherType>(object: any): DispatcherType | undefined {
|
2020-06-30 21:30:39 -07:00
|
|
|
return object[dispatcherSymbol];
|
|
|
|
}
|
|
|
|
|
2023-12-14 10:42:08 -08:00
|
|
|
let maxDispatchersOverride: number | undefined;
|
2023-09-26 15:54:33 -07:00
|
|
|
export function setMaxDispatchersForTest(value: number | undefined) {
|
2023-12-14 10:42:08 -08:00
|
|
|
maxDispatchersOverride = value;
|
|
|
|
}
|
|
|
|
function maxDispatchersForBucket(gcBucket: string) {
|
|
|
|
return maxDispatchersOverride ?? {
|
|
|
|
'JSHandle': 100000,
|
|
|
|
'ElementHandle': 100000,
|
|
|
|
}[gcBucket] ?? 10000;
|
2023-09-26 15:54:33 -07:00
|
|
|
}
|
|
|
|
|
2022-08-26 09:30:27 -07:00
|
|
|
export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeType extends DispatcherScope> extends EventEmitter implements channels.Channel {
|
2020-07-10 16:24:11 -07:00
|
|
|
private _connection: DispatcherConnection;
|
|
|
|
// Parent is always "isScope".
|
2022-08-25 11:58:41 -07:00
|
|
|
private _parent: ParentScopeType | undefined;
|
2020-07-10 16:24:11 -07:00
|
|
|
// Only "isScope" channel owners have registered dispatchers inside.
|
2022-08-25 11:58:41 -07:00
|
|
|
private _dispatchers = new Map<string, DispatcherScope>();
|
2021-08-31 12:51:13 -07:00
|
|
|
protected _disposed = false;
|
2022-07-06 19:32:31 -08:00
|
|
|
protected _eventListeners: RegisteredListener[] = [];
|
2020-07-10 16:24:11 -07:00
|
|
|
|
2020-06-25 16:05:36 -07:00
|
|
|
readonly _guid: string;
|
|
|
|
readonly _type: string;
|
2023-12-14 10:42:08 -08:00
|
|
|
readonly _gcBucket: string;
|
2020-06-29 18:58:09 -07:00
|
|
|
_object: Type;
|
2023-11-01 16:36:39 -07:00
|
|
|
private _openScope = new LongStandingScope();
|
2020-06-25 16:05:36 -07:00
|
|
|
|
2023-12-14 10:42:08 -08:00
|
|
|
constructor(parent: ParentScopeType | DispatcherConnection, object: Type, type: string, initializer: channels.InitializerTraits<Type>, gcBucket?: string) {
|
2020-06-25 16:05:36 -07:00
|
|
|
super();
|
2020-07-10 16:24:11 -07:00
|
|
|
|
|
|
|
this._connection = parent instanceof DispatcherConnection ? parent : parent._connection;
|
|
|
|
this._parent = parent instanceof DispatcherConnection ? undefined : parent;
|
|
|
|
|
2021-04-20 23:03:56 -07:00
|
|
|
const guid = object.guid;
|
2023-09-26 15:54:33 -07:00
|
|
|
this._guid = guid;
|
|
|
|
this._type = type;
|
|
|
|
this._object = object;
|
2023-12-14 10:42:08 -08:00
|
|
|
this._gcBucket = gcBucket ?? type;
|
2023-09-26 15:54:33 -07:00
|
|
|
|
|
|
|
(object as any)[dispatcherSymbol] = this;
|
|
|
|
|
|
|
|
this._connection.registerDispatcher(this);
|
2020-07-10 16:24:11 -07:00
|
|
|
if (this._parent) {
|
|
|
|
assert(!this._parent._dispatchers.has(guid));
|
|
|
|
this._parent._dispatchers.set(guid, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._parent)
|
2024-03-20 21:01:17 -07:00
|
|
|
this._connection.sendCreate(this._parent, type, guid, initializer);
|
2023-12-14 10:42:08 -08:00
|
|
|
this._connection.maybeDisposeStaleDispatchers(this._gcBucket);
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2022-08-26 09:30:27 -07:00
|
|
|
parentScope(): ParentScopeType {
|
|
|
|
return this._parent!;
|
2022-08-25 11:58:41 -07:00
|
|
|
}
|
|
|
|
|
2022-07-06 19:32:31 -08:00
|
|
|
addObjectListener(eventName: (string | symbol), handler: (...args: any[]) => void) {
|
|
|
|
this._eventListeners.push(eventsHelper.addEventListener(this._object as unknown as EventEmitter, eventName, handler));
|
|
|
|
}
|
|
|
|
|
2022-08-25 11:58:41 -07:00
|
|
|
adopt(child: DispatcherScope) {
|
2023-07-12 14:51:13 -07:00
|
|
|
if (child._parent === this)
|
|
|
|
return;
|
2022-07-15 07:56:47 -08:00
|
|
|
const oldParent = child._parent!;
|
|
|
|
oldParent._dispatchers.delete(child._guid);
|
|
|
|
this._dispatchers.set(child._guid, child);
|
|
|
|
child._parent = this;
|
|
|
|
this._connection.sendAdopt(this, child);
|
|
|
|
}
|
|
|
|
|
2023-11-01 16:36:39 -07:00
|
|
|
async _handleCommand(callMetadata: CallMetadata, method: string, validParams: any) {
|
|
|
|
const commandPromise = (this as any)[method](validParams, callMetadata);
|
|
|
|
try {
|
|
|
|
return await this._openScope.race(commandPromise);
|
|
|
|
} catch (e) {
|
2023-11-06 15:13:41 -08:00
|
|
|
if (callMetadata.potentiallyClosesScope && isTargetClosedError(e))
|
2023-11-01 16:36:39 -07:00
|
|
|
return await commandPromise;
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-17 15:26:01 -08:00
|
|
|
_dispatchEvent<T extends keyof channels.EventsTraits<ChannelType>>(method: T, params?: channels.EventsTraits<ChannelType>[T]) {
|
2020-09-10 16:20:12 -07:00
|
|
|
if (this._disposed) {
|
|
|
|
if (isUnderTest())
|
2022-05-23 22:59:33 +03:00
|
|
|
throw new Error(`${this._guid} is sending "${String(method)}" event after being disposed`);
|
2020-09-10 16:20:12 -07:00
|
|
|
// Just ignore this event outside of tests.
|
|
|
|
return;
|
|
|
|
}
|
2024-03-20 21:01:17 -07:00
|
|
|
this._connection.sendEvent(this, method as string, params);
|
2020-06-30 22:21:17 -07:00
|
|
|
}
|
|
|
|
|
2023-09-26 15:54:33 -07:00
|
|
|
_dispose(reason?: 'gc') {
|
2023-11-01 16:36:39 -07:00
|
|
|
this._disposeRecursively(new TargetClosedError());
|
2023-09-26 15:54:33 -07:00
|
|
|
this._connection.sendDispose(this, reason);
|
2022-08-26 09:30:27 -07:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:54:53 -08:00
|
|
|
protected _onDispose() {
|
|
|
|
}
|
|
|
|
|
2023-11-01 16:36:39 -07:00
|
|
|
private _disposeRecursively(error: Error) {
|
2022-07-15 07:56:47 -08:00
|
|
|
assert(!this._disposed, `${this._guid} is disposed more than once`);
|
2022-12-19 15:54:53 -08:00
|
|
|
this._onDispose();
|
2021-04-05 11:50:28 -07:00
|
|
|
this._disposed = true;
|
2022-07-06 19:32:31 -08:00
|
|
|
eventsHelper.removeEventListeners(this._eventListeners);
|
2020-07-01 13:55:29 -07:00
|
|
|
|
2020-07-10 16:24:11 -07:00
|
|
|
// Clean up from parent and connection.
|
2023-09-26 15:54:33 -07:00
|
|
|
this._parent?._dispatchers.delete(this._guid);
|
2023-12-14 10:42:08 -08:00
|
|
|
const list = this._connection._dispatchersByBucket.get(this._gcBucket);
|
2023-09-26 15:54:33 -07:00
|
|
|
list?.delete(this._guid);
|
2020-07-10 16:24:11 -07:00
|
|
|
this._connection._dispatchers.delete(this._guid);
|
2020-07-01 13:55:29 -07:00
|
|
|
|
2020-07-10 16:24:11 -07:00
|
|
|
// Dispose all children.
|
2020-07-27 10:21:39 -07:00
|
|
|
for (const dispatcher of [...this._dispatchers.values()])
|
2023-11-01 16:36:39 -07:00
|
|
|
dispatcher._disposeRecursively(error);
|
2020-07-10 16:24:11 -07:00
|
|
|
this._dispatchers.clear();
|
2022-07-11 12:10:08 -08:00
|
|
|
delete (this._object as any)[dispatcherSymbol];
|
2023-11-01 16:36:39 -07:00
|
|
|
this._openScope.close(error);
|
2020-06-30 22:21:17 -07:00
|
|
|
}
|
2020-06-25 16:05:36 -07:00
|
|
|
|
2020-07-10 16:24:11 -07:00
|
|
|
_debugScopeState(): any {
|
|
|
|
return {
|
|
|
|
_guid: this._guid,
|
2020-07-27 10:21:39 -07:00
|
|
|
objects: Array.from(this._dispatchers.values()).map(o => o._debugScopeState()),
|
2020-07-10 16:24:11 -07:00
|
|
|
};
|
2020-06-30 22:21:17 -07:00
|
|
|
}
|
2021-02-13 20:31:06 -08:00
|
|
|
|
|
|
|
async waitForEventInfo(): Promise<void> {
|
|
|
|
// Instrumentation takes care of this.
|
|
|
|
}
|
2020-07-10 16:24:11 -07:00
|
|
|
}
|
|
|
|
|
2022-08-25 11:58:41 -07:00
|
|
|
export type DispatcherScope = Dispatcher<any, any, any>;
|
|
|
|
|
|
|
|
export class RootDispatcher extends Dispatcher<{ guid: '' }, any, any> {
|
2021-08-19 17:31:14 +02:00
|
|
|
private _initialized = false;
|
|
|
|
|
2022-08-25 11:58:41 -07:00
|
|
|
constructor(connection: DispatcherConnection, private readonly createPlaywright?: (scope: RootDispatcher, options: channels.RootInitializeParams) => Promise<PlaywrightDispatcher>) {
|
2022-08-26 09:30:27 -07:00
|
|
|
super(connection, { guid: '' }, 'Root', {});
|
2021-08-19 17:31:14 +02:00
|
|
|
}
|
|
|
|
|
2021-08-20 21:32:21 +02:00
|
|
|
async initialize(params: channels.RootInitializeParams): Promise<channels.RootInitializeResult> {
|
2021-08-19 17:31:14 +02:00
|
|
|
assert(this.createPlaywright);
|
|
|
|
assert(!this._initialized);
|
|
|
|
this._initialized = true;
|
|
|
|
return {
|
2021-08-20 21:32:21 +02:00
|
|
|
playwright: await this.createPlaywright(this, params),
|
2021-08-19 17:31:14 +02:00
|
|
|
};
|
2020-07-01 13:55:29 -07:00
|
|
|
}
|
2020-06-30 22:21:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export class DispatcherConnection {
|
2022-08-25 11:58:41 -07:00
|
|
|
readonly _dispatchers = new Map<string, DispatcherScope>();
|
2023-12-14 10:42:08 -08:00
|
|
|
readonly _dispatchersByBucket = new Map<string, Set<string>>();
|
2020-07-13 08:31:20 -07:00
|
|
|
onmessage = (message: object) => {};
|
2021-04-18 17:02:34 -10:00
|
|
|
private _waitOperations = new Map<string, CallMetadata>();
|
2022-07-05 08:58:34 -07:00
|
|
|
private _isLocal: boolean;
|
|
|
|
|
|
|
|
constructor(isLocal?: boolean) {
|
|
|
|
this._isLocal = !!isLocal;
|
|
|
|
}
|
2020-06-30 22:21:17 -07:00
|
|
|
|
2024-03-20 21:01:17 -07:00
|
|
|
sendEvent(dispatcher: DispatcherScope, event: string, params: any) {
|
2022-07-01 09:58:07 -07:00
|
|
|
const validator = findValidator(dispatcher._type, event, 'Event');
|
2022-07-05 08:58:34 -07:00
|
|
|
params = validator(params, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' });
|
2024-03-20 21:01:17 -07:00
|
|
|
this.onmessage({ guid: dispatcher._guid, method: event, params });
|
2022-07-01 09:58:07 -07:00
|
|
|
}
|
|
|
|
|
2024-03-20 21:01:17 -07:00
|
|
|
sendCreate(parent: DispatcherScope, type: string, guid: string, initializer: any) {
|
2022-07-01 09:58:07 -07:00
|
|
|
const validator = findValidator(type, '', 'Initializer');
|
2022-07-05 08:58:34 -07:00
|
|
|
initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' });
|
2024-03-20 21:01:17 -07:00
|
|
|
this.onmessage({ guid: parent._guid, method: '__create__', params: { type, initializer, guid } });
|
2022-07-01 09:58:07 -07:00
|
|
|
}
|
|
|
|
|
2022-08-25 11:58:41 -07:00
|
|
|
sendAdopt(parent: DispatcherScope, dispatcher: DispatcherScope) {
|
2024-03-20 21:01:17 -07:00
|
|
|
this.onmessage({ guid: parent._guid, method: '__adopt__', params: { guid: dispatcher._guid } });
|
2022-07-15 07:56:47 -08:00
|
|
|
}
|
|
|
|
|
2023-09-26 15:54:33 -07:00
|
|
|
sendDispose(dispatcher: DispatcherScope, reason?: 'gc') {
|
2024-03-20 21:01:17 -07:00
|
|
|
this.onmessage({ guid: dispatcher._guid, method: '__dispose__', params: { reason } });
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2022-07-01 09:58:07 -07:00
|
|
|
private _tChannelImplFromWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext): any {
|
|
|
|
if (arg && typeof arg === 'object' && typeof arg.guid === 'string') {
|
|
|
|
const guid = arg.guid;
|
|
|
|
const dispatcher = this._dispatchers.get(guid);
|
|
|
|
if (!dispatcher)
|
|
|
|
throw new ValidationError(`${path}: no object with guid ${guid}`);
|
|
|
|
if (names !== '*' && !names.includes(dispatcher._type))
|
|
|
|
throw new ValidationError(`${path}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}`);
|
|
|
|
return dispatcher;
|
|
|
|
}
|
|
|
|
throw new ValidationError(`${path}: expected guid for ${names.toString()}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _tChannelImplToWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext): any {
|
|
|
|
if (arg instanceof Dispatcher) {
|
|
|
|
if (names !== '*' && !names.includes(arg._type))
|
|
|
|
throw new ValidationError(`${path}: dispatcher with guid ${arg._guid} has type ${arg._type}, expected ${names.toString()}`);
|
|
|
|
return { guid: arg._guid };
|
|
|
|
}
|
|
|
|
throw new ValidationError(`${path}: expected dispatcher ${names.toString()}`);
|
2020-07-01 13:55:29 -07:00
|
|
|
}
|
|
|
|
|
2023-09-26 15:54:33 -07:00
|
|
|
registerDispatcher(dispatcher: DispatcherScope) {
|
|
|
|
assert(!this._dispatchers.has(dispatcher._guid));
|
|
|
|
this._dispatchers.set(dispatcher._guid, dispatcher);
|
2023-12-14 10:42:08 -08:00
|
|
|
let list = this._dispatchersByBucket.get(dispatcher._gcBucket);
|
2023-09-26 15:54:33 -07:00
|
|
|
if (!list) {
|
|
|
|
list = new Set();
|
2023-12-14 10:42:08 -08:00
|
|
|
this._dispatchersByBucket.set(dispatcher._gcBucket, list);
|
2023-09-26 15:54:33 -07:00
|
|
|
}
|
|
|
|
list.add(dispatcher._guid);
|
2023-11-16 15:07:43 -08:00
|
|
|
}
|
|
|
|
|
2023-12-14 10:42:08 -08:00
|
|
|
maybeDisposeStaleDispatchers(gcBucket: string) {
|
|
|
|
const maxDispatchers = maxDispatchersForBucket(gcBucket);
|
|
|
|
const list = this._dispatchersByBucket.get(gcBucket);
|
|
|
|
if (!list || list.size <= maxDispatchers)
|
|
|
|
return;
|
|
|
|
const dispatchersArray = [...list];
|
|
|
|
const disposeCount = (maxDispatchers / 10) | 0;
|
|
|
|
this._dispatchersByBucket.set(gcBucket, new Set(dispatchersArray.slice(disposeCount)));
|
|
|
|
for (let i = 0; i < disposeCount; ++i) {
|
2023-09-26 15:54:33 -07:00
|
|
|
const d = this._dispatchers.get(dispatchersArray[i]);
|
|
|
|
if (!d)
|
|
|
|
continue;
|
|
|
|
d._dispose('gc');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 08:31:20 -07:00
|
|
|
async dispatch(message: object) {
|
2020-09-10 19:25:44 -07:00
|
|
|
const { id, guid, method, params, metadata } = message as any;
|
2020-06-30 22:21:17 -07:00
|
|
|
const dispatcher = this._dispatchers.get(guid);
|
|
|
|
if (!dispatcher) {
|
2023-10-12 11:05:34 -07:00
|
|
|
this.onmessage({ id, error: serializeError(new TargetClosedError()) });
|
2020-06-30 22:21:17 -07:00
|
|
|
return;
|
|
|
|
}
|
2021-02-10 21:55:46 -08:00
|
|
|
|
|
|
|
let validParams: any;
|
|
|
|
let validMetadata: channels.Metadata;
|
2020-06-27 11:10:07 -07:00
|
|
|
try {
|
2022-07-01 09:58:07 -07:00
|
|
|
const validator = findValidator(dispatcher._type, method, 'Params');
|
2022-07-05 08:58:34 -07:00
|
|
|
validParams = validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._isLocal ? 'buffer' : 'fromBase64' });
|
|
|
|
validMetadata = metadataValidator(metadata, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._isLocal ? 'buffer' : 'fromBase64' });
|
2021-01-04 13:54:55 -08:00
|
|
|
if (typeof (dispatcher as any)[method] !== 'function')
|
|
|
|
throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
|
2021-02-10 21:55:46 -08:00
|
|
|
} catch (e) {
|
|
|
|
this.onmessage({ id, error: serializeError(e) });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-01 12:20:04 -08:00
|
|
|
const sdkObject = dispatcher._object instanceof SdkObject ? dispatcher._object : undefined;
|
2021-04-29 09:28:19 -07:00
|
|
|
const callMetadata: CallMetadata = {
|
2021-04-23 09:28:18 -07:00
|
|
|
id: `call@${id}`,
|
2023-03-21 10:03:49 -07:00
|
|
|
wallTime: validMetadata.wallTime || Date.now(),
|
2023-02-22 21:08:47 -08:00
|
|
|
location: validMetadata.location,
|
2021-09-03 13:08:17 -07:00
|
|
|
apiName: validMetadata.apiName,
|
2021-10-30 11:26:38 -08:00
|
|
|
internal: validMetadata.internal,
|
2021-04-23 09:28:18 -07:00
|
|
|
objectId: sdkObject?.guid,
|
2021-06-30 17:56:48 -07:00
|
|
|
pageId: sdkObject?.attribution?.page?.guid,
|
|
|
|
frameId: sdkObject?.attribution?.frame?.guid,
|
2021-02-10 21:55:46 -08:00
|
|
|
startTime: monotonicTime(),
|
|
|
|
endTime: 0,
|
|
|
|
type: dispatcher._type,
|
|
|
|
method,
|
2021-04-08 22:59:05 +08:00
|
|
|
params: params || {},
|
2021-02-10 21:55:46 -08:00
|
|
|
log: [],
|
|
|
|
};
|
|
|
|
|
2021-04-29 09:28:19 -07:00
|
|
|
if (sdkObject && params?.info?.waitId) {
|
2021-09-09 21:20:26 -07:00
|
|
|
// Process logs for waitForNavigation/waitForLoadState/etc.
|
2021-04-29 09:28:19 -07:00
|
|
|
const info = params.info;
|
|
|
|
switch (info.phase) {
|
|
|
|
case 'before': {
|
|
|
|
this._waitOperations.set(info.waitId, callMetadata);
|
|
|
|
await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata);
|
2021-09-09 21:20:26 -07:00
|
|
|
this.onmessage({ id });
|
2021-04-29 09:28:19 -07:00
|
|
|
return;
|
|
|
|
} case 'log': {
|
|
|
|
const originalMetadata = this._waitOperations.get(info.waitId)!;
|
|
|
|
originalMetadata.log.push(info.message);
|
2022-01-12 07:37:48 -08:00
|
|
|
sdkObject.instrumentation.onCallLog(sdkObject, originalMetadata, 'api', info.message);
|
2021-09-09 21:20:26 -07:00
|
|
|
this.onmessage({ id });
|
2021-04-29 09:28:19 -07:00
|
|
|
return;
|
|
|
|
} case 'after': {
|
|
|
|
const originalMetadata = this._waitOperations.get(info.waitId)!;
|
|
|
|
originalMetadata.endTime = monotonicTime();
|
2021-07-02 14:33:38 -07:00
|
|
|
originalMetadata.error = info.error ? { error: { name: 'Error', message: info.error } } : undefined;
|
2021-04-29 09:28:19 -07:00
|
|
|
this._waitOperations.delete(info.waitId);
|
|
|
|
await sdkObject.instrumentation.onAfterCall(sdkObject, originalMetadata);
|
2021-09-09 21:20:26 -07:00
|
|
|
this.onmessage({ id });
|
2021-04-29 09:28:19 -07:00
|
|
|
return;
|
2021-04-18 17:02:34 -10:00
|
|
|
}
|
|
|
|
}
|
2021-04-29 09:28:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
await sdkObject?.instrumentation.onBeforeCall(sdkObject, callMetadata);
|
2024-02-02 16:41:08 -08:00
|
|
|
const response: any = { id };
|
2021-04-29 09:28:19 -07:00
|
|
|
try {
|
2023-11-01 16:36:39 -07:00
|
|
|
const result = await dispatcher._handleCommand(callMetadata, method, validParams);
|
2022-07-01 09:58:07 -07:00
|
|
|
const validator = findValidator(dispatcher._type, method, 'Result');
|
2024-02-02 16:41:08 -08:00
|
|
|
response.result = validator(result, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' });
|
2024-02-12 21:03:38 +01:00
|
|
|
callMetadata.result = result;
|
2020-06-27 11:10:07 -07:00
|
|
|
} catch (e) {
|
2023-10-17 15:35:41 -07:00
|
|
|
if (isTargetClosedError(e) && sdkObject) {
|
|
|
|
const reason = closeReason(sdkObject);
|
|
|
|
if (reason)
|
|
|
|
rewriteErrorMessage(e, reason);
|
|
|
|
} else if (isProtocolError(e)) {
|
2023-10-16 20:32:13 -07:00
|
|
|
if (e.type === 'closed') {
|
2023-10-17 15:35:41 -07:00
|
|
|
const reason = sdkObject ? closeReason(sdkObject) : undefined;
|
|
|
|
e = new TargetClosedError(reason, e.browserLogMessage());
|
|
|
|
} else if (e.type === 'crashed') {
|
|
|
|
rewriteErrorMessage(e, 'Target crashed ' + e.browserLogMessage());
|
2023-10-16 20:32:13 -07:00
|
|
|
}
|
2023-10-16 13:13:00 -07:00
|
|
|
}
|
2024-02-02 16:41:08 -08:00
|
|
|
response.error = serializeError(e);
|
|
|
|
// The command handler could have set error in the metada, do not reset it if there was no exception.
|
|
|
|
callMetadata.error = response.error;
|
2021-02-10 21:55:46 -08:00
|
|
|
} finally {
|
2021-02-17 22:10:13 -08:00
|
|
|
callMetadata.endTime = monotonicTime();
|
2021-04-29 09:28:19 -07:00
|
|
|
await sdkObject?.instrumentation.onAfterCall(sdkObject, callMetadata);
|
2020-06-27 11:10:07 -07:00
|
|
|
}
|
2021-04-29 09:28:19 -07:00
|
|
|
|
2024-02-02 16:41:08 -08:00
|
|
|
if (response.error)
|
2023-10-12 11:05:34 -07:00
|
|
|
response.log = callMetadata.log;
|
2021-09-27 09:19:59 -07:00
|
|
|
this.onmessage(response);
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
}
|
2023-10-16 20:32:13 -07:00
|
|
|
|
2023-10-17 15:35:41 -07:00
|
|
|
function closeReason(sdkObject: SdkObject): string | undefined {
|
2023-10-16 20:32:13 -07:00
|
|
|
return sdkObject.attribution.page?._closeReason ||
|
|
|
|
sdkObject.attribution.context?._closeReason ||
|
2023-10-17 15:35:41 -07:00
|
|
|
sdkObject.attribution.browser?._closeReason;
|
2023-10-16 20:32:13 -07:00
|
|
|
}
|