This commit is contained in:
Yury Semikhatsky 2024-11-05 16:38:58 -08:00
parent d2787c1ac6
commit 8d9b9078b5
3 changed files with 15 additions and 46 deletions

View file

@ -22,7 +22,7 @@ import type * as api from '../../types/types';
import { serializeError, isTargetClosedError, TargetClosedError } from './errors'; import { serializeError, isTargetClosedError, TargetClosedError } from './errors';
import { TimeoutSettings } from '../common/timeoutSettings'; import { TimeoutSettings } from '../common/timeoutSettings';
import type * as channels from '@protocol/channels'; import type * as channels from '@protocol/channels';
import { assert, headersObjectToArray, isObject, isRegExp, isString, LongStandingScope, urlMatches, urlMatchesEqual, mkdirIfNeeded, trimStringWithEllipsis, type URLMatch, zones } from '../utils'; import { assert, headersObjectToArray, isObject, isRegExp, isString, LongStandingScope, urlMatches, urlMatchesEqual, mkdirIfNeeded, trimStringWithEllipsis, type URLMatch } from '../utils';
import { Accessibility } from './accessibility'; import { Accessibility } from './accessibility';
import { Artifact } from './artifact'; import { Artifact } from './artifact';
import type { BrowserContext } from './browserContext'; import type { BrowserContext } from './browserContext';

View file

@ -17,7 +17,7 @@
import type { EventEmitter } from 'events'; import type { EventEmitter } from 'events';
import { rewriteErrorMessage } from '../utils/stackTrace'; import { rewriteErrorMessage } from '../utils/stackTrace';
import { TimeoutError } from './errors'; import { TimeoutError } from './errors';
import { createGuid, ZoneReference, zones } from '../utils'; import { createGuid } from '../utils';
import type * as channels from '@protocol/channels'; import type * as channels from '@protocol/channels';
import type { ChannelOwner } from './channelOwner'; import type { ChannelOwner } from './channelOwner';
@ -29,14 +29,10 @@ export class Waiter {
private _channelOwner: ChannelOwner<channels.EventTargetChannel>; private _channelOwner: ChannelOwner<channels.EventTargetChannel>;
private _waitId: string; private _waitId: string;
private _error: string | undefined; private _error: string | undefined;
private _savedZone: ZoneReference;
constructor(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) { constructor(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) {
this._waitId = createGuid(); this._waitId = createGuid();
this._channelOwner = channelOwner; this._channelOwner = channelOwner;
// Save current chain before the wait for event API call (we don't nest API calls)
// and restore it later to find proper parent for the event listener.
this._savedZone = zones.currentZoneBefore('apiZone');
this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {}); this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {});
this._dispose = [ this._dispose = [
() => this._channelOwner._wrapApiCall(async () => { () => this._channelOwner._wrapApiCall(async () => {
@ -45,17 +41,17 @@ export class Waiter {
]; ];
} }
static createForEvent(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string): Waiter { static createForEvent(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) {
return new Waiter(channelOwner, event); return new Waiter(channelOwner, event);
} }
async waitForEvent<T = void>(emitter: EventEmitter, event: string, predicate?: (arg: T) => boolean | Promise<boolean>): Promise<T> { async waitForEvent<T = void>(emitter: EventEmitter, event: string, predicate?: (arg: T) => boolean | Promise<boolean>): Promise<T> {
const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate); const { promise, dispose } = waitForEvent(emitter, event, predicate);
return await this.waitForPromise(promise, dispose); return await this.waitForPromise(promise, dispose);
} }
rejectOnEvent<T = void>(emitter: EventEmitter, event: string, error: Error | (() => Error), predicate?: (arg: T) => boolean | Promise<boolean>) { rejectOnEvent<T = void>(emitter: EventEmitter, event: string, error: Error | (() => Error), predicate?: (arg: T) => boolean | Promise<boolean>) {
const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate); const { promise, dispose } = waitForEvent(emitter, event, predicate);
this._rejectOn(promise.then(() => { throw (typeof error === 'function' ? error() : error); }), dispose); this._rejectOn(promise.then(() => { throw (typeof error === 'function' ? error() : error); }), dispose);
} }
@ -107,11 +103,10 @@ export class Waiter {
} }
} }
function waitForEvent<T = void>(emitter: EventEmitter, event: string, savedZone: ZoneReference, predicate?: (arg: T) => boolean | Promise<boolean>): { promise: Promise<T>, dispose: () => void } { function waitForEvent<T = void>(emitter: EventEmitter, event: string, predicate?: (arg: T) => boolean | Promise<boolean>): { promise: Promise<T>, dispose: () => void } {
let listener: (eventArg: any) => void; let listener: (eventArg: any) => void;
const promise = new Promise<T>((resolve, reject) => { const promise = new Promise<T>((resolve, reject) => {
listener = async (eventArg: any) => { listener = async (eventArg: any) => {
savedZone.runInZone(async () => {
try { try {
if (predicate && !(await predicate(eventArg))) if (predicate && !(await predicate(eventArg)))
return; return;
@ -121,7 +116,6 @@ function waitForEvent<T = void>(emitter: EventEmitter, event: string, savedZone:
emitter.removeListener(event, listener); emitter.removeListener(event, listener);
reject(e); reject(e);
} }
});
}; };
emitter.addListener(event, listener); emitter.addListener(event, listener);
}); });

View file

@ -39,17 +39,6 @@ class ZoneManager {
return this._asyncLocalStorage.run(undefined, func); return this._asyncLocalStorage.run(undefined, func);
} }
currentZoneBefore(type: ZoneType): ZoneReference {
let zone = this._asyncLocalStorage.getStore();
while (zone && zone.type === type)
zone = zone.previous;
return new ZoneReference(zone, this._asyncLocalStorage);
}
currentZone(): ZoneReference {
return new ZoneReference(this._asyncLocalStorage.getStore(), this._asyncLocalStorage);
}
printZones() { printZones() {
const zones = []; const zones = [];
for (let zone = this._asyncLocalStorage.getStore(); zone; zone = zone.previous) { for (let zone = this._asyncLocalStorage.getStore(); zone; zone = zone.previous) {
@ -64,20 +53,6 @@ class ZoneManager {
} }
} }
export class ZoneReference {
private _zone: Zone<unknown> | undefined;
private _asyncLocalStorage: AsyncLocalStorage<Zone<unknown>|undefined>;
constructor(zone: Zone<unknown> | undefined, asyncLocalStorage: AsyncLocalStorage<Zone<unknown>|undefined>) {
this._zone = zone;
this._asyncLocalStorage = asyncLocalStorage;
}
runInZone<R>(func: () => R): R {
return this._asyncLocalStorage.run(this._zone, func);
}
}
class Zone<T> { class Zone<T> {
readonly type: ZoneType; readonly type: ZoneType;
readonly data: T; readonly data: T;