Use Electron capturePage for browser screenshots

This commit is contained in:
Mohamed Boudra
2026-07-02 18:53:57 +02:00
parent 172de4fbc2
commit 6fea8305a7
3 changed files with 79 additions and 72 deletions
@@ -25,7 +25,10 @@ export interface TabContents {
goBack(): void;
goForward(): void;
reload(): void;
capturePage(): Promise<TabImage>;
capturePage(options?: TabCapturePageOptions): Promise<TabImage>;
invalidate(): void;
isBackgroundThrottlingAllowed(): boolean;
setBackgroundThrottling(allowed: boolean): void;
getConsoleMessages?(): BrowserAutomationConsoleLogEntry[];
getCookies?(url: string): Promise<BrowserAutomationCookieEntry[]>;
sendDebugCommand?(command: string, params?: Record<string, unknown>): Promise<unknown>;
@@ -42,6 +45,10 @@ export interface TabImage {
getSize(): { width: number; height: number };
}
export interface TabCapturePageOptions {
stayHidden?: boolean;
}
export interface BrowserRegistry {
listRegisteredBrowserIds(): string[];
listRegisteredBrowserIdsForWorkspace(workspaceId: string): string[];
@@ -104,6 +111,17 @@ async function withPixelCaptureTimeout<T>(capture: Promise<T>): Promise<T> {
}
}
async function capturePaintedViewport(contents: TabContents): Promise<TabImage> {
const previousBackgroundThrottling = contents.isBackgroundThrottlingAllowed();
contents.setBackgroundThrottling(false);
try {
contents.invalidate();
return await withPixelCaptureTimeout(contents.capturePage({ stayHidden: false }));
} finally {
contents.setBackgroundThrottling(previousBackgroundThrottling);
}
}
function tabInfoFromContents(
browserId: string,
contents: TabContents,
@@ -1104,41 +1122,9 @@ async function executeScreenshot(
if ("ok" in target) {
return target;
}
if (target.contents.sendDebugCommand) {
let screenshot: CdpCaptureScreenshotResult;
try {
screenshot = (await withPixelCaptureTimeout(
target.contents.sendDebugCommand("Page.captureScreenshot", {
format: "png",
fromSurface: false,
}),
)) as CdpCaptureScreenshotResult;
} catch (error) {
if (isScreenshotNoFrameError(error)) {
return screenshotNoFrameFailure(requestId);
}
throw error;
}
if (!screenshot.data) {
return fail(requestId, "browser_unsupported", "browser_screenshot returned no data");
}
const viewport = await getViewportSize(target.contents);
return {
requestId,
ok: true,
result: {
command: "screenshot",
browserId: target.browserId,
mimeType: "image/png",
dataBase64: screenshot.data,
width: viewport.width,
height: viewport.height,
},
};
}
let image: TabImage;
try {
image = await withPixelCaptureTimeout(target.contents.capturePage());
image = await capturePaintedViewport(target.contents);
} catch (error) {
if (isScreenshotNoFrameError(error)) {
return screenshotNoFrameFailure(requestId);
@@ -1183,20 +1169,6 @@ interface CdpCaptureScreenshotResult {
data?: string;
}
async function getViewportSize(contents: TabContents): Promise<{ width: number; height: number }> {
const result = await contents.executeJavaScript(
"({ width: Math.round(window.innerWidth), height: Math.round(window.innerHeight) })",
);
if (!result || typeof result !== "object") {
return { width: 0, height: 0 };
}
const record = result as { width?: unknown; height?: unknown };
return {
width: typeof record.width === "number" && Number.isFinite(record.width) ? record.width : 0,
height: typeof record.height === "number" && Number.isFinite(record.height) ? record.height : 0,
};
}
async function getCdpLayoutMetrics(contents: TabContents): Promise<{
viewportWidth: number;
viewportHeight: number;