365 lines
14 KiB
Plaintext
365 lines
14 KiB
Plaintext
"use strict";
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var dispatcher_exports = {};
|
|
__export(dispatcher_exports, {
|
|
Dispatcher: () => Dispatcher,
|
|
DispatcherConnection: () => DispatcherConnection,
|
|
RootDispatcher: () => RootDispatcher,
|
|
setMaxDispatchersForTest: () => setMaxDispatchersForTest
|
|
});
|
|
module.exports = __toCommonJS(dispatcher_exports);
|
|
var import_events = require("events");
|
|
var import_eventsHelper = require("../utils/eventsHelper");
|
|
var import_validator = require("../../protocol/validator");
|
|
var import_utils = require("../../utils");
|
|
var import_debug = require("../utils/debug");
|
|
var import_errors = require("../errors");
|
|
var import_instrumentation = require("../instrumentation");
|
|
var import_protocolError = require("../protocolError");
|
|
var import_callLog = require("../callLog");
|
|
var import_protocolMetainfo = require("../../utils/isomorphic/protocolMetainfo");
|
|
var import_progress = require("../progress");
|
|
const metadataValidator = (0, import_validator.createMetadataValidator)();
|
|
let maxDispatchersOverride;
|
|
function setMaxDispatchersForTest(value) {
|
|
maxDispatchersOverride = value;
|
|
}
|
|
function maxDispatchersForBucket(gcBucket) {
|
|
return maxDispatchersOverride ?? {
|
|
"JSHandle": 1e5,
|
|
"ElementHandle": 1e5
|
|
}[gcBucket] ?? 1e4;
|
|
}
|
|
class Dispatcher extends import_events.EventEmitter {
|
|
constructor(parent, object, type, initializer, gcBucket) {
|
|
super();
|
|
this._dispatchers = /* @__PURE__ */ new Map();
|
|
this._disposed = false;
|
|
this._eventListeners = [];
|
|
this._activeProgressControllers = /* @__PURE__ */ new Set();
|
|
this.connection = parent instanceof DispatcherConnection ? parent : parent.connection;
|
|
this._parent = parent instanceof DispatcherConnection ? void 0 : parent;
|
|
const guid = object.guid;
|
|
this._guid = guid;
|
|
this._type = type;
|
|
this._object = object;
|
|
this._gcBucket = gcBucket ?? type;
|
|
this.connection.registerDispatcher(this);
|
|
if (this._parent) {
|
|
(0, import_utils.assert)(!this._parent._dispatchers.has(guid));
|
|
this._parent._dispatchers.set(guid, this);
|
|
}
|
|
if (this._parent)
|
|
this.connection.sendCreate(this._parent, type, guid, initializer);
|
|
this.connection.maybeDisposeStaleDispatchers(this._gcBucket);
|
|
}
|
|
parentScope() {
|
|
return this._parent;
|
|
}
|
|
addObjectListener(eventName, handler) {
|
|
this._eventListeners.push(import_eventsHelper.eventsHelper.addEventListener(this._object, eventName, handler));
|
|
}
|
|
adopt(child) {
|
|
if (child._parent === this)
|
|
return;
|
|
const oldParent = child._parent;
|
|
oldParent._dispatchers.delete(child._guid);
|
|
this._dispatchers.set(child._guid, child);
|
|
child._parent = this;
|
|
this.connection.sendAdopt(this, child);
|
|
}
|
|
async _runCommand(callMetadata, method, validParams) {
|
|
const controller = import_progress.ProgressController.createForSdkObject(this._object, callMetadata);
|
|
this._activeProgressControllers.add(controller);
|
|
try {
|
|
return await controller.run((progress) => this[method](validParams, progress), validParams?.timeout);
|
|
} finally {
|
|
this._activeProgressControllers.delete(controller);
|
|
}
|
|
}
|
|
_dispatchEvent(method, params) {
|
|
if (this._disposed) {
|
|
if ((0, import_debug.isUnderTest)())
|
|
throw new Error(`${this._guid} is sending "${String(method)}" event after being disposed`);
|
|
return;
|
|
}
|
|
this.connection.sendEvent(this, method, params);
|
|
}
|
|
_dispose(reason) {
|
|
this._disposeRecursively(new import_errors.TargetClosedError(this._object.closeReason()));
|
|
this.connection.sendDispose(this, reason);
|
|
}
|
|
_onDispose() {
|
|
}
|
|
async stopPendingOperations(error) {
|
|
const controllers = [];
|
|
const collect = (dispatcher) => {
|
|
controllers.push(...dispatcher._activeProgressControllers);
|
|
for (const child of [...dispatcher._dispatchers.values()])
|
|
collect(child);
|
|
};
|
|
collect(this);
|
|
await Promise.all(controllers.map((controller) => controller.abort(error)));
|
|
}
|
|
_disposeRecursively(error) {
|
|
(0, import_utils.assert)(!this._disposed, `${this._guid} is disposed more than once`);
|
|
for (const controller of this._activeProgressControllers) {
|
|
if (!controller.metadata.potentiallyClosesScope)
|
|
controller.abort(error).catch(() => {
|
|
});
|
|
}
|
|
this._onDispose();
|
|
this._disposed = true;
|
|
import_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
|
this._parent?._dispatchers.delete(this._guid);
|
|
const list = this.connection._dispatchersByBucket.get(this._gcBucket);
|
|
list?.delete(this._guid);
|
|
this.connection._dispatcherByGuid.delete(this._guid);
|
|
this.connection._dispatcherByObject.delete(this._object);
|
|
for (const dispatcher of [...this._dispatchers.values()])
|
|
dispatcher._disposeRecursively(error);
|
|
this._dispatchers.clear();
|
|
}
|
|
_debugScopeState() {
|
|
return {
|
|
_guid: this._guid,
|
|
objects: Array.from(this._dispatchers.values()).map((o) => o._debugScopeState())
|
|
};
|
|
}
|
|
async waitForEventInfo() {
|
|
}
|
|
}
|
|
class RootDispatcher extends Dispatcher {
|
|
constructor(connection, createPlaywright) {
|
|
super(connection, (0, import_instrumentation.createRootSdkObject)(), "Root", {});
|
|
this.createPlaywright = createPlaywright;
|
|
this._initialized = false;
|
|
}
|
|
async initialize(params, progress) {
|
|
(0, import_utils.assert)(this.createPlaywright);
|
|
(0, import_utils.assert)(!this._initialized);
|
|
this._initialized = true;
|
|
return {
|
|
playwright: await this.createPlaywright(this, params)
|
|
};
|
|
}
|
|
}
|
|
class DispatcherConnection {
|
|
constructor(isLocal) {
|
|
this._dispatcherByGuid = /* @__PURE__ */ new Map();
|
|
this._dispatcherByObject = /* @__PURE__ */ new Map();
|
|
this._dispatchersByBucket = /* @__PURE__ */ new Map();
|
|
this.onmessage = (message) => {
|
|
};
|
|
this._waitOperations = /* @__PURE__ */ new Map();
|
|
this._isLocal = !!isLocal;
|
|
}
|
|
sendEvent(dispatcher, event, params) {
|
|
const validator = (0, import_validator.findValidator)(dispatcher._type, event, "Event");
|
|
params = validator(params, "", this._validatorToWireContext());
|
|
this.onmessage({ guid: dispatcher._guid, method: event, params });
|
|
}
|
|
sendCreate(parent, type, guid, initializer) {
|
|
const validator = (0, import_validator.findValidator)(type, "", "Initializer");
|
|
initializer = validator(initializer, "", this._validatorToWireContext());
|
|
this.onmessage({ guid: parent._guid, method: "__create__", params: { type, initializer, guid } });
|
|
}
|
|
sendAdopt(parent, dispatcher) {
|
|
this.onmessage({ guid: parent._guid, method: "__adopt__", params: { guid: dispatcher._guid } });
|
|
}
|
|
sendDispose(dispatcher, reason) {
|
|
this.onmessage({ guid: dispatcher._guid, method: "__dispose__", params: { reason } });
|
|
}
|
|
_validatorToWireContext() {
|
|
return {
|
|
tChannelImpl: this._tChannelImplToWire.bind(this),
|
|
binary: this._isLocal ? "buffer" : "toBase64",
|
|
isUnderTest: import_debug.isUnderTest
|
|
};
|
|
}
|
|
_validatorFromWireContext() {
|
|
return {
|
|
tChannelImpl: this._tChannelImplFromWire.bind(this),
|
|
binary: this._isLocal ? "buffer" : "fromBase64",
|
|
isUnderTest: import_debug.isUnderTest
|
|
};
|
|
}
|
|
_tChannelImplFromWire(names, arg, path, context) {
|
|
if (arg && typeof arg === "object" && typeof arg.guid === "string") {
|
|
const guid = arg.guid;
|
|
const dispatcher = this._dispatcherByGuid.get(guid);
|
|
if (!dispatcher)
|
|
throw new import_validator.ValidationError(`${path}: no object with guid ${guid}`);
|
|
if (names !== "*" && !names.includes(dispatcher._type))
|
|
throw new import_validator.ValidationError(`${path}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}`);
|
|
return dispatcher;
|
|
}
|
|
throw new import_validator.ValidationError(`${path}: expected guid for ${names.toString()}`);
|
|
}
|
|
_tChannelImplToWire(names, arg, path, context) {
|
|
if (arg instanceof Dispatcher) {
|
|
if (names !== "*" && !names.includes(arg._type))
|
|
throw new import_validator.ValidationError(`${path}: dispatcher with guid ${arg._guid} has type ${arg._type}, expected ${names.toString()}`);
|
|
return { guid: arg._guid };
|
|
}
|
|
throw new import_validator.ValidationError(`${path}: expected dispatcher ${names.toString()}`);
|
|
}
|
|
existingDispatcher(object) {
|
|
return this._dispatcherByObject.get(object);
|
|
}
|
|
registerDispatcher(dispatcher) {
|
|
(0, import_utils.assert)(!this._dispatcherByGuid.has(dispatcher._guid));
|
|
this._dispatcherByGuid.set(dispatcher._guid, dispatcher);
|
|
this._dispatcherByObject.set(dispatcher._object, dispatcher);
|
|
let list = this._dispatchersByBucket.get(dispatcher._gcBucket);
|
|
if (!list) {
|
|
list = /* @__PURE__ */ new Set();
|
|
this._dispatchersByBucket.set(dispatcher._gcBucket, list);
|
|
}
|
|
list.add(dispatcher._guid);
|
|
}
|
|
maybeDisposeStaleDispatchers(gcBucket) {
|
|
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) {
|
|
const d = this._dispatcherByGuid.get(dispatchersArray[i]);
|
|
if (!d)
|
|
continue;
|
|
d._dispose("gc");
|
|
}
|
|
}
|
|
async dispatch(message) {
|
|
const { id, guid, method, params, metadata } = message;
|
|
const dispatcher = this._dispatcherByGuid.get(guid);
|
|
if (!dispatcher) {
|
|
this.onmessage({ id, error: (0, import_errors.serializeError)(new import_errors.TargetClosedError(void 0)) });
|
|
return;
|
|
}
|
|
let validParams;
|
|
let validMetadata;
|
|
try {
|
|
const validator = (0, import_validator.findValidator)(dispatcher._type, method, "Params");
|
|
const validatorContext = this._validatorFromWireContext();
|
|
validParams = validator(params, "", validatorContext);
|
|
validMetadata = metadataValidator(metadata, "", validatorContext);
|
|
if (typeof dispatcher[method] !== "function")
|
|
throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
|
|
} catch (e) {
|
|
this.onmessage({ id, error: (0, import_errors.serializeError)(e) });
|
|
return;
|
|
}
|
|
const metainfo = (0, import_protocolMetainfo.getMetainfo)({ type: dispatcher._type, method });
|
|
if (metainfo?.internal) {
|
|
validMetadata.internal = true;
|
|
}
|
|
const sdkObject = dispatcher._object;
|
|
const callMetadata = {
|
|
id: `call@${id}`,
|
|
location: validMetadata.location,
|
|
title: validMetadata.title,
|
|
internal: validMetadata.internal,
|
|
stepId: validMetadata.stepId,
|
|
objectId: sdkObject.guid,
|
|
pageId: sdkObject.attribution?.page?.guid,
|
|
frameId: sdkObject.attribution?.frame?.guid,
|
|
startTime: (0, import_utils.monotonicTime)(),
|
|
endTime: 0,
|
|
type: dispatcher._type,
|
|
method,
|
|
params: params || {},
|
|
log: []
|
|
};
|
|
if (params?.info?.waitId) {
|
|
const info = params.info;
|
|
switch (info.phase) {
|
|
case "before": {
|
|
this._waitOperations.set(info.waitId, callMetadata);
|
|
await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata);
|
|
this.onmessage({ id });
|
|
return;
|
|
}
|
|
case "log": {
|
|
const originalMetadata = this._waitOperations.get(info.waitId);
|
|
originalMetadata.log.push(info.message);
|
|
sdkObject.instrumentation.onCallLog(sdkObject, originalMetadata, "api", info.message);
|
|
this.onmessage({ id });
|
|
return;
|
|
}
|
|
case "after": {
|
|
const originalMetadata = this._waitOperations.get(info.waitId);
|
|
originalMetadata.endTime = (0, import_utils.monotonicTime)();
|
|
originalMetadata.error = info.error ? { error: { name: "Error", message: info.error } } : void 0;
|
|
this._waitOperations.delete(info.waitId);
|
|
await sdkObject.instrumentation.onAfterCall(sdkObject, originalMetadata);
|
|
this.onmessage({ id });
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata);
|
|
const response = { id };
|
|
try {
|
|
if (this._dispatcherByGuid.get(guid) !== dispatcher)
|
|
throw new import_errors.TargetClosedError(sdkObject.closeReason());
|
|
const result = await dispatcher._runCommand(callMetadata, method, validParams);
|
|
const validator = (0, import_validator.findValidator)(dispatcher._type, method, "Result");
|
|
response.result = validator(result, "", this._validatorToWireContext());
|
|
callMetadata.result = result;
|
|
} catch (e) {
|
|
if ((0, import_errors.isTargetClosedError)(e)) {
|
|
const reason = sdkObject.closeReason();
|
|
if (reason)
|
|
(0, import_utils.rewriteErrorMessage)(e, reason);
|
|
} else if ((0, import_protocolError.isProtocolError)(e)) {
|
|
if (e.type === "closed")
|
|
e = new import_errors.TargetClosedError(sdkObject.closeReason(), e.browserLogMessage());
|
|
else if (e.type === "crashed")
|
|
(0, import_utils.rewriteErrorMessage)(e, "Target crashed " + e.browserLogMessage());
|
|
}
|
|
response.error = (0, import_errors.serializeError)(e);
|
|
callMetadata.error = response.error;
|
|
} finally {
|
|
callMetadata.endTime = (0, import_utils.monotonicTime)();
|
|
await sdkObject.instrumentation.onAfterCall(sdkObject, callMetadata);
|
|
if (metainfo?.slowMo)
|
|
await this._doSlowMo(sdkObject);
|
|
}
|
|
if (response.error)
|
|
response.log = (0, import_callLog.compressCallLog)(callMetadata.log);
|
|
this.onmessage(response);
|
|
}
|
|
async _doSlowMo(sdkObject) {
|
|
const slowMo = sdkObject.attribution.browser?.options.slowMo;
|
|
if (slowMo)
|
|
await new Promise((f) => setTimeout(f, slowMo));
|
|
}
|
|
}
|
|
// Annotate the CommonJS export names for ESM import in node:
|
|
0 && (module.exports = {
|
|
Dispatcher,
|
|
DispatcherConnection,
|
|
RootDispatcher,
|
|
setMaxDispatchersForTest
|
|
});
|