diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index 9e3e8b136c..7bff6221e5 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -16,7 +16,7 @@ import { eventsHelper } from '../utils/eventsHelper'; import { Browser } from '../browser'; -import { BrowserContext, assertBrowserContextIsNotOwned, verifyGeolocation } from '../browserContext'; +import { BrowserContext, verifyGeolocation } from '../browserContext'; import * as network from '../network'; import { BidiConnection } from './bidiConnection'; import { bidiBytesValueToString } from './bidiNetworkManager'; @@ -151,12 +151,12 @@ export class BidiBrowser extends Browser { if (event.parent) { const parentFrameId = event.parent; for (const page of this._bidiPages.values()) { - const parentFrame = page._page._frameManager.frame(parentFrameId); + const parentFrame = page._page.frameManager.frame(parentFrameId); if (!parentFrame) continue; page._session.addFrameBrowsingContext(event.context); - page._page._frameManager.frameAttached(event.context, parentFrameId); - const frame = page._page._frameManager.frame(event.context); + page._page.frameManager.frameAttached(event.context, parentFrameId); + const frame = page._page.frameManager.frame(event.context); if (frame) frame._url = event.url; return; @@ -180,10 +180,10 @@ export class BidiBrowser extends Browser { this._browserSession.removeFrameBrowsingContext(event.context); const parentFrameId = event.parent; for (const page of this._bidiPages.values()) { - const parentFrame = page._page._frameManager.frame(parentFrameId); + const parentFrame = page._page.frameManager.frame(parentFrameId); if (!parentFrame) continue; - page._page._frameManager.frameDetached(event.context); + page._page.frameManager.frameDetached(event.context); return; } return; @@ -266,7 +266,6 @@ export class BidiBrowserContext extends BrowserContext { } override async doCreateNewPage(): Promise { - assertBrowserContextIsNotOwned(this); const { context } = await this._browser._browserSession.send('browsingContext.create', { type: bidi.BrowsingContext.CreateType.Window, userContext: this._browserContextId, @@ -368,7 +367,7 @@ export class BidiBrowserContext extends BrowserContext { async doSetHTTPCredentials(httpCredentials?: types.Credentials): Promise { this._options.httpCredentials = httpCredentials; for (const page of this.pages()) - await (page._delegate as BidiPage).updateHttpCredentials(); + await (page.delegate as BidiPage).updateHttpCredentials(); } async doAddInitScript(initScript: InitScript) { diff --git a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts index 5da061fcdf..8456787030 100644 --- a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts +++ b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts @@ -59,7 +59,7 @@ export class BidiNetworkManager { if (param.request.url.startsWith('data:')) return; const redirectedFrom = param.redirectCount ? (this._requests.get(param.request.request) || null) : null; - const frame = redirectedFrom ? redirectedFrom.request.frame() : (param.context ? this._page._frameManager.frame(param.context) : null); + const frame = redirectedFrom ? redirectedFrom.request.frame() : (param.context ? this._page.frameManager.frame(param.context) : null); if (!frame) return; if (redirectedFrom) @@ -82,7 +82,7 @@ export class BidiNetworkManager { } const request = new BidiRequest(frame, redirectedFrom, param, route); this._requests.set(request._id, request); - this._page._frameManager.requestStarted(request.request, route); + this._page.frameManager.requestStarted(request.request, route); } private _onResponseStarted(params: bidi.Network.ResponseStartedParameters) { @@ -115,7 +115,7 @@ export class BidiNetworkManager { // "raw" headers are the same as "provisional" headers in Bidi. response.setRawResponseHeaders(null); response.setResponseHeadersSize(params.response.headersSize); - this._page._frameManager.requestReceivedResponse(response); + this._page.frameManager.requestReceivedResponse(response); if (params.navigation) this._onNavigationResponseStarted(params); } @@ -139,7 +139,7 @@ export class BidiNetworkManager { response._requestFinished(responseEndTime); } response._setHttpVersion(params.response.protocol); - this._page._frameManager.reportRequestFinished(request.request, response); + this._page.frameManager.reportRequestFinished(request.request, response); } @@ -156,12 +156,12 @@ export class BidiNetworkManager { } request.request._setFailureText(params.errorText); // TODO: support canceled flag - this._page._frameManager.requestFailed(request.request, params.errorText === 'NS_BINDING_ABORTED'); + this._page.frameManager.requestFailed(request.request, params.errorText === 'NS_BINDING_ABORTED'); } private _onAuthRequired(params: bidi.Network.AuthRequiredParameters) { const isBasic = params.response.authChallenges?.some(challenge => challenge.scheme.startsWith('Basic')); - const credentials = this._page._browserContext._options.httpCredentials; + const credentials = this._page.browserContext._options.httpCredentials; if (isBasic && credentials) { this._session.sendMayFail('network.continueWithAuth', { request: params.request.request, @@ -230,7 +230,7 @@ class BidiRequest { redirectedFrom._redirectedTo = this; // TODO: missing in the spec? const postDataBuffer = null; - this.request = new network.Request(frame._page._browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigation ?? undefined, + this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigation ?? undefined, payload.request.url, 'other', payload.request.method, postDataBuffer, fromBidiHeaders(payload.request.headers)); // "raw" headers are the same as "provisional" headers in Bidi. this.request.setRawRequestHeaders(null); diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index d5d39134cb..d1b675d852 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -103,7 +103,7 @@ export class BidiPage implements PageDelegate { } private _onFrameAttached(frameId: string, parentFrameId: string | null): frames.Frame { - return this._page._frameManager.frameAttached(frameId, parentFrameId); + return this._page.frameManager.frameAttached(frameId, parentFrameId); } private _removeContextsForFrame(frame: frames.Frame, notifyFrame: boolean) { @@ -121,7 +121,7 @@ export class BidiPage implements PageDelegate { return; if (realmInfo.type !== 'window') return; - const frame = this._page._frameManager.frame(realmInfo.context); + const frame = this._page.frameManager.frame(realmInfo.context); if (!frame) return; let worldName: types.World; @@ -172,47 +172,47 @@ export class BidiPage implements PageDelegate { private _onNavigationStarted(params: bidi.BrowsingContext.NavigationInfo) { const frameId = params.context; - this._page._frameManager.frameRequestedNavigation(frameId, params.navigation!); + this._page.frameManager.frameRequestedNavigation(frameId, params.navigation!); const url = params.url.toLowerCase(); if (url.startsWith('file:') || url.startsWith('data:') || url === 'about:blank') { // Navigation to file urls doesn't emit network events, so we fire 'commit' event right when navigation is started. // Doing it in domcontentload would be too late as we'd clear frame tree. - const frame = this._page._frameManager.frame(frameId)!; + const frame = this._page.frameManager.frame(frameId)!; if (frame) - this._page._frameManager.frameCommittedNewDocumentNavigation(frameId, params.url, '', params.navigation!, /* initial */ false); + this._page.frameManager.frameCommittedNewDocumentNavigation(frameId, params.url, '', params.navigation!, /* initial */ false); } } // TODO: there is no separate event for committed navigation, so we approximate it with responseStarted. private _onNavigationResponseStarted(params: bidi.Network.ResponseStartedParameters) { const frameId = params.context!; - const frame = this._page._frameManager.frame(frameId); + const frame = this._page.frameManager.frame(frameId); assert(frame); - this._page._frameManager.frameCommittedNewDocumentNavigation(frameId, params.response.url, '', params.navigation!, /* initial */ false); + this._page.frameManager.frameCommittedNewDocumentNavigation(frameId, params.response.url, '', params.navigation!, /* initial */ false); // if (!initial) // this._firstNonInitialNavigationCommittedFulfill(); } private _onDomContentLoaded(params: bidi.BrowsingContext.NavigationInfo) { const frameId = params.context; - this._page._frameManager.frameLifecycleEvent(frameId, 'domcontentloaded'); + this._page.frameManager.frameLifecycleEvent(frameId, 'domcontentloaded'); } private _onLoad(params: bidi.BrowsingContext.NavigationInfo) { - this._page._frameManager.frameLifecycleEvent(params.context, 'load'); + this._page.frameManager.frameLifecycleEvent(params.context, 'load'); } private _onNavigationAborted(params: bidi.BrowsingContext.NavigationInfo) { - this._page._frameManager.frameAbortedNavigation(params.context, 'Navigation aborted', params.navigation || undefined); + this._page.frameManager.frameAbortedNavigation(params.context, 'Navigation aborted', params.navigation || undefined); } private _onNavigationFailed(params: bidi.BrowsingContext.NavigationInfo) { - this._page._frameManager.frameAbortedNavigation(params.context, 'Navigation failed', params.navigation || undefined); + this._page.frameManager.frameAbortedNavigation(params.context, 'Navigation failed', params.navigation || undefined); } private _onFragmentNavigated(params: bidi.BrowsingContext.NavigationInfo) { - this._page._frameManager.frameCommittedSameDocumentNavigation(params.context, params.url); + this._page.frameManager.frameCommittedSameDocumentNavigation(params.context, params.url); } private _onUserPromptOpened(event: bidi.BrowsingContext.UserPromptOpenedParameters) { @@ -235,7 +235,7 @@ export class BidiPage implements PageDelegate { return; const callFrame = params.stackTrace?.callFrames[0]; const location = callFrame ?? { url: '', lineNumber: 1, columnNumber: 1 }; - this._page._addConsoleMessage(entry.method, entry.args.map(arg => createHandle(context, arg)), location, params.text || undefined); + this._page.addConsoleMessage(entry.method, entry.args.map(arg => createHandle(context, arg)), location, params.text || undefined); } async navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise { @@ -328,7 +328,7 @@ export class BidiPage implements PageDelegate { return; if (event.data.type !== 'string') return; - await this._page._onBindingCalled(event.data.value, context); + await this._page.onBindingCalled(event.data.value, context); } async addInitScript(initScript: InitScript): Promise { @@ -380,7 +380,7 @@ export class BidiPage implements PageDelegate { const frameId = await executionContext.contentFrameIdForFrame(handle); if (!frameId) return null; - return this._page._frameManager.frame(frameId); + return this._page.frameManager.frame(frameId); } async getOwnerFrame(handle: dom.ElementHandle): Promise { diff --git a/packages/playwright-core/src/server/browser.ts b/packages/playwright-core/src/server/browser.ts index 7cf00f73c8..f2c9d68692 100644 --- a/packages/playwright-core/src/server/browser.ts +++ b/packages/playwright-core/src/server/browser.ts @@ -146,7 +146,7 @@ export abstract class Browser extends SdkObject { this._idToVideo.set(videoId, { context, artifact }); pageOrError.then(page => { if (page instanceof Page) { - page._video = artifact; + page.video = artifact; page.emitOnContext(BrowserContext.Events.VideoStarted, artifact); page.emit(Page.Events.Video, artifact); } diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index a3e5941d6e..7b83d49b59 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -216,11 +216,11 @@ export abstract class BrowserContext extends SdkObject { } // Unless dialogs are dismissed, setting extra http headers below does not respond. - page?._frameManager.setCloseAllOpeningDialogs(true); - await page?._frameManager.closeOpenDialogs(); + page?.frameManager.setCloseAllOpeningDialogs(true); + await page?.frameManager.closeOpenDialogs(); // Navigate to about:blank first to ensure no page scripts are running after this point. await page?.mainFrame().goto(metadata, 'about:blank', { timeout: 0 }); - page?._frameManager.setCloseAllOpeningDialogs(false); + page?.frameManager.setCloseAllOpeningDialogs(false); await this._resetStorage(); await this._removeExposedBindings(); @@ -544,7 +544,7 @@ export abstract class BrowserContext extends SdkObject { if (originsToSave.size) { const internalMetadata = serverSideCallMetadata(); const page = await this.newPage(internalMetadata); - await page._setServerRequestInterceptor(handler => { + await page.setServerRequestInterceptor(handler => { handler.fulfill({ body: '' }).catch(() => {}); return true; }); @@ -574,7 +574,7 @@ export abstract class BrowserContext extends SdkObject { // as a user-visible page. isServerSide: false, }); - await page._setServerRequestInterceptor(handler => { + await page.setServerRequestInterceptor(handler => { handler.fulfill({ body: '' }).catch(() => {}); return true; }); @@ -585,7 +585,7 @@ export abstract class BrowserContext extends SdkObject { await frame.resetStorageForCurrentOriginBestEffort(newOrigins.get(origin)); } - await page._setServerRequestInterceptor(undefined); + await page.setServerRequestInterceptor(undefined); this._origins = new Set([...newOrigins.keys()]); // It is safe to not restore the URL to about:blank since we are doing it in Page::resetForReuse. @@ -609,7 +609,7 @@ export abstract class BrowserContext extends SdkObject { if (state.origins && state.origins.length) { const internalMetadata = serverSideCallMetadata(); const page = await this.newPage(internalMetadata); - await page._setServerRequestInterceptor(handler => { + await page.setServerRequestInterceptor(handler => { handler.fulfill({ body: '' }).catch(() => {}); return true; }); @@ -670,13 +670,6 @@ export abstract class BrowserContext extends SdkObject { } } -export function assertBrowserContextIsNotOwned(context: BrowserContext) { - for (const page of context.pages()) { - if (page._ownedContext) - throw new Error('Please use browser.newContext() for multi-page scripts that share the context.'); - } -} - export function validateBrowserContextOptions(options: types.BrowserContextOptions, browserOptions: BrowserOptions) { if (options.noDefaultViewport && options.deviceScaleFactor !== undefined) throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`); diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index 72dca5dac0..2887f9a393 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -21,7 +21,7 @@ import { assert } from '../../utils/isomorphic/assert'; import { createGuid } from '../utils/crypto'; import { Artifact } from '../artifact'; import { Browser } from '../browser'; -import { BrowserContext, assertBrowserContextIsNotOwned, verifyGeolocation } from '../browserContext'; +import { BrowserContext, verifyGeolocation } from '../browserContext'; import { Frame } from '../frames'; import * as network from '../network'; import { Page } from '../page'; @@ -245,7 +245,7 @@ export class CRBrowser extends Browser { private _findOwningPage(frameId: string) { for (const crPage of this._crPages.values()) { - const frame = crPage._page._frameManager.frame(frameId); + const frame = crPage._page.frameManager.frame(frameId); if (frame) return crPage; } @@ -288,7 +288,7 @@ export class CRBrowser extends Browser { async startTracing(page?: Page, options: { screenshots?: boolean; categories?: string[]; } = {}) { assert(!this._tracingRecording, 'Cannot start recording trace while already recording trace.'); - this._tracingClient = page ? (page._delegate as CRPage)._mainFrameSession._client : this._session; + this._tracingClient = page ? (page.delegate as CRPage)._mainFrameSession._client : this._session; const defaultCategories = [ '-*', 'devtools.timeline', 'v8.execute', 'disabled-by-default-devtools.timeline', @@ -372,7 +372,6 @@ export class CRBrowserContext extends BrowserContext { } override async doCreateNewPage(): Promise { - assertBrowserContextIsNotOwned(this); const { targetId } = await this._browser._session.send('Target.createTarget', { url: 'about:blank', browserContextId: this._browserContextId }); return this._browser._crPages.get(targetId)!._page; } @@ -435,13 +434,13 @@ export class CRBrowserContext extends BrowserContext { verifyGeolocation(geolocation); this._options.geolocation = geolocation; for (const page of this.pages()) - await (page._delegate as CRPage).updateGeolocation(); + await (page.delegate as CRPage).updateGeolocation(); } async setExtraHTTPHeaders(headers: types.HeadersArray): Promise { this._options.extraHTTPHeaders = headers; for (const page of this.pages()) - await (page._delegate as CRPage).updateExtraHTTPHeaders(); + await (page.delegate as CRPage).updateExtraHTTPHeaders(); for (const sw of this.serviceWorkers()) await (sw as CRServiceWorker).updateExtraHTTPHeaders(); } @@ -449,14 +448,14 @@ export class CRBrowserContext extends BrowserContext { async setUserAgent(userAgent: string | undefined): Promise { this._options.userAgent = userAgent; for (const page of this.pages()) - await (page._delegate as CRPage).updateUserAgent(); + await (page.delegate as CRPage).updateUserAgent(); // TODO: service workers don't have Emulation domain? } async setOffline(offline: boolean): Promise { this._options.offline = offline; for (const page of this.pages()) - await (page._delegate as CRPage).updateOffline(); + await (page.delegate as CRPage).updateOffline(); for (const sw of this.serviceWorkers()) await (sw as CRServiceWorker).updateOffline(); } @@ -464,24 +463,24 @@ export class CRBrowserContext extends BrowserContext { async doSetHTTPCredentials(httpCredentials?: types.Credentials): Promise { this._options.httpCredentials = httpCredentials; for (const page of this.pages()) - await (page._delegate as CRPage).updateHttpCredentials(); + await (page.delegate as CRPage).updateHttpCredentials(); for (const sw of this.serviceWorkers()) await (sw as CRServiceWorker).updateHttpCredentials(); } async doAddInitScript(initScript: InitScript) { for (const page of this.pages()) - await (page._delegate as CRPage).addInitScript(initScript); + await (page.delegate as CRPage).addInitScript(initScript); } async doRemoveNonInternalInitScripts() { for (const page of this.pages()) - await (page._delegate as CRPage).removeNonInternalInitScripts(); + await (page.delegate as CRPage).removeNonInternalInitScripts(); } async doUpdateRequestInterception(): Promise { for (const page of this.pages()) - await (page._delegate as CRPage).updateRequestInterception(); + await (page.delegate as CRPage).updateRequestInterception(); for (const sw of this.serviceWorkers()) await (sw as CRServiceWorker).updateRequestInterception(); } @@ -493,7 +492,7 @@ export class CRBrowserContext extends BrowserContext { // beforeunload. const openedBeforeUnloadDialogs: Dialog[] = []; for (const crPage of this._crPages()) { - const dialogs = [...crPage._page._frameManager._openedDialogs].filter(dialog => dialog.type() === 'beforeunload'); + const dialogs = [...crPage._page.frameManager._openedDialogs].filter(dialog => dialog.type() === 'beforeunload'); openedBeforeUnloadDialogs.push(...dialogs); } await Promise.all(openedBeforeUnloadDialogs.map(dialog => dialog.dismiss())); @@ -508,7 +507,7 @@ export class CRBrowserContext extends BrowserContext { await this._browser._session.send('Target.disposeBrowserContext', { browserContextId: this._browserContextId }); this._browser._contexts.delete(this._browserContextId); for (const [targetId, serviceWorker] of this._browser._serviceWorkers) { - if (serviceWorker._browserContext !== this) + if (serviceWorker.browserContext !== this) continue; // When closing a browser context, service workers are shutdown // asynchronously and we get detached from them later. @@ -559,15 +558,15 @@ export class CRBrowserContext extends BrowserContext { } serviceWorkers(): Worker[] { - return Array.from(this._browser._serviceWorkers.values()).filter(serviceWorker => serviceWorker._browserContext === this); + return Array.from(this._browser._serviceWorkers.values()).filter(serviceWorker => serviceWorker.browserContext === this); } async newCDPSession(page: Page | Frame): Promise { let targetId: string | null = null; if (page instanceof Page) { - targetId = (page._delegate as CRPage)._targetId; + targetId = (page.delegate as CRPage)._targetId; } else if (page instanceof Frame) { - const session = (page._page._delegate as CRPage)._sessions.get(page._id); + const session = (page._page.delegate as CRPage)._sessions.get(page._id); if (!session) throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`); targetId = session._targetId; diff --git a/packages/playwright-core/src/server/chromium/crNetworkManager.ts b/packages/playwright-core/src/server/chromium/crNetworkManager.ts index beb0032f6b..27a4cba97f 100644 --- a/packages/playwright-core/src/server/chromium/crNetworkManager.ts +++ b/packages/playwright-core/src/server/chromium/crNetworkManager.ts @@ -74,13 +74,13 @@ export class CRNetworkManager { ]; if (this._page) { sessionInfo.eventListeners.push(...[ - eventsHelper.addEventListener(session, 'Network.webSocketCreated', e => this._page!._frameManager.onWebSocketCreated(e.requestId, e.url)), - eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page!._frameManager.onWebSocketRequest(e.requestId)), - eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page!._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)), - eventsHelper.addEventListener(session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page!._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)), - eventsHelper.addEventListener(session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page!._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)), - eventsHelper.addEventListener(session, 'Network.webSocketClosed', e => this._page!._frameManager.webSocketClosed(e.requestId)), - eventsHelper.addEventListener(session, 'Network.webSocketFrameError', e => this._page!._frameManager.webSocketError(e.requestId, e.errorMessage)), + eventsHelper.addEventListener(session, 'Network.webSocketCreated', e => this._page!.frameManager.onWebSocketCreated(e.requestId, e.url)), + eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page!.frameManager.onWebSocketRequest(e.requestId)), + eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page!.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)), + eventsHelper.addEventListener(session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page!.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)), + eventsHelper.addEventListener(session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page!.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)), + eventsHelper.addEventListener(session, 'Network.webSocketClosed', e => this._page!.frameManager.webSocketClosed(e.requestId)), + eventsHelper.addEventListener(session, 'Network.webSocketFrameError', e => this._page!.frameManager.webSocketError(e.requestId, e.errorMessage)), ]); } this._sessions.set(session, sessionInfo); @@ -287,19 +287,19 @@ export class CRNetworkManager { redirectedFrom = request; } } - let frame = requestWillBeSentEvent.frameId ? this._page?._frameManager.frame(requestWillBeSentEvent.frameId) : requestWillBeSentSessionInfo.workerFrame; + let frame = requestWillBeSentEvent.frameId ? this._page?.frameManager.frame(requestWillBeSentEvent.frameId) : requestWillBeSentSessionInfo.workerFrame; // Requests from workers lack frameId, because we receive Network.requestWillBeSent // on the worker target. However, we receive Fetch.requestPaused on the page target, // and lack workerFrame there. Luckily, Fetch.requestPaused provides a frameId. if (!frame && this._page && requestPausedEvent && requestPausedEvent.frameId) - frame = this._page._frameManager.frame(requestPausedEvent.frameId); + frame = this._page.frameManager.frame(requestPausedEvent.frameId); // Check if it's main resource request interception (targetId === main frame id). - if (!frame && this._page && requestWillBeSentEvent.frameId === (this._page?._delegate as CRPage)._targetId) { + if (!frame && this._page && requestWillBeSentEvent.frameId === (this._page?.delegate as CRPage)._targetId) { // Main resource request for the page is being intercepted so the Frame is not created // yet. Precreate it here for the purposes of request interception. It will be updated // later as soon as the request continues and we receive frame tree from the page. - frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null); + frame = this._page.frameManager.frameAttached(requestWillBeSentEvent.frameId, null); } // CORS options preflight request is generated by the network stack. If interception is enabled, @@ -349,7 +349,7 @@ export class CRNetworkManager { const documentId = isNavigationRequest ? requestWillBeSentEvent.loaderId : undefined; const request = new InterceptableRequest({ session: requestWillBeSentSessionInfo.session, - context: (this._page || this._serviceWorker)!._browserContext, + context: (this._page || this._serviceWorker)!.browserContext, frame: frame || null, serviceWorker: this._serviceWorker || null, documentId, @@ -367,7 +367,7 @@ export class CRNetworkManager { // right away, so that client can call it from the route handler. request.request.setRawRequestHeaders(headersObjectToArray(requestPausedEvent!.request.headers, '\n')); } - (this._page?._frameManager || this._serviceWorker)!.requestStarted(request.request, route || undefined); + (this._page?.frameManager || this._serviceWorker)!.requestStarted(request.request, route || undefined); } _createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response, hasExtraInfo: boolean): network.Response { @@ -454,8 +454,8 @@ export class CRNetworkManager { response.setEncodedBodySize(null); response._requestFinished((timestamp - request._timestamp) * 1000); this._deleteRequest(request); - (this._page?._frameManager || this._serviceWorker)!.requestReceivedResponse(response); - (this._page?._frameManager || this._serviceWorker)!.reportRequestFinished(request.request, response); + (this._page?.frameManager || this._serviceWorker)!.requestReceivedResponse(response); + (this._page?.frameManager || this._serviceWorker)!.reportRequestFinished(request.request, response); } _onResponseReceivedExtraInfo(event: Protocol.Network.responseReceivedExtraInfoPayload) { @@ -479,7 +479,7 @@ export class CRNetworkManager { if (!request) return; const response = this._createResponse(request, event.response, event.hasExtraInfo); - (this._page?._frameManager || this._serviceWorker)!.requestReceivedResponse(response); + (this._page?.frameManager || this._serviceWorker)!.requestReceivedResponse(response); } _onLoadingFinished(sessionInfo: SessionInfo, event: Protocol.Network.loadingFinishedPayload) { @@ -501,7 +501,7 @@ export class CRNetworkManager { response._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request._timestamp)); } this._deleteRequest(request); - (this._page?._frameManager || this._serviceWorker)!.reportRequestFinished(request.request, response); + (this._page?.frameManager || this._serviceWorker)!.reportRequestFinished(request.request, response); } _onLoadingFailed(sessionInfo: SessionInfo, event: Protocol.Network.loadingFailedPayload) { @@ -537,7 +537,7 @@ export class CRNetworkManager { } this._deleteRequest(request); request.request._setFailureText(event.errorText || event.blockedReason || ''); - (this._page?._frameManager || this._serviceWorker)!.requestFailed(request.request, !!event.canceled); + (this._page?.frameManager || this._serviceWorker)!.requestFailed(request.request, !!event.canceled); } private _maybeUpdateRequestSession(sessionInfo: SessionInfo, request: InterceptableRequest) { diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index 18a22e6e16..b464905a2b 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -79,7 +79,7 @@ export class CRPage implements PageDelegate { readonly _nextWindowOpenPopupFeatures: string[][] = []; static mainFrameSession(page: Page): FrameSession { - const crPage = page._delegate as CRPage; + const crPage = page.delegate as CRPage; return crPage._mainFrameSession; } @@ -108,7 +108,7 @@ export class CRPage implements PageDelegate { const features = opener._nextWindowOpenPopupFeatures.shift() || []; const viewportSize = helper.getViewportSizeFromWindowFeatures(features); if (viewportSize) - this._page._emulatedSize = { viewport: viewportSize, screen: viewportSize }; + this._page.setEmulatedSize({ viewport: viewportSize, screen: viewportSize }); } const createdEvent = this._isBackgroundPage ? CRBrowserContext.CREvents.BackgroundPage : BrowserContext.Events.Page; @@ -441,7 +441,7 @@ class FrameSession { } async _initialize(hasUIWindow: boolean) { - const isSettingStorageState = this._page._browserContext.isSettingStorageState(); + const isSettingStorageState = this._page.browserContext.isSettingStorageState(); if (!isSettingStorageState && hasUIWindow && !this._crPage._browserContext._browser.isClank() && !this._crPage._browserContext._options.noDefaultViewport) { @@ -480,7 +480,7 @@ class FrameSession { this._addRendererListeners(); } - const localFrames = this._isMainFrame() ? this._page.frames() : [this._page._frameManager.frame(this._targetId)!]; + const localFrames = this._isMainFrame() ? this._page.frames() : [this._page.frameManager.frame(this._targetId)!]; for (const frame of localFrames) { // Note: frames might be removed before we send these. this._client._sendMayFail('Page.createIsolatedWorld', { @@ -574,9 +574,9 @@ class FrameSession { if (this._eventBelongsToStaleFrame(event.frameId)) return; if (event.name === 'load') - this._page._frameManager.frameLifecycleEvent(event.frameId, 'load'); + this._page.frameManager.frameLifecycleEvent(event.frameId, 'load'); else if (event.name === 'DOMContentLoaded') - this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded'); + this._page.frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded'); } _handleFrameTree(frameTree: Protocol.Page.FrameTree) { @@ -590,7 +590,7 @@ class FrameSession { } private _eventBelongsToStaleFrame(frameId: string) { - const frame = this._page._frameManager.frame(frameId); + const frame = this._page.frameManager.frame(frameId); // Subtree may be already gone because some ancestor navigation destroyed the oopif. if (!frame) return true; @@ -607,26 +607,26 @@ class FrameSession { if (frameSession && frameId !== this._targetId) { // This is a remote -> local frame transition. frameSession._swappedIn = true; - const frame = this._page._frameManager.frame(frameId); + const frame = this._page.frameManager.frame(frameId); // Frame or even a whole subtree may be already gone, because some ancestor did navigate. if (frame) - this._page._frameManager.removeChildFramesRecursively(frame); + this._page.frameManager.removeChildFramesRecursively(frame); return; } - if (parentFrameId && !this._page._frameManager.frame(parentFrameId)) { + if (parentFrameId && !this._page.frameManager.frame(parentFrameId)) { // Parent frame may be gone already because some ancestor frame navigated and // destroyed the whole subtree of some oopif, while oopif's process is still sending us events. // Be careful to not confuse this with "main frame navigated cross-process" scenario // where parentFrameId is null. return; } - this._page._frameManager.frameAttached(frameId, parentFrameId); + this._page.frameManager.frameAttached(frameId, parentFrameId); } _onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) { if (this._eventBelongsToStaleFrame(framePayload.id)) return; - this._page._frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ''), framePayload.name || '', framePayload.loaderId, initial); + this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ''), framePayload.name || '', framePayload.loaderId, initial); if (!initial) this._firstNonInitialNavigationCommittedFulfill(); } @@ -635,13 +635,13 @@ class FrameSession { if (this._eventBelongsToStaleFrame(payload.frameId)) return; if (payload.disposition === 'currentTab') - this._page._frameManager.frameRequestedNavigation(payload.frameId); + this._page.frameManager.frameRequestedNavigation(payload.frameId); } _onFrameNavigatedWithinDocument(frameId: string, url: string) { if (this._eventBelongsToStaleFrame(frameId)) return; - this._page._frameManager.frameCommittedSameDocumentNavigation(frameId, url); + this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url); } _onFrameDetached(frameId: string, reason: 'remove' | 'swap') { @@ -655,17 +655,17 @@ class FrameSession { // This is a local -> remote frame transition, where // Page.frameDetached arrives before Target.attachedToTarget. // We should keep the frame in the tree, and it will be used for the new target. - const frame = this._page._frameManager.frame(frameId); + const frame = this._page.frameManager.frame(frameId); if (frame) - this._page._frameManager.removeChildFramesRecursively(frame); + this._page.frameManager.removeChildFramesRecursively(frame); return; } // Just a regular frame detach. - this._page._frameManager.frameDetached(frameId); + this._page.frameManager.frameDetached(frameId); } _onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) { - const frame = contextPayload.auxData ? this._page._frameManager.frame(contextPayload.auxData.frameId) : null; + const frame = contextPayload.auxData ? this._page.frameManager.frame(contextPayload.auxData.frameId) : null; if (!frame || this._eventBelongsToStaleFrame(frame._id)) return; const delegate = new CRExecutionContext(this._client, contextPayload); @@ -699,10 +699,10 @@ class FrameSession { if (event.targetInfo.type === 'iframe') { // Frame id equals target id. const targetId = event.targetInfo.targetId; - const frame = this._page._frameManager.frame(targetId); + const frame = this._page.frameManager.frame(targetId); if (!frame) return; // Subtree may be already gone due to renderer/browser race. - this._page._frameManager.removeChildFramesRecursively(frame); + this._page.frameManager.removeChildFramesRecursively(frame); for (const [contextId, context] of this._contextIdToContext) { if (context.frame === frame) this._onExecutionContextDestroyed(contextId); @@ -720,22 +720,22 @@ class FrameSession { const url = event.targetInfo.url; const worker = new Worker(this._page, url); - this._page._addWorker(event.sessionId, worker); + this._page.addWorker(event.sessionId, worker); this._workerSessions.set(event.sessionId, session); session.once('Runtime.executionContextCreated', async event => { - worker._createExecutionContext(new CRExecutionContext(session, event.context)); + worker.createExecutionContext(new CRExecutionContext(session, event.context)); }); // This might fail if the target is closed before we initialize. session._sendMayFail('Runtime.enable'); // TODO: attribute workers to the right frame. - this._crPage._networkManager.addSession(session, this._page._frameManager.frame(this._targetId) ?? undefined).catch(() => {}); + this._crPage._networkManager.addSession(session, this._page.frameManager.frame(this._targetId) ?? undefined).catch(() => {}); session._sendMayFail('Runtime.runIfWaitingForDebugger'); session._sendMayFail('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); session.on('Target.attachedToTarget', event => this._onAttachedToTarget(event)); session.on('Target.detachedFromTarget', event => this._onDetachedFromTarget(event)); session.on('Runtime.consoleAPICalled', event => { - const args = event.args.map(o => createHandle(worker._existingExecutionContext!, o)); - this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace)); + const args = event.args.map(o => createHandle(worker.existingExecutionContext!, o)); + this._page.addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace)); }); session.on('Runtime.exceptionThrown', exception => this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exception.exceptionDetails), this._page)); } @@ -745,7 +745,7 @@ class FrameSession { const workerSession = this._workerSessions.get(event.sessionId); if (workerSession) { workerSession.dispose(); - this._page._removeWorker(event.sessionId); + this._page.removeWorker(event.sessionId); return; } @@ -768,7 +768,7 @@ class FrameSession { // Child was not swapped in - that means frameAttached did not happen and // this is remote detach rather than remote -> local swap. if (!childFrameSession._swappedIn) - this._page._frameManager.frameDetached(event.targetId!); + this._page.frameManager.frameDetached(event.targetId!); childFrameSession.dispose(); }); } @@ -798,7 +798,7 @@ class FrameSession { if (!context) return; const values = event.args.map(arg => createHandle(context, arg)); - this._page._addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace)); + this._page.addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace)); } async _onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) { @@ -806,12 +806,12 @@ class FrameSession { if (!(pageOrError instanceof Error)) { const context = this._contextIdToContext.get(event.executionContextId); if (context) - await this._page._onBindingCalled(event.payload, context); + await this._page.onBindingCalled(event.payload, context); } } _onDialog(event: Protocol.Page.javascriptDialogOpeningPayload) { - if (!this._page._frameManager.frame(this._targetId)) + if (!this._page.frameManager.frame(this._targetId)) return; // Our frame/subtree may be gone already. this._page.emitOnContext(BrowserContext.Events.Dialog, new dialog.Dialog( this._page, @@ -820,7 +820,7 @@ class FrameSession { async (accept: boolean, promptText?: string) => { // TODO: this should actually be a CDP event that notifies about a cancelled navigation attempt. if (this._isMainFrame() && event.type === 'beforeunload' && !accept) - this._page._frameManager.frameAbortedNavigation(this._page.mainFrame()._id, 'navigation cancelled by beforeunload dialog'); + this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, 'navigation cancelled by beforeunload dialog'); await this._client.send('Page.handleJavaScriptDialog', { accept, promptText }); }, event.defaultPrompt)); @@ -845,14 +845,14 @@ class FrameSession { lineNumber: lineNumber || 0, columnNumber: 0, }; - this._page._addConsoleMessage(level, [], location, text); + this._page.addConsoleMessage(level, [], location, text); } } async _onFileChooserOpened(event: Protocol.Page.fileChooserOpenedPayload) { if (!event.backendNodeId) return; - const frame = this._page._frameManager.frame(event.frameId); + const frame = this._page.frameManager.frame(event.frameId); if (!frame) return; let handle; @@ -1070,7 +1070,7 @@ class FrameSession { }); if (!nodeInfo || typeof nodeInfo.node.frameId !== 'string') return null; - return this._page._frameManager.frame(nodeInfo.node.frameId); + return this._page.frameManager.frame(nodeInfo.node.frameId); } async _getOwnerFrame(handle: dom.ElementHandle): Promise { @@ -1112,7 +1112,7 @@ class FrameSession { } private async _framePosition(): Promise { - const frame = this._page._frameManager.frame(this._targetId); + const frame = this._page.frameManager.frame(this._targetId); if (!frame) return null; if (frame === this._page.mainFrame()) diff --git a/packages/playwright-core/src/server/chromium/crServiceWorker.ts b/packages/playwright-core/src/server/chromium/crServiceWorker.ts index deb858cfa2..baa8c15271 100644 --- a/packages/playwright-core/src/server/chromium/crServiceWorker.ts +++ b/packages/playwright-core/src/server/chromium/crServiceWorker.ts @@ -23,18 +23,18 @@ import type { CRBrowserContext } from './crBrowser'; import type { CRSession } from './crConnection'; export class CRServiceWorker extends Worker { - readonly _browserContext: CRBrowserContext; - readonly _networkManager?: CRNetworkManager; + readonly browserContext: CRBrowserContext; + private readonly _networkManager?: CRNetworkManager; private _session: CRSession; constructor(browserContext: CRBrowserContext, session: CRSession, url: string) { super(browserContext, url); this._session = session; - this._browserContext = browserContext; + this.browserContext = browserContext; if (!!process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS) this._networkManager = new CRNetworkManager(null, this); session.once('Runtime.executionContextCreated', event => { - this._createExecutionContext(new CRExecutionContext(session, event.context)); + this.createExecutionContext(new CRExecutionContext(session, event.context)); }); if (this._networkManager && this._isNetworkInspectionEnabled()) { @@ -62,19 +62,19 @@ export class CRServiceWorker extends Worker { async updateOffline(): Promise { if (!this._isNetworkInspectionEnabled()) return; - await this._networkManager?.setOffline(!!this._browserContext._options.offline).catch(() => {}); + await this._networkManager?.setOffline(!!this.browserContext._options.offline).catch(() => {}); } async updateHttpCredentials(): Promise { if (!this._isNetworkInspectionEnabled()) return; - await this._networkManager?.authenticate(this._browserContext._options.httpCredentials || null).catch(() => {}); + await this._networkManager?.authenticate(this.browserContext._options.httpCredentials || null).catch(() => {}); } async updateExtraHTTPHeaders(): Promise { if (!this._isNetworkInspectionEnabled()) return; - await this._networkManager?.setExtraHTTPHeaders(this._browserContext._options.extraHTTPHeaders || []).catch(() => {}); + await this._networkManager?.setExtraHTTPHeaders(this.browserContext._options.extraHTTPHeaders || []).catch(() => {}); } async updateRequestInterception(): Promise { @@ -84,32 +84,32 @@ export class CRServiceWorker extends Worker { } needsRequestInterception(): boolean { - return this._isNetworkInspectionEnabled() && !!this._browserContext._requestInterceptor; + return this._isNetworkInspectionEnabled() && !!this.browserContext._requestInterceptor; } reportRequestFinished(request: network.Request, response: network.Response | null) { - this._browserContext.emit(BrowserContext.Events.RequestFinished, { request, response }); + this.browserContext.emit(BrowserContext.Events.RequestFinished, { request, response }); } requestFailed(request: network.Request, _canceled: boolean) { - this._browserContext.emit(BrowserContext.Events.RequestFailed, request); + this.browserContext.emit(BrowserContext.Events.RequestFailed, request); } requestReceivedResponse(response: network.Response) { - this._browserContext.emit(BrowserContext.Events.Response, response); + this.browserContext.emit(BrowserContext.Events.Response, response); } requestStarted(request: network.Request, route?: network.RouteDelegate) { - this._browserContext.emit(BrowserContext.Events.Request, request); + this.browserContext.emit(BrowserContext.Events.Request, request); if (route) { const r = new network.Route(request, route); - if (this._browserContext._requestInterceptor?.(r, request)) + if (this.browserContext._requestInterceptor?.(r, request)) return; r.continue({ isFallback: true }).catch(() => {}); } } private _isNetworkInspectionEnabled(): boolean { - return this._browserContext._options.serviceWorkers !== 'block'; + return this.browserContext._options.serviceWorkers !== 'block'; } } diff --git a/packages/playwright-core/src/server/debugController.ts b/packages/playwright-core/src/server/debugController.ts index 62f45ef69a..70a8b82c6c 100644 --- a/packages/playwright-core/src/server/debugController.ts +++ b/packages/playwright-core/src/server/debugController.ts @@ -77,7 +77,7 @@ export class DebugController extends SdkObject { async resetForReuse() { const contexts = new Set(); for (const page of this._playwright.allPages()) - contexts.add(page.context()); + contexts.add(page.browserContext); for (const context of contexts) await context.resetForReuse(internalMetadata, null); } @@ -111,7 +111,7 @@ export class DebugController extends SdkObject { // Update test id attribute. if (params.testIdAttributeName) { for (const page of this._playwright.allPages()) - page.context().selectors().setTestIdAttributeName(params.testIdAttributeName); + page.browserContext.selectors().setTestIdAttributeName(params.testIdAttributeName); } // Toggle the mode. for (const recorder of await this._allRecorders()) { @@ -170,7 +170,7 @@ export class DebugController extends SdkObject { private async _allRecorders(): Promise { const contexts = new Set(); for (const page of this._playwright.allPages()) - contexts.add(page.context()); + contexts.add(page.browserContext); const result = await Promise.all([...contexts].map(c => Recorder.showInspector(c, { omitCallTracking: true }, () => Promise.resolve(new InspectingRecorderApp(this))))); return result.filter(Boolean) as Recorder[]; } diff --git a/packages/playwright-core/src/server/dialog.ts b/packages/playwright-core/src/server/dialog.ts index 38ec035384..fc6b5619b1 100644 --- a/packages/playwright-core/src/server/dialog.ts +++ b/packages/playwright-core/src/server/dialog.ts @@ -39,7 +39,7 @@ export class Dialog extends SdkObject { this._message = message; this._onHandle = onHandle; this._defaultValue = defaultValue || ''; - this._page._frameManager.dialogDidOpen(this); + this._page.frameManager.dialogDidOpen(this); this.instrumentation.onDialog(this); } @@ -62,14 +62,14 @@ export class Dialog extends SdkObject { async accept(promptText?: string) { assert(!this._handled, 'Cannot accept dialog which is already handled!'); this._handled = true; - this._page._frameManager.dialogWillClose(this); + this._page.frameManager.dialogWillClose(this); await this._onHandle(true, promptText); } async dismiss() { assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); this._handled = true; - this._page._frameManager.dialogWillClose(this); + this._page.frameManager.dialogWillClose(this); await this._onHandle(false); } diff --git a/packages/playwright-core/src/server/dispatchers/dispatcher.ts b/packages/playwright-core/src/server/dispatchers/dispatcher.ts index c96d18339d..10e6dd9df5 100644 --- a/packages/playwright-core/src/server/dispatchers/dispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/dispatcher.ts @@ -385,7 +385,7 @@ export class DispatcherConnection { } function closeReason(sdkObject: SdkObject): string | undefined { - return sdkObject.attribution.page?._closeReason || + return sdkObject.attribution.page?.closeReason || sdkObject.attribution.context?._closeReason || sdkObject.attribution.browser?._closeReason; } diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 166284366c..bcb37e9ace 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -93,10 +93,10 @@ export class PageDispatcher extends Dispatcher this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this, webSocket) })); this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this, worker) })); this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(parentScope, artifact) })); - if (page._video) - this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(this.parentScope(), page._video) }); + if (page.video) + this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(this.parentScope(), page.video) }); // Ensure client knows about all frames. - const frames = page._frameManager.frames(); + const frames = page.frameManager.frames(); for (let i = 1; i < frames.length; i++) this._onFrameAttached(frames[i]); } @@ -183,7 +183,7 @@ export class PageDispatcher extends Dispatcher pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags!) : pattern.glob!); await this._page.setClientRequestInterceptor((route, request) => { - const matchesSome = urlMatchers.some(urlMatch => urlMatches(this._page._browserContext._options.baseURL, request.url(), urlMatch)); + const matchesSome = urlMatchers.some(urlMatch => urlMatches(this._page.browserContext._options.baseURL, request.url(), urlMatch)); if (!matchesSome) return false; this._dispatchEvent('route', { route: RouteDispatcher.from(RequestDispatcher.from(this.parentScope(), request), route) }); @@ -347,7 +347,7 @@ export class WorkerDispatcher extends Dispatcher this._dispatchEvent('close')); } diff --git a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts index 119e574d4d..9ad27dea5f 100644 --- a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts @@ -59,7 +59,7 @@ export class WebSocketRouteDispatcher extends Dispatcher<{ guid: string }, chann static async installIfNeeded(target: Page | BrowserContext) { const kBindingName = '__pwWebSocketBinding'; - const context = target instanceof Page ? target.context() : target; + const context = target instanceof Page ? target.browserContext : target; if (!context.hasBinding(kBindingName)) { await context.exposeBinding(kBindingName, false, (source, payload: ws.BindingPayload) => { if (payload.type === 'onCreate') { diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index c543fb101e..270ca4de54 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -62,7 +62,7 @@ export class FrameExecutionContext extends js.ExecutionContext { override adoptIfNeeded(handle: js.JSHandle): Promise | null { if (handle instanceof ElementHandle && handle._context !== this) - return this.frame._page._delegate.adoptElementHandle(handle, this); + return this.frame._page.delegate.adoptElementHandle(handle, this); return null; } @@ -85,15 +85,15 @@ export class FrameExecutionContext extends js.ExecutionContext { injectedScript(): Promise> { if (!this._injectedScriptPromise) { const customEngines: InjectedScriptOptions['customEngines'] = []; - const selectorsRegistry = this.frame._page.context().selectors(); + const selectorsRegistry = this.frame._page.browserContext.selectors(); for (const [name, { source }] of selectorsRegistry._engines) customEngines.push({ name, source }); const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage; const options: InjectedScriptOptions = { sdkLanguage, testIdAttributeName: selectorsRegistry.testIdAttributeName(), - stableRafCount: this.frame._page._delegate.rafCountForStablePosition(), - browserName: this.frame._page._browserContext._browser.options.name, + stableRafCount: this.frame._page.delegate.rafCountForStablePosition(), + browserName: this.frame._page.browserContext._browser.options.name, inputFileRoleTextbox: process.env.PLAYWRIGHT_INPUT_FILE_TEXTBOX ? true : false, customEngines, }; @@ -160,14 +160,14 @@ export class ElementHandle extends js.JSHandle { } async ownerFrame(): Promise { - const frameId = await this._page._delegate.getOwnerFrame(this); + const frameId = await this._page.delegate.getOwnerFrame(this); if (!frameId) return null; - const frame = this._page._frameManager.frame(frameId); + const frame = this._page.frameManager.frame(frameId); if (frame) return frame; - for (const page of this._page._browserContext.pages()) { - const frame = page._frameManager.frame(frameId); + for (const page of this._page.browserContext.pages()) { + const frame = page.frameManager.frame(frameId); if (frame) return frame; } @@ -182,7 +182,7 @@ export class ElementHandle extends js.JSHandle { const isFrameElement = throwRetargetableDOMError(await this.isIframeElement()); if (!isFrameElement) return null; - return this._page._delegate.getContentFrame(this); + return this._page.delegate.getContentFrame(this); } async generateLocatorString(): Promise { @@ -219,7 +219,7 @@ export class ElementHandle extends js.JSHandle { } async _scrollRectIntoViewIfNeeded(rect?: types.Rect): Promise<'error:notvisible' | 'error:notconnected' | 'done'> { - return await this._page._delegate.scrollRectIntoViewIfNeeded(this, rect); + return await this._page.delegate.scrollRectIntoViewIfNeeded(this, rect); } async _waitAndScrollIntoViewIfNeeded(progress: Progress, waitForVisible: boolean): Promise { @@ -239,7 +239,7 @@ export class ElementHandle extends js.JSHandle { const controller = new ProgressController(metadata, this); return controller.run( progress => this._waitAndScrollIntoViewIfNeeded(progress, false /* waitForVisible */), - this._page._timeoutSettings.timeout(options)); + this._page.timeoutSettings.timeout(options)); } private async _clickablePoint(): Promise { @@ -263,7 +263,7 @@ export class ElementHandle extends js.JSHandle { }; const [quads, metrics] = await Promise.all([ - this._page._delegate.getContentQuads(this), + this._page.delegate.getContentQuads(this), this._page.mainFrame()._utilityContext().then(utility => utility.evaluate(() => ({ width: innerWidth, height: innerHeight }))), ] as const); if (quads === 'error:notconnected') @@ -275,7 +275,7 @@ export class ElementHandle extends js.JSHandle { const filtered = quads.map(quad => intersectQuadWithViewport(quad)).filter(quad => computeQuadArea(quad) > 0.99); if (!filtered.length) return 'error:notinviewport'; - if (this._page._browserContext._browser.options.name === 'firefox') { + if (this._page.browserContext._browser.options.name === 'firefox') { // Firefox internally uses integer coordinates, so 8.x is converted to 8 or 9 when clicking. // // This does not work nicely for small elements. For example, 1x1 square with corners @@ -470,7 +470,7 @@ export class ElementHandle extends js.JSHandle { }); } - const actionResult = await this._page._frameManager.waitForSignalsCreatedBy(progress, options.waitAfter === true, async () => { + const actionResult = await this._page.frameManager.waitForSignalsCreatedBy(progress, options.waitAfter === true, async () => { if ((options as any).__testHookBeforePointerAction) await (options as any).__testHookBeforePointerAction(); progress.throwIfAborted(); // Avoid action that has side-effects. @@ -522,7 +522,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._hover(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } _hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -535,7 +535,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._click(progress, { ...options, waitAfter: !options.noWaitAfter }); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } _click(progress: Progress, options: { waitAfter: boolean | 'disabled' } & types.MouseClickOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -548,7 +548,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._dblclick(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } _dblclick(progress: Progress, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -561,7 +561,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._tap(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } _tap(progress: Progress, options: types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -574,7 +574,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._selectOption(progress, elements, values, options); return throwRetargetableDOMError(result); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async _selectOption(progress: Progress, elements: ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions): Promise { @@ -610,7 +610,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._fill(progress, value, options); assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async _fill(progress: Progress, value: string, options: types.CommonActionOptions): Promise<'error:notconnected' | 'done'> { @@ -656,7 +656,7 @@ export class ElementHandle extends js.JSHandle { }, { force: options.force }); }, options); assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async setInputFiles(metadata: CallMetadata, params: channels.ElementHandleSetInputFilesParams) { @@ -666,7 +666,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._setInputFiles(progress, inputFileItems); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(params)); + }, this._page.timeoutSettings.timeout(params)); } async _setInputFiles(progress: Progress, items: InputFilesItems): Promise<'error:notconnected' | 'done'> { @@ -701,7 +701,7 @@ export class ElementHandle extends js.JSHandle { const waitForInputEvent = localDirectory ? this.evaluate(node => new Promise(fulfill => { node.addEventListener('input', fulfill, { once: true }); })).catch(() => {}) : Promise.resolve(); - await this._page._delegate.setInputFilePaths(retargeted, localPathsOrDirectory); + await this._page.delegate.setInputFilePaths(retargeted, localPathsOrDirectory); await waitForInputEvent; } else { await retargeted.evaluateInUtility(([injected, node, files]) => @@ -735,7 +735,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._type(progress, text, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async _type(progress: Progress, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions): Promise<'error:notconnected' | 'done'> { @@ -755,13 +755,13 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._press(progress, key, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async _press(progress: Progress, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions): Promise<'error:notconnected' | 'done'> { progress.log(`elementHandle.press("${key}")`); await this.instrumentation.onBeforeInputAction(this, progress.metadata); - return this._page._frameManager.waitForSignalsCreatedBy(progress, !options.noWaitAfter, async () => { + return this._page.frameManager.waitForSignalsCreatedBy(progress, !options.noWaitAfter, async () => { const result = await this._focus(progress, true /* resetSelectionIfNotFocused */); if (result !== 'done') return result; @@ -776,7 +776,7 @@ export class ElementHandle extends js.JSHandle { return controller.run(async progress => { const result = await this._setChecked(progress, true, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async uncheck(metadata: CallMetadata, options: { position?: types.Point } & types.PointerActionWaitOptions) { @@ -784,7 +784,7 @@ export class ElementHandle extends js.JSHandle { return controller.run(async progress => { const result = await this._setChecked(progress, false, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async _setChecked(progress: Progress, state: boolean, options: { position?: types.Point } & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -808,7 +808,7 @@ export class ElementHandle extends js.JSHandle { } async boundingBox(): Promise { - return this._page._delegate.getBoundingBox(this); + return this._page.delegate.getBoundingBox(this); } async ariaSnapshot(options: { ref?: boolean, emitGeneric?: boolean, mode?: 'raw' | 'regex' }): Promise { @@ -818,8 +818,8 @@ export class ElementHandle extends js.JSHandle { async screenshot(metadata: CallMetadata, options: ScreenshotOptions & TimeoutOptions = {}): Promise { const controller = new ProgressController(metadata, this); return controller.run( - progress => this._page._screenshotter.screenshotElement(progress, this, options), - this._page._timeoutSettings.timeout(options)); + progress => this._page.screenshotter.screenshotElement(progress, this, options), + this._page.timeoutSettings.timeout(options)); } async querySelector(selector: string, options: types.StrictOptions): Promise { @@ -872,7 +872,7 @@ export class ElementHandle extends js.JSHandle { }, state); }, {}); assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async waitForSelector(metadata: CallMetadata, selector: string, options: types.WaitForElementOptions = {}): Promise | null> { @@ -881,7 +881,7 @@ export class ElementHandle extends js.JSHandle { async _adoptTo(context: FrameExecutionContext): Promise> { if (this._context !== context) { - const adopted = await this._page._delegate.adoptElementHandle(this, context); + const adopted = await this._page.delegate.adoptElementHandle(this, context); this.dispose(); return adopted; } diff --git a/packages/playwright-core/src/server/download.ts b/packages/playwright-core/src/server/download.ts index eeb9476406..5ba6ed8ef0 100644 --- a/packages/playwright-core/src/server/download.ts +++ b/packages/playwright-core/src/server/download.ts @@ -27,14 +27,14 @@ export class Download { private _suggestedFilename: string | undefined; constructor(page: Page, downloadsPath: string, uuid: string, url: string, suggestedFilename?: string) { - const unaccessibleErrorMessage = page._browserContext._options.acceptDownloads === 'deny' ? 'Pass { acceptDownloads: true } when you are creating your browser context.' : undefined; + const unaccessibleErrorMessage = page.browserContext._options.acceptDownloads === 'deny' ? 'Pass { acceptDownloads: true } when you are creating your browser context.' : undefined; this.artifact = new Artifact(page, path.join(downloadsPath, uuid), unaccessibleErrorMessage, () => { - return this._page._browserContext.cancelDownload(uuid); + return this._page.browserContext.cancelDownload(uuid); }); this._page = page; this.url = url; this._suggestedFilename = suggestedFilename; - page._browserContext._downloads.add(this); + page.browserContext._downloads.add(this); if (suggestedFilename !== undefined) this._fireDownloadEvent(); } diff --git a/packages/playwright-core/src/server/electron/electron.ts b/packages/playwright-core/src/server/electron/electron.ts index c780808c88..b261860e04 100644 --- a/packages/playwright-core/src/server/electron/electron.ts +++ b/packages/playwright-core/src/server/electron/electron.ts @@ -142,7 +142,7 @@ export class ElectronApplication extends SdkObject { async browserWindow(page: Page): Promise> { // Assume CRPage as Electron is always Chromium. - const targetId = (page._delegate as CRPage)._targetId; + const targetId = (page.delegate as CRPage)._targetId; const electronHandle = await this._nodeElectronHandlePromise; return await electronHandle.evaluateHandle(({ BrowserWindow, webContents }, targetId) => { const wc = webContents.fromDevToolsTargetId(targetId); diff --git a/packages/playwright-core/src/server/fileUploadUtils.ts b/packages/playwright-core/src/server/fileUploadUtils.ts index 83ea9e015e..9b1668ad80 100644 --- a/packages/playwright-core/src/server/fileUploadUtils.ts +++ b/packages/playwright-core/src/server/fileUploadUtils.ts @@ -58,7 +58,7 @@ export async function prepareFilesForUpload(frame: Frame, params: channels.Eleme lastModifiedMs?: number, }[] | undefined = payloads; - if (!frame._page._browserContext._browser._isCollocatedWithServer) { + if (!frame._page.browserContext._browser._isCollocatedWithServer) { // If the browser is on a different machine read files into buffers. if (localPaths) { if (await filesExceedUploadLimit(localPaths)) diff --git a/packages/playwright-core/src/server/firefox/ffBrowser.ts b/packages/playwright-core/src/server/firefox/ffBrowser.ts index fca962487f..178489d41e 100644 --- a/packages/playwright-core/src/server/firefox/ffBrowser.ts +++ b/packages/playwright-core/src/server/firefox/ffBrowser.ts @@ -17,7 +17,7 @@ import { assert } from '../../utils'; import { Browser } from '../browser'; -import { BrowserContext, assertBrowserContextIsNotOwned, verifyGeolocation } from '../browserContext'; +import { BrowserContext, verifyGeolocation } from '../browserContext'; import { TargetClosedError } from '../errors'; import { kPlaywrightBinding } from '../javascript'; import * as network from '../network'; @@ -137,7 +137,7 @@ export class FFBrowser extends Browser { return; // 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.initializedOrUndefined(); // If it's a new window download, report it on the opener page. @@ -282,7 +282,6 @@ export class FFBrowserContext extends BrowserContext { } override async doCreateNewPage(): Promise { - assertBrowserContextIsNotOwned(this); const { targetId } = await this._browser.session.send('Browser.newPage', { browserContextId: this._browserContextId }).catch(e => { diff --git a/packages/playwright-core/src/server/firefox/ffNetworkManager.ts b/packages/playwright-core/src/server/firefox/ffNetworkManager.ts index fbd7330739..5687ecdeb6 100644 --- a/packages/playwright-core/src/server/firefox/ffNetworkManager.ts +++ b/packages/playwright-core/src/server/firefox/ffNetworkManager.ts @@ -59,7 +59,7 @@ export class FFNetworkManager { _onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) { const redirectedFrom = event.redirectedFrom ? (this._requests.get(event.redirectedFrom) || null) : null; - const frame = redirectedFrom ? redirectedFrom.request.frame() : (event.frameId ? this._page._frameManager.frame(event.frameId) : null); + const frame = redirectedFrom ? redirectedFrom.request.frame() : (event.frameId ? this._page.frameManager.frame(event.frameId) : null); if (!frame) return; if (redirectedFrom) @@ -69,7 +69,7 @@ export class FFNetworkManager { if (event.isIntercepted) route = new FFRouteImpl(this._session, request); this._requests.set(request._id, request); - this._page._frameManager.requestStarted(request.request, route); + this._page.frameManager.requestStarted(request.request, route); } _onResponseReceived(event: Protocol.Network.responseReceivedPayload) { @@ -121,7 +121,7 @@ export class FFNetworkManager { response.setRawResponseHeaders(null); // Headers size are not available in Firefox. response.setResponseHeadersSize(null); - this._page._frameManager.requestReceivedResponse(response); + this._page.frameManager.requestReceivedResponse(response); } _onRequestFinished(event: Protocol.Network.requestFinishedPayload) { @@ -143,7 +143,7 @@ export class FFNetworkManager { } if (event.protocolVersion) response._setHttpVersion(event.protocolVersion); - this._page._frameManager.reportRequestFinished(request.request, response); + this._page.frameManager.reportRequestFinished(request.request, response); } _onRequestFailed(event: Protocol.Network.requestFailedPayload) { @@ -158,7 +158,7 @@ export class FFNetworkManager { response._requestFinished(-1); } request.request._setFailureText(event.errorCode); - this._page._frameManager.requestFailed(request.request, event.errorCode === 'NS_BINDING_ABORTED'); + this._page.frameManager.requestFailed(request.request, event.errorCode === 'NS_BINDING_ABORTED'); } } @@ -204,7 +204,7 @@ class InterceptableRequest { let postDataBuffer = null; if (payload.postData) postDataBuffer = Buffer.from(payload.postData, 'base64'); - this.request = new network.Request(frame._page._browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigationId, + this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigationId, payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, postDataBuffer, payload.headers); // "raw" headers are the same as "provisional" headers in Firefox. this.request.setRawRequestHeaders(null); diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts index cefe0ecc0e..8195f36739 100644 --- a/packages/playwright-core/src/server/firefox/ffPage.ts +++ b/packages/playwright-core/src/server/firefox/ffPage.ts @@ -120,27 +120,27 @@ export class FFPage implements PageDelegate { } _onWebSocketCreated(event: Protocol.Page.webSocketCreatedPayload) { - this._page._frameManager.onWebSocketCreated(webSocketId(event.frameId, event.wsid), event.requestURL); - this._page._frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid)); + this._page.frameManager.onWebSocketCreated(webSocketId(event.frameId, event.wsid), event.requestURL); + this._page.frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid)); } _onWebSocketClosed(event: Protocol.Page.webSocketClosedPayload) { if (event.error) - this._page._frameManager.webSocketError(webSocketId(event.frameId, event.wsid), event.error); - this._page._frameManager.webSocketClosed(webSocketId(event.frameId, event.wsid)); + this._page.frameManager.webSocketError(webSocketId(event.frameId, event.wsid), event.error); + this._page.frameManager.webSocketClosed(webSocketId(event.frameId, event.wsid)); } _onWebSocketFrameReceived(event: Protocol.Page.webSocketFrameReceivedPayload) { - this._page._frameManager.webSocketFrameReceived(webSocketId(event.frameId, event.wsid), event.opcode, event.data); + this._page.frameManager.webSocketFrameReceived(webSocketId(event.frameId, event.wsid), event.opcode, event.data); } _onWebSocketFrameSent(event: Protocol.Page.webSocketFrameSentPayload) { - this._page._frameManager.onWebSocketFrameSent(webSocketId(event.frameId, event.wsid), event.opcode, event.data); + this._page.frameManager.onWebSocketFrameSent(webSocketId(event.frameId, event.wsid), event.opcode, event.data); } _onExecutionContextCreated(payload: Protocol.Runtime.executionContextCreatedPayload) { const { executionContextId, auxData } = payload; - const frame = this._page._frameManager.frame(auxData.frameId!); + const frame = this._page.frameManager.frame(auxData.frameId!); if (!frame) return; const delegate = new FFExecutionContext(this._session, executionContextId); @@ -178,17 +178,17 @@ export class FFPage implements PageDelegate { _onLinkClicked(phase: 'before' | 'after') { if (phase === 'before') - this._page._frameManager.frameWillPotentiallyRequestNavigation(); + this._page.frameManager.frameWillPotentiallyRequestNavigation(); else - this._page._frameManager.frameDidPotentiallyRequestNavigation(); + this._page.frameManager.frameDidPotentiallyRequestNavigation(); } _onNavigationStarted(params: Protocol.Page.navigationStartedPayload) { - this._page._frameManager.frameRequestedNavigation(params.frameId, params.navigationId); + this._page.frameManager.frameRequestedNavigation(params.frameId, params.navigationId); } _onNavigationAborted(params: Protocol.Page.navigationAbortedPayload) { - this._page._frameManager.frameAbortedNavigation(params.frameId, params.errorText, params.navigationId); + this._page.frameManager.frameAbortedNavigation(params.frameId, params.errorText, params.navigationId); } _onNavigationCommitted(params: Protocol.Page.navigationCommittedPayload) { @@ -196,27 +196,27 @@ export class FFPage implements PageDelegate { if (worker.frameId === params.frameId) this._onWorkerDestroyed({ workerId }); } - this._page._frameManager.frameCommittedNewDocumentNavigation(params.frameId, params.url, params.name || '', params.navigationId || '', false); + this._page.frameManager.frameCommittedNewDocumentNavigation(params.frameId, params.url, params.name || '', params.navigationId || '', false); } _onSameDocumentNavigation(params: Protocol.Page.sameDocumentNavigationPayload) { - this._page._frameManager.frameCommittedSameDocumentNavigation(params.frameId, params.url); + this._page.frameManager.frameCommittedSameDocumentNavigation(params.frameId, params.url); } _onFrameAttached(params: Protocol.Page.frameAttachedPayload) { - this._page._frameManager.frameAttached(params.frameId, params.parentFrameId); + this._page.frameManager.frameAttached(params.frameId, params.parentFrameId); } _onFrameDetached(params: Protocol.Page.frameDetachedPayload) { - this._page._frameManager.frameDetached(params.frameId); + this._page.frameManager.frameDetached(params.frameId); } _onEventFired(payload: Protocol.Page.eventFiredPayload) { const { frameId, name } = payload; if (name === 'load') - this._page._frameManager.frameLifecycleEvent(frameId, 'load'); + this._page.frameManager.frameLifecycleEvent(frameId, 'load'); if (name === 'DOMContentLoaded') - this._page._frameManager.frameLifecycleEvent(frameId, 'domcontentloaded'); + this._page.frameManager.frameLifecycleEvent(frameId, 'domcontentloaded'); } _onUncaughtError(params: Protocol.Page.uncaughtErrorPayload) { @@ -233,7 +233,7 @@ export class FFPage implements PageDelegate { if (!context) return; // Juggler reports 'warn' for some internal messages generated by the browser. - this._page._addConsoleMessage(type === 'warn' ? 'warning' : type, args.map(arg => createHandle(context, arg)), location); + this._page.addConsoleMessage(type === 'warn' ? 'warning' : type, args.map(arg => createHandle(context, arg)), location); } _onDialogOpened(params: Protocol.Page.dialogOpenedPayload) { @@ -252,7 +252,7 @@ export class FFPage implements PageDelegate { if (!(pageOrError instanceof Error)) { const context = this._contextIdToContext.get(event.executionContextId); if (context) - await this._page._onBindingCalled(event.payload, context); + await this._page.onBindingCalled(event.payload, context); } } @@ -278,14 +278,14 @@ export class FFPage implements PageDelegate { }); }); this._workers.set(workerId, { session: workerSession, frameId: event.frameId }); - this._page._addWorker(workerId, worker); + this._page.addWorker(workerId, worker); workerSession.once('Runtime.executionContextCreated', event => { - worker._createExecutionContext(new FFExecutionContext(workerSession, event.executionContextId)); + worker.createExecutionContext(new FFExecutionContext(workerSession, event.executionContextId)); }); workerSession.on('Runtime.console', event => { const { type, args, location } = event; - const context = worker._existingExecutionContext!; - this._page._addConsoleMessage(type, args.map(arg => createHandle(context, arg)), location); + const context = worker.existingExecutionContext!; + this._page.addConsoleMessage(type, args.map(arg => createHandle(context, arg)), location); }); // Note: we receive worker exceptions directly from the page. } @@ -297,7 +297,7 @@ export class FFPage implements PageDelegate { return; worker.session.dispose(); this._workers.delete(workerId); - this._page._removeWorker(workerId); + this._page.removeWorker(workerId); } async _onDispatchMessageFromWorker(event: Protocol.Page.dispatchMessageFromWorkerPayload) { @@ -431,7 +431,7 @@ export class FFPage implements PageDelegate { }); if (!contentFrameId) return null; - return this._page._frameManager.frame(contentFrameId); + return this._page.frameManager.frame(contentFrameId); } async getOwnerFrame(handle: dom.ElementHandle): Promise { diff --git a/packages/playwright-core/src/server/frameSelectors.ts b/packages/playwright-core/src/server/frameSelectors.ts index 14e0774ed0..90f24ca2f6 100644 --- a/packages/playwright-core/src/server/frameSelectors.ts +++ b/packages/playwright-core/src/server/frameSelectors.ts @@ -45,8 +45,8 @@ export class FrameSelectors { } private _parseSelector(selector: string | ParsedSelector, options?: types.StrictOptions): SelectorInfo { - const strict = typeof options?.strict === 'boolean' ? options.strict : !!this.frame._page.context()._options.strictSelectors; - return this.frame._page.context().selectors().parseSelector(selector, strict); + const strict = typeof options?.strict === 'boolean' ? options.strict : !!this.frame._page.browserContext._options.strictSelectors; + return this.frame._page.browserContext.selectors().parseSelector(selector, strict); } async query(selector: string, options?: types.StrictOptions, scope?: ElementHandle): Promise | null> { @@ -137,7 +137,7 @@ export class FrameSelectors { const element = handle.asElement() as ElementHandle | null; if (!element) return null; - const maybeFrame = await frame._page._delegate.getContentFrame(element); + const maybeFrame = await frame._page.delegate.getContentFrame(element); element.dispose(); if (!maybeFrame) return null; @@ -163,7 +163,7 @@ export class FrameSelectors { async function adoptIfNeeded(handle: ElementHandle, context: FrameExecutionContext): Promise> { if (handle._context === context) return handle; - const adopted = await handle._page._delegate.adoptElementHandle(handle, context); + const adopted = await handle._page.delegate.adoptElementHandle(handle, context); handle.dispose(); return adopted; } diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index bf5be6e09d..1cef4d7b15 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -171,7 +171,7 @@ export class FrameManager { if (progress) progress.cleanupWhenAborted(() => this._signalBarriers.delete(barrier)); const result = await action(); - await this._page._delegate.inputActionEpilogue(); + await this._page.delegate.inputActionEpilogue(); await barrier.waitFor(); this._signalBarriers.delete(barrier); // Resolve in the next task, after all waitForNavigations. @@ -306,11 +306,11 @@ export class FrameManager { this._page.emitOnContext(BrowserContext.Events.Request, request); if (route) { const r = new network.Route(request, route); - if (this._page._serverRequestInterceptor?.(r, request)) + if (this._page.serverRequestInterceptor?.(r, request)) return; - if (this._page._clientRequestInterceptor?.(r, request)) + if (this._page.clientRequestInterceptor?.(r, request)) return; - if (this._page._browserContext._requestInterceptor?.(r, request)) + if (this._page.browserContext._requestInterceptor?.(r, request)) return; r.continue({ isFallback: true }).catch(() => {}); } @@ -563,7 +563,7 @@ export class Frame extends SdkObject { async raceAgainstEvaluationStallingEvents(cb: () => Promise): Promise { if (this._pendingDocument) throw new Error('Frame is currently attempting a navigation'); - if (this._page._frameManager._openedDialogs.size) + if (this._page.frameManager._openedDialogs.size) throw new Error('Open JavaScript dialog prevents evaluation'); const promise = new ManualPromise(); @@ -645,9 +645,9 @@ export class Frame extends SdkObject { } async goto(metadata: CallMetadata, url: string, options: types.GotoOptions = {}): Promise { - const constructedNavigationURL = constructURLBasedOnBaseURL(this._page._browserContext._options.baseURL, url); + const constructedNavigationURL = constructURLBasedOnBaseURL(this._page.browserContext._options.baseURL, url); const controller = new ProgressController(metadata, this); - return controller.run(progress => this._goto(progress, constructedNavigationURL, options), this._page._timeoutSettings.navigationTimeout(options)); + return controller.run(progress => this._goto(progress, constructedNavigationURL, options), this._page.timeoutSettings.navigationTimeout(options)); } private async _goto(progress: Progress, url: string, options: types.GotoOptions): Promise { @@ -670,7 +670,7 @@ export class Frame extends SdkObject { const navigationEvents: NavigationEvent[] = []; const collectNavigations = (arg: NavigationEvent) => navigationEvents.push(arg); this.on(Frame.Events.InternalNavigation, collectNavigations); - const navigateResult = await this._page._delegate.navigateFrame(this, url, referer).finally( + const navigateResult = await this._page.delegate.navigateFrame(this, url, referer).finally( () => this.off(Frame.Events.InternalNavigation, collectNavigations)); let event: NavigationEvent; @@ -740,7 +740,7 @@ export class Frame extends SdkObject { } async frameElement(): Promise { - return this._page._delegate.getFrameElement(this); + return this._page.delegate.getFrameElement(this); } _context(world: types.World): Promise { @@ -792,7 +792,7 @@ export class Frame extends SdkObject { return controller.run(async progress => { progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`); return await this.waitForSelectorInternal(progress, selector, true, options, scope); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async waitForSelectorInternal(progress: Progress, selector: string, performActionPreChecks: boolean, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise | null> { @@ -916,7 +916,7 @@ export class Frame extends SdkObject { const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`; const context = await this._utilityContext(); const lifecyclePromise = new Promise((resolve, reject) => { - this._page._frameManager._consoleMessageTags.set(tag, () => { + this._page.frameManager._consoleMessageTags.set(tag, () => { // Clear lifecycle right after document.open() - see 'tag' below. this._onClearLifecycle(); this._waitForLoadState(progress, waitUntil).then(resolve).catch(reject); @@ -931,7 +931,7 @@ export class Frame extends SdkObject { await Promise.all([contentPromise, lifecyclePromise]); return null; }); - }, this._page._timeoutSettings.navigationTimeout(options)); + }, this._page.timeoutSettings.navigationTimeout(options)); } name(): string { @@ -976,7 +976,7 @@ export class Frame extends SdkObject { const result = (await context.evaluateHandle(addScriptContent, { content: content!, type })).asElement()!; // Another round trip to the browser to ensure that we receive CSP error messages // (if any) logged asynchronously in a separate task on the content main thread. - if (this._page._delegate.cspErrorsAsynchronousForInlineScripts) + if (this._page.delegate.cspErrorsAsynchronousForInlineScripts) await context.evaluate(() => true); return result; }); @@ -1057,7 +1057,7 @@ export class Frame extends SdkObject { let cspMessage: ConsoleMessage | undefined; const actionPromise = func().then(r => result = r).catch(e => error = e); const errorPromise = new Promise(resolve => { - listeners.push(eventsHelper.addEventListener(this._page._browserContext, BrowserContext.Events.Console, (message: ConsoleMessage) => { + listeners.push(eventsHelper.addEventListener(this._page.browserContext, BrowserContext.Events.Console, (message: ConsoleMessage) => { if (message.page() !== this._page || message.type() !== 'error') return; if (message.text().includes('Content-Security-Policy') || message.text().includes('Content Security Policy')) { @@ -1175,7 +1175,7 @@ export class Frame extends SdkObject { async rafrafTimeoutScreenshotElementWithProgress(progress: Progress, selector: string, timeout: number, options: ScreenshotOptions): Promise { return await this._retryWithProgressIfNotConnected(progress, selector, true /* strict */, true /* performActionPreChecks */, async handle => { await handle._frame.rafrafTimeout(timeout); - return await this._page._screenshotter.screenshotElement(progress, handle, options); + return await this._page.screenshotter.screenshotElement(progress, handle, options); }); } @@ -1183,14 +1183,14 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._click(progress, { ...options, waitAfter: !options.noWaitAfter }))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async dblclick(metadata: CallMetadata, selector: string, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._dblclick(progress, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async dragAndDrop(metadata: CallMetadata, source: string, target: string, options: types.DragActionOptions & types.PointerActionWaitOptions = {}) { @@ -1219,37 +1219,37 @@ export class Frame extends SdkObject { timeout: progress.timeUntilDeadline(), }); })); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async tap(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions) { - if (!this._page._browserContext._options.hasTouch) + if (!this._page.browserContext._options.hasTouch) throw new Error('The page does not support tap. Use hasTouch context option to enable touch support.'); const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._tap(progress, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async fill(metadata: CallMetadata, selector: string, value: string, options: types.TimeoutOptions & types.StrictOptions & { force?: boolean }) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._fill(progress, value, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async focus(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._focus(progress))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async blur(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._blur(progress))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async textContent(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { @@ -1314,7 +1314,7 @@ export class Frame extends SdkObject { return controller.run(async progress => { progress.log(` checking visibility of ${this._asLocator(selector)}`); return await this.isVisibleInternal(selector, options, scope); - }, this._page._timeoutSettings.timeout({})); + }, this._page.timeoutSettings.timeout({})); } async isVisibleInternal(selector: string, options: types.StrictOptions = {}, scope?: dom.ElementHandle): Promise { @@ -1358,14 +1358,14 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._hover(progress, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async selectOption(metadata: CallMetadata, selector: string, elements: dom.ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions = {}): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._selectOption(progress, elements, values, options)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async setInputFiles(metadata: CallMetadata, selector: string, params: channels.FrameSetInputFilesParams): Promise { @@ -1373,35 +1373,35 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params.strict, true /* performActionPreChecks */, handle => handle._setInputFiles(progress, inputFileItems))); - }, this._page._timeoutSettings.timeout(params)); + }, this._page.timeoutSettings.timeout(params)); } async type(metadata: CallMetadata, selector: string, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._type(progress, text, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async press(metadata: CallMetadata, selector: string, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._press(progress, key, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async check(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._setChecked(progress, true, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async uncheck(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._setChecked(progress, false, options))); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async waitForTimeout(metadata: CallMetadata, timeout: number) { @@ -1415,7 +1415,7 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return await this._retryWithProgressIfNotConnected(progress, selector, true /* strict */, true /* performActionPreChecks */, handle => handle.ariaSnapshot(options)); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async expect(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { @@ -1429,7 +1429,7 @@ export class Frame extends SdkObject { private async _expectImpl(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { const lastIntermediateResult: { received?: any, isSet: boolean } = { isSet: false }; try { - let timeout = this._page._timeoutSettings.timeout(options); + let timeout = this._page.timeoutSettings.timeout(options); const start = timeout > 0 ? monotonicTime() : 0; // Step 1: perform locator handlers checkpoint with a specified timeout. @@ -1581,7 +1581,7 @@ export class Frame extends SdkObject { progress.cleanupWhenAborted(() => handle.evaluate(h => h.abort()).catch(() => {})); return handle.evaluateHandle(h => h.result); }); - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } async waitForFunctionValueInUtility(progress: Progress, pageFunction: js.Func1) { @@ -1656,7 +1656,7 @@ export class Frame extends SdkObject { return value!; }); return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; - }, this._page._timeoutSettings.timeout(options)); + }, this._page.timeoutSettings.timeout(options)); } private _setContext(world: types.World, context: dom.FrameExecutionContext | null) { diff --git a/packages/playwright-core/src/server/input.ts b/packages/playwright-core/src/server/input.ts index 62261edfae..9c8a72fccf 100644 --- a/packages/playwright-core/src/server/input.ts +++ b/packages/playwright-core/src/server/input.ts @@ -322,7 +322,7 @@ export class Touchscreen { async tap(x: number, y: number, metadata?: CallMetadata) { if (metadata) metadata.point = { x, y }; - if (!this._page._browserContext._options.hasTouch) + if (!this._page.browserContext._options.hasTouch) throw new Error('hasTouch must be enabled on the browser context before using the touchscreen.'); await this._raw.tap(x, y, this._page.keyboard._modifiers()); } diff --git a/packages/playwright-core/src/server/launchApp.ts b/packages/playwright-core/src/server/launchApp.ts index 7da367af09..1652a611c4 100644 --- a/packages/playwright-core/src/server/launchApp.ts +++ b/packages/playwright-core/src/server/launchApp.ts @@ -75,7 +75,7 @@ export async function launchApp(browserType: BrowserType, options: { async function installAppIcon(page: Page) { const icon = await fs.promises.readFile(require.resolve('./chromium/appIcon.png')); - const crPage = page._delegate as CRPage; + const crPage = page.delegate as CRPage; await crPage._mainFrameSession._client.send('Browser.setDockTile', { image: icon.toString('base64') }); diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index fab06fd7a2..9a93f5860e 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -144,29 +144,28 @@ export class Page extends SdkObject { private _eventsToEmitAfterInitialized: { event: string | symbol, args: any[] }[] = []; private _crashed = false; readonly openScope = new LongStandingScope(); - readonly _browserContext: BrowserContext; + readonly browserContext: BrowserContext; readonly keyboard: input.Keyboard; readonly mouse: input.Mouse; readonly touchscreen: input.Touchscreen; - readonly _timeoutSettings: TimeoutSettings; - readonly _delegate: PageDelegate; - _emulatedSize: EmulatedSize | undefined; + readonly timeoutSettings: TimeoutSettings; + readonly delegate: PageDelegate; + private _emulatedSize: EmulatedSize | undefined; private _extraHTTPHeaders: types.HeadersArray | undefined; private _emulatedMedia: Partial = {}; private _interceptFileChooser = false; private readonly _pageBindings = new Map(); initScripts: InitScript[] = []; - readonly _screenshotter: Screenshotter; - readonly _frameManager: frames.FrameManager; + readonly screenshotter: Screenshotter; + readonly frameManager: frames.FrameManager; readonly accessibility: accessibility.Accessibility; private _workers = new Map(); readonly pdf: ((options: channels.PagePdfParams) => Promise) | undefined; readonly coverage: any; - _clientRequestInterceptor: network.RouteHandler | undefined; - _serverRequestInterceptor: network.RouteHandler | undefined; - _ownedContext: BrowserContext | undefined; - _video: Artifact | null = null; - _opener: Page | undefined; + clientRequestInterceptor: network.RouteHandler | undefined; + serverRequestInterceptor: network.RouteHandler | undefined; + video: Artifact | null = null; + private _opener: Page | undefined; private _isServerSideOnly = false; private _locatorHandlers = new Map }>(); private _lastLocatorHandlerUid = 0; @@ -175,20 +174,20 @@ export class Page extends SdkObject { // Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms. // When throttling for tracing, 200ms between frames, except for 10 frames around the action. private _frameThrottler = new FrameThrottler(10, 35, 200); - _closeReason: string | undefined; + closeReason: string | undefined; constructor(delegate: PageDelegate, browserContext: BrowserContext) { super(browserContext, 'page'); this.attribution.page = this; - this._delegate = delegate; - this._browserContext = browserContext; + this.delegate = delegate; + this.browserContext = browserContext; this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate)); this.keyboard = new input.Keyboard(delegate.rawKeyboard); this.mouse = new input.Mouse(delegate.rawMouse, this); this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this); - this._timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings); - this._screenshotter = new Screenshotter(this); - this._frameManager = new frames.FrameManager(this); + this.timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings); + this.screenshotter = new Screenshotter(this); + this.frameManager = new frames.FrameManager(this); if (delegate.pdf) this.pdf = delegate.pdf.bind(delegate); this.coverage = delegate.coverage ? delegate.coverage() : null; @@ -207,15 +206,15 @@ export class Page extends SdkObject { if (error) { // Initialization error could have happened because of // context/browser closure. Just ignore the page. - if (this._browserContext.isClosingOrClosed()) + if (this.browserContext.isClosingOrClosed()) return; - this._frameManager.createDummyMainFrameIfNeeded(); + this.frameManager.createDummyMainFrameIfNeeded(); } this._initialized = error || this; this.emitOnContext(contextEvent, this); for (const { event, args } of this._eventsToEmitAfterInitialized) - this._browserContext.emit(event, ...args); + this.browserContext.emit(event, ...args); this._eventsToEmitAfterInitialized = []; // It may happen that page initialization finishes after Close event has already been sent, @@ -242,7 +241,7 @@ export class Page extends SdkObject { emitOnContext(event: string | symbol, ...args: any[]) { if (this._isServerSideOnly) return; - this._browserContext.emit(event, ...args); + this.browserContext.emit(event, ...args); } emitOnContextOnceInitialized(event: string | symbol, ...args: any[]) { @@ -253,7 +252,7 @@ export class Page extends SdkObject { // and dispatch it to the client later, either on the live Page, // or on the "errored" Page. if (this._initialized) - this._browserContext.emit(event, ...args); + this.browserContext.emit(event, ...args); else this._eventsToEmitAfterInitialized.push({ event, args }); } @@ -266,7 +265,7 @@ export class Page extends SdkObject { await this._removeExposedBindings(); await this._removeInitScripts(); await this.setClientRequestInterceptor(undefined); - await this._setServerRequestInterceptor(undefined); + await this.setServerRequestInterceptor(undefined); await this.setFileChooserIntercepted(false); // Re-navigate once init scripts are gone. await this.mainFrame().goto(metadata, 'about:blank'); @@ -276,16 +275,16 @@ export class Page extends SdkObject { this._interceptFileChooser = false; await Promise.all([ - this._delegate.updateEmulatedViewportSize(), - this._delegate.updateEmulateMedia(), - this._delegate.updateFileChooserInterception(), + this.delegate.updateEmulatedViewportSize(), + this.delegate.updateEmulateMedia(), + this.delegate.updateFileChooserInterception(), ]); - await this._delegate.resetForReuse(); + await this.delegate.resetForReuse(); } _didClose() { - this._frameManager.dispose(); + this.frameManager.dispose(); this._frameThrottler.dispose(); assert(this._closedState !== 'closed', 'Page closed twice'); this._closedState = 'closed'; @@ -296,7 +295,7 @@ export class Page extends SdkObject { } _didCrash() { - this._frameManager.dispose(); + this.frameManager.dispose(); this._frameThrottler.dispose(); this.emit(Page.Events.Crash); this._crashed = true; @@ -320,42 +319,38 @@ export class Page extends SdkObject { this.emit(Page.Events.FileChooser, fileChooser); } - context(): BrowserContext { - return this._browserContext; - } - opener(): Page | undefined { return this._opener; } mainFrame(): frames.Frame { - return this._frameManager.mainFrame(); + return this.frameManager.mainFrame(); } frames(): frames.Frame[] { - return this._frameManager.frames(); + return this.frameManager.frames(); } setDefaultNavigationTimeout(timeout: number | undefined) { - this._timeoutSettings.setDefaultNavigationTimeout(timeout); + this.timeoutSettings.setDefaultNavigationTimeout(timeout); } setDefaultTimeout(timeout: number | undefined) { - this._timeoutSettings.setDefaultTimeout(timeout); + this.timeoutSettings.setDefaultTimeout(timeout); } async exposeBinding(name: string, needsHandle: boolean, playwrightBinding: frames.FunctionWithSource) { if (this._pageBindings.has(name)) throw new Error(`Function "${name}" has been already registered`); - if (this._browserContext._pageBindings.has(name)) + if (this.browserContext._pageBindings.has(name)) throw new Error(`Function "${name}" has been already registered in the browser context`); const binding = new PageBinding(name, playwrightBinding, needsHandle); this._pageBindings.set(name, binding); - await this._delegate.addInitScript(binding.initScript); + await this.delegate.addInitScript(binding.initScript); await Promise.all(this.frames().map(frame => frame.evaluateExpression(binding.initScript.source).catch(e => {}))); } - async _removeExposedBindings() { + private async _removeExposedBindings() { for (const [key, binding] of this._pageBindings) { if (!binding.internal) this._pageBindings.delete(key); @@ -364,22 +359,22 @@ export class Page extends SdkObject { setExtraHTTPHeaders(headers: types.HeadersArray) { this._extraHTTPHeaders = headers; - return this._delegate.updateExtraHTTPHeaders(); + return this.delegate.updateExtraHTTPHeaders(); } extraHTTPHeaders(): types.HeadersArray | undefined { return this._extraHTTPHeaders; } - async _onBindingCalled(payload: string, context: dom.FrameExecutionContext) { + async onBindingCalled(payload: string, context: dom.FrameExecutionContext) { if (this._closedState === 'closed') return; await PageBinding.dispatch(this, payload, context); } - _addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) { + addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) { const message = new ConsoleMessage(this, type, text, args, location); - const intercepted = this._frameManager.interceptConsoleMessage(message); + const intercepted = this.frameManager.interceptConsoleMessage(message); if (intercepted) { args.forEach(arg => arg.dispose()); return; @@ -395,10 +390,10 @@ export class Page extends SdkObject { const [response] = await Promise.all([ // Reload must be a new document, and should not be confused with a stray pushState. this.mainFrame()._waitForNavigation(progress, true /* requiresNewDocument */, options), - this._delegate.reload(), + this.delegate.reload(), ]); return response; - }), this._timeoutSettings.navigationTimeout(options)); + }), this.timeoutSettings.navigationTimeout(options)); } async goBack(metadata: CallMetadata, options: types.NavigateOptions): Promise { @@ -411,14 +406,14 @@ export class Page extends SdkObject { error = e; return null; }); - const result = await this._delegate.goBack(); + const result = await this.delegate.goBack(); if (!result) return null; const response = await waitPromise; if (error) throw error; return response; - }), this._timeoutSettings.navigationTimeout(options)); + }), this.timeoutSettings.navigationTimeout(options)); } async goForward(metadata: CallMetadata, options: types.NavigateOptions): Promise { @@ -431,18 +426,18 @@ export class Page extends SdkObject { error = e; return null; }); - const result = await this._delegate.goForward(); + const result = await this.delegate.goForward(); if (!result) return null; const response = await waitPromise; if (error) throw error; return response; - }), this._timeoutSettings.navigationTimeout(options)); + }), this.timeoutSettings.navigationTimeout(options)); } requestGC(): Promise { - return this._delegate.requestGC(); + return this.delegate.requestGC(); } registerLocatorHandler(selector: string, noWaitAfter: boolean | undefined) { @@ -477,7 +472,7 @@ export class Page extends SdkObject { private async _performWaitForNavigationCheck(progress: Progress) { if (process.env.PLAYWRIGHT_SKIP_NAVIGATION_CHECK) return; - const mainFrame = this._frameManager.mainFrame(); + const mainFrame = this.frameManager.mainFrame(); if (!mainFrame || !mainFrame.pendingDocument()) return; const url = mainFrame.pendingDocument()?.request?.url(); @@ -535,11 +530,11 @@ export class Page extends SdkObject { if (options.contrast !== undefined) this._emulatedMedia.contrast = options.contrast; - await this._delegate.updateEmulateMedia(); + await this.delegate.updateEmulateMedia(); } emulatedMedia(): EmulatedMedia { - const contextOptions = this._browserContext._options; + const contextOptions = this.browserContext._options; return { media: this._emulatedMedia.media || 'no-override', colorScheme: this._emulatedMedia.colorScheme !== undefined ? this._emulatedMedia.colorScheme : contextOptions.colorScheme ?? 'light', @@ -551,47 +546,51 @@ export class Page extends SdkObject { async setViewportSize(viewportSize: types.Size) { this._emulatedSize = { viewport: { ...viewportSize }, screen: { ...viewportSize } }; - await this._delegate.updateEmulatedViewportSize(); + await this.delegate.updateEmulatedViewportSize(); } viewportSize(): types.Size | null { return this.emulatedSize()?.viewport || null; } + setEmulatedSize(emulatedSize: EmulatedSize) { + this._emulatedSize = emulatedSize; + } + emulatedSize(): EmulatedSize | null { if (this._emulatedSize) return this._emulatedSize; - const contextOptions = this._browserContext._options; + const contextOptions = this.browserContext._options; return contextOptions.viewport ? { viewport: contextOptions.viewport, screen: contextOptions.screen || contextOptions.viewport } : null; } async bringToFront(): Promise { - await this._delegate.bringToFront(); + await this.delegate.bringToFront(); } async addInitScript(source: string, name?: string) { const initScript = new InitScript(source, false /* internal */, name); this.initScripts.push(initScript); - await this._delegate.addInitScript(initScript); + await this.delegate.addInitScript(initScript); } - async _removeInitScripts() { + private async _removeInitScripts() { this.initScripts = this.initScripts.filter(script => script.internal); - await this._delegate.removeNonInternalInitScripts(); + await this.delegate.removeNonInternalInitScripts(); } needsRequestInterception(): boolean { - return !!this._clientRequestInterceptor || !!this._serverRequestInterceptor || !!this._browserContext._requestInterceptor; + return !!this.clientRequestInterceptor || !!this.serverRequestInterceptor || !!this.browserContext._requestInterceptor; } async setClientRequestInterceptor(handler: network.RouteHandler | undefined): Promise { - this._clientRequestInterceptor = handler; - await this._delegate.updateRequestInterception(); + this.clientRequestInterceptor = handler; + await this.delegate.updateRequestInterception(); } - async _setServerRequestInterceptor(handler: network.RouteHandler | undefined): Promise { - this._serverRequestInterceptor = handler; - await this._delegate.updateRequestInterception(); + async setServerRequestInterceptor(handler: network.RouteHandler | undefined): Promise { + this.serverRequestInterceptor = handler; + await this.delegate.updateRequestInterception(); } async expectScreenshot(metadata: CallMetadata, options: ExpectScreenshotOptions = {}): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[] }> { @@ -601,7 +600,7 @@ export class Page extends SdkObject { } : async (progress: Progress, timeout: number) => { await this.performActionPreChecks(progress); await this.mainFrame().rafrafTimeout(timeout); - return await this._screenshotter.screenshotPage(progress, options || {}); + return await this.screenshotter.screenshotPage(progress, options || {}); }; const comparator = getComparator('image/png'); @@ -629,7 +628,7 @@ export class Page extends SdkObject { intermediateResult = { errorMessage: comparatorResult.errorMessage, diff: comparatorResult.diff, actual, previous }; return false; }; - const callTimeout = this._timeoutSettings.timeout(options); + const callTimeout = this.timeoutSettings.timeout(options); return controller.run(async progress => { let actual: Buffer | undefined; let previous: Buffer | undefined; @@ -699,26 +698,24 @@ export class Page extends SdkObject { async screenshot(metadata: CallMetadata, options: ScreenshotOptions & TimeoutOptions = {}): Promise { const controller = new ProgressController(metadata, this); return controller.run( - progress => this._screenshotter.screenshotPage(progress, options), - this._timeoutSettings.timeout(options)); + progress => this.screenshotter.screenshotPage(progress, options), + this.timeoutSettings.timeout(options)); } async close(metadata: CallMetadata, options: { runBeforeUnload?: boolean, reason?: string } = {}) { if (this._closedState === 'closed') return; if (options.reason) - this._closeReason = options.reason; + this.closeReason = options.reason; const runBeforeUnload = !!options.runBeforeUnload; if (this._closedState !== 'closing') { this._closedState = 'closing'; // This might throw if the browser context containing the page closes // while we are trying to close the page. - await this._delegate.closePage(runBeforeUnload).catch(e => debugLogger.log('error', e)); + await this.delegate.closePage(runBeforeUnload).catch(e => debugLogger.log('error', e)); } if (!runBeforeUnload) await this._closedPromise; - if (this._ownedContext) - await this._ownedContext.close(options); } isClosed(): boolean { @@ -733,12 +730,12 @@ export class Page extends SdkObject { return this._closedState !== 'open' || this._crashed; } - _addWorker(workerId: string, worker: Worker) { + addWorker(workerId: string, worker: Worker) { this._workers.set(workerId, worker); this.emit(Page.Events.Worker, worker); } - _removeWorker(workerId: string) { + removeWorker(workerId: string) { const worker = this._workers.get(workerId); if (!worker) return; @@ -746,7 +743,7 @@ export class Page extends SdkObject { this._workers.delete(workerId); } - _clearWorkers() { + clearWorkers() { for (const [workerId, worker] of this._workers) { worker.didClose(); this._workers.delete(workerId); @@ -755,7 +752,7 @@ export class Page extends SdkObject { async setFileChooserIntercepted(enabled: boolean): Promise { this._interceptFileChooser = enabled; - await this._delegate.updateFileChooserInterception(); + await this.delegate.updateFileChooserInterception(); } fileChooserIntercepted() { @@ -766,20 +763,20 @@ export class Page extends SdkObject { this.emit(Page.Events.InternalFrameNavigatedToNewDocument, frame); const origin = frame.origin(); if (origin) - this._browserContext.addVisitedOrigin(origin); + this.browserContext.addVisitedOrigin(origin); } allInitScripts() { - const bindings = [...this._browserContext._pageBindings.values(), ...this._pageBindings.values()]; - return [kUtilityInitScript, ...bindings.map(binding => binding.initScript), ...this._browserContext.initScripts, ...this.initScripts]; + const bindings = [...this.browserContext._pageBindings.values(), ...this._pageBindings.values()]; + return [kUtilityInitScript, ...bindings.map(binding => binding.initScript), ...this.browserContext.initScripts, ...this.initScripts]; } getBinding(name: string) { - return this._pageBindings.get(name) || this._browserContext._pageBindings.get(name); + return this._pageBindings.get(name) || this.browserContext._pageBindings.get(name); } setScreencastOptions(options: { width: number, height: number, quality: number } | null) { - this._delegate.setScreencastOptions(options).catch(e => debugLogger.log('error', e)); + this.delegate.setScreencastOptions(options).catch(e => debugLogger.log('error', e)); this._frameThrottler.setThrottlingEnabled(!!options); } @@ -817,32 +814,28 @@ export class Worker extends SdkObject { Close: 'close', }; - private _url: string; + readonly url: string; private _executionContextPromise: Promise; private _executionContextCallback: (value: js.ExecutionContext) => void; - _existingExecutionContext: js.ExecutionContext | null = null; + existingExecutionContext: js.ExecutionContext | null = null; readonly openScope = new LongStandingScope(); constructor(parent: SdkObject, url: string) { super(parent, 'worker'); - this._url = url; + this.url = url; this._executionContextCallback = () => {}; this._executionContextPromise = new Promise(x => this._executionContextCallback = x); } - _createExecutionContext(delegate: js.ExecutionContextDelegate) { - this._existingExecutionContext = new js.ExecutionContext(this, delegate, 'worker'); - this._executionContextCallback(this._existingExecutionContext); - return this._existingExecutionContext; - } - - url(): string { - return this._url; + createExecutionContext(delegate: js.ExecutionContextDelegate) { + this.existingExecutionContext = new js.ExecutionContext(this, delegate, 'worker'); + this._executionContextCallback(this.existingExecutionContext); + return this.existingExecutionContext; } didClose() { - if (this._existingExecutionContext) - this._existingExecutionContext.contextDestroyed('Worker was closed'); + if (this.existingExecutionContext) + this.existingExecutionContext.contextDestroyed('Worker was closed'); this.emit(Worker.Events.Close, this); this.openScope.close(new Error('Worker closed')); } @@ -882,12 +875,12 @@ export class PageBinding { let result: any; if (binding.needsHandle) { const handle = await context.evaluateExpressionHandle(`arg => ${js.accessUtilityScript()}.takeBindingHandle(arg)`, { isFunction: true }, { name, seq }).catch(e => null); - result = await binding.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, handle); + result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, handle); } else { if (!Array.isArray(serializedArgs)) throw new Error(`serializedArgs is not an array. This can happen when Array.prototype.toJSON is defined incorrectly`); const args = serializedArgs!.map(a => parseEvaluationResultValue(a)); - result = await binding.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, ...args); + result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, ...args); } context.evaluateExpressionHandle(`arg => ${js.accessUtilityScript()}.deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result }).catch(e => debugLogger.log('error', e)); } catch (error) { diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index 7a239e643a..326dc4fe3b 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -57,13 +57,13 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { } async close() { - await this._page.context().close({ reason: 'Recorder window closed' }); + await this._page.browserContext.close({ reason: 'Recorder window closed' }); } private async _init() { await syncLocalStorageWithSettings(this._page, 'recorder'); - await this._page._setServerRequestInterceptor(route => { + await this._page.setServerRequestInterceptor(route => { if (!route.request().url().startsWith('https://playwright/')) return false; @@ -86,7 +86,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { this._page.once('close', () => { this.emit('close'); - this._page.context().close({ reason: 'Recorder window closed' }).catch(() => {}); + this._page.browserContext.close({ reason: 'Recorder window closed' }).catch(() => {}); }); const mainFrame = this._page.mainFrame(); diff --git a/packages/playwright-core/src/server/screenshotter.ts b/packages/playwright-core/src/server/screenshotter.ts index 77c83b8325..cd76958ad8 100644 --- a/packages/playwright-core/src/server/screenshotter.ts +++ b/packages/playwright-core/src/server/screenshotter.ts @@ -262,7 +262,7 @@ export class Screenshotter { async _preparePageForScreenshot(progress: Progress, frame: Frame, screenshotStyle: string | undefined, hideCaret: boolean, disableAnimations: boolean) { if (disableAnimations) progress.log(' disabled all CSS animations'); - const syncAnimations = this._page._delegate.shouldToggleStyleSheetToSyncAnimations(); + const syncAnimations = this._page.delegate.shouldToggleStyleSheetToSyncAnimations(); await this._page.safeNonStallingEvaluateInAllFrames('(' + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, 'utility'); if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) { progress.log('waiting for fonts to load...'); @@ -308,8 +308,8 @@ export class Screenshotter { progress.throwIfAborted(); // Screenshotting is expensive - avoid extra work. const shouldSetDefaultBackground = options.omitBackground && format === 'png'; if (shouldSetDefaultBackground) { - await this._page._delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0 }); - progress.cleanupWhenAborted(() => this._page._delegate.setBackgroundColor()); + await this._page.delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0 }); + progress.cleanupWhenAborted(() => this._page.delegate.setBackgroundColor()); } progress.throwIfAborted(); // Avoid extra work. @@ -317,14 +317,14 @@ export class Screenshotter { progress.throwIfAborted(); // Avoid extra work. const quality = format === 'jpeg' ? options.quality ?? 80 : undefined; - const buffer = await this._page._delegate.takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, options.scale || 'device'); + const buffer = await this._page.delegate.takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, options.scale || 'device'); progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup. await cleanupHighlight(); progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup. if (shouldSetDefaultBackground) - await this._page._delegate.setBackgroundColor(); + await this._page.delegate.setBackgroundColor(); progress.throwIfAborted(); // Avoid side effects. if ((options as any).__testHookAfterScreenshot) await (options as any).__testHookAfterScreenshot(); diff --git a/packages/playwright-core/src/server/webkit/wkBrowser.ts b/packages/playwright-core/src/server/webkit/wkBrowser.ts index 5af64c8bb4..fba243890e 100644 --- a/packages/playwright-core/src/server/webkit/wkBrowser.ts +++ b/packages/playwright-core/src/server/webkit/wkBrowser.ts @@ -17,7 +17,7 @@ import { assert } from '../../utils'; import { Browser } from '../browser'; -import { BrowserContext, assertBrowserContextIsNotOwned, verifyGeolocation } from '../browserContext'; +import { BrowserContext, verifyGeolocation } from '../browserContext'; import * as network from '../network'; import { WKConnection, WKSession, kPageProxyMessageReceived } from './wkConnection'; import { WKPage } from './wkPage'; @@ -121,7 +121,7 @@ export class WKBrowser extends Browser { // TODO: this is racy, because download might be unrelated any navigation, and we will // abort navigation that is still running. We should be able to fix this by // 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.initializedOrUndefined(); // If it's a new window download, report it on the opener page. if (!originPage) { @@ -245,7 +245,6 @@ export class WKBrowserContext extends BrowserContext { } override async doCreateNewPage(): Promise { - assertBrowserContextIsNotOwned(this); const { pageProxyId } = await this._browser._browserSession.send('Playwright.createPage', { browserContextId: this._browserContextId }); return this._browser._wkPages.get(pageProxyId)!._page; } @@ -274,11 +273,11 @@ export class WKBrowserContext extends BrowserContext { } async doGrantPermissions(origin: string, permissions: string[]) { - await Promise.all(this.pages().map(page => (page._delegate as WKPage)._grantPermissions(origin, permissions))); + await Promise.all(this.pages().map(page => (page.delegate as WKPage)._grantPermissions(origin, permissions))); } async doClearPermissions() { - await Promise.all(this.pages().map(page => (page._delegate as WKPage)._clearPermissions())); + await Promise.all(this.pages().map(page => (page.delegate as WKPage)._clearPermissions())); } async setGeolocation(geolocation?: types.Geolocation): Promise { @@ -291,40 +290,40 @@ export class WKBrowserContext extends BrowserContext { async setExtraHTTPHeaders(headers: types.HeadersArray): Promise { this._options.extraHTTPHeaders = headers; for (const page of this.pages()) - await (page._delegate as WKPage).updateExtraHTTPHeaders(); + await (page.delegate as WKPage).updateExtraHTTPHeaders(); } async setUserAgent(userAgent: string | undefined): Promise { this._options.userAgent = userAgent; for (const page of this.pages()) - await (page._delegate as WKPage).updateUserAgent(); + await (page.delegate as WKPage).updateUserAgent(); } async setOffline(offline: boolean): Promise { this._options.offline = offline; for (const page of this.pages()) - await (page._delegate as WKPage).updateOffline(); + await (page.delegate as WKPage).updateOffline(); } async doSetHTTPCredentials(httpCredentials?: types.Credentials): Promise { this._options.httpCredentials = httpCredentials; for (const page of this.pages()) - await (page._delegate as WKPage).updateHttpCredentials(); + await (page.delegate as WKPage).updateHttpCredentials(); } async doAddInitScript(initScript: InitScript) { for (const page of this.pages()) - await (page._delegate as WKPage)._updateBootstrapScript(); + await (page.delegate as WKPage)._updateBootstrapScript(); } async doRemoveNonInternalInitScripts() { for (const page of this.pages()) - await (page._delegate as WKPage)._updateBootstrapScript(); + await (page.delegate as WKPage)._updateBootstrapScript(); } async doUpdateRequestInterception(): Promise { for (const page of this.pages()) - await (page._delegate as WKPage).updateRequestInterception(); + await (page.delegate as WKPage).updateRequestInterception(); } onClosePersistent() {} diff --git a/packages/playwright-core/src/server/webkit/wkInput.ts b/packages/playwright-core/src/server/webkit/wkInput.ts index 1e4ccf6b0b..dd4b1f9456 100644 --- a/packages/playwright-core/src/server/webkit/wkInput.ts +++ b/packages/playwright-core/src/server/webkit/wkInput.ts @@ -152,7 +152,7 @@ export class RawMouseImpl implements input.RawMouse { } async wheel(x: number, y: number, buttons: Set, modifiers: Set, deltaX: number, deltaY: number): Promise { - if (this._page?._browserContext._options.isMobile) + if (this._page?.browserContext._options.isMobile) throw new Error('Mouse wheel is not supported in mobile WebKit'); await this._session!.send('Page.updateScrollingState'); // Wheel events hit the compositor first, so wait one frame for it to be synced. diff --git a/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts b/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts index 45494d6cc9..e4cd9a78f0 100644 --- a/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts +++ b/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts @@ -57,7 +57,7 @@ export class WKInterceptableRequest { this._wallTime = event.walltime * 1000; if (event.request.postData) postDataBuffer = Buffer.from(event.request.postData, 'base64'); - this.request = new network.Request(frame._page._browserContext, frame, null, redirectedFrom?.request || null, documentId, event.request.url, + this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom?.request || null, documentId, event.request.url, resourceType, event.request.method, postDataBuffer, headersObjectToArray(event.request.headers)); } diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 7737c5163f..2482f369a4 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -109,12 +109,12 @@ export class WKPage implements PageDelegate { const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures); opener._nextWindowOpenPopupFeatures = undefined; if (viewportSize) - this._page._emulatedSize = { viewport: viewportSize, screen: viewportSize }; + this._page.setEmulatedSize({ viewport: viewportSize, screen: viewportSize }); } } private async _initializePageProxySession() { - if (this._page._browserContext.isSettingStorageState()) + if (this._page.browserContext.isSettingStorageState()) return; const promises: Promise[] = [ this._pageProxySession.send('Dialog.enable'), @@ -187,7 +187,7 @@ export class WKPage implements PageDelegate { promises.push(session.send('Network.setResourceCachingDisabled', { disabled: true })); promises.push(session.send('Network.addInterception', { url: '.*', stage: 'request', isRegex: true })); } - if (this._page._browserContext.isSettingStorageState()) { + if (this._page.browserContext.isSettingStorageState()) { await Promise.all(promises); return; } @@ -290,7 +290,7 @@ export class WKPage implements PageDelegate { let errorText = event.error; if (errorText.includes('cancelled')) errorText += '; maybe frame was detached?'; - this._page._frameManager.frameAbortedNavigation(this._page.mainFrame()._id, errorText, event.loaderId); + this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, errorText, event.loaderId); } handleWindowOpen(event: Protocol.Playwright.windowOpenPayload) { @@ -368,8 +368,8 @@ export class WKPage implements PageDelegate { eventsHelper.addEventListener(this._session, 'Page.willCheckNavigationPolicy', event => this._onWillCheckNavigationPolicy(event.frameId)), eventsHelper.addEventListener(this._session, 'Page.didCheckNavigationPolicy', event => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)), eventsHelper.addEventListener(this._session, 'Page.frameScheduledNavigation', event => this._onFrameScheduledNavigation(event.frameId, event.delay, event.targetIsCurrentFrame)), - eventsHelper.addEventListener(this._session, 'Page.loadEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'load')), - eventsHelper.addEventListener(this._session, 'Page.domContentEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded')), + eventsHelper.addEventListener(this._session, 'Page.loadEventFired', event => this._page.frameManager.frameLifecycleEvent(event.frameId, 'load')), + eventsHelper.addEventListener(this._session, 'Page.domContentEventFired', event => this._page.frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded')), eventsHelper.addEventListener(this._session, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)), eventsHelper.addEventListener(this._session, 'Runtime.bindingCalled', event => this._onBindingCalled(event.contextId, event.argument)), eventsHelper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)), @@ -381,13 +381,13 @@ export class WKPage implements PageDelegate { eventsHelper.addEventListener(this._session, 'Network.responseReceived', e => this._onResponseReceived(this._session, e)), eventsHelper.addEventListener(this._session, 'Network.loadingFinished', e => this._onLoadingFinished(e)), eventsHelper.addEventListener(this._session, 'Network.loadingFailed', e => this._onLoadingFailed(this._session, e)), - eventsHelper.addEventListener(this._session, 'Network.webSocketCreated', e => this._page._frameManager.onWebSocketCreated(e.requestId, e.url)), - eventsHelper.addEventListener(this._session, 'Network.webSocketWillSendHandshakeRequest', e => this._page._frameManager.onWebSocketRequest(e.requestId)), - eventsHelper.addEventListener(this._session, 'Network.webSocketHandshakeResponseReceived', e => this._page._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)), - eventsHelper.addEventListener(this._session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)), - eventsHelper.addEventListener(this._session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)), - eventsHelper.addEventListener(this._session, 'Network.webSocketClosed', e => this._page._frameManager.webSocketClosed(e.requestId)), - eventsHelper.addEventListener(this._session, 'Network.webSocketFrameError', e => this._page._frameManager.webSocketError(e.requestId, e.errorMessage)), + eventsHelper.addEventListener(this._session, 'Network.webSocketCreated', e => this._page.frameManager.onWebSocketCreated(e.requestId, e.url)), + eventsHelper.addEventListener(this._session, 'Network.webSocketWillSendHandshakeRequest', e => this._page.frameManager.onWebSocketRequest(e.requestId)), + eventsHelper.addEventListener(this._session, 'Network.webSocketHandshakeResponseReceived', e => this._page.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)), + eventsHelper.addEventListener(this._session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)), + eventsHelper.addEventListener(this._session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)), + eventsHelper.addEventListener(this._session, 'Network.webSocketClosed', e => this._page.frameManager.webSocketClosed(e.requestId)), + eventsHelper.addEventListener(this._session, 'Network.webSocketFrameError', e => this._page.frameManager.webSocketError(e.requestId, e.errorMessage)), ]; } private async _updateState( @@ -420,7 +420,7 @@ export class WKPage implements PageDelegate { // one. if (this._provisionalPage) return; - this._page._frameManager.frameRequestedNavigation(frameId); + this._page.frameManager.frameRequestedNavigation(frameId); } private _onDidCheckNavigationPolicy(frameId: string, cancel?: boolean) { @@ -430,19 +430,19 @@ export class WKPage implements PageDelegate { // the provisional page. Bail out as we are tracking it. if (this._provisionalPage) return; - this._page._frameManager.frameAbortedNavigation(frameId, 'Navigation canceled by policy check'); + this._page.frameManager.frameAbortedNavigation(frameId, 'Navigation canceled by policy check'); } private _onFrameScheduledNavigation(frameId: string, delay: number, targetIsCurrentFrame: boolean) { if (targetIsCurrentFrame) - this._page._frameManager.frameRequestedNavigation(frameId); + this._page.frameManager.frameRequestedNavigation(frameId); } private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) { this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null); this._onFrameNavigated(frameTree.frame, true); - this._page._frameManager.frameLifecycleEvent(frameTree.frame.id, 'domcontentloaded'); - this._page._frameManager.frameLifecycleEvent(frameTree.frame.id, 'load'); + this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, 'domcontentloaded'); + this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, 'load'); if (!frameTree.childFrames) return; @@ -451,26 +451,26 @@ export class WKPage implements PageDelegate { } _onFrameAttached(frameId: string, parentFrameId: string | null): frames.Frame { - return this._page._frameManager.frameAttached(frameId, parentFrameId); + return this._page.frameManager.frameAttached(frameId, parentFrameId); } private _onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) { - const frame = this._page._frameManager.frame(framePayload.id); + const frame = this._page.frameManager.frame(framePayload.id); assert(frame); this._removeContextsForFrame(frame, true); if (!framePayload.parentId) this._workers.clear(); - this._page._frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || '', framePayload.loaderId, initial); + this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || '', framePayload.loaderId, initial); if (!initial) this._firstNonInitialNavigationCommittedFulfill(); } private _onFrameNavigatedWithinDocument(frameId: string, url: string) { - this._page._frameManager.frameCommittedSameDocumentNavigation(frameId, url); + this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url); } private _onFrameDetached(frameId: string) { - this._page._frameManager.frameDetached(frameId); + this._page.frameManager.frameDetached(frameId); } private _removeContextsForFrame(frame: frames.Frame, notifyFrame: boolean) { @@ -486,7 +486,7 @@ export class WKPage implements PageDelegate { private _onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) { if (this._contextIdToContext.has(contextPayload.id)) return; - const frame = this._page._frameManager.frame(contextPayload.frameId); + const frame = this._page.frameManager.frame(contextPayload.frameId); if (!frame) return; const delegate = new WKExecutionContext(this._session, contextPayload.id); @@ -506,7 +506,7 @@ export class WKPage implements PageDelegate { if (!(pageOrError instanceof Error)) { const context = this._contextIdToContext.get(contextId); if (context) - await this._page._onBindingCalled(argument, context); + await this._page.onBindingCalled(argument, context); } } @@ -587,7 +587,7 @@ export class WKPage implements PageDelegate { location } = this._lastConsoleMessage; for (let i = count; i < event.count; ++i) - this._page._addConsoleMessage(derivedType, handles, location, handles.length ? undefined : text); + this._page.addConsoleMessage(derivedType, handles, location, handles.length ? undefined : text); this._lastConsoleMessage.count = event.count; } } @@ -600,7 +600,7 @@ export class WKPage implements PageDelegate { async (accept: boolean, promptText?: string) => { // TODO: this should actually be a RDP event that notifies about a cancelled navigation attempt. if (event.type === 'beforeunload' && !accept) - this._page._frameManager.frameAbortedNavigation(this._page.mainFrame()._id, 'navigation cancelled by beforeunload dialog'); + this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, 'navigation cancelled by beforeunload dialog'); await this._pageProxySession.send('Dialog.handleJavaScriptDialog', { accept, promptText }); }, event.defaultPrompt)); @@ -609,7 +609,7 @@ export class WKPage implements PageDelegate { private async _onFileChooserOpened(event: {frameId: Protocol.Network.FrameId, element: Protocol.Runtime.RemoteObject}) { let handle; try { - const context = await this._page._frameManager.frame(event.frameId)!._mainContext(); + const context = await this._page.frameManager.frame(event.frameId)!._mainContext(); handle = createHandle(context, event.element).asElement()!; } catch (e) { // During async processing, frame/context may go away. We should not throw. @@ -774,7 +774,7 @@ export class WKPage implements PageDelegate { private _calculateBootstrapScript(): string { const scripts: string[] = []; - if (!this._page.context()._options.isMobile) { + if (!this._page.browserContext._options.isMobile) { scripts.push('delete window.orientation'); scripts.push('delete window.ondevicemotion'); scripts.push('delete window.ondeviceorientation'); @@ -802,7 +802,7 @@ export class WKPage implements PageDelegate { } private _toolbarHeight(): number { - if (this._page._browserContext._browser?.options.headful) + if (this._page.browserContext._browser?.options.headful) return hostPlatform === 'mac10.15' ? 55 : 59; return 0; } @@ -831,8 +831,8 @@ export class WKPage implements PageDelegate { // (see https://github.com/microsoft/playwright/issues/16727). if (process.platform === 'darwin') return; - if (!omitDeviceScaleFactor && this._page._browserContext._options.deviceScaleFactor) - side = Math.ceil(side * this._page._browserContext._options.deviceScaleFactor); + if (!omitDeviceScaleFactor && this._page.browserContext._options.deviceScaleFactor) + side = Math.ceil(side * this._page.browserContext._options.deviceScaleFactor); if (side > 32767) throw new Error('Cannot take screenshot larger than 32767 pixels on any dimension'); } @@ -856,7 +856,7 @@ export class WKPage implements PageDelegate { }); if (!nodeInfo.contentFrameId) return null; - return this._page._frameManager.frame(nodeInfo.contentFrameId); + return this._page.frameManager.frame(nodeInfo.contentFrameId); } async getOwnerFrame(handle: dom.ElementHandle): Promise { @@ -1035,7 +1035,7 @@ export class WKPage implements PageDelegate { redirectedFrom = request; } } - const frame = redirectedFrom ? redirectedFrom.request.frame() : this._page._frameManager.frame(event.frameId); + const frame = redirectedFrom ? redirectedFrom.request.frame() : this._page.frameManager.frame(event.frameId); // sometimes we get stray network events for detached frames // TODO(einbinder) why? if (!frame) @@ -1053,7 +1053,7 @@ export class WKPage implements PageDelegate { request.request.setRawRequestHeaders(null); } this._requestIdToRequest.set(event.requestId, request); - this._page._frameManager.requestStarted(request.request, route); + this._page.frameManager.requestStarted(request.request, route); } private _handleRequestRedirect(request: WKInterceptableRequest, requestId: string, responsePayload: Protocol.Network.Response, timestamp: number) { @@ -1064,8 +1064,8 @@ export class WKPage implements PageDelegate { response.setEncodedBodySize(null); response._requestFinished(responsePayload.timing ? helper.secondsToRoundishMillis(timestamp - request._timestamp) : -1); this._requestIdToRequest.delete(requestId); - this._page._frameManager.requestReceivedResponse(response); - this._page._frameManager.reportRequestFinished(request.request, response); + this._page.frameManager.requestReceivedResponse(response); + this._page.frameManager.reportRequestFinished(request.request, response); } _onRequestIntercepted(session: WKSession, event: Protocol.Network.requestInterceptedPayload) { @@ -1095,7 +1095,7 @@ export class WKPage implements PageDelegate { this._requestIdToResponseReceivedPayloadEvent.set(event.requestId, event); const response = request.createResponse(event.response); - this._page._frameManager.requestReceivedResponse(response); + this._page.frameManager.requestReceivedResponse(response); if (response.status() === 204 && request.request.isNavigationRequest()) { this._onLoadingFailed(session, { @@ -1138,7 +1138,7 @@ export class WKPage implements PageDelegate { this._requestIdToResponseReceivedPayloadEvent.delete(event.requestId); this._requestIdToRequest.delete(event.requestId); - this._page._frameManager.reportRequestFinished(request.request, response); + this._page.frameManager.reportRequestFinished(request.request, response); } _onLoadingFailed(session: WKSession, event: Protocol.Network.loadingFailedPayload) { @@ -1169,7 +1169,7 @@ export class WKPage implements PageDelegate { } this._requestIdToRequest.delete(event.requestId); request.request._setFailureText(event.errorText); - this._page._frameManager.requestFailed(request.request, event.errorText.includes('cancelled')); + this._page.frameManager.requestFailed(request.request, event.errorText.includes('cancelled')); } async _grantPermissions(origin: string, permissions: string[]) { diff --git a/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts b/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts index 63d4cc6f0a..13c5ecef16 100644 --- a/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts +++ b/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts @@ -49,7 +49,7 @@ export class WKProvisionalPage { return (payload: any) => { // Pretend that the events happened in the same process. if (payload.frameId) - payload.frameId = this._wkPage._page._frameManager.mainFrame()._id; + payload.frameId = this._wkPage._page.frameManager.mainFrame()._id; handler(payload); }; }; diff --git a/packages/playwright-core/src/server/webkit/wkWorkers.ts b/packages/playwright-core/src/server/webkit/wkWorkers.ts index dc7c633433..689ca95ccc 100644 --- a/packages/playwright-core/src/server/webkit/wkWorkers.ts +++ b/packages/playwright-core/src/server/webkit/wkWorkers.ts @@ -48,8 +48,8 @@ export class WKWorkers { }); }); this._workerSessions.set(event.workerId, workerSession); - worker._createExecutionContext(new WKExecutionContext(workerSession, undefined)); - this._page._addWorker(event.workerId, worker); + worker.createExecutionContext(new WKExecutionContext(workerSession, undefined)); + this._page.addWorker(event.workerId, worker); workerSession.on('Console.messageAdded', event => this._onConsoleMessage(worker, event)); Promise.all([ workerSession.send('Runtime.enable'), @@ -57,7 +57,7 @@ export class WKWorkers { session.send('Worker.initialized', { workerId: event.workerId }) ]).catch(e => { // Worker can go as we are initializing it. - this._page._removeWorker(event.workerId); + this._page.removeWorker(event.workerId); }); }), eventsHelper.addEventListener(session, 'Worker.dispatchMessageFromWorker', (event: Protocol.Worker.dispatchMessageFromWorkerPayload) => { @@ -72,13 +72,13 @@ export class WKWorkers { return; workerSession.dispose(); this._workerSessions.delete(event.workerId); - this._page._removeWorker(event.workerId); + this._page.removeWorker(event.workerId); }) ]; } clear() { - this._page._clearWorkers(); + this._page.clearWorkers(); this._workerSessions.clear(); } @@ -95,13 +95,13 @@ export class WKWorkers { derivedType = 'timeEnd'; const handles = (parameters || []).map(p => { - return createHandle(worker._existingExecutionContext!, p); + return createHandle(worker.existingExecutionContext!, p); }); const location: types.ConsoleMessageLocation = { url: url || '', lineNumber: (lineNumber || 1) - 1, columnNumber: (columnNumber || 1) - 1 }; - this._page._addConsoleMessage(derivedType, handles, location, handles.length ? undefined : text); + this._page.addConsoleMessage(derivedType, handles, location, handles.length ? undefined : text); } } diff --git a/tests/config/traceViewerFixtures.ts b/tests/config/traceViewerFixtures.ts index e3947ff092..eb025c97dc 100644 --- a/tests/config/traceViewerFixtures.ts +++ b/tests/config/traceViewerFixtures.ts @@ -142,7 +142,7 @@ export const traceViewerFixtures: Fixtures { const pageImpl = await runTraceViewerApp(traces, browserName, { headless, host, port }); - const contextImpl = pageImpl.context(); + const contextImpl = pageImpl.browserContext; const browser = await playwright.chromium.connectOverCDP(contextImpl._browser.options.wsEndpoint); browsers.push(browser); contextImpls.push(contextImpl); diff --git a/tests/library/page-event-crash.spec.ts b/tests/library/page-event-crash.spec.ts index d185fa5a19..1a2cfeb8b0 100644 --- a/tests/library/page-event-crash.spec.ts +++ b/tests/library/page-event-crash.spec.ts @@ -23,9 +23,9 @@ const test = testBase.extend<{ crash: () => void }, { dummy: string }>({ if (browserName === 'chromium') page.goto('chrome://crash').catch(e => {}); else if (browserName === 'webkit') - toImpl(page)._delegate._session.send('Page.crash', {}).catch(e => {}); + toImpl(page).delegate._session.send('Page.crash', {}).catch(e => {}); else if (browserName === 'firefox') - toImpl(page)._delegate._session.send('Page.crash', {}).catch(e => {}); + toImpl(page).delegate._session.send('Page.crash', {}).catch(e => {}); }); }, // Force a separate worker to avoid messing up with other tests. diff --git a/tests/page/frame-evaluate.spec.ts b/tests/page/frame-evaluate.spec.ts index 6908f03e86..5a1c3fa874 100644 --- a/tests/page/frame-evaluate.spec.ts +++ b/tests/page/frame-evaluate.spec.ts @@ -37,9 +37,9 @@ it('should have correct execution contexts @smoke', async ({ page, server }) => function expectContexts(pageImpl, count, browserName) { if (browserName === 'chromium') - expect(pageImpl._delegate._mainFrameSession._contextIdToContext.size).toBe(count); + expect(pageImpl.delegate._mainFrameSession._contextIdToContext.size).toBe(count); else - expect(pageImpl._delegate._contextIdToContext.size).toBe(count); + expect(pageImpl.delegate._contextIdToContext.size).toBe(count); } it('should dispose context on navigation', async ({ page, server, toImpl, browserName, isElectron }) => {