From 60582b7fbc220a5efa19a19a8bcdcfc21ae14913 Mon Sep 17 00:00:00 2001 From: jrandolf <101637635+jrandolf@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:14:06 +0200 Subject: [PATCH] refactor: migrate browsingContext methods to frame (#10881) --- docs/api/index.md | 1 + docs/api/puppeteer.frame.goto.md | 14 +- docs/api/puppeteer.frame.md | 2 +- docs/api/puppeteer.frame.waitfornavigation.md | 15 +-- docs/api/puppeteer.gotooptions.md | 20 +++ docs/api/puppeteer.page.goto.md | 44 +++--- docs/api/puppeteer.page.md | 4 +- docs/api/puppeteer.page.reload.md | 18 +-- docs/api/puppeteer.puppeteerlifecycleevent.md | 14 ++ docs/api/puppeteer.waitforoptions.md | 8 +- packages/puppeteer-core/src/api/Frame.ts | 66 ++++++--- packages/puppeteer-core/src/api/Page.ts | 126 +++++------------- .../src/bidi/BrowsingContext.ts | 122 +---------------- packages/puppeteer-core/src/bidi/Frame.ts | 106 +++++++++++---- packages/puppeteer-core/src/bidi/Page.ts | 54 ++++++-- .../src/cdp/LifecycleWatcher.ts | 15 +++ packages/puppeteer-core/src/cdp/Page.ts | 15 +-- 17 files changed, 308 insertions(+), 336 deletions(-) create mode 100644 docs/api/puppeteer.gotooptions.md diff --git a/docs/api/index.md b/docs/api/index.md index 4bbe55fc3a0..24b135509ca 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -94,6 +94,7 @@ sidebar_label: API | [FrameEvents](./puppeteer.frameevents.md) | | | [FrameWaitForFunctionOptions](./puppeteer.framewaitforfunctionoptions.md) | | | [GeolocationOptions](./puppeteer.geolocationoptions.md) | | +| [GoToOptions](./puppeteer.gotooptions.md) | | | [InterceptResolutionState](./puppeteer.interceptresolutionstate.md) | | | [InternalNetworkConditions](./puppeteer.internalnetworkconditions.md) | | | [JSCoverageEntry](./puppeteer.jscoverageentry.md) | The CoverageEntry class for JavaScript | diff --git a/docs/api/puppeteer.frame.goto.md b/docs/api/puppeteer.frame.goto.md index df5e6a40d11..52e76313a11 100644 --- a/docs/api/puppeteer.frame.goto.md +++ b/docs/api/puppeteer.frame.goto.md @@ -4,7 +4,7 @@ sidebar_label: Frame.goto # Frame.goto() method -Navigates a frame to the given url. +Navigates the frame to the given `url`. #### Signature: @@ -24,10 +24,10 @@ class Frame { ## Parameters -| Parameter | Type | Description | -| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| url | string | the URL to navigate the frame to. This should include the scheme, e.g. https://. | -| options | { referer?: string; referrerPolicy?: string; timeout?: number; waitUntil?: [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) \| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md)\[\]; } | _(Optional)_ navigation options. waitUntil is useful to define when the navigation should be considered successful - see the docs for [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) for more details. | +| Parameter | Type | Description | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | +| url | string | URL to navigate the frame to. The URL should include scheme, e.g. https:// | +| options | { referer?: string; referrerPolicy?: string; timeout?: number; waitUntil?: [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) \| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md)\[\]; } | _(Optional)_ Options to configure waiting behavior. | **Returns:** @@ -37,9 +37,9 @@ A promise which resolves to the main resource response. In case of multiple redi ## Exceptions -This method will throw an error if: +If: -- there's an SSL error (e.g. in case of self-signed certificates). - target URL is invalid. - the `timeout` is exceeded during navigation. - the remote server does not respond or is unreachable. - the main resource failed to load. +- there's an SSL error (e.g. in case of self-signed certificates). - target URL is invalid. - the timeout is exceeded during navigation. - the remote server does not respond or is unreachable. - the main resource failed to load. This method will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling [HTTPResponse.status()](./puppeteer.httpresponse.status.md). diff --git a/docs/api/puppeteer.frame.md b/docs/api/puppeteer.frame.md index 4f23be6fd91..1254fbc1e7e 100644 --- a/docs/api/puppeteer.frame.md +++ b/docs/api/puppeteer.frame.md @@ -85,7 +85,7 @@ console.log(text); | [evaluate(pageFunction, args)](./puppeteer.frame.evaluate.md) | | Behaves identically to [Page.evaluate()](./puppeteer.page.evaluate.md) except it's run within the the context of this frame. | | [evaluateHandle(pageFunction, args)](./puppeteer.frame.evaluatehandle.md) | | Behaves identically to [Page.evaluateHandle()](./puppeteer.page.evaluatehandle.md) except it's run within the context of this frame. | | [focus(selector)](./puppeteer.frame.focus.md) | | Focuses the first element that matches the selector. | -| [goto(url, options)](./puppeteer.frame.goto.md) | | Navigates a frame to the given url. | +| [goto(url, options)](./puppeteer.frame.goto.md) | | Navigates the frame to the given url. | | [hover(selector)](./puppeteer.frame.hover.md) | | Hovers the pointer over the center of the first element that matches the selector. | | [isDetached()](./puppeteer.frame.isdetached.md) | | Istrue if the frame has been detached. Otherwise, false. | | [isOOPFrame()](./puppeteer.frame.isoopframe.md) | | Is true if the frame is an out-of-process (OOP) frame. Otherwise, false. | diff --git a/docs/api/puppeteer.frame.waitfornavigation.md b/docs/api/puppeteer.frame.waitfornavigation.md index d8f96604c5d..418ebf3a716 100644 --- a/docs/api/puppeteer.frame.waitfornavigation.md +++ b/docs/api/puppeteer.frame.waitfornavigation.md @@ -12,24 +12,23 @@ Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/Hist ```typescript class Frame { - abstract waitForNavigation(options?: { - timeout?: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - }): Promise; + abstract waitForNavigation( + options?: WaitForOptions + ): Promise; } ``` ## Parameters -| Parameter | Type | Description | -| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| options | { timeout?: number; waitUntil?: [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) \| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md)\[\]; } | _(Optional)_ options to configure when the navigation is consided finished. | +| Parameter | Type | Description | +| --------- | ----------------------------------------------- | --------------------------------------------------- | +| options | [WaitForOptions](./puppeteer.waitforoptions.md) | _(Optional)_ Options to configure waiting behavior. | **Returns:** Promise<[HTTPResponse](./puppeteer.httpresponse.md) \| null> -a promise that resolves when the frame navigates to a new URL. +A promise which resolves to the main resource response. ## Example diff --git a/docs/api/puppeteer.gotooptions.md b/docs/api/puppeteer.gotooptions.md new file mode 100644 index 00000000000..0fba18279e3 --- /dev/null +++ b/docs/api/puppeteer.gotooptions.md @@ -0,0 +1,20 @@ +--- +sidebar_label: GoToOptions +--- + +# GoToOptions interface + +#### Signature: + +```typescript +export interface GoToOptions extends WaitForOptions +``` + +**Extends:** [WaitForOptions](./puppeteer.waitforoptions.md) + +## Properties + +| Property | Modifiers | Type | Description | Default | +| -------------- | --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| referer | optional | string | If provided, it will take preference over the referer header value set by [page.setExtraHTTPHeaders()](./puppeteer.page.setextrahttpheaders.md). | | +| referrerPolicy | optional | string | If provided, it will take preference over the referer-policy header value set by [page.setExtraHTTPHeaders()](./puppeteer.page.setextrahttpheaders.md). | | diff --git a/docs/api/puppeteer.page.goto.md b/docs/api/puppeteer.page.goto.md index f75be4f13c3..1494c64a22e 100644 --- a/docs/api/puppeteer.page.goto.md +++ b/docs/api/puppeteer.page.goto.md @@ -4,51 +4,45 @@ sidebar_label: Page.goto # Page.goto() method +Navigates the page to the given `url`. + #### Signature: ```typescript class Page { - goto( - url: string, - options?: WaitForOptions & { - referer?: string; - referrerPolicy?: string; - } - ): Promise; + goto(url: string, options?: GoToOptions): Promise; } ``` ## Parameters -| Parameter | Type | Description | -| --------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -| url | string | URL to navigate page to. The URL should include scheme, e.g. https:// | -| options | [WaitForOptions](./puppeteer.waitforoptions.md) & { referer?: string; referrerPolicy?: string; } | _(Optional)_ Navigation Parameter | +| Parameter | Type | Description | +| --------- | ----------------------------------------- | ---------------------------------------------------------------------------------- | +| url | string | URL to navigate page to. The URL should include scheme, e.g. https:// | +| options | [GoToOptions](./puppeteer.gotooptions.md) | _(Optional)_ Options to configure waiting behavior. | **Returns:** Promise<[HTTPResponse](./puppeteer.httpresponse.md) \| null> -Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. +A promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. -## Remarks +## Exceptions -The argument `options` might have the following properties: - -- `timeout` : Maximum navigation time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout. The default value can be changed by using the [Page.setDefaultNavigationTimeout()](./puppeteer.page.setdefaultnavigationtimeout.md) or [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md) methods. - -- `waitUntil`:When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
- `load` : consider navigation to be finished when the load event is fired.
- `domcontentloaded` : consider navigation to be finished when the DOMContentLoaded event is fired.
- `networkidle0` : consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
- `networkidle2` : consider navigation to be finished when there are no more than 2 network connections for at least `500` ms. - -- `referer` : Referer header value. If provided it will take preference over the referer header value set by [page.setExtraHTTPHeaders()](./puppeteer.page.setextrahttpheaders.md).
- `referrerPolicy` : ReferrerPolicy. If provided it will take preference over the referer-policy header value set by [page.setExtraHTTPHeaders()](./puppeteer.page.setextrahttpheaders.md). - -`page.goto` will throw an error if: +If: - there's an SSL error (e.g. in case of self-signed certificates). - target URL is invalid. - the timeout is exceeded during navigation. - the remote server does not respond or is unreachable. - the main resource failed to load. -`page.goto` will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling response.status(). +This method will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling [HTTPResponse.status()](./puppeteer.httpresponse.status.md). -NOTE: `page.goto` either throws an error or returns a main resource response. The only exceptions are navigation to about:blank or navigation to the same URL with a different hash, which would succeed and return null. +## Remarks -NOTE: Headless mode doesn't support navigation to a PDF document. See the [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295). +Navigation to `about:blank` or navigation to the same URL with a different hash will succeed and return `null`. + +:::warning + +Headless mode doesn't support navigation to a PDF document. See the [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295). + +::: Shortcut for [page.mainFrame().goto(url, options)](./puppeteer.frame.goto.md). diff --git a/docs/api/puppeteer.page.md b/docs/api/puppeteer.page.md index 6cf7870eddc..7ba3c73d187 100644 --- a/docs/api/puppeteer.page.md +++ b/docs/api/puppeteer.page.md @@ -112,7 +112,7 @@ page.off('request', logRequest); | [getDefaultTimeout()](./puppeteer.page.getdefaulttimeout.md) | | Maximum time in milliseconds. | | [goBack(options)](./puppeteer.page.goback.md) | | This method navigate to the previous page in history. | | [goForward(options)](./puppeteer.page.goforward.md) | | This method navigate to the next page in history. | -| [goto(url, options)](./puppeteer.page.goto.md) | | | +| [goto(url, options)](./puppeteer.page.goto.md) | | Navigates the page to the given url. | | [hover(selector)](./puppeteer.page.hover.md) | | This method fetches an element with selector, scrolls it into view if needed, and then uses [Page.mouse](./puppeteer.page.md) to hover over the center of the element. If there's no element matching selector, the method throws an error. | | [isClosed()](./puppeteer.page.isclosed.md) | | Indicates that the page has been closed. | | [isDragInterceptionEnabled()](./puppeteer.page.isdraginterceptionenabled.md) | | true if drag events are being intercepted, false otherwise. | @@ -124,7 +124,7 @@ page.off('request', logRequest); | [metrics()](./puppeteer.page.metrics.md) | | Object containing metrics as key/value pairs. | | [pdf(options)](./puppeteer.page.pdf.md) | | Generates a PDF of the page with the print CSS media type. | | [queryObjects(prototypeHandle)](./puppeteer.page.queryobjects.md) | | This method iterates the JavaScript heap and finds all objects with the given prototype. | -| [reload(options)](./puppeteer.page.reload.md) | | | +| [reload(options)](./puppeteer.page.reload.md) | | Reloads the page. | | [removeExposedFunction(name)](./puppeteer.page.removeexposedfunction.md) | | The method removes a previously added function via $[Page.exposeFunction()](./puppeteer.page.exposefunction.md) called name from the page's window object. | | [removeScriptToEvaluateOnNewDocument(identifier)](./puppeteer.page.removescripttoevaluateonnewdocument.md) | | Removes script that injected into page by Page.evaluateOnNewDocument. | | [screenshot(options)](./puppeteer.page.screenshot.md) | | Captures screenshot of the current page. | diff --git a/docs/api/puppeteer.page.reload.md b/docs/api/puppeteer.page.reload.md index f483a097aef..bb00e2a2835 100644 --- a/docs/api/puppeteer.page.reload.md +++ b/docs/api/puppeteer.page.reload.md @@ -4,6 +4,8 @@ sidebar_label: Page.reload # Page.reload() method +Reloads the page. + #### Signature: ```typescript @@ -14,20 +16,12 @@ class Page { ## Parameters -| Parameter | Type | Description | -| --------- | ----------------------------------------------- | ----------------------------------------------------------------------------- | -| options | [WaitForOptions](./puppeteer.waitforoptions.md) | _(Optional)_ Navigation parameters which might have the following properties: | +| Parameter | Type | Description | +| --------- | ----------------------------------------------- | --------------------------------------------------- | +| options | [WaitForOptions](./puppeteer.waitforoptions.md) | _(Optional)_ Options to configure waiting behavior. | **Returns:** Promise<[HTTPResponse](./puppeteer.httpresponse.md) \| null> -Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. - -## Remarks - -The argument `options` might have the following properties: - -- `timeout` : Maximum navigation time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout. The default value can be changed by using the [Page.setDefaultNavigationTimeout()](./puppeteer.page.setdefaultnavigationtimeout.md) or [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md) methods. - -- `waitUntil`: When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
- `load` : consider navigation to be finished when the load event is fired.
- `domcontentloaded` : consider navigation to be finished when the DOMContentLoaded event is fired.
- `networkidle0` : consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
- `networkidle2` : consider navigation to be finished when there are no more than 2 network connections for at least `500` ms. +A promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. diff --git a/docs/api/puppeteer.puppeteerlifecycleevent.md b/docs/api/puppeteer.puppeteerlifecycleevent.md index 012b9cf9b33..79de057cfda 100644 --- a/docs/api/puppeteer.puppeteerlifecycleevent.md +++ b/docs/api/puppeteer.puppeteerlifecycleevent.md @@ -8,8 +8,22 @@ sidebar_label: PuppeteerLifeCycleEvent ```typescript export type PuppeteerLifeCycleEvent = + /** + * Waits for the 'load' event. + */ | 'load' + /** + * Waits for the 'DOMContentLoaded' event. + */ | 'domcontentloaded' + /** + * Waits till there are no more than 0 network connections for at least `500` + * ms. + */ | 'networkidle0' + /** + * Waits till there are no more than 2 network connections for at least `500` + * ms. + */ | 'networkidle2'; ``` diff --git a/docs/api/puppeteer.waitforoptions.md b/docs/api/puppeteer.waitforoptions.md index a9e54488953..d6a7e15dceb 100644 --- a/docs/api/puppeteer.waitforoptions.md +++ b/docs/api/puppeteer.waitforoptions.md @@ -12,7 +12,7 @@ export interface WaitForOptions ## Properties -| Property | Modifiers | Type | Description | Default | -| --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | -| timeout | optional | number |

Maximum wait time in milliseconds. Pass 0 to disable the timeout.

The default value can be changed by using the [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md) or [Page.setDefaultNavigationTimeout()](./puppeteer.page.setdefaultnavigationtimeout.md) methods.

| 30000 | -| waitUntil | optional | [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) \| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md)\[\] | | | +| Property | Modifiers | Type | Description | Default | +| --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | +| timeout | optional | number |

