mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
refactor: replace deferred with rxjs (#12406)
This commit is contained in:
parent
93bf52bdfc
commit
bc17e339bc
@ -6,18 +6,22 @@
|
|||||||
|
|
||||||
import type {Protocol} from 'devtools-protocol';
|
import type {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {firstValueFrom, map, raceWith} from '../../third_party/rxjs/rxjs.js';
|
||||||
import type {CDPSession} from '../api/CDPSession.js';
|
import type {CDPSession} from '../api/CDPSession.js';
|
||||||
import type {ElementHandle} from '../api/ElementHandle.js';
|
import type {ElementHandle} from '../api/ElementHandle.js';
|
||||||
import type {JSHandle} from '../api/JSHandle.js';
|
import type {JSHandle} from '../api/JSHandle.js';
|
||||||
import {Realm} from '../api/Realm.js';
|
import {Realm} from '../api/Realm.js';
|
||||||
|
import {EventEmitter} from '../common/EventEmitter.js';
|
||||||
import type {TimeoutSettings} from '../common/TimeoutSettings.js';
|
import type {TimeoutSettings} from '../common/TimeoutSettings.js';
|
||||||
import type {EvaluateFunc, HandleFor} from '../common/types.js';
|
import type {EvaluateFunc, HandleFor} from '../common/types.js';
|
||||||
import {withSourcePuppeteerURLIfNone} from '../common/util.js';
|
import {
|
||||||
import {Deferred} from '../util/Deferred.js';
|
fromEmitterEvent,
|
||||||
|
withSourcePuppeteerURLIfNone,
|
||||||
|
} from '../common/util.js';
|
||||||
import {disposeSymbol} from '../util/disposable.js';
|
import {disposeSymbol} from '../util/disposable.js';
|
||||||
|
|
||||||
import {CdpElementHandle} from './ElementHandle.js';
|
import {CdpElementHandle} from './ElementHandle.js';
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import type {ExecutionContext} from './ExecutionContext.js';
|
||||||
import type {CdpFrame} from './Frame.js';
|
import type {CdpFrame} from './Frame.js';
|
||||||
import type {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
import type {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||||
import {CdpJSHandle} from './JSHandle.js';
|
import {CdpJSHandle} from './JSHandle.js';
|
||||||
@ -44,7 +48,13 @@ export interface IsolatedWorldChart {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class IsolatedWorld extends Realm {
|
export class IsolatedWorld extends Realm {
|
||||||
#context = Deferred.create<ExecutionContext>();
|
#context?: ExecutionContext;
|
||||||
|
#emitter = new EventEmitter<{
|
||||||
|
// Emitted when the isolated world gets a new execution context.
|
||||||
|
context: ExecutionContext;
|
||||||
|
// Emitted when the isolated world is disposed.
|
||||||
|
disposed: undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
readonly #frameOrWorker: CdpFrame | CdpWebWorker;
|
readonly #frameOrWorker: CdpFrame | CdpWebWorker;
|
||||||
|
|
||||||
@ -65,36 +75,48 @@ export class IsolatedWorld extends Realm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContext(context: ExecutionContext): void {
|
setContext(context: ExecutionContext): void {
|
||||||
const existingContext = this.#context.value();
|
this.#context?.[disposeSymbol]();
|
||||||
if (existingContext instanceof ExecutionContext) {
|
|
||||||
existingContext[disposeSymbol]();
|
|
||||||
}
|
|
||||||
context.once('disposed', () => {
|
context.once('disposed', () => {
|
||||||
// The message has to match the CDP message expected by the WaitTask class.
|
this.#context = undefined;
|
||||||
this.#context?.reject(new Error('Execution context was destroyed'));
|
|
||||||
this.#context = Deferred.create();
|
|
||||||
if ('clearDocumentHandle' in this.#frameOrWorker) {
|
if ('clearDocumentHandle' in this.#frameOrWorker) {
|
||||||
this.#frameOrWorker.clearDocumentHandle();
|
this.#frameOrWorker.clearDocumentHandle();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.#context.resolve(context);
|
this.#context = context;
|
||||||
|
this.#emitter.emit('context', context);
|
||||||
void this.taskManager.rerunAll();
|
void this.taskManager.rerunAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
hasContext(): boolean {
|
hasContext(): boolean {
|
||||||
return this.#context.resolved();
|
return !!this.#context;
|
||||||
}
|
}
|
||||||
|
|
||||||
#executionContext(): Promise<ExecutionContext> {
|
#executionContext(): ExecutionContext | undefined {
|
||||||
if (this.disposed) {
|
if (this.disposed) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Execution context is not available in detached frame "${this.environment.url()}" (are you trying to evaluate?)`
|
`Execution context is not available in detached frame "${this.environment.url()}" (are you trying to evaluate?)`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (this.#context === null) {
|
return this.#context;
|
||||||
throw new Error(`Execution content promise is missing`);
|
}
|
||||||
}
|
|
||||||
return this.#context.valueOrThrow();
|
/**
|
||||||
|
* Waits for the next context to be set on the isolated world.
|
||||||
|
*/
|
||||||
|
async #waitForExecutionContext(): Promise<ExecutionContext> {
|
||||||
|
const result = await firstValueFrom(
|
||||||
|
fromEmitterEvent(this.#emitter, 'context').pipe(
|
||||||
|
raceWith(
|
||||||
|
fromEmitterEvent(this.#emitter, 'disposed').pipe(
|
||||||
|
map(() => {
|
||||||
|
// The message has to match the CDP message expected by the WaitTask class.
|
||||||
|
throw new Error('Execution context was destroyed');
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateHandle<
|
async evaluateHandle<
|
||||||
@ -108,7 +130,13 @@ export class IsolatedWorld extends Realm {
|
|||||||
this.evaluateHandle.name,
|
this.evaluateHandle.name,
|
||||||
pageFunction
|
pageFunction
|
||||||
);
|
);
|
||||||
const context = await this.#executionContext();
|
// This code needs to schedule evaluateHandle call synchroniously (at
|
||||||
|
// least when the context is there) so we cannot unconditionally
|
||||||
|
// await.
|
||||||
|
let context = this.#executionContext();
|
||||||
|
if (!context) {
|
||||||
|
context = await this.#waitForExecutionContext();
|
||||||
|
}
|
||||||
return await context.evaluateHandle(pageFunction, ...args);
|
return await context.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +151,12 @@ export class IsolatedWorld extends Realm {
|
|||||||
this.evaluate.name,
|
this.evaluate.name,
|
||||||
pageFunction
|
pageFunction
|
||||||
);
|
);
|
||||||
let context = this.#context.value();
|
// This code needs to schedule evaluate call synchroniously (at
|
||||||
if (!context || !(context instanceof ExecutionContext)) {
|
// least when the context is there) so we cannot unconditionally
|
||||||
context = await this.#executionContext();
|
// await.
|
||||||
|
let context = this.#executionContext();
|
||||||
|
if (!context) {
|
||||||
|
context = await this.#waitForExecutionContext();
|
||||||
}
|
}
|
||||||
return await context.evaluate(pageFunction, ...args);
|
return await context.evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
@ -133,10 +164,16 @@ export class IsolatedWorld extends Realm {
|
|||||||
override async adoptBackendNode(
|
override async adoptBackendNode(
|
||||||
backendNodeId?: Protocol.DOM.BackendNodeId
|
backendNodeId?: Protocol.DOM.BackendNodeId
|
||||||
): Promise<JSHandle<Node>> {
|
): Promise<JSHandle<Node>> {
|
||||||
const executionContext = await this.#executionContext();
|
// This code needs to schedule resolveNode call synchroniously (at
|
||||||
|
// least when the context is there) so we cannot unconditionally
|
||||||
|
// await.
|
||||||
|
let context = this.#executionContext();
|
||||||
|
if (!context) {
|
||||||
|
context = await this.#waitForExecutionContext();
|
||||||
|
}
|
||||||
const {object} = await this.client.send('DOM.resolveNode', {
|
const {object} = await this.client.send('DOM.resolveNode', {
|
||||||
backendNodeId: backendNodeId,
|
backendNodeId: backendNodeId,
|
||||||
executionContextId: executionContext._contextId,
|
executionContextId: context._contextId,
|
||||||
});
|
});
|
||||||
return this.createCdpHandle(object) as JSHandle<Node>;
|
return this.createCdpHandle(object) as JSHandle<Node>;
|
||||||
}
|
}
|
||||||
@ -186,10 +223,8 @@ export class IsolatedWorld extends Realm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[disposeSymbol](): void {
|
[disposeSymbol](): void {
|
||||||
const existingContext = this.#context.value();
|
this.#context?.[disposeSymbol]();
|
||||||
if (existingContext instanceof ExecutionContext) {
|
this.#emitter.emit('disposed', undefined);
|
||||||
existingContext[disposeSymbol]();
|
|
||||||
}
|
|
||||||
super[disposeSymbol]();
|
super[disposeSymbol]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user