From 3866e462bc14896423f9722a16dcc96222f915f9 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Tue, 28 Mar 2023 13:02:59 +0200 Subject: [PATCH] chore: add basic screenshot to BiDi (#9923) --- packages/puppeteer-core/src/api/Page.ts | 27 +++++++++- packages/puppeteer-core/src/common/Page.ts | 24 +++------ .../src/common/bidi/Connection.ts | 4 ++ .../puppeteer-core/src/common/bidi/Page.ts | 49 +++++++++++++------ 4 files changed, 70 insertions(+), 34 deletions(-) diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts index 912442b1..a28d2624 100644 --- a/packages/puppeteer-core/src/api/Page.ts +++ b/packages/puppeteer-core/src/api/Page.ts @@ -58,7 +58,7 @@ import type { HandleFor, NodeFor, } from '../common/types.js'; -import {isNumber, isString} from '../common/util.js'; +import {importFSPromises, isNumber, isString} from '../common/util.js'; import type {WebWorker} from '../common/WebWorker.js'; import {assert} from '../util/assert.js'; @@ -2083,6 +2083,31 @@ export class Page extends EventEmitter { throw new Error('Not implemented'); } + /** + * @internal + */ + async _maybeWriteBufferToFile( + path: string | undefined, + buffer: Buffer + ): Promise { + if (!path) { + return; + } + + try { + const fs = await importFSPromises(); + + await fs.writeFile(path, buffer); + } catch (error) { + if (error instanceof TypeError) { + throw new Error( + 'Can only pass a file path in a Node-like environment.' + ); + } + throw error; + } + } + /** * @remarks * Options object which might have the following properties: diff --git a/packages/puppeteer-core/src/common/Page.ts b/packages/puppeteer-core/src/common/Page.ts index be94960f..7c52963c 100644 --- a/packages/puppeteer-core/src/common/Page.ts +++ b/packages/puppeteer-core/src/common/Page.ts @@ -91,7 +91,6 @@ import { getExceptionMessage, getReadableAsBuffer, getReadableFromProtocolStream, - importFSPromises, isString, pageBindingInitString, releaseObject, @@ -1440,24 +1439,13 @@ export class CDPPage extends Page { await this.setViewport(this.#viewport); } - const buffer = - options.encoding === 'base64' - ? result.data - : Buffer.from(result.data, 'base64'); - - if (options.path) { - try { - const fs = await importFSPromises(); - await fs.writeFile(options.path, buffer); - } catch (error) { - if (error instanceof TypeError) { - throw new Error( - 'Screenshots can only be written to a file path in a Node-like environment.' - ); - } - throw error; - } + if (options.encoding === 'base64') { + return result.data; } + + const buffer = Buffer.from(result.data, 'base64'); + await this._maybeWriteBufferToFile(options.path, buffer); + return buffer; function processClip(clip: ScreenshotClip): ScreenshotClip { diff --git a/packages/puppeteer-core/src/common/bidi/Connection.ts b/packages/puppeteer-core/src/common/bidi/Connection.ts index ff90c290..e86ace8c 100644 --- a/packages/puppeteer-core/src/common/bidi/Connection.ts +++ b/packages/puppeteer-core/src/common/bidi/Connection.ts @@ -59,6 +59,10 @@ interface Commands { params: Bidi.BrowsingContext.PrintParameters; returnType: Bidi.BrowsingContext.PrintResult; }; + 'browsingContext.captureScreenshot': { + params: Bidi.BrowsingContext.CaptureScreenshotParameters; + returnType: Bidi.BrowsingContext.CaptureScreenshotResult; + }; 'session.new': { params: {capabilities?: Record}; // TODO: Update Types in chromium bidi diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index 9da38978..90046c96 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -22,6 +22,7 @@ import {HTTPResponse} from '../../api/HTTPResponse.js'; import { Page as PageBase, PageEmittedEvents, + ScreenshotOptions, WaitForOptions, } from '../../api/Page.js'; import {isErrorLike} from '../../util/ErrorLike.js'; @@ -29,7 +30,7 @@ import {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js'; import {Handler} from '../EventEmitter.js'; import {PDFOptions} from '../PDFOptions.js'; import {EvaluateFunc, HandleFor} from '../types.js'; -import {debugError, importFSPromises, waitWithTimeout} from '../util.js'; +import {debugError, waitWithTimeout} from '../util.js'; import {Context, getBidiHandle} from './Context.js'; import {BidiSerializer} from './Serializer.js'; @@ -226,20 +227,7 @@ export class Page extends PageBase { const buffer = Buffer.from(result.data, 'base64'); - try { - if (path) { - const fs = await importFSPromises(); - - await fs.writeFile(path, buffer); - } - } catch (error) { - if (error instanceof TypeError) { - throw new Error( - 'Can only pass a file path in a Node-like environment.' - ); - } - throw error; - } + await this._maybeWriteBufferToFile(path, buffer); return buffer; } @@ -260,6 +248,37 @@ export class Page extends PageBase { throw error; } } + + override screenshot( + options: ScreenshotOptions & {encoding: 'base64'} + ): Promise; + override screenshot( + options?: ScreenshotOptions & {encoding?: 'binary'} + ): never; + override async screenshot( + options: ScreenshotOptions = {} + ): Promise { + const {path = undefined, encoding, ...args} = options; + if (Object.keys(args).length >= 1) { + throw new Error('BiDi only supports "encoding" and "path" options'); + } + + const {result} = await this.#context.connection.send( + 'browsingContext.captureScreenshot', + { + context: this.#context._contextId, + } + ); + + if (encoding === 'base64') { + return result.data; + } + + const buffer = Buffer.from(result.data, 'base64'); + await this._maybeWriteBufferToFile(path, buffer); + + return buffer; + } } function isConsoleLogEntry(