UNPKG

brave-real-playwright-core

Version:

Brave-optimized Playwright Core (v1.55.0) with comprehensive stealth patches and error stack sanitization

1,079 lines (1,078 loc) 47 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var crPage_exports = {}; __export(crPage_exports, { CRPage: () => CRPage }); module.exports = __toCommonJS(crPage_exports); var import_path = __toESM(require("path")); var import_assert = require("../../utils/isomorphic/assert"); var import_crypto = require("../utils/crypto"); var import_eventsHelper = require("../utils/eventsHelper"); var import_stackTrace = require("../../utils/isomorphic/stackTrace"); var dialog = __toESM(require("../dialog")); var dom = __toESM(require("../dom")); var frames = __toESM(require("../frames")); var import_helper = require("../helper"); var network = __toESM(require("../network")); var import_page = require("../page"); var import_registry = require("../registry"); var import_crAccessibility = require("./crAccessibility"); var import_crBrowser = require("./crBrowser"); var import_crCoverage = require("./crCoverage"); var import_crDragDrop = require("./crDragDrop"); var import_crExecutionContext = require("./crExecutionContext"); var import_crInput = require("./crInput"); var import_crNetworkManager = require("./crNetworkManager"); var import_crPdf = require("./crPdf"); var import_crProtocolHelper = require("./crProtocolHelper"); var import_defaultFontFamilies = require("./defaultFontFamilies"); var import_videoRecorder = require("./videoRecorder"); var import_browserContext = require("../browserContext"); var import_errors = require("../errors"); var import_protocolError = require("../protocolError"); class CRPage { constructor(client, targetId, browserContext, opener, bits) { this._sessions = /* @__PURE__ */ new Map(); // Holds window features for the next popup being opened via window.open, // until the popup target arrives. This could be racy if two oopifs // simultaneously call window.open with window features: the order // of their Page.windowOpen events is not guaranteed to match the order // of new popup targets. this._nextWindowOpenPopupFeatures = []; this._targetId = targetId; this._opener = opener; this._isBackgroundPage = bits.isBackgroundPage; const dragManager = new import_crDragDrop.DragManager(this); this.rawKeyboard = new import_crInput.RawKeyboardImpl(client, browserContext._browser._platform() === "mac", dragManager); this.rawMouse = new import_crInput.RawMouseImpl(this, client, dragManager); this.rawTouchscreen = new import_crInput.RawTouchscreenImpl(client); this._pdf = new import_crPdf.CRPDF(client); this._coverage = new import_crCoverage.CRCoverage(client); this._browserContext = browserContext; this._page = new import_page.Page(this, browserContext); this.utilityWorldName = `__playwright_utility_world_${this._page.guid}`; this._networkManager = new import_crNetworkManager.CRNetworkManager(this._page, null); this.updateOffline(); this.updateExtraHTTPHeaders(); this.updateHttpCredentials(); this.updateRequestInterception(); this._mainFrameSession = new FrameSession(this, client, targetId, null); this._sessions.set(targetId, this._mainFrameSession); if (opener && !browserContext._options.noDefaultViewport) { const features = opener._nextWindowOpenPopupFeatures.shift() || []; const viewportSize = import_helper.helper.getViewportSizeFromWindowFeatures(features); if (viewportSize) this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize }); } const createdEvent = this._isBackgroundPage ? import_crBrowser.CRBrowserContext.CREvents.BackgroundPage : import_browserContext.BrowserContext.Events.Page; this._mainFrameSession._initialize(bits.hasUIWindow).then( () => this._page.reportAsNew(this._opener?._page, void 0, createdEvent), (error) => this._page.reportAsNew(this._opener?._page, error, createdEvent) ); } static mainFrameSession(page) { const crPage = page.delegate; return crPage._mainFrameSession; } async _forAllFrameSessions(cb) { const frameSessions = Array.from(this._sessions.values()); await Promise.all(frameSessions.map((frameSession) => { if (frameSession._isMainFrame()) return cb(frameSession); return cb(frameSession).catch((e) => { if ((0, import_protocolError.isSessionClosedError)(e)) return; throw e; }); })); } _sessionForFrame(frame) { while (!this._sessions.has(frame._id)) { const parent = frame.parentFrame(); if (!parent) throw new Error(`Frame has been detached.`); frame = parent; } return this._sessions.get(frame._id); } _sessionForHandle(handle) { const frame = handle._context.frame; return this._sessionForFrame(frame); } willBeginDownload() { this._mainFrameSession._willBeginDownload(); } didClose() { for (const session of this._sessions.values()) session.dispose(); this._page._didClose(); } async navigateFrame(frame, url, referrer) { return this._sessionForFrame(frame)._navigate(frame, url, referrer); } async updateExtraHTTPHeaders() { const headers = network.mergeHeaders([ this._browserContext._options.extraHTTPHeaders, this._page.extraHTTPHeaders() ]); await this._networkManager.setExtraHTTPHeaders(headers); } async updateGeolocation() { await this._forAllFrameSessions((frame) => frame._updateGeolocation(false)); } async updateOffline() { await this._networkManager.setOffline(!!this._browserContext._options.offline); } async updateHttpCredentials() { await this._networkManager.authenticate(this._browserContext._options.httpCredentials || null); } async updateEmulatedViewportSize(preserveWindowBoundaries) { await this._mainFrameSession._updateViewport(preserveWindowBoundaries); } async bringToFront() { await this._mainFrameSession._client.send("Page.bringToFront"); } async updateEmulateMedia() { await this._forAllFrameSessions((frame) => frame._updateEmulateMedia()); } async updateUserAgent() { await this._forAllFrameSessions((frame) => frame._updateUserAgent()); } async updateRequestInterception() { await this._networkManager.setRequestInterception(this._page.needsRequestInterception()); } async updateFileChooserInterception() { await this._forAllFrameSessions((frame) => frame._updateFileChooserInterception(false)); } async reload() { await this._mainFrameSession._client.send("Page.reload"); } async _go(delta) { const history = await this._mainFrameSession._client.send("Page.getNavigationHistory"); const entry = history.entries[history.currentIndex + delta]; if (!entry) return false; await this._mainFrameSession._client.send("Page.navigateToHistoryEntry", { entryId: entry.id }); return true; } goBack() { return this._go(-1); } goForward() { return this._go(1); } async requestGC() { await this._mainFrameSession._client.send("HeapProfiler.collectGarbage"); } async addInitScript(initScript, world = "main") { await this._forAllFrameSessions((frame) => frame._evaluateOnNewDocument(initScript, world)); } async exposePlaywrightBinding() { await this._forAllFrameSessions((frame) => frame.exposePlaywrightBinding()); } async removeInitScripts(initScripts) { await this._forAllFrameSessions((frame) => frame._removeEvaluatesOnNewDocument(initScripts)); } async closePage(runBeforeUnload) { if (runBeforeUnload) await this._mainFrameSession._client.send("Page.close"); else await this._browserContext._browser._closePage(this); } async setBackgroundColor(color) { await this._mainFrameSession._client.send("Emulation.setDefaultBackgroundColorOverride", { color }); } async takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, scale) { const { visualViewport } = await progress.race(this._mainFrameSession._client.send("Page.getLayoutMetrics")); if (!documentRect) { documentRect = { x: visualViewport.pageX + viewportRect.x, y: visualViewport.pageY + viewportRect.y, ...import_helper.helper.enclosingIntSize({ width: viewportRect.width / visualViewport.scale, height: viewportRect.height / visualViewport.scale }) }; } const clip = { ...documentRect, scale: viewportRect ? visualViewport.scale : 1 }; if (scale === "css") { const deviceScaleFactor = this._browserContext._options.deviceScaleFactor || 1; clip.scale /= deviceScaleFactor; } const result = await progress.race(this._mainFrameSession._client.send("Page.captureScreenshot", { format, quality, clip, captureBeyondViewport: !fitsViewport })); return Buffer.from(result.data, "base64"); } async getContentFrame(handle) { return this._sessionForHandle(handle)._getContentFrame(handle); } async getOwnerFrame(handle) { return this._sessionForHandle(handle)._getOwnerFrame(handle); } async getBoundingBox(handle) { return this._sessionForHandle(handle)._getBoundingBox(handle); } async scrollRectIntoViewIfNeeded(handle, rect) { return this._sessionForHandle(handle)._scrollRectIntoViewIfNeeded(handle, rect); } async setScreencastOptions(options) { if (options) { await this._mainFrameSession._startScreencast(this, { format: "jpeg", quality: options.quality, maxWidth: options.width, maxHeight: options.height }); } else { await this._mainFrameSession._stopScreencast(this); } } rafCountForStablePosition() { return 1; } async getContentQuads(handle) { return this._sessionForHandle(handle)._getContentQuads(handle); } async setInputFilePaths(handle, files) { const frame = await handle.ownerFrame(); if (!frame) throw new Error("Cannot set input files to detached input element"); const parentSession = this._sessionForFrame(frame); await parentSession._client.send("DOM.setFileInputFiles", { objectId: handle._objectId, files }); } async adoptElementHandle(handle, to) { return this._sessionForHandle(handle)._adoptElementHandle(handle, to); } async getAccessibilityTree(needle) { return (0, import_crAccessibility.getAccessibilityTree)(this._mainFrameSession._client, needle); } async inputActionEpilogue() { await this._mainFrameSession._client.send("Page.enable").catch((e) => { }); } async resetForReuse(progress) { await this.rawMouse.move(progress, -1, -1, "none", /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), true); } async pdf(options) { return this._pdf.generate(options); } coverage() { return this._coverage; } async getFrameElement(frame) { let parent = frame.parentFrame(); if (!parent) throw new Error("Frame has been detached."); const parentSession = this._sessionForFrame(parent); const { backendNodeId } = await parentSession._client.send("DOM.getFrameOwner", { frameId: frame._id }).catch((e) => { if (e instanceof Error && e.message.includes("Frame with the given id was not found.")) (0, import_stackTrace.rewriteErrorMessage)(e, "Frame has been detached."); throw e; }); parent = frame.parentFrame(); if (!parent) throw new Error("Frame has been detached."); return parentSession._adoptBackendNodeId(backendNodeId, await parent._mainContext()); } shouldToggleStyleSheetToSyncAnimations() { return false; } } class FrameSession { constructor(crPage, client, targetId, parentSession) { this._childSessions = /* @__PURE__ */ new Set(); this._contextIdToContext = /* @__PURE__ */ new Map(); this._eventListeners = []; this._firstNonInitialNavigationCommittedFulfill = () => { }; this._firstNonInitialNavigationCommittedReject = (e) => { }; // Marks the oopif session that remote -> local transition has happened in the parent. // See Target.detachedFromTarget handler for details. this._swappedIn = false; this._videoRecorder = null; this._screencastId = null; this._screencastClients = /* @__PURE__ */ new Set(); this._workerSessions = /* @__PURE__ */ new Map(); this._initScriptIds = /* @__PURE__ */ new Map(); this._client = client; this._crPage = crPage; this._page = crPage._page; this._targetId = targetId; this._parentSession = parentSession; if (parentSession) parentSession._childSessions.add(this); this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => { this._firstNonInitialNavigationCommittedFulfill = f; this._firstNonInitialNavigationCommittedReject = r; }); this._firstNonInitialNavigationCommittedPromise.catch(() => { }); } _isMainFrame() { return this._targetId === this._crPage._targetId; } _addRendererListeners() { this._eventListeners.push(...[ import_eventsHelper.eventsHelper.addEventListener(this._client, "Log.entryAdded", (event) => this._onLogEntryAdded(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.fileChooserOpened", (event) => this._onFileChooserOpened(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.frameAttached", (event) => this._onFrameAttached(event.frameId, event.parentFrameId)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.frameDetached", (event) => this._onFrameDetached(event.frameId, event.reason)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.frameNavigated", (event) => this._onFrameNavigated(event.frame, false)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.frameRequestedNavigation", (event) => this._onFrameRequestedNavigation(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.javascriptDialogOpening", (event) => this._onDialog(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.navigatedWithinDocument", (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.bindingCalled", (event) => this._onBindingCalled(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.consoleAPICalled", (event) => this._onConsoleAPI(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.exceptionThrown", (exception) => this._handleException(exception.exceptionDetails)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextDestroyed", (event) => this._onExecutionContextDestroyed(event.executionContextId)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", (event) => this._onExecutionContextsCleared()), import_eventsHelper.eventsHelper.addEventListener(this._client, "Target.attachedToTarget", (event) => this._onAttachedToTarget(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Target.detachedFromTarget", (event) => this._onDetachedFromTarget(event)) ]); } _addBrowserListeners() { this._eventListeners.push(...[ import_eventsHelper.eventsHelper.addEventListener(this._client, "Inspector.targetCrashed", (event) => this._onTargetCrashed()), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.screencastFrame", (event) => this._onScreencastFrame(event)), import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.windowOpen", (event) => this._onWindowOpen(event)) ]); } async _initialize(hasUIWindow) { if (!this._page.isStorageStatePage && hasUIWindow && !this._crPage._browserContext._browser.isClank() && !this._crPage._browserContext._options.noDefaultViewport) { const { windowId } = await this._client.send("Browser.getWindowForTarget"); this._windowId = windowId; } let screencastOptions; if (!this._page.isStorageStatePage && this._isMainFrame() && this._crPage._browserContext._options.recordVideo && hasUIWindow) { const screencastId = (0, import_crypto.createGuid)(); const outputFile = import_path.default.join(this._crPage._browserContext._options.recordVideo.dir, screencastId + ".webm"); screencastOptions = { // validateBrowserContextOptions ensures correct video size. ...this._crPage._browserContext._options.recordVideo.size, outputFile }; await this._crPage._browserContext._ensureVideosPath(); await this._createVideoRecorder(screencastId, screencastOptions); this._crPage._page.waitForInitializedOrError().then((p) => { if (p instanceof Error) this._stopVideoRecording().catch(() => { }); }); } let lifecycleEventsEnabled; if (!this._isMainFrame()) this._addRendererListeners(); this._addBrowserListeners(); const promises = [ this._client.send("Page.enable"), this._client.send("Page.getFrameTree").then(({ frameTree }) => { if (this._isMainFrame()) { this._handleFrameTree(frameTree); this._addRendererListeners(); } const localFrames = this._isMainFrame() ? this._page.frames() : [this._page.frameManager.frame(this._targetId)]; for (const frame of localFrames) { this._client._sendMayFail("Page.createIsolatedWorld", { frameId: frame._id, grantUniveralAccess: true, worldName: this._crPage.utilityWorldName }); } const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ":"; if (isInitialEmptyPage) { lifecycleEventsEnabled.catch((e) => { }).then(() => { this._eventListeners.push(import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.lifecycleEvent", (event) => this._onLifecycleEvent(event))); }); } else { this._firstNonInitialNavigationCommittedFulfill(); this._eventListeners.push(import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.lifecycleEvent", (event) => this._onLifecycleEvent(event))); } }), this._client.send("Log.enable", {}), lifecycleEventsEnabled = this._client.send("Page.setLifecycleEventsEnabled", { enabled: true }), this._client.send("Runtime.enable", {}), this._client.send("Page.addScriptToEvaluateOnNewDocument", { source: "", worldName: this._crPage.utilityWorldName }), this._crPage._networkManager.addSession(this._client, void 0, this._isMainFrame()), this._client.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true, filter: [ { type: "iframe" }, { type: "worker" }, { type: "service_worker", exclude: !process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS } ] }) ]; if (!this._page.isStorageStatePage) { if (this._crPage._browserContext.needsPlaywrightBinding()) promises.push(this.exposePlaywrightBinding()); if (this._isMainFrame()) promises.push(this._client.send("Emulation.setFocusEmulationEnabled", { enabled: true })); const options = this._crPage._browserContext._options; if (options.bypassCSP) promises.push(this._client.send("Page.setBypassCSP", { enabled: true })); if (options.ignoreHTTPSErrors || options.internalIgnoreHTTPSErrors) promises.push(this._client.send("Security.setIgnoreCertificateErrors", { ignore: true })); if (this._isMainFrame()) promises.push(this._updateViewport()); if (options.hasTouch) promises.push(this._client.send("Emulation.setTouchEmulationEnabled", { enabled: true })); if (options.javaScriptEnabled === false) promises.push(this._client.send("Emulation.setScriptExecutionDisabled", { value: true })); if (options.userAgent || options.locale) promises.push(this._updateUserAgent()); if (options.locale) promises.push(emulateLocale(this._client, options.locale)); if (options.timezoneId) promises.push(emulateTimezone(this._client, options.timezoneId)); if (!this._crPage._browserContext._browser.options.headful) promises.push(this._setDefaultFontFamilies(this._client)); promises.push(this._updateGeolocation(true)); promises.push(this._updateEmulateMedia()); promises.push(this._updateFileChooserInterception(true)); for (const initScript of this._crPage._page.allInitScripts()) promises.push(this._evaluateOnNewDocument( initScript, "main", true /* runImmediately */ )); if (screencastOptions) promises.push(this._startVideoRecording(screencastOptions)); } promises.push(this._client.send("Runtime.runIfWaitingForDebugger")); promises.push(this._firstNonInitialNavigationCommittedPromise); await Promise.all(promises); } dispose() { this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError()); for (const childSession of this._childSessions) childSession.dispose(); if (this._parentSession) this._parentSession._childSessions.delete(this); import_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners); this._crPage._networkManager.removeSession(this._client); this._crPage._sessions.delete(this._targetId); this._client.dispose(); } async _navigate(frame, url, referrer) { const response = await this._client.send("Page.navigate", { url, referrer, frameId: frame._id, referrerPolicy: "unsafeUrl" }); if (response.isDownload) throw new frames.NavigationAbortedError(response.loaderId, "Download is starting"); if (response.errorText) throw new frames.NavigationAbortedError(response.loaderId, `${response.errorText} at ${url}`); return { newDocumentId: response.loaderId }; } _onLifecycleEvent(event) { if (this._eventBelongsToStaleFrame(event.frameId)) return; if (event.name === "load") this._page.frameManager.frameLifecycleEvent(event.frameId, "load"); else if (event.name === "DOMContentLoaded") this._page.frameManager.frameLifecycleEvent(event.frameId, "domcontentloaded"); } _handleFrameTree(frameTree) { this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null); this._onFrameNavigated(frameTree.frame, true); if (!frameTree.childFrames) return; for (const child of frameTree.childFrames) this._handleFrameTree(child); } _eventBelongsToStaleFrame(frameId) { const frame = this._page.frameManager.frame(frameId); if (!frame) return true; const session = this._crPage._sessionForFrame(frame); return session && session !== this && !session._swappedIn; } _onFrameAttached(frameId, parentFrameId) { const frameSession = this._crPage._sessions.get(frameId); if (frameSession && frameId !== this._targetId) { frameSession._swappedIn = true; const frame = this._page.frameManager.frame(frameId); if (frame) this._page.frameManager.removeChildFramesRecursively(frame); return; } if (parentFrameId && !this._page.frameManager.frame(parentFrameId)) { return; } this._page.frameManager.frameAttached(frameId, parentFrameId); } _onFrameNavigated(framePayload, initial) { if (this._eventBelongsToStaleFrame(framePayload.id)) return; this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ""), framePayload.name || "", framePayload.loaderId, initial); if (!initial) this._firstNonInitialNavigationCommittedFulfill(); } _onFrameRequestedNavigation(payload) { if (this._eventBelongsToStaleFrame(payload.frameId)) return; if (payload.disposition === "currentTab") this._page.frameManager.frameRequestedNavigation(payload.frameId); } _onFrameNavigatedWithinDocument(frameId, url) { if (this._eventBelongsToStaleFrame(frameId)) return; this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url); } _onFrameDetached(frameId, reason) { if (this._crPage._sessions.has(frameId)) { return; } if (reason === "swap") { const frame = this._page.frameManager.frame(frameId); if (frame) this._page.frameManager.removeChildFramesRecursively(frame); return; } this._page.frameManager.frameDetached(frameId); } _onExecutionContextCreated(contextPayload) { const frame = contextPayload.auxData ? this._page.frameManager.frame(contextPayload.auxData.frameId) : null; if (!frame || this._eventBelongsToStaleFrame(frame._id)) return; const delegate = new import_crExecutionContext.CRExecutionContext(this._client, contextPayload); let worldName = null; if (contextPayload.auxData && !!contextPayload.auxData.isDefault) worldName = "main"; else if (contextPayload.name === this._crPage.utilityWorldName) worldName = "utility"; const context = new dom.FrameExecutionContext(delegate, frame, worldName); if (worldName) frame._contextCreated(worldName, context); this._contextIdToContext.set(contextPayload.id, context); } _onExecutionContextDestroyed(executionContextId) { const context = this._contextIdToContext.get(executionContextId); if (!context) return; this._contextIdToContext.delete(executionContextId); context.frame._contextDestroyed(context); } _onExecutionContextsCleared() { for (const contextId of Array.from(this._contextIdToContext.keys())) this._onExecutionContextDestroyed(contextId); } _onAttachedToTarget(event) { const session = this._client.createChildSession(event.sessionId); if (event.targetInfo.type === "iframe") { const targetId = event.targetInfo.targetId; const frame = this._page.frameManager.frame(targetId); if (!frame) return; this._page.frameManager.removeChildFramesRecursively(frame); for (const [contextId, context] of this._contextIdToContext) { if (context.frame === frame) this._onExecutionContextDestroyed(contextId); } const frameSession = new FrameSession(this._crPage, session, targetId, this); this._crPage._sessions.set(targetId, frameSession); frameSession._initialize(false).catch((e) => e); return; } if (event.targetInfo.type !== "worker") { session.detach().catch(() => { }); return; } const url = event.targetInfo.url; const worker = new import_page.Worker(this._page, url); this._page.addWorker(event.sessionId, worker); this._workerSessions.set(event.sessionId, session); session.once("Runtime.executionContextCreated", async (event2) => { worker.createExecutionContext(new import_crExecutionContext.CRExecutionContext(session, event2.context)); }); session._sendMayFail("Runtime.enable"); this._crPage._networkManager.addSession(session, this._page.frameManager.frame(this._targetId) ?? void 0).catch(() => { }); session._sendMayFail("Runtime.runIfWaitingForDebugger"); session._sendMayFail("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true, filter: [ { type: "worker" }, { type: "service_worker", exclude: !process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS } ] }); session.on("Target.attachedToTarget", (event2) => this._onAttachedToTarget(event2)); session.on("Target.detachedFromTarget", (event2) => this._onDetachedFromTarget(event2)); session.on("Runtime.consoleAPICalled", (event2) => { const args = event2.args.map((o) => (0, import_crExecutionContext.createHandle)(worker.existingExecutionContext, o)); this._page.addConsoleMessage(event2.type, args, (0, import_crProtocolHelper.toConsoleMessageLocation)(event2.stackTrace)); }); session.on("Runtime.exceptionThrown", (exception) => this._page.emitOnContextOnceInitialized(import_browserContext.BrowserContext.Events.PageError, (0, import_crProtocolHelper.exceptionToError)(exception.exceptionDetails), this._page)); } _onDetachedFromTarget(event) { const workerSession = this._workerSessions.get(event.sessionId); if (workerSession) { workerSession.dispose(); this._page.removeWorker(event.sessionId); return; } const childFrameSession = this._crPage._sessions.get(event.targetId); if (!childFrameSession) return; if (childFrameSession._swappedIn) { childFrameSession.dispose(); return; } this._client.send("Page.enable").catch((e) => null).then(() => { if (!childFrameSession._swappedIn) this._page.frameManager.frameDetached(event.targetId); childFrameSession.dispose(); }); } _onWindowOpen(event) { this._crPage._nextWindowOpenPopupFeatures.push(event.windowFeatures); } async _onConsoleAPI(event) { if (event.executionContextId === 0) { return; } const context = this._contextIdToContext.get(event.executionContextId); if (!context) return; const values = event.args.map((arg) => (0, import_crExecutionContext.createHandle)(context, arg)); this._page.addConsoleMessage(event.type, values, (0, import_crProtocolHelper.toConsoleMessageLocation)(event.stackTrace)); } async _onBindingCalled(event) { const pageOrError = await this._crPage._page.waitForInitializedOrError(); if (!(pageOrError instanceof Error)) { const context = this._contextIdToContext.get(event.executionContextId); if (context) await this._page.onBindingCalled(event.payload, context); } } _onDialog(event) { if (!this._page.frameManager.frame(this._targetId)) return; this._page.browserContext.dialogManager.dialogDidOpen(new dialog.Dialog( this._page, event.type, event.message, async (accept, promptText) => { if (this._isMainFrame() && event.type === "beforeunload" && !accept) this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, "navigation cancelled by beforeunload dialog"); await this._client.send("Page.handleJavaScriptDialog", { accept, promptText }); }, event.defaultPrompt )); } _handleException(exceptionDetails) { this._page.emitOnContextOnceInitialized(import_browserContext.BrowserContext.Events.PageError, (0, import_crProtocolHelper.exceptionToError)(exceptionDetails), this._page); } async _onTargetCrashed() { this._client._markAsCrashed(); this._page._didCrash(); } _onLogEntryAdded(event) { const { level, text, args, source, url, lineNumber } = event.entry; if (args) args.map((arg) => (0, import_crProtocolHelper.releaseObject)(this._client, arg.objectId)); if (source !== "worker") { const location = { url: url || "", lineNumber: lineNumber || 0, columnNumber: 0 }; this._page.addConsoleMessage(level, [], location, text); } } async _onFileChooserOpened(event) { if (!event.backendNodeId) return; const frame = this._page.frameManager.frame(event.frameId); if (!frame) return; let handle; try { const utilityContext = await frame._utilityContext(); handle = await this._adoptBackendNodeId(event.backendNodeId, utilityContext); } catch (e) { return; } await this._page._onFileChooserOpened(handle); } _willBeginDownload() { if (!this._crPage._page.initializedOrUndefined()) { this._firstNonInitialNavigationCommittedReject(new Error("Starting new page download")); } } _onScreencastFrame(payload) { this._page.throttleScreencastFrameAck(() => { this._client.send("Page.screencastFrameAck", { sessionId: payload.sessionId }).catch(() => { }); }); const buffer = Buffer.from(payload.data, "base64"); this._page.emit(import_page.Page.Events.ScreencastFrame, { buffer, frameSwapWallTime: payload.metadata.timestamp ? payload.metadata.timestamp * 1e3 : void 0, width: payload.metadata.deviceWidth, height: payload.metadata.deviceHeight }); } async _createVideoRecorder(screencastId, options) { (0, import_assert.assert)(!this._screencastId); const ffmpegPath = import_registry.registry.findExecutable("ffmpeg").executablePathOrDie(this._page.browserContext._browser.sdkLanguage()); this._videoRecorder = await import_videoRecorder.VideoRecorder.launch(this._crPage._page, ffmpegPath, options); this._screencastId = screencastId; } async _startVideoRecording(options) { const screencastId = this._screencastId; (0, import_assert.assert)(screencastId); this._page.once(import_page.Page.Events.Close, () => this._stopVideoRecording().catch(() => { })); const gotFirstFrame = new Promise((f) => this._client.once("Page.screencastFrame", f)); await this._startScreencast(this._videoRecorder, { format: "jpeg", quality: 90, maxWidth: options.width, maxHeight: options.height }); gotFirstFrame.then(() => { this._crPage._browserContext._browser._videoStarted(this._crPage._browserContext, screencastId, options.outputFile, this._crPage._page.waitForInitializedOrError()); }); } async _stopVideoRecording() { if (!this._screencastId) return; const screencastId = this._screencastId; this._screencastId = null; const recorder = this._videoRecorder; this._videoRecorder = null; await this._stopScreencast(recorder); await recorder.stop().catch(() => { }); const video = this._crPage._browserContext._browser._takeVideo(screencastId); video?.reportFinished(); } async _startScreencast(client, options = {}) { this._screencastClients.add(client); if (this._screencastClients.size === 1) await this._client.send("Page.startScreencast", options); } async _stopScreencast(client) { this._screencastClients.delete(client); if (!this._screencastClients.size) await this._client._sendMayFail("Page.stopScreencast"); } async _updateGeolocation(initial) { const geolocation = this._crPage._browserContext._options.geolocation; if (!initial || geolocation) await this._client.send("Emulation.setGeolocationOverride", geolocation || {}); } async _updateViewport(preserveWindowBoundaries) { if (this._crPage._browserContext._browser.isClank()) return; (0, import_assert.assert)(this._isMainFrame()); const options = this._crPage._browserContext._options; const emulatedSize = this._page.emulatedSize(); if (!emulatedSize) return; const viewportSize = emulatedSize.viewport; const screenSize = emulatedSize.screen; const isLandscape = screenSize.width > screenSize.height; const metricsOverride = { mobile: !!options.isMobile, width: viewportSize.width, height: viewportSize.height, screenWidth: screenSize.width, screenHeight: screenSize.height, deviceScaleFactor: options.deviceScaleFactor || 1, screenOrientation: !!options.isMobile ? isLandscape ? { angle: 90, type: "landscapePrimary" } : { angle: 0, type: "portraitPrimary" } : { angle: 0, type: "landscapePrimary" }, dontSetVisibleSize: preserveWindowBoundaries }; if (JSON.stringify(this._metricsOverride) === JSON.stringify(metricsOverride)) return; const promises = []; if (!preserveWindowBoundaries && this._windowId) { let insets = { width: 0, height: 0 }; if (this._crPage._browserContext._browser.options.headful) { insets = { width: 24, height: 88 }; if (process.platform === "win32") insets = { width: 16, height: 88 }; else if (process.platform === "linux") insets = { width: 8, height: 85 }; else if (process.platform === "darwin") insets = { width: 2, height: 80 }; if (this._crPage._browserContext.isPersistentContext()) { insets.height += 46; } } promises.push(this.setWindowBounds({ width: viewportSize.width + insets.width, height: viewportSize.height + insets.height })); } promises.push(this._client.send("Emulation.setDeviceMetricsOverride", metricsOverride)); await Promise.all(promises); this._metricsOverride = metricsOverride; } async windowBounds() { const { bounds } = await this._client.send("Browser.getWindowBounds", { windowId: this._windowId }); return bounds; } async setWindowBounds(bounds) { return await this._client.send("Browser.setWindowBounds", { windowId: this._windowId, bounds }); } async _updateEmulateMedia() { const emulatedMedia = this._page.emulatedMedia(); const media = emulatedMedia.media === "no-override" ? "" : emulatedMedia.media; const colorScheme = emulatedMedia.colorScheme === "no-override" ? "" : emulatedMedia.colorScheme; const reducedMotion = emulatedMedia.reducedMotion === "no-override" ? "" : emulatedMedia.reducedMotion; const forcedColors = emulatedMedia.forcedColors === "no-override" ? "" : emulatedMedia.forcedColors; const contrast = emulatedMedia.contrast === "no-override" ? "" : emulatedMedia.contrast; const features = [ { name: "prefers-color-scheme", value: colorScheme }, { name: "prefers-reduced-motion", value: reducedMotion }, { name: "forced-colors", value: forcedColors }, { name: "prefers-contrast", value: contrast } ]; await this._client.send("Emulation.setEmulatedMedia", { media, features }); } async _updateUserAgent() { const options = this._crPage._browserContext._options; await this._client.send("Emulation.setUserAgentOverride", { userAgent: options.userAgent || "", acceptLanguage: options.locale, userAgentMetadata: calculateUserAgentMetadata(options) }); } async _setDefaultFontFamilies(session) { const fontFamilies = import_defaultFontFamilies.platformToFontFamilies[this._crPage._browserContext._browser._platform()]; await session.send("Page.setFontFamilies", fontFamilies); } async _updateFileChooserInterception(initial) { const enabled = this._page.fileChooserIntercepted(); if (initial && !enabled) return; await this._client.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => { }); } async _evaluateOnNewDocument(initScript, world, runImmediately) { const worldName = world === "utility" ? this._crPage.utilityWorldName : void 0; const { identifier } = await this._client.send("Page.addScriptToEvaluateOnNewDocument", { source: initScript.source, worldName, runImmediately }); this._initScriptIds.set(initScript, identifier); } async _removeEvaluatesOnNewDocument(initScripts) { const ids = []; for (const script of initScripts) { const id = this._initScriptIds.get(script); if (id) ids.push(id); this._initScriptIds.delete(script); } await Promise.all(ids.map((identifier) => this._client.send("Page.removeScriptToEvaluateOnNewDocument", { identifier }).catch(() => { }))); } async exposePlaywrightBinding() { await this._client.send("Runtime.addBinding", { name: import_page.PageBinding.kBindingName }); } async _getContentFrame(handle) { const nodeInfo = await this._client.send("DOM.describeNode", { objectId: handle._objectId }); if (!nodeInfo || typeof nodeInfo.node.frameId !== "string") return null; return this._page.frameManager.frame(nodeInfo.node.frameId); } async _getOwnerFrame(handle) { const documentElement = await handle.evaluateHandle((node) => { const doc = node; if (doc.documentElement && doc.documentElement.ownerDocument === doc) return doc.documentElement; return node.ownerDocument ? node.ownerDocument.documentElement : null; }); if (!documentElement) return null; if (!documentElement._objectId) return null; const nodeInfo = await this._client.send("DOM.describeNode", { objectId: documentElement._objectId }); const frameId = nodeInfo && typeof nodeInfo.node.frameId === "string" ? nodeInfo.node.frameId : null; documentElement.dispose(); return frameId; } async _getBoundingBox(handle) { const result = await this._client._sendMayFail("DOM.getBoxModel", { objectId: handle._objectId }); if (!result) return null; const quad = result.model.border; const x = Math.min(quad[0], quad[2], quad[4], quad[6]); const y = Math.min(quad[1], quad[3], quad[5], quad[7]); const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x; const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y; const position = await this._framePosition(); if (!position) return null; return { x: x + position.x, y: y + position.y, width, height }; } async _framePosition() { const frame = this._page.frameManager.frame(this._targetId); if (!frame) return null; if (frame === this._page.mainFrame()) return { x: 0, y: 0 }; const element = await frame.frameElement(); const box = await element.boundingBox(); return box; } async _scrollRectIntoViewIfNeeded(handle, rect) { return await this._client.send("DOM.scrollIntoViewIfNeeded", { objectId: handle._objectId, rect }).then(() => "done").catch((e) => { if (e instanceof Error && e.message.includes("Node does not have a layout object")) return "error:notvisible"; if (e instanceof Error && e.message.includes("Node is detached from document")) return "error:notconnected"; throw e; }); } async _getContentQuads(handle) { const result = await this._client._sendMayFail("DOM.getContentQuads", { objectId: handle._objectId }); if (!result) return null; const position = await this._framePosition(); if (!position) return null; return result.quads.map((quad) => [ { x: quad[0] + position.x, y: quad[1] + position.y }, { x: quad[2] + position.x, y: quad[3] + position.y }, { x: quad[4] + position.x, y: quad[5] + position.y }, { x: quad[6] + position.x, y: quad[7] + position.y } ]); } async _adoptElementHandle(handle, to) { const nodeInfo = await this._client.send("DOM.describeNode", { objectId: handle._objectId }); return this._adoptBackendNodeId(nodeInfo.node.backendNodeId, to); } async _adoptBackendNodeId(backendNodeId, to) { const result = await this._client._sendMayFail("DOM.resolveNode", { backendNodeId, executionContextId: to.delegate._contextId }); if (!result || result.object.subtype === "null") throw new Error(dom.kUnableToAdoptErrorMessage); return (0, import_crExecutionContext.createHandle)(to, result.object).asElement(); } } async function emulateLocale(session, locale) { try { await session.send("Emulation.setLocaleOverride", { locale }); } catch (exception) { if (exception.message.includes("Another locale override is already in effect")) return; throw exception; } } async function emulateTimezone(session, timezoneId) { try { await session.send("Emulation.setTimezoneOverride", { timezoneId }); } catch (exception) { if (exception.message.includes("Timezone override is already in effect")) return; if (exception.message.includes("Invalid timezone")) throw new Error(`Invalid timezone ID: ${timezoneId}`); throw exception; } } function calculateUserAgentMetadata(options) { const ua = options.userAgent; if (!ua) return void 0; const metadata = { mobile: !!options.isMobile, model: "", architecture: "x86", platform: "Windows", platformVersion: "" }; const androidMatch = ua.match(/Android (\d+(\.\d+)?(\.\d+)?)/); const iPhoneMatch = ua.match(/iPhone OS (\d+(_\d+)?)/); const iPadMatch = ua.match(/iPad; CPU OS (\d+(_\d+)?)/); const macOSMatch = ua.match(/Mac OS X (\d+(_\d+)?(_\d+)?)/); const windowsMatch = ua.match(/Windows\D+(\d+(\.\d+)?(\.\d+)?)/); if (androidMatch) { metadata.platform = "Android"; metadata.platformVersion = androidMatch[1]; metadata.architecture = "arm"; } else if (iPhoneMatch) { metadata.platform = "iOS"; metadata.platformVersion = iPhoneMatch[1]; metadata.architecture = "arm"; } else if (iPadMatch) { metadata.platform = "iOS"; metadata.platformVersion = iPadMatch[1]; metadata.architecture = "arm"; } else if (macOSMatch) { metadata.platform = "macOS"; metadata.platformVersion = macOSMatch[1]; if (!ua.includes("Intel")) metadata.architecture = "arm"; } else if (windowsMatch) { metadata.platform = "Windows"; metadata.platformVersion = windowsMatch[1]; } else if (ua.toLowerCase().includes("linux")) { metadata.platform = "Linux"; } if (ua.includes("ARM")) metadata.architecture = "arm"; return metadata; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CRPage });