more cleanup

This commit is contained in:
Dmitry Gozman 2024-12-13 15:19:01 +00:00
parent b0dc3efc2d
commit fc8112f904
11 changed files with 42 additions and 58 deletions

View file

@ -80,9 +80,9 @@ export class BidiPage implements PageDelegate {
// Initialize main frame. // Initialize main frame.
// TODO: Wait for first execution context to be created and maybe about:blank navigated. // TODO: Wait for first execution context to be created and maybe about:blank navigated.
this._initialize() this._initialize().then(
.finally(() => this._page.initOpener(this._opener?._page)) () => this._page.reportAsNew(this._opener?._page),
.then(() => this._page.reportAsNew(), error => this._page.reportAsNew(error)); error => this._page.reportAsNew(this._opener?._page, error));
} }
private async _initialize() { private async _initialize() {
@ -101,10 +101,6 @@ export class BidiPage implements PageDelegate {
return Promise.all(this._page.allInitScripts().map(initScript => this.addInitScript(initScript))); return Promise.all(this._page.allInitScripts().map(initScript => this.addInitScript(initScript)));
} }
potentiallyUninitializedPage(): Page {
return this._page;
}
didClose() { didClose() {
this._session.dispose(); this._session.dispose();
eventsHelper.removeEventListeners(this._sessionListeners); eventsHelper.removeEventListeners(this._sessionListeners);

View file

@ -257,7 +257,7 @@ export abstract class BrowserContext extends SdkObject {
} }
pages(): Page[] { pages(): Page[] {
return this.possiblyUninitializedPages().filter(page => page.initialized()); return this.possiblyUninitializedPages().filter(page => page.initializedOrUndefined());
} }
// BrowserContext methods. // BrowserContext methods.

View file

@ -259,10 +259,10 @@ export class CRBrowser extends Browser {
} }
page.willBeginDownload(); page.willBeginDownload();
let originPage = page._page.initialized(); let originPage = page._page.initializedOrUndefined();
// If it's a new window download, report it on the opener page. // If it's a new window download, report it on the opener page.
if (!originPage && page._opener) if (!originPage && page._opener)
originPage = page._opener._page.initialized(); originPage = page._opener._page.initializedOrUndefined();
if (!originPage) if (!originPage)
return; return;
this._downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename); this._downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename);
@ -544,7 +544,7 @@ export class CRBrowserContext extends BrowserContext {
// When persistent context is closed, we do not necessary get Target.detachedFromTarget // When persistent context is closed, we do not necessary get Target.detachedFromTarget
// for all the background pages. // for all the background pages.
for (const [targetId, backgroundPage] of this._browser._backgroundPages.entries()) { for (const [targetId, backgroundPage] of this._browser._backgroundPages.entries()) {
if (backgroundPage._browserContext === this && backgroundPage._page.initialized()) { if (backgroundPage._browserContext === this && backgroundPage._page.initializedOrUndefined()) {
backgroundPage.didClose(); backgroundPage.didClose();
this._browser._backgroundPages.delete(targetId); this._browser._backgroundPages.delete(targetId);
} }
@ -569,7 +569,7 @@ export class CRBrowserContext extends BrowserContext {
backgroundPages(): Page[] { backgroundPages(): Page[] {
const result: Page[] = []; const result: Page[] = [];
for (const backgroundPage of this._browser._backgroundPages.values()) { for (const backgroundPage of this._browser._backgroundPages.values()) {
if (backgroundPage._browserContext === this && backgroundPage._page.initialized()) if (backgroundPage._browserContext === this && backgroundPage._page.initializedOrUndefined())
result.push(backgroundPage._page); result.push(backgroundPage._page);
} }
return result; return result;

View file

@ -108,9 +108,9 @@ export class CRPage implements PageDelegate {
} }
const createdEvent = this._isBackgroundPage ? CRBrowserContext.CREvents.BackgroundPage : BrowserContext.Events.Page; const createdEvent = this._isBackgroundPage ? CRBrowserContext.CREvents.BackgroundPage : BrowserContext.Events.Page;
this._mainFrameSession._initialize(bits.hasUIWindow) this._mainFrameSession._initialize(bits.hasUIWindow).then(
.finally(() => this._page.initOpener(this._opener?._page)) () => this._page.reportAsNew(this._opener?._page, undefined, createdEvent),
.then(() => this._page.reportAsNew(undefined, createdEvent), error => this._page.reportAsNew(error, createdEvent)); error => this._page.reportAsNew(this._opener?._page, error, createdEvent));
} }
private async _forAllFrameSessions(cb: (frame: FrameSession) => Promise<any>) { private async _forAllFrameSessions(cb: (frame: FrameSession) => Promise<any>) {
@ -873,7 +873,7 @@ class FrameSession {
} }
_willBeginDownload() { _willBeginDownload() {
if (!this._crPage._page.initialized()) { if (!this._crPage._page.initializedOrUndefined()) {
// Resume the page creation with an error. The page will automatically close right // Resume the page creation with an error. The page will automatically close right
// after the download begins. // after the download begins.
this._firstNonInitialNavigationCommittedReject(new Error('Starting new page download')); this._firstNonInitialNavigationCommittedReject(new Error('Starting new page download'));

View file

@ -133,7 +133,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
const requestDispatcher = RequestDispatcher.from(this, request); const requestDispatcher = RequestDispatcher.from(this, request);
this._dispatchEvent('request', { this._dispatchEvent('request', {
request: requestDispatcher, request: requestDispatcher,
page: PageDispatcher.fromNullable(this, request.frame()?._page.initialized()) page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
}); });
}); });
this.addObjectListener(BrowserContext.Events.Response, (response: Response) => { this.addObjectListener(BrowserContext.Events.Response, (response: Response) => {
@ -142,7 +142,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
return; return;
this._dispatchEvent('response', { this._dispatchEvent('response', {
response: ResponseDispatcher.from(this, response), response: ResponseDispatcher.from(this, response),
page: PageDispatcher.fromNullable(this, response.frame()?._page.initialized()) page: PageDispatcher.fromNullable(this, response.frame()?._page.initializedOrUndefined())
}); });
}); });
this.addObjectListener(BrowserContext.Events.RequestFailed, (request: Request) => { this.addObjectListener(BrowserContext.Events.RequestFailed, (request: Request) => {
@ -153,7 +153,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
request: RequestDispatcher.from(this, request), request: RequestDispatcher.from(this, request),
failureText: request._failureText || undefined, failureText: request._failureText || undefined,
responseEndTiming: request._responseEndTiming, responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this, request.frame()?._page.initialized()) page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
}); });
}); });
this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request, response }: { request: Request, response: Response | null }) => { this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request, response }: { request: Request, response: Response | null }) => {
@ -164,13 +164,13 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
request: RequestDispatcher.from(this, request), request: RequestDispatcher.from(this, request),
response: ResponseDispatcher.fromNullable(this, response), response: ResponseDispatcher.fromNullable(this, response),
responseEndTiming: request._responseEndTiming, responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this, request.frame()?._page.initialized()), page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined()),
}); });
}); });
} }
private _shouldDispatchNetworkEvent(request: Request, event: channels.BrowserContextUpdateSubscriptionParams['event'] & channels.PageUpdateSubscriptionParams['event']): boolean { private _shouldDispatchNetworkEvent(request: Request, event: channels.BrowserContextUpdateSubscriptionParams['event'] & channels.PageUpdateSubscriptionParams['event']): boolean {
return this._shouldDispatchEvent(request.frame()?._page?.initialized(), event); return this._shouldDispatchEvent(request.frame()?._page?.initializedOrUndefined(), event);
} }
private _shouldDispatchEvent(page: Page | undefined, event: channels.BrowserContextUpdateSubscriptionParams['event'] & channels.PageUpdateSubscriptionParams['event']): boolean { private _shouldDispatchEvent(page: Page | undefined, event: channels.BrowserContextUpdateSubscriptionParams['event'] & channels.PageUpdateSubscriptionParams['event']): boolean {

View file

@ -24,7 +24,7 @@ export class DialogDispatcher extends Dispatcher<Dialog, channels.DialogChannel,
_type_Dialog = true; _type_Dialog = true;
constructor(scope: BrowserContextDispatcher, dialog: Dialog) { constructor(scope: BrowserContextDispatcher, dialog: Dialog) {
const page = PageDispatcher.fromNullable(scope, dialog.page().initialized()); const page = PageDispatcher.fromNullable(scope, dialog.page().initializedOrUndefined());
// Prefer scoping to the page, unless we don't have one. // Prefer scoping to the page, unless we don't have one.
super(page || scope, dialog, 'Dialog', { super(page || scope, dialog, 'Dialog', {
page, page,

View file

@ -136,14 +136,14 @@ export class FFBrowser extends Browser {
// Abort the navigation that turned into download. // Abort the navigation that turned into download.
ffPage._page._frameManager.frameAbortedNavigation(payload.frameId, 'Download is starting'); ffPage._page._frameManager.frameAbortedNavigation(payload.frameId, 'Download is starting');
let originPage = ffPage._page.initialized(); let originPage = ffPage._page.initializedOrUndefined();
// If it's a new window download, report it on the opener page. // If it's a new window download, report it on the opener page.
if (!originPage) { if (!originPage) {
// Resume the page creation with an error. The page will automatically close right // Resume the page creation with an error. The page will automatically close right
// after the download begins. // after the download begins.
ffPage._markAsError(new Error('Starting new page download')); ffPage._markAsError(new Error('Starting new page download'));
if (ffPage._opener) if (ffPage._opener)
originPage = ffPage._opener._page.initialized(); originPage = ffPage._opener._page.initializedOrUndefined();
} }
if (!originPage) if (!originPage)
return; return;

View file

@ -48,7 +48,7 @@ export class FFPage implements PageDelegate {
readonly _page: Page; readonly _page: Page;
readonly _networkManager: FFNetworkManager; readonly _networkManager: FFNetworkManager;
readonly _browserContext: FFBrowserContext; readonly _browserContext: FFBrowserContext;
private _initializationFailed = false; private _reportedAsNew = false;
readonly _opener: FFPage | null; readonly _opener: FFPage | null;
private readonly _contextIdToContext: Map<string, dom.FrameExecutionContext>; private readonly _contextIdToContext: Map<string, dom.FrameExecutionContext>;
private _eventListeners: RegisteredListener[]; private _eventListeners: RegisteredListener[];
@ -99,31 +99,23 @@ export class FFPage implements PageDelegate {
eventsHelper.addEventListener(this._session, 'Page.screencastFrame', this._onScreencastFrame.bind(this)), eventsHelper.addEventListener(this._session, 'Page.screencastFrame', this._onScreencastFrame.bind(this)),
]; ];
this._session.once('Page.ready', async () => { this._session.once('Page.ready', () => {
await this._page.initOpener(this._opener?._page); if (this._reportedAsNew)
if (this._initializationFailed)
return; return;
this._page.reportAsNew(); this._reportedAsNew = true;
this._page.reportAsNew(this._opener?._page);
}); });
// Ideally, we somehow ensure that utility world is created before Page.ready arrives, but currently it is racy. // Ideally, we somehow ensure that utility world is created before Page.ready arrives, but currently it is racy.
// Therefore, we can end up with an initialized page without utility world, although very unlikely. // Therefore, we can end up with an initialized page without utility world, although very unlikely.
this.addInitScript(new InitScript('', true), UTILITY_WORLD_NAME).catch(e => this._markAsError(e)); this.addInitScript(new InitScript('', true), UTILITY_WORLD_NAME).catch(e => this._markAsError(e));
} }
potentiallyUninitializedPage(): Page {
return this._page;
}
async _markAsError(error: Error) { async _markAsError(error: Error) {
// Same error may be reported twice: channel disconnected and session.send fails. // Same error may be reported twice: channel disconnected and session.send fails.
if (this._initializationFailed) if (this._reportedAsNew)
return; return;
this._initializationFailed = true; this._reportedAsNew = true;
this._page.reportAsNew(this._opener?._page, error);
if (!this._page.initialized()) {
await this._page.initOpener(this._opener?._page);
this._page.reportAsNew(error);
}
} }
_onWebSocketCreated(event: Protocol.Page.webSocketCreatedPayload) { _onWebSocketCreated(event: Protocol.Page.webSocketCreatedPayload) {

View file

@ -192,15 +192,16 @@ export class Page extends SdkObject {
this.coverage = delegate.coverage ? delegate.coverage() : null; this.coverage = delegate.coverage ? delegate.coverage() : null;
} }
async initOpener(opener: Page | undefined) { async reportAsNew(opener: Page | undefined, error: Error | undefined = undefined, contextEvent: string = BrowserContext.Events.Page) {
if (!opener) if (opener) {
return; const openerPageOrError = await opener.waitForInitializedOrError();
const openerPageOrError = await opener.waitForInitializedOrError(); if (openerPageOrError instanceof Page && !openerPageOrError.isClosed())
if (openerPageOrError instanceof Page && !openerPageOrError.isClosed()) this._opener = openerPageOrError;
this._opener = openerPageOrError; }
this._markInitialized(error, contextEvent);
} }
reportAsNew(error: Error | undefined = undefined, contextEvent: string = BrowserContext.Events.Page) { private _markInitialized(error: Error | undefined = undefined, contextEvent: string = BrowserContext.Events.Page) {
if (error) { if (error) {
// Initialization error could have happened because of // Initialization error could have happened because of
// context/browser closure. Just ignore the page. // context/browser closure. Just ignore the page.
@ -228,7 +229,7 @@ export class Page extends SdkObject {
this._initializedPromise.resolve(this._initialized); this._initializedPromise.resolve(this._initialized);
} }
initialized(): Page | undefined { initializedOrUndefined(): Page | undefined {
return this._initialized ? this : undefined; return this._initialized ? this : undefined;
} }

View file

@ -121,14 +121,14 @@ export class WKBrowser extends Browser {
// abort navigation that is still running. We should be able to fix this by // abort navigation that is still running. We should be able to fix this by
// instrumenting policy decision start/proceed/cancel. // instrumenting policy decision start/proceed/cancel.
page._page._frameManager.frameAbortedNavigation(payload.frameId, 'Download is starting'); page._page._frameManager.frameAbortedNavigation(payload.frameId, 'Download is starting');
let originPage = page._page.initialized(); let originPage = page._page.initializedOrUndefined();
// If it's a new window download, report it on the opener page. // If it's a new window download, report it on the opener page.
if (!originPage) { if (!originPage) {
// Resume the page creation with an error. The page will automatically close right // Resume the page creation with an error. The page will automatically close right
// after the download begins. // after the download begins.
page._firstNonInitialNavigationCommittedReject(new Error('Starting new page download')); page._firstNonInitialNavigationCommittedReject(new Error('Starting new page download'));
if (page._opener) if (page._opener)
originPage = page._opener._page.initialized(); originPage = page._opener._page.initializedOrUndefined();
} }
if (!originPage) if (!originPage)
return; return;

View file

@ -108,10 +108,6 @@ export class WKPage implements PageDelegate {
} }
} }
potentiallyUninitializedPage(): Page {
return this._page;
}
private async _initializePageProxySession() { private async _initializePageProxySession() {
if (this._page._browserContext.isSettingStorageState()) if (this._page._browserContext.isSettingStorageState())
return; return;
@ -280,7 +276,7 @@ export class WKPage implements PageDelegate {
} }
handleProvisionalLoadFailed(event: Protocol.Playwright.provisionalLoadFailedPayload) { handleProvisionalLoadFailed(event: Protocol.Playwright.provisionalLoadFailedPayload) {
if (!this._page.initialized()) { if (!this._page.initializedOrUndefined()) {
this._firstNonInitialNavigationCommittedReject(new Error('Initial load failed')); this._firstNonInitialNavigationCommittedReject(new Error('Initial load failed'));
return; return;
} }
@ -309,7 +305,7 @@ export class WKPage implements PageDelegate {
assert(targetInfo.type === 'page', 'Only page targets are expected in WebKit, received: ' + targetInfo.type); assert(targetInfo.type === 'page', 'Only page targets are expected in WebKit, received: ' + targetInfo.type);
if (!targetInfo.isProvisional) { if (!targetInfo.isProvisional) {
assert(!this._page.initialized()); assert(!this._page.initializedOrUndefined());
let pageOrError: Page | Error; let pageOrError: Page | Error;
try { try {
this._setSession(session); this._setSession(session);
@ -336,8 +332,7 @@ export class WKPage implements PageDelegate {
// Avoid rejection on disconnect. // Avoid rejection on disconnect.
this._firstNonInitialNavigationCommittedPromise.catch(() => {}); this._firstNonInitialNavigationCommittedPromise.catch(() => {});
} }
await this._page.initOpener(this._opener?._page); this._page.reportAsNew(this._opener?._page, pageOrError instanceof Page ? undefined : pageOrError);
this._page.reportAsNew(pageOrError instanceof Page ? undefined : pageOrError);
} else { } else {
assert(targetInfo.isProvisional); assert(targetInfo.isProvisional);
assert(!this._provisionalPage); assert(!this._provisionalPage);