mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: implement BiDi workers (#11909)
This commit is contained in:
parent
0b74d752fb
commit
d14c47097d
@ -21,7 +21,9 @@ import type {BrowsingContext} from './core/BrowsingContext.js';
|
||||
import {UserContext} from './core/UserContext.js';
|
||||
import type {BidiFrame} from './Frame.js';
|
||||
import {BidiPage} from './Page.js';
|
||||
import {BidiWorkerTarget} from './Target.js';
|
||||
import {BidiFrameTarget, BidiPageTarget} from './Target.js';
|
||||
import type {BidiWebWorker} from './WebWorker.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -54,7 +56,10 @@ export class BidiBrowserContext extends BrowserContext {
|
||||
readonly #pages = new WeakMap<BrowsingContext, BidiPage>();
|
||||
readonly #targets = new Map<
|
||||
BidiPage,
|
||||
[BidiPageTarget, Map<BidiFrame, BidiFrameTarget>]
|
||||
[
|
||||
BidiPageTarget,
|
||||
Map<BidiFrame | BidiWebWorker, BidiFrameTarget | BidiWorkerTarget>,
|
||||
]
|
||||
>();
|
||||
|
||||
private constructor(
|
||||
@ -91,17 +96,18 @@ export class BidiBrowserContext extends BrowserContext {
|
||||
|
||||
// -- Target stuff starts here --
|
||||
const pageTarget = new BidiPageTarget(page);
|
||||
const frameTargets = new Map();
|
||||
this.#targets.set(page, [pageTarget, frameTargets]);
|
||||
const pageTargets = new Map();
|
||||
this.#targets.set(page, [pageTarget, pageTargets]);
|
||||
|
||||
page.trustedEmitter.on(PageEvent.FrameAttached, frame => {
|
||||
const bidiFrame = frame as BidiFrame;
|
||||
const target = new BidiFrameTarget(bidiFrame);
|
||||
frameTargets.set(bidiFrame, target);
|
||||
pageTargets.set(bidiFrame, target);
|
||||
this.trustedEmitter.emit(BrowserContextEvent.TargetCreated, target);
|
||||
});
|
||||
page.trustedEmitter.on(PageEvent.FrameNavigated, frame => {
|
||||
const bidiFrame = frame as BidiFrame;
|
||||
const target = frameTargets.get(bidiFrame);
|
||||
const target = pageTargets.get(bidiFrame);
|
||||
// If there is no target, then this is the page's frame.
|
||||
if (target === undefined) {
|
||||
this.trustedEmitter.emit(BrowserContextEvent.TargetChanged, pageTarget);
|
||||
@ -111,13 +117,30 @@ export class BidiBrowserContext extends BrowserContext {
|
||||
});
|
||||
page.trustedEmitter.on(PageEvent.FrameDetached, frame => {
|
||||
const bidiFrame = frame as BidiFrame;
|
||||
const target = frameTargets.get(bidiFrame);
|
||||
const target = pageTargets.get(bidiFrame);
|
||||
if (target === undefined) {
|
||||
return;
|
||||
}
|
||||
frameTargets.delete(bidiFrame);
|
||||
pageTargets.delete(bidiFrame);
|
||||
this.trustedEmitter.emit(BrowserContextEvent.TargetDestroyed, target);
|
||||
});
|
||||
|
||||
page.trustedEmitter.on(PageEvent.WorkerCreated, worker => {
|
||||
const bidiWorker = worker as BidiWebWorker;
|
||||
const target = new BidiWorkerTarget(bidiWorker);
|
||||
pageTargets.set(bidiWorker, target);
|
||||
this.trustedEmitter.emit(BrowserContextEvent.TargetCreated, target);
|
||||
});
|
||||
page.trustedEmitter.on(PageEvent.WorkerDestroyed, worker => {
|
||||
const bidiWorker = worker as BidiWebWorker;
|
||||
const target = pageTargets.get(bidiWorker);
|
||||
if (target === undefined) {
|
||||
return;
|
||||
}
|
||||
pageTargets.delete(worker);
|
||||
this.trustedEmitter.emit(BrowserContextEvent.TargetDestroyed, target);
|
||||
});
|
||||
|
||||
page.trustedEmitter.on(PageEvent.Close, () => {
|
||||
this.#targets.delete(page);
|
||||
this.trustedEmitter.emit(BrowserContextEvent.TargetDestroyed, pageTarget);
|
||||
|
@ -47,6 +47,7 @@ import type {BidiPage} from './Page.js';
|
||||
import type {BidiRealm} from './Realm.js';
|
||||
import {BidiFrameRealm} from './Realm.js';
|
||||
import {rewriteNavigationError} from './util.js';
|
||||
import {BidiWebWorker} from './WebWorker.js';
|
||||
|
||||
export class BidiFrame extends Frame {
|
||||
static from(
|
||||
@ -142,6 +143,7 @@ export class BidiFrame extends Frame {
|
||||
return;
|
||||
}
|
||||
if (isConsoleLogEntry(entry)) {
|
||||
console.log(entry.args);
|
||||
const args = entry.args.map(arg => {
|
||||
return this.mainRealm().createHandle(arg);
|
||||
});
|
||||
@ -194,6 +196,14 @@ export class BidiFrame extends Frame {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.browsingContext.on('worker', ({realm}) => {
|
||||
const worker = BidiWebWorker.from(this, realm);
|
||||
realm.on('destroyed', () => {
|
||||
this.page().trustedEmitter.emit(PageEvent.WorkerDestroyed, worker);
|
||||
});
|
||||
this.page().trustedEmitter.emit(PageEvent.WorkerCreated, worker);
|
||||
});
|
||||
}
|
||||
|
||||
#createFrameTarget(browsingContext: BrowsingContext) {
|
||||
|
@ -48,6 +48,7 @@ import type {BidiHTTPResponse} from './HTTPResponse.js';
|
||||
import {BidiKeyboard, BidiMouse, BidiTouchscreen} from './Input.js';
|
||||
import type {BidiJSHandle} from './JSHandle.js';
|
||||
import {rewriteNavigationError} from './util.js';
|
||||
import type {BidiWebWorker} from './WebWorker.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -68,6 +69,7 @@ export class BidiPage extends Page {
|
||||
readonly #browserContext: BidiBrowserContext;
|
||||
readonly #frame: BidiFrame;
|
||||
#viewport: Viewport | null = null;
|
||||
readonly #workers = new Set<BidiWebWorker>();
|
||||
|
||||
readonly keyboard: BidiKeyboard;
|
||||
readonly mouse: BidiMouse;
|
||||
@ -103,6 +105,13 @@ export class BidiPage extends Page {
|
||||
this.trustedEmitter.emit(PageEvent.Close, undefined);
|
||||
this.trustedEmitter.removeAllListeners();
|
||||
});
|
||||
|
||||
this.trustedEmitter.on(PageEvent.WorkerCreated, worker => {
|
||||
this.#workers.add(worker as BidiWebWorker);
|
||||
});
|
||||
this.trustedEmitter.on(PageEvent.WorkerDestroyed, worker => {
|
||||
this.#workers.delete(worker as BidiWebWorker);
|
||||
});
|
||||
}
|
||||
|
||||
override async setUserAgent(
|
||||
@ -475,8 +484,8 @@ export class BidiPage extends Page {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
|
||||
override workers(): never {
|
||||
throw new UnsupportedOperation();
|
||||
override workers(): BidiWebWorker[] {
|
||||
return [...this.#workers];
|
||||
}
|
||||
|
||||
override setRequestInterception(): never {
|
||||
|
@ -22,7 +22,11 @@ import {
|
||||
import type PuppeteerUtil from '../injected/injected.js';
|
||||
import {stringifyFunction} from '../util/Function.js';
|
||||
|
||||
import type {Realm as BidiRealmCore} from './core/Realm.js';
|
||||
import type {
|
||||
Realm as BidiRealmCore,
|
||||
DedicatedWorkerRealm,
|
||||
SharedWorkerRealm,
|
||||
} from './core/Realm.js';
|
||||
import type {WindowRealm} from './core/Realm.js';
|
||||
import {BidiDeserializer} from './Deserializer.js';
|
||||
import {BidiElementHandle} from './ElementHandle.js';
|
||||
@ -30,9 +34,13 @@ import type {BidiFrame} from './Frame.js';
|
||||
import {BidiJSHandle} from './JSHandle.js';
|
||||
import {BidiSerializer} from './Serializer.js';
|
||||
import {createEvaluationError} from './util.js';
|
||||
import type {BidiWebWorker} from './WebWorker.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export abstract class BidiRealm extends Realm {
|
||||
realm: BidiRealmCore;
|
||||
readonly realm: BidiRealmCore;
|
||||
|
||||
constructor(realm: BidiRealmCore, timeoutSettings: TimeoutSettings) {
|
||||
super(timeoutSettings);
|
||||
@ -250,6 +258,9 @@ export abstract class BidiRealm extends Realm {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class BidiFrameRealm extends BidiRealm {
|
||||
static from(realm: WindowRealm, frame: BidiFrame): BidiFrameRealm {
|
||||
const frameRealm = new BidiFrameRealm(realm, frame);
|
||||
@ -297,3 +308,36 @@ export class BidiFrameRealm extends BidiRealm {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class BidiWorkerRealm extends BidiRealm {
|
||||
static from(
|
||||
realm: DedicatedWorkerRealm | SharedWorkerRealm,
|
||||
worker: BidiWebWorker
|
||||
): BidiWorkerRealm {
|
||||
const workerRealm = new BidiWorkerRealm(realm, worker);
|
||||
workerRealm.initialize();
|
||||
return workerRealm;
|
||||
}
|
||||
declare readonly realm: DedicatedWorkerRealm | SharedWorkerRealm;
|
||||
|
||||
readonly #worker: BidiWebWorker;
|
||||
|
||||
private constructor(
|
||||
realm: DedicatedWorkerRealm | SharedWorkerRealm,
|
||||
frame: BidiWebWorker
|
||||
) {
|
||||
super(realm, frame.timeoutSettings);
|
||||
this.#worker = frame;
|
||||
}
|
||||
|
||||
override get environment(): BidiWebWorker {
|
||||
return this.#worker;
|
||||
}
|
||||
|
||||
override async adoptBackendNode(): Promise<JSHandle<Node>> {
|
||||
throw new Error('Cannot adopt DOM nodes into a worker.');
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import type {BidiBrowser} from './Browser.js';
|
||||
import type {BidiBrowserContext} from './BrowserContext.js';
|
||||
import type {BidiFrame} from './Frame.js';
|
||||
import {BidiPage} from './Page.js';
|
||||
import type {BidiWebWorker} from './WebWorker.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -130,3 +131,40 @@ export class BidiFrameTarget extends Target {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class BidiWorkerTarget extends Target {
|
||||
#worker: BidiWebWorker;
|
||||
|
||||
constructor(worker: BidiWebWorker) {
|
||||
super();
|
||||
this.#worker = worker;
|
||||
}
|
||||
|
||||
override async page(): Promise<BidiPage> {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
override async asPage(): Promise<BidiPage> {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
override url(): string {
|
||||
return this.#worker.url();
|
||||
}
|
||||
override createCDPSession(): Promise<CDPSession> {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
override type(): TargetType {
|
||||
return TargetType.OTHER;
|
||||
}
|
||||
override browser(): BidiBrowser {
|
||||
return this.browserContext().browser();
|
||||
}
|
||||
override browserContext(): BidiBrowserContext {
|
||||
return this.#worker.frame.page().browserContext();
|
||||
}
|
||||
override opener(): Target | undefined {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
}
|
||||
|
48
packages/puppeteer-core/src/bidi/WebWorker.ts
Normal file
48
packages/puppeteer-core/src/bidi/WebWorker.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
import {WebWorker} from '../api/WebWorker.js';
|
||||
import {UnsupportedOperation} from '../common/Errors.js';
|
||||
import type {CDPSession} from '../puppeteer-core.js';
|
||||
|
||||
import type {DedicatedWorkerRealm, SharedWorkerRealm} from './core/Realm.js';
|
||||
import type {BidiFrame} from './Frame.js';
|
||||
import {BidiWorkerRealm} from './Realm.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class BidiWebWorker extends WebWorker {
|
||||
static from(
|
||||
frame: BidiFrame,
|
||||
realm: DedicatedWorkerRealm | SharedWorkerRealm
|
||||
): BidiWebWorker {
|
||||
const worker = new BidiWebWorker(frame, realm);
|
||||
return worker;
|
||||
}
|
||||
|
||||
readonly #frame: BidiFrame;
|
||||
readonly #realm: BidiWorkerRealm;
|
||||
private constructor(
|
||||
frame: BidiFrame,
|
||||
realm: DedicatedWorkerRealm | SharedWorkerRealm
|
||||
) {
|
||||
super(realm.origin);
|
||||
this.#frame = frame;
|
||||
this.#realm = BidiWorkerRealm.from(realm, this);
|
||||
}
|
||||
|
||||
get frame(): BidiFrame {
|
||||
return this.#frame;
|
||||
}
|
||||
|
||||
mainRealm(): BidiWorkerRealm {
|
||||
return this.#realm;
|
||||
}
|
||||
|
||||
get client(): CDPSession {
|
||||
throw new UnsupportedOperation();
|
||||
}
|
||||
}
|
@ -327,6 +327,12 @@
|
||||
"testIdPattern": "[worker.spec] *",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["webDriverBiDi"],
|
||||
"expectations": ["PASS"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] *",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["firefox"],
|
||||
"expectations": ["SKIP"]
|
||||
},
|
||||
{
|
||||
@ -1308,12 +1314,6 @@
|
||||
"parameters": ["firefox", "webDriverBiDi"],
|
||||
"expectations": ["SKIP"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] *",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["cdp", "firefox"],
|
||||
"expectations": ["SKIP"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[accessibility.spec] Accessibility filtering children of leaf nodes rich text editable fields should have children",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
@ -4613,34 +4613,10 @@
|
||||
"expectations": ["SKIP"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] Workers should report console logs",
|
||||
"testIdPattern": "[worker.spec] Workers can be closed",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["chrome", "webDriverBiDi"],
|
||||
"expectations": ["PASS"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] Workers should report console logs",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["chrome", "webDriverBiDi"],
|
||||
"expectations": ["PASS"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] Workers should report errors",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["firefox", "webDriverBiDi"],
|
||||
"expectations": ["PASS"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] Workers should report errors",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["chrome", "webDriverBiDi"],
|
||||
"expectations": ["PASS"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[worker.spec] Workers should report errors",
|
||||
"platforms": ["darwin", "linux", "win32"],
|
||||
"parameters": ["chrome", "webDriverBiDi"],
|
||||
"expectations": ["PASS"]
|
||||
"expectations": ["FAIL"]
|
||||
},
|
||||
{
|
||||
"testIdPattern": "[CDPSession.spec] Target.createCDPSession should send events",
|
||||
|
@ -52,7 +52,10 @@ describe('Workers', function () {
|
||||
const error = await workerThisObj.getProperty('self').catch(error => {
|
||||
return error;
|
||||
});
|
||||
expect(error.message).toContain('Most likely the worker has been closed.');
|
||||
expect(error.message).atLeastOneToContain([
|
||||
'Most likely the worker has been closed.',
|
||||
'Realm already destroyed.',
|
||||
]);
|
||||
});
|
||||
it('should report console logs', async () => {
|
||||
const {page} = await getTestState();
|
||||
@ -70,7 +73,7 @@ describe('Workers', function () {
|
||||
columnNumber: 8,
|
||||
});
|
||||
});
|
||||
it('should have JSHandles for console logs', async () => {
|
||||
it('should work with console logs', async () => {
|
||||
const {page} = await getTestState();
|
||||
|
||||
const logPromise = waitEvent<ConsoleMessage>(page, 'console');
|
||||
@ -80,9 +83,6 @@ describe('Workers', function () {
|
||||
const log = await logPromise;
|
||||
expect(log.text()).toBe('1 2 3 JSHandle@object');
|
||||
expect(log.args()).toHaveLength(4);
|
||||
expect(await (await log.args()[3]!.getProperty('origin')).jsonValue()).toBe(
|
||||
'null'
|
||||
);
|
||||
});
|
||||
it('should have an execution context', async () => {
|
||||
const {page} = await getTestState();
|
||||
|
Loading…
Reference in New Issue
Block a user