mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
refactor: adopt core/UserContext
on BidiBrowserContext
(#11734)
This commit is contained in:
parent
d57b1044f2
commit
398b31de26
@ -27,6 +27,7 @@ import {BrowsingContext, BrowsingContextEvent} from './BrowsingContext.js';
|
|||||||
import type {BidiConnection} from './Connection.js';
|
import type {BidiConnection} from './Connection.js';
|
||||||
import type {Browser as BrowserCore} from './core/Browser.js';
|
import type {Browser as BrowserCore} from './core/Browser.js';
|
||||||
import {Session} from './core/Session.js';
|
import {Session} from './core/Session.js';
|
||||||
|
import type {UserContext} from './core/UserContext.js';
|
||||||
import {
|
import {
|
||||||
BiDiBrowserTarget,
|
BiDiBrowserTarget,
|
||||||
BiDiBrowsingContextTarget,
|
BiDiBrowsingContextTarget,
|
||||||
@ -95,9 +96,8 @@ export class BidiBrowser extends Browser {
|
|||||||
#closeCallback?: BrowserCloseCallback;
|
#closeCallback?: BrowserCloseCallback;
|
||||||
#browserCore: BrowserCore;
|
#browserCore: BrowserCore;
|
||||||
#defaultViewport: Viewport | null;
|
#defaultViewport: Viewport | null;
|
||||||
#defaultContext: BidiBrowserContext;
|
|
||||||
#targets = new Map<string, BidiTarget>();
|
#targets = new Map<string, BidiTarget>();
|
||||||
#contexts: BidiBrowserContext[] = [];
|
#browserContexts = new WeakMap<UserContext, BidiBrowserContext>();
|
||||||
#browserTarget: BiDiBrowserTarget;
|
#browserTarget: BiDiBrowserTarget;
|
||||||
|
|
||||||
#connectionEventHandlers = new Map<
|
#connectionEventHandlers = new Map<
|
||||||
@ -111,25 +111,21 @@ export class BidiBrowser extends Browser {
|
|||||||
['browsingContext.navigationStarted', this.#onContextNavigation.bind(this)],
|
['browsingContext.navigationStarted', this.#onContextNavigation.bind(this)],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
constructor(browserCore: BrowserCore, opts: BidiBrowserOptions) {
|
private constructor(browserCore: BrowserCore, opts: BidiBrowserOptions) {
|
||||||
super();
|
super();
|
||||||
this.#process = opts.process;
|
this.#process = opts.process;
|
||||||
this.#closeCallback = opts.closeCallback;
|
this.#closeCallback = opts.closeCallback;
|
||||||
this.#browserCore = browserCore;
|
this.#browserCore = browserCore;
|
||||||
this.#defaultViewport = opts.defaultViewport;
|
this.#defaultViewport = opts.defaultViewport;
|
||||||
this.#defaultContext = new BidiBrowserContext(this, {
|
this.#browserTarget = new BiDiBrowserTarget(this);
|
||||||
defaultViewport: this.#defaultViewport,
|
this.#createBrowserContext(this.#browserCore.defaultUserContext);
|
||||||
isDefault: true,
|
|
||||||
});
|
|
||||||
this.#browserTarget = new BiDiBrowserTarget(this.#defaultContext);
|
|
||||||
this.#contexts.push(this.#defaultContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#initialize() {
|
#initialize() {
|
||||||
this.#browserCore.once('disconnected', () => {
|
this.#browserCore.once('disconnected', () => {
|
||||||
this.emit(BrowserEvent.Disconnected, undefined);
|
this.emit(BrowserEvent.Disconnected, undefined);
|
||||||
});
|
});
|
||||||
this.#process?.once('close', async () => {
|
this.#process?.once('close', () => {
|
||||||
this.#browserCore.dispose('Browser process exited.', true);
|
this.#browserCore.dispose('Browser process exited.', true);
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
});
|
});
|
||||||
@ -150,6 +146,14 @@ export class BidiBrowser extends Browser {
|
|||||||
throw new UnsupportedOperation();
|
throw new UnsupportedOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#createBrowserContext(userContext: UserContext) {
|
||||||
|
const browserContext = new BidiBrowserContext(this, userContext, {
|
||||||
|
defaultViewport: this.#defaultViewport,
|
||||||
|
});
|
||||||
|
this.#browserContexts.set(userContext, browserContext);
|
||||||
|
return browserContext;
|
||||||
|
}
|
||||||
|
|
||||||
#onContextDomLoaded(event: Bidi.BrowsingContext.Info) {
|
#onContextDomLoaded(event: Bidi.BrowsingContext.Info) {
|
||||||
const target = this.#targets.get(event.context);
|
const target = this.#targets.get(event.context);
|
||||||
if (target) {
|
if (target) {
|
||||||
@ -255,13 +259,8 @@ export class BidiBrowser extends Browser {
|
|||||||
override async createIncognitoBrowserContext(
|
override async createIncognitoBrowserContext(
|
||||||
_options?: BrowserContextOptions
|
_options?: BrowserContextOptions
|
||||||
): Promise<BidiBrowserContext> {
|
): Promise<BidiBrowserContext> {
|
||||||
// TODO: implement incognito context https://github.com/w3c/webdriver-bidi/issues/289.
|
const userContext = await this.#browserCore.createUserContext();
|
||||||
const context = new BidiBrowserContext(this, {
|
return this.#createBrowserContext(userContext);
|
||||||
defaultViewport: this.#defaultViewport,
|
|
||||||
isDefault: false,
|
|
||||||
});
|
|
||||||
this.#contexts.push(context);
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override async version(): Promise<string> {
|
override async version(): Promise<string> {
|
||||||
@ -269,28 +268,17 @@ export class BidiBrowser extends Browser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override browserContexts(): BidiBrowserContext[] {
|
override browserContexts(): BidiBrowserContext[] {
|
||||||
// TODO: implement incognito context https://github.com/w3c/webdriver-bidi/issues/289.
|
return [...this.#browserCore.userContexts].map(context => {
|
||||||
return this.#contexts;
|
return this.#browserContexts.get(context)!;
|
||||||
}
|
|
||||||
|
|
||||||
async _closeContext(browserContext: BidiBrowserContext): Promise<void> {
|
|
||||||
this.#contexts = this.#contexts.filter(c => {
|
|
||||||
return c !== browserContext;
|
|
||||||
});
|
});
|
||||||
for (const target of browserContext.targets()) {
|
|
||||||
const page = await target?.page();
|
|
||||||
await page?.close().catch(error => {
|
|
||||||
debugError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override defaultBrowserContext(): BidiBrowserContext {
|
override defaultBrowserContext(): BidiBrowserContext {
|
||||||
return this.#defaultContext;
|
return this.#browserContexts.get(this.#browserCore.defaultUserContext)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
override newPage(): Promise<Page> {
|
override newPage(): Promise<Page> {
|
||||||
return this.#defaultContext.newPage();
|
return this.defaultBrowserContext().newPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
override targets(): Target[] {
|
override targets(): Target[] {
|
||||||
|
@ -11,10 +11,12 @@ import {BrowserContext} from '../api/BrowserContext.js';
|
|||||||
import type {Page} from '../api/Page.js';
|
import type {Page} from '../api/Page.js';
|
||||||
import type {Target} from '../api/Target.js';
|
import type {Target} from '../api/Target.js';
|
||||||
import {UnsupportedOperation} from '../common/Errors.js';
|
import {UnsupportedOperation} from '../common/Errors.js';
|
||||||
|
import {debugError} from '../common/util.js';
|
||||||
import type {Viewport} from '../common/Viewport.js';
|
import type {Viewport} from '../common/Viewport.js';
|
||||||
|
|
||||||
import type {BidiBrowser} from './Browser.js';
|
import type {BidiBrowser} from './Browser.js';
|
||||||
import type {BidiConnection} from './Connection.js';
|
import type {BidiConnection} from './Connection.js';
|
||||||
|
import {UserContext} from './core/UserContext.js';
|
||||||
import type {BidiPage} from './Page.js';
|
import type {BidiPage} from './Page.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,7 +24,6 @@ import type {BidiPage} from './Page.js';
|
|||||||
*/
|
*/
|
||||||
export interface BidiBrowserContextOptions {
|
export interface BidiBrowserContextOptions {
|
||||||
defaultViewport: Viewport | null;
|
defaultViewport: Viewport | null;
|
||||||
isDefault: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,14 +33,18 @@ export class BidiBrowserContext extends BrowserContext {
|
|||||||
#browser: BidiBrowser;
|
#browser: BidiBrowser;
|
||||||
#connection: BidiConnection;
|
#connection: BidiConnection;
|
||||||
#defaultViewport: Viewport | null;
|
#defaultViewport: Viewport | null;
|
||||||
#isDefault = false;
|
#userContext: UserContext;
|
||||||
|
|
||||||
constructor(browser: BidiBrowser, options: BidiBrowserContextOptions) {
|
constructor(
|
||||||
|
browser: BidiBrowser,
|
||||||
|
userContext: UserContext,
|
||||||
|
options: BidiBrowserContextOptions
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.#browser = browser;
|
this.#browser = browser;
|
||||||
|
this.#userContext = userContext;
|
||||||
this.#connection = this.#browser.connection;
|
this.#connection = this.#browser.connection;
|
||||||
this.#defaultViewport = options.defaultViewport;
|
this.#defaultViewport = options.defaultViewport;
|
||||||
this.#isDefault = options.isDefault;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override targets(): Target[] {
|
override targets(): Target[] {
|
||||||
@ -90,11 +95,25 @@ export class BidiBrowserContext extends BrowserContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override async close(): Promise<void> {
|
override async close(): Promise<void> {
|
||||||
if (this.#isDefault) {
|
if (!this.isIncognito()) {
|
||||||
throw new Error('Default context cannot be closed!');
|
throw new Error('Default context cannot be closed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.#browser._closeContext(this);
|
// TODO: Remove once we have adopted the new browsing contexts.
|
||||||
|
for (const target of this.targets()) {
|
||||||
|
const page = await target?.page();
|
||||||
|
try {
|
||||||
|
await page?.close();
|
||||||
|
} catch (error) {
|
||||||
|
debugError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.#userContext.remove();
|
||||||
|
} catch (error) {
|
||||||
|
debugError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override browser(): BidiBrowser {
|
override browser(): BidiBrowser {
|
||||||
@ -113,7 +132,7 @@ export class BidiBrowserContext extends BrowserContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override isIncognito(): boolean {
|
override isIncognito(): boolean {
|
||||||
return !this.#isDefault;
|
return this.#userContext.id !== UserContext.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
override overridePermissions(): never {
|
override overridePermissions(): never {
|
||||||
|
@ -53,7 +53,14 @@ export abstract class BidiTarget extends Target {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class BiDiBrowserTarget extends BidiTarget {
|
export class BiDiBrowserTarget extends Target {
|
||||||
|
#browser: BidiBrowser;
|
||||||
|
|
||||||
|
constructor(browser: BidiBrowser) {
|
||||||
|
super();
|
||||||
|
this.#browser = browser;
|
||||||
|
}
|
||||||
|
|
||||||
override url(): string {
|
override url(): string {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -61,6 +68,26 @@ export class BiDiBrowserTarget extends BidiTarget {
|
|||||||
override type(): TargetType {
|
override type(): TargetType {
|
||||||
return TargetType.BROWSER;
|
return TargetType.BROWSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override asPage(): Promise<Page> {
|
||||||
|
throw new UnsupportedOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
override browser(): BidiBrowser {
|
||||||
|
return this.#browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
override browserContext(): BidiBrowserContext {
|
||||||
|
return this.#browser.defaultBrowserContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
override opener(): never {
|
||||||
|
throw new UnsupportedOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
override createCDPSession(): Promise<CDPSession> {
|
||||||
|
throw new UnsupportedOperation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +55,7 @@ export class Browser extends EventEmitter<{
|
|||||||
#closed = false;
|
#closed = false;
|
||||||
#reason: string | undefined;
|
#reason: string | undefined;
|
||||||
readonly #disposables = new DisposableStack();
|
readonly #disposables = new DisposableStack();
|
||||||
readonly #userContexts = new Map();
|
readonly #userContexts = new Map<string, UserContext>();
|
||||||
readonly session: Session;
|
readonly session: Session;
|
||||||
// keep-sorted end
|
// keep-sorted end
|
||||||
|
|
||||||
@ -65,7 +65,10 @@ export class Browser extends EventEmitter<{
|
|||||||
this.session = session;
|
this.session = session;
|
||||||
// keep-sorted end
|
// keep-sorted end
|
||||||
|
|
||||||
this.#userContexts.set('', UserContext.create(this, ''));
|
this.#userContexts.set(
|
||||||
|
UserContext.DEFAULT,
|
||||||
|
UserContext.create(this, UserContext.DEFAULT)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #initialize() {
|
async #initialize() {
|
||||||
@ -120,7 +123,7 @@ export class Browser extends EventEmitter<{
|
|||||||
}
|
}
|
||||||
get defaultUserContext(): UserContext {
|
get defaultUserContext(): UserContext {
|
||||||
// SAFETY: A UserContext is always created for the default context.
|
// SAFETY: A UserContext is always created for the default context.
|
||||||
return this.#userContexts.get('')!;
|
return this.#userContexts.get(UserContext.DEFAULT)!;
|
||||||
}
|
}
|
||||||
get disconnected(): boolean {
|
get disconnected(): boolean {
|
||||||
return this.#reason !== undefined;
|
return this.#reason !== undefined;
|
||||||
@ -182,6 +185,32 @@ export class Browser extends EventEmitter<{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static userContextId = 0;
|
||||||
|
@throwIfDisposed<Browser>(browser => {
|
||||||
|
// SAFETY: By definition of `disposed`, `#reason` is defined.
|
||||||
|
return browser.#reason!;
|
||||||
|
})
|
||||||
|
async createUserContext(): Promise<UserContext> {
|
||||||
|
// TODO: implement incognito context https://github.com/w3c/webdriver-bidi/issues/289.
|
||||||
|
// TODO: Call `createUserContext` once available.
|
||||||
|
// Generating a monotonically increasing context id.
|
||||||
|
const context = `${++Browser.userContextId}`;
|
||||||
|
|
||||||
|
const userContext = UserContext.create(this, context);
|
||||||
|
this.#userContexts.set(userContext.id, userContext);
|
||||||
|
|
||||||
|
const userContextEmitter = this.#disposables.use(
|
||||||
|
new EventEmitter(userContext)
|
||||||
|
);
|
||||||
|
userContextEmitter.once('closed', () => {
|
||||||
|
userContextEmitter.removeAllListeners();
|
||||||
|
|
||||||
|
this.#userContexts.delete(context);
|
||||||
|
});
|
||||||
|
|
||||||
|
return userContext;
|
||||||
|
}
|
||||||
|
|
||||||
[disposeSymbol](): void {
|
[disposeSymbol](): void {
|
||||||
this.#reason ??=
|
this.#reason ??=
|
||||||
'Browser was disconnected, probably because the session ended.';
|
'Browser was disconnected, probably because the session ended.';
|
||||||
|
@ -43,6 +43,8 @@ export class UserContext extends EventEmitter<{
|
|||||||
reason: string;
|
reason: string;
|
||||||
};
|
};
|
||||||
}> {
|
}> {
|
||||||
|
static DEFAULT = 'default';
|
||||||
|
|
||||||
static create(browser: Browser, id: string): UserContext {
|
static create(browser: Browser, id: string): UserContext {
|
||||||
const context = new UserContext(browser, id);
|
const context = new UserContext(browser, id);
|
||||||
context.#initialize();
|
context.#initialize();
|
||||||
@ -54,8 +56,6 @@ export class UserContext extends EventEmitter<{
|
|||||||
// Note these are only top-level contexts.
|
// Note these are only top-level contexts.
|
||||||
readonly #browsingContexts = new Map<string, BrowsingContext>();
|
readonly #browsingContexts = new Map<string, BrowsingContext>();
|
||||||
readonly #disposables = new DisposableStack();
|
readonly #disposables = new DisposableStack();
|
||||||
// @ts-expect-error -- TODO: This will be used once the WebDriver BiDi
|
|
||||||
// protocol supports it.
|
|
||||||
readonly #id: string;
|
readonly #id: string;
|
||||||
readonly browser: Browser;
|
readonly browser: Browser;
|
||||||
// keep-sorted end
|
// keep-sorted end
|
||||||
@ -118,6 +118,9 @@ export class UserContext extends EventEmitter<{
|
|||||||
get disposed(): boolean {
|
get disposed(): boolean {
|
||||||
return this.closed;
|
return this.closed;
|
||||||
}
|
}
|
||||||
|
get id(): string {
|
||||||
|
return this.#id;
|
||||||
|
}
|
||||||
// keep-sorted end
|
// keep-sorted end
|
||||||
|
|
||||||
@inertIfDisposed
|
@inertIfDisposed
|
||||||
@ -156,13 +159,9 @@ export class UserContext extends EventEmitter<{
|
|||||||
// SAFETY: Disposal implies this exists.
|
// SAFETY: Disposal implies this exists.
|
||||||
return context.#reason!;
|
return context.#reason!;
|
||||||
})
|
})
|
||||||
async close(): Promise<void> {
|
async remove(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const promises = [];
|
// TODO: Call `removeUserContext` once available.
|
||||||
for (const browsingContext of this.#browsingContexts.values()) {
|
|
||||||
promises.push(browsingContext.close());
|
|
||||||
}
|
|
||||||
await Promise.all(promises);
|
|
||||||
} finally {
|
} finally {
|
||||||
this.dispose('User context already closed.');
|
this.dispose('User context already closed.');
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user