diff options
Diffstat (limited to 'remote/test/puppeteer/packages/puppeteer-core/src/common')
13 files changed, 296 insertions, 163 deletions
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts index 217e53bedd..4c8308da6e 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts @@ -14,7 +14,6 @@ import {isErrorLike} from '../util/ErrorLike.js'; import type {ConnectionTransport} from './ConnectionTransport.js'; import type {ConnectOptions} from './ConnectOptions.js'; import type {BrowserConnectOptions} from './ConnectOptions.js'; -import {getFetch} from './fetch.js'; const getWebSocketTransportClass = async () => { return isNode @@ -93,9 +92,8 @@ async function getConnectionTransport( async function getWSEndpoint(browserURL: string): Promise<string> { const endpointURL = new URL('/json/version', browserURL); - const fetch = await getFetch(); try { - const result = await fetch(endpointURL.toString(), { + const result = await globalThis.fetch(endpointURL.toString(), { method: 'GET', }); if (!result.ok) { diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts index c64d109a7c..fe71e57587 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts @@ -32,7 +32,13 @@ export interface Configuration { * See {@link PuppeteerNode.launch | puppeteer.launch} on how executable path * is inferred. * - * @defaultValue A compatible-revision of the browser. + * Use a specific browser version (e.g., 119.0.6045.105). If you use an alias + * such `stable` or `canary` it will only work during the installation of + * Puppeteer and it will fail when launching the browser. + * + * @example 119.0.6045.105 + * @defaultValue The pinned browser version supported by the current Puppeteer + * version. */ browserRevision?: string; /** @@ -51,20 +57,12 @@ export interface Configuration { * @remarks * This must include the protocol and may even need a path prefix. * - * @defaultValue Either https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing or + * @defaultValue Either https://storage.googleapis.com/chrome-for-testing-public or * https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central, * depending on the product. */ downloadBaseUrl?: string; /** - * Specifies the path for the downloads folder. - * - * Can be overridden by `PUPPETEER_DOWNLOAD_PATH`. - * - * @defaultValue `<cacheDirectory>` - */ - downloadPath?: string; - /** * Specifies an executable path to be used in * {@link PuppeteerNode.launch | puppeteer.launch}. * diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts index 85d2db9f75..c2aad7679d 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts @@ -35,7 +35,7 @@ export type ConsoleMessageType = | 'debug' | 'info' | 'error' - | 'warning' + | 'warn' | 'dir' | 'dirxml' | 'table' diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts new file mode 100644 index 0000000000..c9f7283075 --- /dev/null +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts @@ -0,0 +1,186 @@ +/** + * @license + * Copyright 2024 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Represents the cookie's 'SameSite' status: + * https://tools.ietf.org/html/draft-west-first-party-cookies + * + * @public + */ +export type CookieSameSite = 'Strict' | 'Lax' | 'None'; + +/** + * Represents the cookie's 'Priority' status: + * https://tools.ietf.org/html/draft-west-cookie-priority-00 + * + * @public + */ +export type CookiePriority = 'Low' | 'Medium' | 'High'; + +/** + * Represents the source scheme of the origin that originally set the cookie. A value of + * "Unset" allows protocol clients to emulate legacy cookie scope for the scheme. + * This is a temporary ability and it will be removed in the future. + * + * @public + */ +export type CookieSourceScheme = 'Unset' | 'NonSecure' | 'Secure'; + +/** + * Represents a cookie object. + * + * @public + */ +export interface Cookie { + /** + * Cookie name. + */ + name: string; + /** + * Cookie value. + */ + value: string; + /** + * Cookie domain. + */ + domain: string; + /** + * Cookie path. + */ + path: string; + /** + * Cookie expiration date as the number of seconds since the UNIX epoch. Set to `-1` for + * session cookies + */ + expires: number; + /** + * Cookie size. + */ + size: number; + /** + * True if cookie is http-only. + */ + httpOnly: boolean; + /** + * True if cookie is secure. + */ + secure: boolean; + /** + * True in case of session cookie. + */ + session: boolean; + /** + * Cookie SameSite type. + */ + sameSite?: CookieSameSite; + /** + * Cookie Priority. Supported only in Chrome. + */ + priority?: CookiePriority; + /** + * True if cookie is SameParty. Supported only in Chrome. + */ + sameParty?: boolean; + /** + * Cookie source scheme type. Supported only in Chrome. + */ + sourceScheme?: CookieSourceScheme; + /** + * Cookie partition key. The site of the top-level URL the browser was visiting at the + * start of the request to the endpoint that set the cookie. Supported only in Chrome. + */ + partitionKey?: string; + /** + * True if cookie partition key is opaque. Supported only in Chrome. + */ + partitionKeyOpaque?: boolean; +} + +/** + * Cookie parameter object + * + * @public + */ +export interface CookieParam { + /** + * Cookie name. + */ + name: string; + /** + * Cookie value. + */ + value: string; + /** + * The request-URI to associate with the setting of the cookie. This value can affect + * the default domain, path, and source scheme values of the created cookie. + */ + url?: string; + /** + * Cookie domain. + */ + domain?: string; + /** + * Cookie path. + */ + path?: string; + /** + * True if cookie is secure. + */ + secure?: boolean; + /** + * True if cookie is http-only. + */ + httpOnly?: boolean; + /** + * Cookie SameSite type. + */ + sameSite?: CookieSameSite; + /** + * Cookie expiration date, session cookie if not set + */ + expires?: number; + /** + * Cookie Priority. Supported only in Chrome. + */ + priority?: CookiePriority; + /** + * True if cookie is SameParty. Supported only in Chrome. + */ + sameParty?: boolean; + /** + * Cookie source scheme type. Supported only in Chrome. + */ + sourceScheme?: CookieSourceScheme; + /** + * Cookie partition key. The site of the top-level URL the browser was visiting at the + * start of the request to the endpoint that set the cookie. If not set, the cookie will + * be set as not partitioned. + */ + partitionKey?: string; +} + +/** + * @public + */ +export interface DeleteCookiesRequest { + /** + * Name of the cookies to remove. + */ + name: string; + /** + * If specified, deletes all the cookies with the given name where domain and path match + * provided URL. Otherwise, deletes only cookies related to the current page's domain. + */ + url?: string; + /** + * If specified, deletes only cookies with the exact domain. + */ + domain?: string; + /** + * If specified, deletes only cookies with the exact path. + */ + path?: string; +} diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts index dbf5c13c95..1f1a35dd0b 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts @@ -1543,10 +1543,3 @@ for (const device of knownDevices) { * @public */ export const KnownDevices = Object.freeze(knownDevicesByName); - -/** - * @deprecated Import {@link KnownDevices} - * - * @public - */ -export const devices = KnownDevices; diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts index 8225d64f07..4d0a43ea33 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts @@ -5,11 +5,11 @@ */ /** - * @deprecated Do not use. + * The base class for all Puppeteer-specific errors * * @public */ -export class CustomError extends Error { +export class PuppeteerError extends Error { /** * @internal */ @@ -36,14 +36,14 @@ export class CustomError extends Error { * * @public */ -export class TimeoutError extends CustomError {} +export class TimeoutError extends PuppeteerError {} /** * ProtocolError is emitted whenever there is an error from the protocol. * * @public */ -export class ProtocolError extends CustomError { +export class ProtocolError extends PuppeteerError { #code?: number; #originalMessage = ''; @@ -76,49 +76,9 @@ export class ProtocolError extends CustomError { * * @public */ -export class UnsupportedOperation extends CustomError {} +export class UnsupportedOperation extends PuppeteerError {} /** * @internal */ export class TargetCloseError extends ProtocolError {} - -/** - * @deprecated Do not use. - * - * @public - */ -export interface PuppeteerErrors { - TimeoutError: typeof TimeoutError; - ProtocolError: typeof ProtocolError; -} - -/** - * @deprecated Import error classes directly. - * - * Puppeteer methods might throw errors if they are unable to fulfill a request. - * For example, `page.waitForSelector(selector[, options])` might fail if the - * selector doesn't match any nodes during the given timeframe. - * - * For certain types of errors Puppeteer uses specific error classes. These - * classes are available via `puppeteer.errors`. - * - * @example - * An example of handling a timeout error: - * - * ```ts - * try { - * await page.waitForSelector('.foo'); - * } catch (e) { - * if (e instanceof TimeoutError) { - * // Do something if this is a timeout. - * } - * } - * ``` - * - * @public - */ -export const errors: PuppeteerErrors = Object.freeze({ - TimeoutError, - ProtocolError, -}); diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts index cf05ef6700..f3875e99e8 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts @@ -19,7 +19,7 @@ describe('EventEmitter', () => { }); describe('on', () => { - const onTests = (methodName: 'on' | 'addListener'): void => { + const onTests = (methodName: 'on'): void => { it(`${methodName}: adds an event listener that is fired when the event is emitted`, () => { const listener = sinon.spy(); emitter[methodName]('foo', listener); @@ -43,12 +43,10 @@ describe('EventEmitter', () => { }); }; onTests('on'); - // we support addListener for legacy reasons - onTests('addListener'); }); describe('off', () => { - const offTests = (methodName: 'off' | 'removeListener'): void => { + const offTests = (methodName: 'off'): void => { it(`${methodName}: removes the listener so it is no longer called`, () => { const listener = sinon.spy(); emitter.on('foo', listener); @@ -67,8 +65,6 @@ describe('EventEmitter', () => { }); }; offTests('off'); - // we support removeListener for legacy reasons - offTests('removeListener'); }); describe('once', () => { diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts index 4a8bcb801f..0aace8b2eb 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts @@ -27,18 +27,6 @@ export interface CommonEventEmitter<Events extends Record<EventType, unknown>> { handler?: Handler<Events[Key]> ): this; emit<Key extends keyof Events>(type: Key, event: Events[Key]): boolean; - /* To maintain parity with the built in NodeJS event emitter which uses removeListener - * rather than `off`. - * If you're implementing new code you should use `off`. - */ - addListener<Key extends keyof Events>( - type: Key, - handler: Handler<Events[Key]> - ): this; - removeListener<Key extends keyof Events>( - type: Key, - handler: Handler<Events[Key]> - ): this; once<Key extends keyof Events>( type: Key, handler: Handler<Events[Key]> @@ -149,30 +137,6 @@ export class EventEmitter<Events extends Record<EventType, unknown>> } /** - * Remove an event listener. - * - * @deprecated please use {@link EventEmitter.off} instead. - */ - removeListener<Key extends keyof EventsWithWildcard<Events>>( - type: Key, - handler: Handler<EventsWithWildcard<Events>[Key]> - ): this { - return this.off(type, handler); - } - - /** - * Add an event listener. - * - * @deprecated please use {@link EventEmitter.on} instead. - */ - addListener<Key extends keyof EventsWithWildcard<Events>>( - type: Key, - handler: Handler<EventsWithWildcard<Events>[Key]> - ): this { - return this.on(type, handler); - } - - /** * Like `on` but the listener will only be fired once and then it will be removed. * @param type - the event you'd like to listen to * @param handler - the handler function to run when the event occurs diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts index 7cae9191a9..f87ec6817b 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts @@ -158,11 +158,23 @@ export interface PDFOptions { omitBackground?: boolean; /** * Generate tagged (accessible) PDF. - * @defaultValue `false` + * @defaultValue `true` * @experimental */ tagged?: boolean; /** + * Generate document outline. + * + * @remarks + * If this is enabled the PDF will also be tagged (accessible) + * Currently only works in old Headless (headless = 'shell') + * crbug/840455#c47 + * + * @defaultValue `false` + * @experimental + */ + outline?: boolean; + /** * Timeout in milliseconds. Pass `0` to disable timeout. * @defaultValue `30_000` */ diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts index 0264c9175f..d505d6c5ff 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts @@ -1,3 +1,8 @@ +/** + * @license + * Copyright 2024 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ import {source as injectedSource} from '../generated/injected.js'; /** diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts index 6ef8925605..bf4274fcf1 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts @@ -10,12 +10,12 @@ export * from './Configuration.js'; export * from './ConnectionTransport.js'; export * from './ConnectOptions.js'; export * from './ConsoleMessage.js'; +export * from './Cookie.js'; export * from './CustomQueryHandler.js'; export * from './Debug.js'; export * from './Device.js'; export * from './Errors.js'; export * from './EventEmitter.js'; -export * from './fetch.js'; export * from './FileChooser.js'; export * from './GetQueryHandler.js'; export * from './HandleIterator.js'; diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts deleted file mode 100644 index 6c7a2b451c..0000000000 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright 2020 Google Inc. - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * Gets the global version if we're in the browser, else loads the node-fetch module. - * - * @internal - */ -export const getFetch = async (): Promise<typeof fetch> => { - return (globalThis as any).fetch || (await import('cross-fetch')).fetch; -}; diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts index 2c8f76f664..f84453c612 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts @@ -5,13 +5,19 @@ */ import type FS from 'fs/promises'; -import type {Readable} from 'stream'; -import {map, NEVER, Observable, timer} from '../../third_party/rxjs/rxjs.js'; +import type {OperatorFunction} from '../../third_party/rxjs/rxjs.js'; +import { + filter, + from, + map, + mergeMap, + NEVER, + Observable, + timer, +} from '../../third_party/rxjs/rxjs.js'; import type {CDPSession} from '../api/CDPSession.js'; -import {isNode} from '../environment.js'; import {assert} from '../util/assert.js'; -import {isErrorLike} from '../util/ErrorLike.js'; import {debug} from './Debug.js'; import {TimeoutError} from './Errors.js'; @@ -209,29 +215,39 @@ export async function importFSPromises(): Promise<typeof FS> { * @internal */ export async function getReadableAsBuffer( - readable: Readable, + readable: ReadableStream<Uint8Array>, path?: string ): Promise<Buffer | null> { - const buffers = []; + const buffers: Uint8Array[] = []; + const reader = readable.getReader(); if (path) { const fs = await importFSPromises(); const fileHandle = await fs.open(path, 'w+'); try { - for await (const chunk of readable) { - buffers.push(chunk); - await fileHandle.writeFile(chunk); + while (true) { + const {done, value} = await reader.read(); + if (done) { + break; + } + buffers.push(value); + await fileHandle.writeFile(value); } } finally { await fileHandle.close(); } } else { - for await (const chunk of readable) { - buffers.push(chunk); + while (true) { + const {done, value} = await reader.read(); + if (done) { + break; + } + buffers.push(value); } } try { return Buffer.concat(buffers); } catch (error) { + debugError(error); return null; } } @@ -239,39 +255,34 @@ export async function getReadableAsBuffer( /** * @internal */ + +/** + * @internal + */ export async function getReadableFromProtocolStream( client: CDPSession, handle: string -): Promise<Readable> { - // TODO: Once Node 18 becomes the lowest supported version, we can migrate to - // ReadableStream. - if (!isNode) { - throw new Error('Cannot create a stream outside of Node.js environment.'); - } +): Promise<ReadableStream<Uint8Array>> { + return new ReadableStream({ + async pull(controller) { + function getUnit8Array(data: string, isBase64: boolean): Uint8Array { + if (isBase64) { + return Uint8Array.from(atob(data), m => { + return m.codePointAt(0)!; + }); + } + const encoder = new TextEncoder(); + return encoder.encode(data); + } - const {Readable} = await import('stream'); + const {data, base64Encoded, eof} = await client.send('IO.read', { + handle, + }); - let eof = false; - return new Readable({ - async read(size: number) { + controller.enqueue(getUnit8Array(data, base64Encoded ?? false)); if (eof) { - return; - } - - try { - const response = await client.send('IO.read', {handle, size}); - this.push(response.data, response.base64Encoded ? 'base64' : undefined); - if (response.eof) { - eof = true; - await client.send('IO.close', {handle}); - this.push(null); - } - } catch (error) { - if (isErrorLike(error)) { - this.destroy(error); - return; - } - throw error; + await client.send('IO.close', {handle}); + controller.close(); } }, }); @@ -349,7 +360,8 @@ export function parsePDFOptions( pageRanges: '', preferCSSPageSize: false, omitBackground: false, - tagged: false, + outline: false, + tagged: true, }; let width = 8.5; @@ -375,6 +387,11 @@ export function parsePDFOptions( convertPrintParameterToInches(options.margin?.right, lengthUnit) || 0, }; + // Quirk https://bugs.chromium.org/p/chromium/issues/detail?id=840455#c44 + if (options.outline) { + options.tagged = true; + } + return { ...defaults, ...options, @@ -445,3 +462,21 @@ export function fromEmitterEvent< }; }); } + +/** + * @internal + */ +export function filterAsync<T>( + predicate: (value: T) => boolean | PromiseLike<boolean> +): OperatorFunction<T, T> { + return mergeMap<T, Observable<T>>((value): Observable<T> => { + return from(Promise.resolve(predicate(value))).pipe( + filter(isMatch => { + return isMatch; + }), + map(() => { + return value; + }) + ); + }); +} |