rebrowser-playwright-core
Version:
A drop-in replacement for playwright-core patched with rebrowser-patches. It allows to pass modern automation detection tests.
347 lines (346 loc) • 14 kB
JavaScript
"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,
dispatcherSymbol: () => dispatcherSymbol,
existingDispatcher: () => existingDispatcher,
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");
const dispatcherSymbol = Symbol("dispatcher");
const metadataValidator = (0, import_validator.createMetadataValidator)();
function existingDispatcher(object) {
return object[dispatcherSymbol];
}
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();
// Only "isScope" channel owners have registered dispatchers inside.
this._dispatchers = /* @__PURE__ */ new Map();
this._disposed = false;
this._eventListeners = [];
this._openScope = new import_utils.LongStandingScope();
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;
object[dispatcherSymbol] = this;
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 _handleCommand(callMetadata, method, validParams) {
const commandPromise = this[method](validParams, callMetadata);
try {
return await this._openScope.race(commandPromise);
} catch (e) {
if (callMetadata.potentiallyClosesScope && (0, import_errors.isTargetClosedError)(e))
return await commandPromise;
throw e;
}
}
_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._connection.sendDispose(this, reason);
}
_onDispose() {
}
_disposeRecursively(error) {
(0, import_utils.assert)(!this._disposed, `${this._guid} is disposed more than once`);
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._dispatchers.delete(this._guid);
for (const dispatcher of [...this._dispatchers.values()])
dispatcher._disposeRecursively(error);
this._dispatchers.clear();
delete this._object[dispatcherSymbol];
this._openScope.close(error);
}
_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, { guid: "" }, "Root", {});
this.createPlaywright = createPlaywright;
this._initialized = false;
}
async initialize(params) {
(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._dispatchers = /* @__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._dispatchers.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()}`);
}
registerDispatcher(dispatcher) {
(0, import_utils.assert)(!this._dispatchers.has(dispatcher._guid));
this._dispatchers.set(dispatcher._guid, 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._dispatchers.get(dispatchersArray[i]);
if (!d)
continue;
d._dispose("gc");
}
}
async dispatch(message) {
const { id, guid, method, params, metadata } = message;
const dispatcher = this._dispatchers.get(guid);
if (!dispatcher) {
this.onmessage({ id, error: (0, import_errors.serializeError)(new import_errors.TargetClosedError()) });
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 sdkObject = dispatcher._object instanceof import_instrumentation.SdkObject ? dispatcher._object : void 0;
const callMetadata = {
id: `call@${id}`,
location: validMetadata.location,
apiName: validMetadata.apiName,
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 (sdkObject && 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 {
const result = await dispatcher._handleCommand(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) && sdkObject) {
const reason = closeReason(sdkObject);
if (reason)
(0, import_utils.rewriteErrorMessage)(e, reason);
} else if ((0, import_protocolError.isProtocolError)(e)) {
if (e.type === "closed") {
const reason = sdkObject ? closeReason(sdkObject) : void 0;
e = new import_errors.TargetClosedError(reason, 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 (response.error)
response.log = (0, import_callLog.compressCallLog)(callMetadata.log);
this.onmessage(response);
}
}
function closeReason(sdkObject) {
return sdkObject.attribution.page?._closeReason || sdkObject.attribution.context?._closeReason || sdkObject.attribution.browser?._closeReason;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Dispatcher,
DispatcherConnection,
RootDispatcher,
dispatcherSymbol,
existingDispatcher,
setMaxDispatchersForTest
});