Maximum wait time in milliseconds. Pass 0 to disable the timeout.

The default value can be changed by using the [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md) or [Page.setDefaultNavigationTimeout()](./puppeteer.page.setdefaultnavigationtimeout.md) methods.

| 30000 | +| waitUntil | optional | [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) \| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md)\[\] | When to consider waiting succeeds. Given an array of event strings, waiting is considered to be successful after all events have been fired. | 'load' | diff --git a/packages/puppeteer-core/src/api/Frame.ts b/packages/puppeteer-core/src/api/Frame.ts index 989ea15b7b8..e2d4f0815aa 100644 --- a/packages/puppeteer-core/src/api/Frame.ts +++ b/packages/puppeteer-core/src/api/Frame.ts @@ -54,6 +54,45 @@ import { } from './locators/locators.js'; import {type Realm} from './Realm.js'; +/** + * @public + */ +export interface WaitForOptions { + /** + * Maximum wait time in milliseconds. Pass 0 to disable the timeout. + * + * The default value can be changed by using the + * {@link Page.setDefaultTimeout} or {@link Page.setDefaultNavigationTimeout} + * methods. + * + * @defaultValue `30000` + */ + timeout?: number; + /** + * When to consider waiting succeeds. Given an array of event strings, waiting + * is considered to be successful after all events have been fired. + * + * @defaultValue `'load'` + */ + waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; +} + +/** + * @public + */ +export interface GoToOptions extends WaitForOptions { + /** + * If provided, it will take preference over the referer header value set by + * {@link Page.setExtraHTTPHeaders | page.setExtraHTTPHeaders()}. + */ + referer?: string; + /** + * If provided, it will take preference over the referer-policy header value + * set by {@link Page.setExtraHTTPHeaders | page.setExtraHTTPHeaders()}. + */ + referrerPolicy?: string; +} + /** * @public */ @@ -278,7 +317,7 @@ export abstract class Frame extends EventEmitter { } /** - * Navigates a frame to the given url. + * Navigates the frame to the given `url`. * * @remarks * Navigation to `about:blank` or navigation to the same URL with a different @@ -292,20 +331,17 @@ export abstract class Frame extends EventEmitter { * * ::: * - * @param url - the URL to navigate the frame to. This should include the - * scheme, e.g. `https://`. - * @param options - navigation options. `waitUntil` is useful to define when - * the navigation should be considered successful - see the docs for - * {@link PuppeteerLifeCycleEvent} for more details. - * + * @param url - URL to navigate the frame to. The URL should include scheme, + * e.g. `https://` + * @param options - Options to configure waiting behavior. * @returns A promise which resolves to the main resource response. In case of * multiple redirects, the navigation will resolve with the response of the * last redirect. - * @throws This method will throw an error if: + * @throws If: * * - there's an SSL error (e.g. in case of self-signed certificates). * - target URL is invalid. - * - the `timeout` is exceeded during navigation. + * - the timeout is exceeded during navigation. * - the remote server does not respond or is unreachable. * - the main resource failed to load. * @@ -343,14 +379,12 @@ export abstract class Frame extends EventEmitter { * ]); * ``` * - * @param options - options to configure when the navigation is consided - * finished. - * @returns a promise that resolves when the frame navigates to a new URL. + * @param options - Options to configure waiting behavior. + * @returns A promise which resolves to the main resource response. */ - abstract waitForNavigation(options?: { - timeout?: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - }): Promise; + abstract waitForNavigation( + options?: WaitForOptions + ): Promise; /** * @internal diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts index ba9a5409f48..cffe72a523a 100644 --- a/packages/puppeteer-core/src/api/Page.ts +++ b/packages/puppeteer-core/src/api/Page.ts @@ -28,11 +28,11 @@ import { fromEvent, map, merge, - type Observable, of, raceWith, startWith, switchMap, + type Observable, } from '../../third_party/rxjs/rxjs.js'; import type {HTTPRequest} from '../api/HTTPRequest.js'; import type {HTTPResponse} from '../api/HTTPResponse.js'; @@ -40,12 +40,11 @@ import type {BidiNetworkManager} from '../bidi/NetworkManager.js'; import type {Accessibility} from '../cdp/Accessibility.js'; import type {Coverage} from '../cdp/Coverage.js'; import {type DeviceRequestPrompt} from '../cdp/DeviceRequestPrompt.js'; -import type {PuppeteerLifeCycleEvent} from '../cdp/LifecycleWatcher.js'; import { + NetworkManagerEvent, type NetworkManager as CdpNetworkManager, type Credentials, type NetworkConditions, - NetworkManagerEvent, } from '../cdp/NetworkManager.js'; import type {Tracing} from '../cdp/Tracing.js'; import type {WebWorker} from '../cdp/WebWorker.js'; @@ -60,8 +59,8 @@ import { } from '../common/EventEmitter.js'; import type {FileChooser} from '../common/FileChooser.js'; import { - type LowerCasePaperFormat, paperFormats, + type LowerCasePaperFormat, type ParsedPDFOptions, type PDFOptions, } from '../common/PDFOptions.js'; @@ -94,6 +93,8 @@ import type { FrameAddScriptTagOptions, FrameAddStyleTagOptions, FrameWaitForFunctionOptions, + GoToOptions, + WaitForOptions, } from './Frame.js'; import { type Keyboard, @@ -103,10 +104,10 @@ import { } from './Input.js'; import type {JSHandle} from './JSHandle.js'; import { - type AwaitedLocator, FunctionLocator, Locator, NodeLocator, + type AwaitedLocator, } from './locators/locators.js'; import type {Target} from './Target.js'; @@ -144,23 +145,6 @@ export interface WaitTimeoutOptions { timeout?: number; } -/** - * @public - */ -export interface WaitForOptions { - /** - * Maximum wait time in milliseconds. Pass 0 to disable the timeout. - * - * The default value can be changed by using the - * {@link Page.setDefaultTimeout} or {@link Page.setDefaultNavigationTimeout} - * methods. - * - * @defaultValue `30000` - */ - timeout?: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; -} - /** * @public */ @@ -1553,40 +1537,29 @@ export abstract class Page extends EventEmitter { } /** + * Navigates the page to the given `url`. + * + * @remarks + * Navigation to `about:blank` or navigation to the same URL with a different + * hash will succeed and return `null`. + * + * :::warning + * + * Headless mode doesn't support navigation to a PDF document. See the {@link + * https://bugs.chromium.org/p/chromium/issues/detail?id=761295 | upstream + * issue}. + * + * ::: + * + * Shortcut for {@link Frame.goto | page.mainFrame().goto(url, options)}. + * * @param url - URL to navigate page to. The URL should include scheme, e.g. * `https://` - * @param options - Navigation Parameter - * @returns Promise which resolves to the main resource response. In case of + * @param options - Options to configure waiting behavior. + * @returns A promise which resolves to the main resource response. In case of * multiple redirects, the navigation will resolve with the response of the * last redirect. - * @remarks - * The argument `options` might have the following properties: - * - * - `timeout` : Maximum navigation time in milliseconds, defaults to 30 - * seconds, pass 0 to disable timeout. The default value can be changed by - * using the {@link Page.setDefaultNavigationTimeout} or - * {@link Page.setDefaultTimeout} methods. - * - * - `waitUntil`:When to consider navigation succeeded, defaults to `load`. - * Given an array of event strings, navigation is considered to be - * successful after all events have been fired. Events can be either:
- * - `load` : consider navigation to be finished when the load event is - * fired.
- * - `domcontentloaded` : consider navigation to be finished when the - * DOMContentLoaded event is fired.
- * - `networkidle0` : consider navigation to be finished when there are no - * more than 0 network connections for at least `500` ms.
- * - `networkidle2` : consider navigation to be finished when there are no - * more than 2 network connections for at least `500` ms. - * - * - `referer` : Referer header value. If provided it will take preference - * over the referer header value set by - * {@link Page.setExtraHTTPHeaders |page.setExtraHTTPHeaders()}.
- * - `referrerPolicy` : ReferrerPolicy. If provided it will take preference - * over the referer-policy header value set by - * {@link Page.setExtraHTTPHeaders |page.setExtraHTTPHeaders()}. - * - * `page.goto` will throw an error if: + * @throws If: * * - there's an SSL error (e.g. in case of self-signed certificates). * - target URL is invalid. @@ -1594,53 +1567,22 @@ export abstract class Page extends EventEmitter { * - the remote server does not respond or is unreachable. * - the main resource failed to load. * - * `page.goto` will not throw an error when any valid HTTP status code is - * returned by the remote server, including 404 "Not Found" and 500 - * "Internal Server Error". The status code for such responses can be - * retrieved by calling response.status(). - * - * NOTE: `page.goto` either throws an error or returns a main resource - * response. The only exceptions are navigation to about:blank or navigation - * to the same URL with a different hash, which would succeed and return null. - * - * NOTE: Headless mode doesn't support navigation to a PDF document. See the - * {@link https://bugs.chromium.org/p/chromium/issues/detail?id=761295 | - * upstream issue}. - * - * Shortcut for {@link Frame.goto | page.mainFrame().goto(url, options)}. + * This method will not throw an error when any valid HTTP status code is + * returned by the remote server, including 404 "Not Found" and 500 "Internal + * Server Error". The status code for such responses can be retrieved by + * calling {@link HTTPResponse.status}. */ - async goto( - url: string, - options?: WaitForOptions & {referer?: string; referrerPolicy?: string} - ): Promise { + async goto(url: string, options?: GoToOptions): Promise { return await this.mainFrame().goto(url, options); } /** - * @param options - Navigation parameters which might have the following - * properties: - * @returns Promise which resolves to the main resource response. In case of + * Reloads the page. + * + * @param options - Options to configure waiting behavior. + * @returns A promise which resolves to the main resource response. In case of * multiple redirects, the navigation will resolve with the response of the * last redirect. - * @remarks - * The argument `options` might have the following properties: - * - * - `timeout` : Maximum navigation time in milliseconds, defaults to 30 - * seconds, pass 0 to disable timeout. The default value can be changed by - * using the {@link Page.setDefaultNavigationTimeout} or - * {@link Page.setDefaultTimeout} methods. - * - * - `waitUntil`: When to consider navigation succeeded, defaults to `load`. - * Given an array of event strings, navigation is considered to be - * successful after all events have been fired. Events can be either:
- * - `load` : consider navigation to be finished when the load event is - * fired.
- * - `domcontentloaded` : consider navigation to be finished when the - * DOMContentLoaded event is fired.
- * - `networkidle0` : consider navigation to be finished when there are no - * more than 0 network connections for at least `500` ms.
- * - `networkidle2` : consider navigation to be finished when there are no - * more than 2 network connections for at least `500` ms. */ async reload(options?: WaitForOptions): Promise; async reload(): Promise { diff --git a/packages/puppeteer-core/src/bidi/BrowsingContext.ts b/packages/puppeteer-core/src/bidi/BrowsingContext.ts index 16f9bae8b54..c5bdb13687a 100644 --- a/packages/puppeteer-core/src/bidi/BrowsingContext.ts +++ b/packages/puppeteer-core/src/bidi/BrowsingContext.ts @@ -1,17 +1,11 @@ -import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; +import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import type ProtocolMapping from 'devtools-protocol/types/protocol-mapping.js'; import {CDPSession} from '../api/CDPSession.js'; -import {type WaitForOptions} from '../api/Page.js'; import {type Connection as CdpConnection} from '../cdp/Connection.js'; import {type PuppeteerLifeCycleEvent} from '../cdp/LifecycleWatcher.js'; -import { - ProtocolError, - TargetCloseError, - TimeoutError, -} from '../common/Errors.js'; +import {TargetCloseError} from '../common/Errors.js'; import {type EventType} from '../common/EventEmitter.js'; -import {setPageContent, waitWithTimeout} from '../common/util.js'; import {assert} from '../util/assert.js'; import {Deferred} from '../util/Deferred.js'; @@ -30,17 +24,6 @@ export const lifeCycleToSubscribedEvent = new Map< ['domcontentloaded', 'browsingContext.domContentLoaded'], ]); -/** - * @internal - */ -const lifeCycleToReadinessState = new Map< - PuppeteerLifeCycleEvent, - Bidi.BrowsingContext.ReadinessState ->([ - ['load', Bidi.BrowsingContext.ReadinessState.Complete], - ['domcontentloaded', Bidi.BrowsingContext.ReadinessState.Interactive], -]); - /** * @internal */ @@ -202,101 +185,6 @@ export class BrowsingContext extends BidiRealm { return this.#cdpSession; } - async goto( - url: string, - options: { - referer?: string; - referrerPolicy?: string; - timeout: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - } - ): Promise { - const {waitUntil = 'load', timeout} = options; - - const readinessState = lifeCycleToReadinessState.get( - getWaitUntilSingle(waitUntil) - ) as Bidi.BrowsingContext.ReadinessState; - - try { - const {result} = await waitWithTimeout( - this.connection.send('browsingContext.navigate', { - url: url, - context: this.#id, - wait: readinessState, - }), - 'Navigation', - timeout - ); - this.#url = result.url; - - return result.navigation; - } catch (error) { - if (error instanceof ProtocolError) { - error.message += ` at ${url}`; - } else if (error instanceof TimeoutError) { - error.message = 'Navigation timeout of ' + timeout + ' ms exceeded'; - } - throw error; - } - } - - async reload( - options: WaitForOptions & {timeout: number} - ): Promise { - const {waitUntil = 'load', timeout} = options; - - const readinessState = lifeCycleToReadinessState.get( - getWaitUntilSingle(waitUntil) - ) as Bidi.BrowsingContext.ReadinessState; - - try { - const {result} = await waitWithTimeout( - this.connection.send('browsingContext.reload', { - context: this.#id, - wait: readinessState, - }), - 'Navigation', - timeout - ); - - return result.navigation; - } catch (error) { - if (error instanceof ProtocolError) { - error.message += ` at ${this.url}`; - } else if (error instanceof TimeoutError) { - error.message = 'Navigation timeout of ' + timeout + ' ms exceeded'; - } - throw error; - } - } - - async setContent( - html: string, - options: { - timeout: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - } - ): Promise { - const {waitUntil = 'load', timeout} = options; - - const waitUntilEvent = lifeCycleToSubscribedEvent.get( - getWaitUntilSingle(waitUntil) - ) as string; - - await Promise.all([ - setPageContent(this, html), - waitWithTimeout( - new Promise(resolve => { - this.once(waitUntilEvent, () => { - resolve(); - }); - }), - waitUntilEvent, - timeout - ), - ]); - } - async sendCdpCommand( method: T, ...paramArgs: ProtocolMapping.Commands[T]['paramsType'] @@ -304,12 +192,6 @@ export class BrowsingContext extends BidiRealm { return await this.#cdpSession.send(method, ...paramArgs); } - title(): Promise { - return this.evaluate(() => { - return document.title; - }); - } - dispose(): void { this.removeAllListeners(); this.connection.unregisterBrowsingContexts(this.#id); diff --git a/packages/puppeteer-core/src/bidi/Frame.ts b/packages/puppeteer-core/src/bidi/Frame.ts index b91a0d0d496..94381be16c6 100644 --- a/packages/puppeteer-core/src/bidi/Frame.ts +++ b/packages/puppeteer-core/src/bidi/Frame.ts @@ -17,17 +17,28 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import {type CDPSession} from '../api/CDPSession.js'; -import {Frame, throwIfDetached} from '../api/Frame.js'; +import { + Frame, + type GoToOptions, + type WaitForOptions, + throwIfDetached, +} from '../api/Frame.js'; import {type PuppeteerLifeCycleEvent} from '../cdp/LifecycleWatcher.js'; +import {ProtocolError, TimeoutError} from '../common/Errors.js'; import {type TimeoutSettings} from '../common/TimeoutSettings.js'; import {type Awaitable} from '../common/types.js'; -import {UTILITY_WORLD_NAME, waitForEvent} from '../common/util.js'; +import { + UTILITY_WORLD_NAME, + setPageContent, + waitForEvent, + waitWithTimeout, +} from '../common/util.js'; import {Deferred} from '../util/Deferred.js'; import { - type BrowsingContext, getWaitUntilSingle, lifeCycleToSubscribedEvent, + type BrowsingContext, } from './BrowsingContext.js'; import {ExposeableFunction} from './ExposedFunction.js'; import {type BidiHTTPResponse} from './HTTPResponse.js'; @@ -39,6 +50,17 @@ import { type SandboxChart, } from './Sandbox.js'; +/** + * @internal + */ +export const lifeCycleToReadinessState = new Map< + PuppeteerLifeCycleEvent, + Bidi.BrowsingContext.ReadinessState +>([ + ['load', Bidi.BrowsingContext.ReadinessState.Complete], + ['domcontentloaded', Bidi.BrowsingContext.ReadinessState.Interactive], +]); + /** * Puppeteer's Frame class could be viewed as a BiDi BrowsingContext implementation * @internal @@ -107,32 +129,65 @@ export class BidiFrame extends Frame { @throwIfDetached override async goto( url: string, - options?: { - referer?: string; - referrerPolicy?: string; - timeout?: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - } + options: GoToOptions = {} ): Promise { - const navigationId = await this.#context.goto(url, { - ...options, - timeout: options?.timeout ?? this.#timeoutSettings.navigationTimeout(), - }); - return this.#page.getNavigationResponse(navigationId); + const { + waitUntil = 'load', + timeout = this.#timeoutSettings.navigationTimeout(), + } = options; + + const readinessState = lifeCycleToReadinessState.get( + getWaitUntilSingle(waitUntil) + ) as Bidi.BrowsingContext.ReadinessState; + + try { + const {result} = await waitWithTimeout( + this.#context.connection.send('browsingContext.navigate', { + url: url, + context: this._id, + wait: readinessState, + }), + 'Navigation', + timeout + ); + + return this.#page.getNavigationResponse(result.navigation); + } catch (error) { + if (error instanceof ProtocolError) { + error.message += ` at ${url}`; + } else if (error instanceof TimeoutError) { + error.message = 'Navigation timeout of ' + timeout + ' ms exceeded'; + } + throw error; + } } @throwIfDetached - override setContent( + override async setContent( html: string, - options: { - timeout?: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - } + options: WaitForOptions = {} ): Promise { - return this.#context.setContent(html, { - ...options, - timeout: options?.timeout ?? this.#timeoutSettings.navigationTimeout(), - }); + const { + waitUntil = 'load', + timeout = this.#timeoutSettings.navigationTimeout(), + } = options; + + const waitUntilEvent = lifeCycleToSubscribedEvent.get( + getWaitUntilSingle(waitUntil) + ) as string; + + await Promise.all([ + setPageContent(this, html), + waitWithTimeout( + new Promise(resolve => { + this.#context.once(waitUntilEvent, () => { + resolve(); + }); + }), + waitUntilEvent, + timeout + ), + ]); } context(): BrowsingContext { @@ -141,10 +196,7 @@ export class BidiFrame extends Frame { @throwIfDetached override async waitForNavigation( - options: { - timeout?: number; - waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; - } = {} + options: WaitForOptions = {} ): Promise { const { waitUntil = 'load', diff --git a/packages/puppeteer-core/src/bidi/Page.ts b/packages/puppeteer-core/src/bidi/Page.ts index 2168111aeae..b6c99ac851b 100644 --- a/packages/puppeteer-core/src/bidi/Page.ts +++ b/packages/puppeteer-core/src/bidi/Page.ts @@ -20,14 +20,14 @@ import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import type Protocol from 'devtools-protocol'; import {type CDPSession} from '../api/CDPSession.js'; +import {type WaitForOptions} from '../api/Frame.js'; import { + Page, + PageEvent, type GeolocationOptions, type MediaFeature, type NewDocumentScriptEvaluation, - Page, - PageEvent, type ScreenshotOptions, - type WaitForOptions, } from '../api/Page.js'; import {Accessibility} from '../cdp/Accessibility.js'; import {Coverage} from '../cdp/Coverage.js'; @@ -39,7 +39,11 @@ import { ConsoleMessage, type ConsoleMessageLocation, } from '../common/ConsoleMessage.js'; -import {TargetCloseError} from '../common/Errors.js'; +import { + ProtocolError, + TargetCloseError, + TimeoutError, +} from '../common/Errors.js'; import {type Handler} from '../common/EventEmitter.js'; import {type PDFOptions} from '../common/PDFOptions.js'; import {TimeoutSettings} from '../common/TimeoutSettings.js'; @@ -59,14 +63,15 @@ import {Deferred} from '../util/Deferred.js'; import {type BidiBrowser} from './Browser.js'; import {type BidiBrowserContext} from './BrowserContext.js'; import { - type BrowsingContext, BrowsingContextEvent, CdpSessionWrapper, + getWaitUntilSingle, + type BrowsingContext, } from './BrowsingContext.js'; import {type BidiConnection} from './Connection.js'; import {BidiDialog} from './Dialog.js'; import {EmulationManager} from './EmulationManager.js'; -import {BidiFrame} from './Frame.js'; +import {BidiFrame, lifeCycleToReadinessState} from './Frame.js'; import {type BidiHTTPRequest} from './HTTPRequest.js'; import {type BidiHTTPResponse} from './HTTPResponse.js'; import {BidiKeyboard, BidiMouse, BidiTouchscreen} from './Input.js'; @@ -414,15 +419,36 @@ export class BidiPage extends Page { } override async reload( - options?: WaitForOptions + options: WaitForOptions = {} ): Promise { - const navigationId = await this.mainFrame() - .context() - .reload({ - ...options, - timeout: options?.timeout ?? this.#timeoutSettings.navigationTimeout(), - }); - return this.getNavigationResponse(navigationId); + const { + waitUntil = 'load', + timeout = this.#timeoutSettings.navigationTimeout(), + } = options; + + const readinessState = lifeCycleToReadinessState.get( + getWaitUntilSingle(waitUntil) + ) as Bidi.BrowsingContext.ReadinessState; + + try { + const {result} = await waitWithTimeout( + this.#connection.send('browsingContext.reload', { + context: this.mainFrame()._id, + wait: readinessState, + }), + 'Navigation', + timeout + ); + + return this.getNavigationResponse(result.navigation); + } catch (error) { + if (error instanceof ProtocolError) { + error.message += ` at ${this.url}`; + } else if (error instanceof TimeoutError) { + error.message = 'Navigation timeout of ' + timeout + ' ms exceeded'; + } + throw error; + } } override setDefaultNavigationTimeout(timeout: number): void { diff --git a/packages/puppeteer-core/src/cdp/LifecycleWatcher.ts b/packages/puppeteer-core/src/cdp/LifecycleWatcher.ts index 490d5b9c192..d4290199eec 100644 --- a/packages/puppeteer-core/src/cdp/LifecycleWatcher.ts +++ b/packages/puppeteer-core/src/cdp/LifecycleWatcher.ts @@ -27,13 +27,28 @@ import {type CdpFrame} from './Frame.js'; import {FrameManagerEvent} from './FrameManager.js'; import {type CdpHTTPRequest} from './HTTPRequest.js'; import {type NetworkManager, NetworkManagerEvent} from './NetworkManager.js'; + /** * @public */ export type PuppeteerLifeCycleEvent = + /** + * Waits for the 'load' event. + */ | 'load' + /** + * Waits for the 'DOMContentLoaded' event. + */ | 'domcontentloaded' + /** + * Waits till there are no more than 0 network connections for at least `500` + * ms. + */ | 'networkidle0' + /** + * Waits till there are no more than 2 network connections for at least `500` + * ms. + */ | 'networkidle2'; /** diff --git a/packages/puppeteer-core/src/cdp/Page.ts b/packages/puppeteer-core/src/cdp/Page.ts index 608c7937f53..b4bf27c9f9a 100644 --- a/packages/puppeteer-core/src/cdp/Page.ts +++ b/packages/puppeteer-core/src/cdp/Page.ts @@ -20,22 +20,21 @@ import {Protocol} from 'devtools-protocol'; import type {Browser} from '../api/Browser.js'; import type {BrowserContext} from '../api/BrowserContext.js'; -import {type CDPSession, CDPSessionEvent} from '../api/CDPSession.js'; +import {CDPSessionEvent, type CDPSession} from '../api/CDPSession.js'; import {type ElementHandle} from '../api/ElementHandle.js'; -import {type Frame} from '../api/Frame.js'; +import {type WaitForOptions, type Frame} from '../api/Frame.js'; import {type HTTPRequest} from '../api/HTTPRequest.js'; import {type HTTPResponse} from '../api/HTTPResponse.js'; import {type JSHandle} from '../api/JSHandle.js'; import { + Page, + PageEvent, type GeolocationOptions, type MediaFeature, type Metrics, type NewDocumentScriptEvaluation, - Page, - PageEvent, type ScreenshotClip, type ScreenshotOptions, - type WaitForOptions, type WaitTimeoutOptions, } from '../api/Page.js'; import { @@ -80,9 +79,9 @@ import {FrameManager, FrameManagerEvent} from './FrameManager.js'; import {CdpKeyboard, CdpMouse, CdpTouchscreen} from './Input.js'; import {MAIN_WORLD} from './IsolatedWorlds.js'; import { + NetworkManagerEvent, type Credentials, type NetworkConditions, - NetworkManagerEvent, } from './NetworkManager.js'; import {type CdpTarget} from './Target.js'; import {TargetManagerEvent} from './TargetManager.js'; @@ -865,12 +864,12 @@ export class CdpPage extends Page { override async reload( options?: WaitForOptions ): Promise { - const result = await Promise.all([ + const [result] = await Promise.all([ this.waitForNavigation(options), this.#client.send('Page.reload'), ]); - return result[0]; + return result; } override async createCDPSession(): Promise {