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
JavaScript
"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
});