/** * @license * Copyright 2017 Google Inc. * SPDX-License-Identifier: Apache-2.0 */ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; if (async) inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; env.stack.push({ value: value, dispose: dispose, async: async }); } else if (async) { env.stack.push({ async: true }); } return value; }; var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { return function (env) { function fail(e) { env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } var r, s = 0; function next() { while (r = env.stack.pop()) { try { if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (r.dispose) { var result = r.dispose.call(r.value); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } else s |= 1; } catch (e) { fail(e); } } if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); }; })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }); import { concat, distinctUntilChanged, EMPTY, filter, first, firstValueFrom, from, map, merge, mergeMap, mergeScan, of, raceWith, ReplaySubject, startWith, switchMap, take, takeUntil, timer, } from '../../third_party/rxjs/rxjs.js'; import { TargetCloseError } from '../common/Errors.js'; import { EventEmitter, } from '../common/EventEmitter.js'; import { TimeoutSettings } from '../common/TimeoutSettings.js'; import { debugError, fromEmitterEvent, filterAsync, isString, NETWORK_IDLE_TIME, timeout, withSourcePuppeteerURLIfNone, fromAbortSignal, } from '../common/util.js'; import { environment } from '../environment.js'; import { guarded } from '../util/decorators.js'; import { AsyncDisposableStack, asyncDisposeSymbol, DisposableStack, disposeSymbol, } from '../util/disposable.js'; import { stringToTypedArray } from '../util/encoding.js'; import { FunctionLocator, Locator, NodeLocator, } from './locators/locators.js'; /** * @internal */ export function setDefaultScreenshotOptions(options) { options.optimizeForSpeed ??= false; options.type ??= 'png'; options.fromSurface ??= true; options.fullPage ??= false; options.omitBackground ??= false; options.encoding ??= 'binary'; options.captureBeyondViewport ??= true; } /** * Page provides methods to interact with a single tab or * {@link https://developer.chrome.com/extensions/background_pages | extension background page} * in the browser. * * :::note * * One Browser instance might have multiple Page instances. * * ::: * * @example * This example creates a page, navigates it to a URL, and then saves a screenshot: * * ```ts * import puppeteer from 'puppeteer'; * * const browser = await puppeteer.launch(); * const page = await browser.newPage(); * await page.goto('https://example.com'); * await page.screenshot({path: 'screenshot.png'}); * await browser.close(); * ``` * * The Page class extends from Puppeteer's {@link EventEmitter} class and will * emit various events which are documented in the {@link PageEvent} enum. * * @example * This example logs a message for a single page `load` event: * * ```ts * page.once('load', () => console.log('Page loaded!')); * ``` * * To unsubscribe from events use the {@link EventEmitter.off} method: * * ```ts * function logRequest(interceptedRequest) { * console.log('A request was made:', interceptedRequest.url()); * } * page.on('request', logRequest); * // Sometime later... * page.off('request', logRequest); * ``` * * @public */ let Page = (() => { let _classSuper = EventEmitter; let _instanceExtraInitializers = []; let _screenshot_decorators; return class Page extends _classSuper { static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; __esDecorate(this, null, _screenshot_decorators, { kind: "method", name: "screenshot", static: false, private: false, access: { has: obj => "screenshot" in obj, get: obj => obj.screenshot }, metadata: _metadata }, null, _instanceExtraInitializers); if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); } /** * @internal */ _isDragging = (__runInitializers(this, _instanceExtraInitializers), false); /** * @internal */ _timeoutSettings = new TimeoutSettings(); #requestHandlers = new WeakMap(); #inflight$ = new ReplaySubject(1); /** * @internal */ constructor() { super(); fromEmitterEvent(this, "request" /* PageEvent.Request */) .pipe(mergeMap(originalRequest => { return concat(of(1), merge(fromEmitterEvent(this, "requestfailed" /* PageEvent.RequestFailed */), fromEmitterEvent(this, "requestfinished" /* PageEvent.RequestFinished */), fromEmitterEvent(this, "response" /* PageEvent.Response */).pipe(map(response => { return response.request(); }))).pipe(filter(request => { return request.id === originalRequest.id; }), take(1), map(() => { return -1; }))); }), mergeScan((acc, addend) => { return of(acc + addend); }, 0), takeUntil(fromEmitterEvent(this, "close" /* PageEvent.Close */)), startWith(0)) .subscribe(this.#inflight$); } /** * Listen to page events. * * @remarks * This method exists to define event typings and handle proper wireup of * cooperative request interception. Actual event listening and dispatching is * delegated to {@link EventEmitter}. * * @internal */ on(type, handler) { if (type !== "request" /* PageEvent.Request */) { return super.on(type, handler); } let wrapper = this.#requestHandlers.get(handler); if (wrapper === undefined) { wrapper = (event) => { event.enqueueInterceptAction(() => { return handler(event); }); }; this.#requestHandlers.set(handler, wrapper); } return super.on(type, wrapper); } /** * @internal */ off(type, handler) { if (type === "request" /* PageEvent.Request */) { handler = this.#requestHandlers.get(handler) || handler; } return super.off(type, handler); } /** * {@inheritDoc Accessibility} */ get accessibility() { return this.mainFrame().accessibility; } locator(input) { if (typeof input === 'string') { return NodeLocator.create(this, input); } else { return FunctionLocator.create(this, input); } } /** * A shortcut for {@link Locator.race} that does not require static imports. * * @internal */ locatorRace(locators) { return Locator.race(locators); } /** * Finds the first element that matches the selector. If no element matches * the selector, the return value resolves to `null`. * * @param selector - * {@link https://pptr.dev/guides/page-interactions#selectors | selector} * to query the page for. * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors} * can be passed as-is and a * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax} * allows querying by * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text}, * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name}, * and * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath} * and * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}. * Alternatively, you can specify the selector type using a * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. * * @remarks * * Shortcut for {@link Frame.$ | Page.mainFrame().$(selector) }. */ async $(selector) { return await this.mainFrame().$(selector); } /** * Finds elements on the page that match the selector. If no elements * match the selector, the return value resolves to `[]`. * * @param selector - * {@link https://pptr.dev/guides/page-interactions#selectors | selector} * to query the page for. * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors} * can be passed as-is and a * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax} * allows querying by * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text}, * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name}, * and * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath} * and * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}. * Alternatively, you can specify the selector type using a * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. * * @remarks * * Shortcut for {@link Frame.$$ | Page.mainFrame().$$(selector) }. */ async $$(selector, options) { return await this.mainFrame().$$(selector, options); } /** * @remarks * * The only difference between {@link Page.evaluate | page.evaluate} and * `page.evaluateHandle` is that `evaluateHandle` will return the value * wrapped in an in-page object. * * If the function passed to `page.evaluateHandle` returns a Promise, the * function will wait for the promise to resolve and return its value. * * You can pass a string instead of a function (although functions are * recommended as they are easier to debug and use with TypeScript): * * @example * * ```ts * const aHandle = await page.evaluateHandle('document'); * ``` * * @example * {@link JSHandle} instances can be passed as arguments to the `pageFunction`: * * ```ts * const aHandle = await page.evaluateHandle(() => document.body); * const resultHandle = await page.evaluateHandle( * body => body.innerHTML, * aHandle, * ); * console.log(await resultHandle.jsonValue()); * await resultHandle.dispose(); * ``` * * Most of the time this function returns a {@link JSHandle}, * but if `pageFunction` returns a reference to an element, * you instead get an {@link ElementHandle} back: * * @example * * ```ts * const button = await page.evaluateHandle(() => * document.querySelector('button'), * ); * // can call `click` because `button` is an `ElementHandle` * await button.click(); * ``` * * The TypeScript definitions assume that `evaluateHandle` returns * a `JSHandle`, but if you know it's going to return an * `ElementHandle`, pass it as the generic argument: * * ```ts * const button = await page.evaluateHandle(...); * ``` * * @param pageFunction - a function that is run within the page * @param args - arguments to be passed to the pageFunction */ async evaluateHandle(pageFunction, ...args) { pageFunction = withSourcePuppeteerURLIfNone(this.evaluateHandle.name, pageFunction); return await this.mainFrame().evaluateHandle(pageFunction, ...args); } /** * This method finds the first element within the page that matches the selector * and passes the result as the first argument to the `pageFunction`. * * @remarks * * If no element is found matching `selector`, the method will throw an error. * * If `pageFunction` returns a promise `$eval` will wait for the promise to * resolve and then return its value. * * @example * * ```ts * const searchValue = await page.$eval('#search', el => el.value); * const preloadHref = await page.$eval('link[rel=preload]', el => el.href); * const html = await page.$eval('.main-container', el => el.outerHTML); * ``` * * If you are using TypeScript, you may have to provide an explicit type to the * first argument of the `pageFunction`. * By default it is typed as `Element`, but you may need to provide a more * specific sub-type: * * @example * * ```ts * // if you don't provide HTMLInputElement here, TS will error * // as `value` is not on `Element` * const searchValue = await page.$eval( * '#search', * (el: HTMLInputElement) => el.value, * ); * ``` * * The compiler should be able to infer the return type * from the `pageFunction` you provide. If it is unable to, you can use the generic * type to tell the compiler what return type you expect from `$eval`: * * @example * * ```ts * // The compiler can infer the return type in this case, but if it can't * // or if you want to be more explicit, provide it as the generic type. * const searchValue = await page.$eval( * '#search', * (el: HTMLInputElement) => el.value, * ); * ``` * * @param selector - * {@link https://pptr.dev/guides/page-interactions#selectors | selector} * to query the page for. * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors} * can be passed as-is and a * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax} * allows querying by * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text}, * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name}, * and * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath} * and * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}. * Alternatively, you can specify the selector type using a * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. * @param pageFunction - the function to be evaluated in the page context. * Will be passed the result of the element matching the selector as its * first argument. * @param args - any additional arguments to pass through to `pageFunction`. * * @returns The result of calling `pageFunction`. If it returns an element it * is wrapped in an {@link ElementHandle}, else the raw value itself is * returned. */ async $eval(selector, pageFunction, ...args) { pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction); return await this.mainFrame().$eval(selector, pageFunction, ...args); } /** * This method returns all elements matching the selector and passes the * resulting array as the first argument to the `pageFunction`. * * @remarks * If `pageFunction` returns a promise `$$eval` will wait for the promise to * resolve and then return its value. * * @example * * ```ts * // get the amount of divs on the page * const divCount = await page.$$eval('div', divs => divs.length); * * // get the text content of all the `.options` elements: * const options = await page.$$eval('div > span.options', options => { * return options.map(option => option.textContent); * }); * ``` * * If you are using TypeScript, you may have to provide an explicit type to the * first argument of the `pageFunction`. * By default it is typed as `Element[]`, but you may need to provide a more * specific sub-type: * * @example * * ```ts * await page.$$eval('input', elements => { * return elements.map(e => e.value); * }); * ``` * * The compiler should be able to infer the return type * from the `pageFunction` you provide. If it is unable to, you can use the generic * type to tell the compiler what return type you expect from `$$eval`: * * @example * * ```ts * const allInputValues = await page.$$eval('input', elements => * elements.map(e => e.textContent), * ); * ``` * * @param selector - * {@link https://pptr.dev/guides/page-interactions#selectors | selector} * to query the page for. * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors} * can be passed as-is and a * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax} * allows querying by * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text}, * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name}, * and * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath} * and * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}. * Alternatively, you can specify the selector type using a * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. * @param pageFunction - the function to be evaluated in the page context. * Will be passed an array of matching elements as its first argument. * @param args - any additional arguments to pass through to `pageFunction`. * * @returns The result of calling `pageFunction`. If it returns an element it * is wrapped in an {@link ElementHandle}, else the raw value itself is * returned. */ async $$eval(selector, pageFunction, ...args) { pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction); return await this.mainFrame().$$eval(selector, pageFunction, ...args); } /** * Adds a `