feat!: switch to Chrome for Testing instead of Chromium (#10054)

Co-authored-by: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com>
This commit is contained in:
Alex Rudenko 2023-05-02 08:53:40 +02:00 committed by GitHub
parent edbfff7b04
commit df4d60c187
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 259 additions and 2454 deletions

View File

@ -150,12 +150,12 @@ jobs:
PUPPETEER_SKIP_DOWNLOAD: true
- name: Build packages
run: npm run build --workspace @puppeteer-test/test
- name: Setup cache for Chromium binary
- name: Setup cache for Chrome binary
uses: actions/cache@v3
with:
path: ~/.cache/puppeteer/chrome
key: ${{ runner.os }}-chromium-${{ hashFiles('packages/puppeteer-core/src/revisions.ts') }}-${{ hashFiles('packages/puppeteer/src/node/install.ts') }}
- name: Install Chromium
key: ${{ runner.os }}-Chrome-${{ hashFiles('packages/puppeteer-core/src/revisions.ts') }}-${{ hashFiles('packages/puppeteer/src/node/install.ts') }}
- name: Install Chrome
run: npm run postinstall
- name: Tests types
run: npm run test-types

View File

@ -1,54 +0,0 @@
name: ToT CI
# Checks Puppeteer against the latest ToT build of Chromium.
# Declare default permissions as read only.
permissions: read-all
on:
workflow_dispatch:
schedule:
# * is a special character in YAML so you have to quote this string
# Supposed to be every day at 8 am (UTC).
- cron: '0 8 * * *'
# TODO: how to make install & build steps re-usable across jobs.
# Currently, the install step is duplicated but should be the same for all jobs.
jobs:
ci:
name: ${{ matrix.suite }} tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
suite:
- chrome-headless
- chrome-headful
- chrome-new-headless
steps:
- name: Check out repository
uses: actions/checkout@v3.0.2
- name: Set up Node.js
uses: actions/setup-node@v3.5.1
with:
cache: npm
node-version: lts/*
- name: Install dependencies
run: npm ci
env:
PUPPETEER_SKIP_DOWNLOAD: true
- name: Install linux dependencies.
run: sudo apt-get install xvfb
- name: Build packages
run: npm run build
- name: Get latest revision
run: |
REV=$(node tools/check_availability.js -p linux)
cat packages/puppeteer-core/src/revisions.ts | sed "s/[0-9]\{6,\}/$REV/" > packages/puppeteer-core/src/revisions.ts.replaced
mv packages/puppeteer-core/src/revisions.ts.replaced packages/puppeteer-core/src/revisions.ts
- name: Rebuild `puppeteer-core`
run: npm run build --workspace puppeteer-core
- name: Install Chrome
run: npm run postinstall --workspace puppeteer
- name: Run tests
run: xvfb-run --auto-servernum npm run test -- --test-suite ${{ matrix.suite }}

View File

@ -12,7 +12,7 @@
> [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
> Puppeteer runs in
> [headless](https://developer.chrome.com/articles/new-headless/)
> mode by default, but can be configured to run in full (non-headless)
> mode by default, but can be configured to run in full ("headful")
> Chrome/Chromium.
#### What can I do?
@ -46,7 +46,7 @@ pnpm i puppeteer
```
When you install Puppeteer, it automatically downloads a recent version of
Chromium (~170MB macOS, ~282MB Linux, ~280MB Windows) that is [guaranteed to
[Chrome for Testing](https://goo.gle/chrome-for-testing) (~170MB macOS, ~282MB Linux, ~280MB Windows) that is [guaranteed to
work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
with Puppeteer. The browser is downloaded to the `$HOME/.cache/puppeteer` folder
by default (starting with Puppeteer v19.0.0).
@ -94,14 +94,14 @@ Every release since v1.7.0 we publish two packages:
- [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core)
`puppeteer` is a _product_ for browser automation. When installed, it downloads
a version of Chromium, which it then drives using `puppeteer-core`. Being an
a version of Chrome, which it then drives using `puppeteer-core`. Being an
end-user product, `puppeteer` automates several workflows using reasonable
defaults [that can be customized](https://pptr.dev/guides/configuration).
`puppeteer-core` is a _library_ to help drive anything that supports DevTools
protocol. Being a library, `puppeteer-core` is fully driven through its
programmatic interface implying no defaults are assumed and `puppeteer-core`
will not download Chromium when installed.
will not download Chrome when installed.
You should use `puppeteer-core` if you are
[connecting to a remote browser](https://pptr.dev/api/puppeteer.puppeteer.connect)
@ -176,7 +176,7 @@ import puppeteer from 'puppeteer';
**1. Uses Headless mode**
By default Puppeteer launches Chromium in
By default Puppeteer launches Chrome in
[old Headless mode](https://developer.chrome.com/articles/new-headless/).
```ts
@ -193,7 +193,7 @@ We recommend you try it out before the switch:
const browser = await puppeteer.launch({headless: 'new'});
```
To launch a "headful" version of Chromium, set the
To launch a "headful" version of Chrome, set the
[`headless`](https://pptr.dev/api/puppeteer.browserlaunchargumentoptions) to `false`
option when launching a browser:
@ -201,9 +201,9 @@ option when launching a browser:
const browser = await puppeteer.launch({headless: false});
```
**2. Runs a bundled version of Chromium**
**2. Runs a bundled version of Chrome**
By default, Puppeteer downloads and uses a specific version of Chromium so its
By default, Puppeteer downloads and uses a specific version of Chrome so its
API is guaranteed to work out of the box. To use Puppeteer with a different
version of Chrome or Chromium, pass in the executable's path when creating a
`Browser` instance:

View File

@ -1,7 +1,7 @@
FROM node:18
# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
# Note: this installs the necessary libs to make the bundled version of Chrome that Puppeteer
# installs, work.
RUN apt-get update \
&& apt-get install -y wget gnupg \

View File

@ -16,7 +16,7 @@ docker build -t puppeteer-chrome-linux . # `puppeteer-chrome-linux` is the name
docker run -i --init --rm --cap-add=SYS_ADMIN --name puppeteer-chrome puppeteer-chrome-linux node -e "`cat test.js`"
```
`--cap-add=SYS_ADMIN` capability is needed to enable Chromium sandbox that makes the browser more secure. Alternatively, it should be possible to start the browser binary with the `--no-sandbox` flag.
`--cap-add=SYS_ADMIN` capability is needed to enable Chrome sandbox that makes the browser more secure. Alternatively, it should be possible to start the browser binary with the `--no-sandbox` flag.
## GitHub Actions

View File

@ -9,10 +9,9 @@ sidebar_label: API
| Class | Description |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [AbortError](./puppeteer.aborterror.md) | AbortError is emitted whenever certain operations are terminated due to an abort request. |
| [Accessibility](./puppeteer.accessibility.md) | The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access). |
| [Browser](./puppeteer.browser.md) | A Browser is created when Puppeteer connects to a Chromium instance, either through [PuppeteerNode.launch()](./puppeteer.puppeteernode.launch.md) or [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
| [Accessibility](./puppeteer.accessibility.md) | The Accessibility class provides methods for inspecting the browser's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access). |
| [Browser](./puppeteer.browser.md) | A Browser is created when Puppeteer connects to a browser instance, either through [PuppeteerNode.launch()](./puppeteer.puppeteernode.launch.md) or [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
| [BrowserContext](./puppeteer.browsercontext.md) | BrowserContexts provide a way to operate multiple independent browser sessions. When a browser is launched, it has a single BrowserContext used by default. The method [Browser.newPage](./puppeteer.browser.newpage.md) creates a page in the default browser context. |
| [BrowserFetcher](./puppeteer.browserfetcher.md) | BrowserFetcher can download and manage different versions of Chromium and Firefox. |
| [CDPSession](./puppeteer.cdpsession.md) | The <code>CDPSession</code> instances are used to talk raw Chrome Devtools Protocol. |
| [Connection](./puppeteer.connection.md) | |
| [ConsoleMessage](./puppeteer.consolemessage.md) | ConsoleMessage objects are dispatched by page via the 'console' event. |
@ -32,7 +31,7 @@ sidebar_label: API
| [JSHandle](./puppeteer.jshandle.md) | <p>Represents a reference to a JavaScript object. Instances can be created using [Page.evaluateHandle()](./puppeteer.page.evaluatehandle.md).</p><p>Handles prevent the referenced JavaScript object from being garbage-collected unless the handle is purposely [disposed](./puppeteer.jshandle.dispose.md). JSHandles are auto-disposed when their associated frame is navigated away or the parent context gets destroyed.</p><p>Handles can be used as arguments for any evaluation function such as [Page.$eval()](./puppeteer.page._eval.md), [Page.evaluate()](./puppeteer.page.evaluate.md), and [Page.evaluateHandle()](./puppeteer.page.evaluatehandle.md). They are resolved to their referenced object.</p> |
| [Keyboard](./puppeteer.keyboard.md) | Keyboard provides an api for managing a virtual keyboard. The high level api is [Keyboard.type()](./puppeteer.keyboard.type.md), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page. |
| [Mouse](./puppeteer.mouse.md) | The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport. |
| [Page](./puppeteer.page.md) | <p>Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium.</p><p>:::note</p><p>One Browser instance might have multiple Page instances.</p><p>:::</p> |
| [Page](./puppeteer.page.md) | <p>Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in the browser.</p><p>:::note</p><p>One Browser instance might have multiple Page instances.</p><p>:::</p> |
| [ProductLauncher](./puppeteer.productlauncher.md) | Describes a launcher - a class that is able to create and launch a browser instance. |
| [ProtocolError](./puppeteer.protocolerror.md) | ProtocolError is emitted whenever there is an error from the protocol. |
| [Puppeteer](./puppeteer.puppeteer.md) | <p>The main Puppeteer class.</p><p>IMPORTANT: if you are using Puppeteer in a Node environment, you will get an instance of [PuppeteerNode](./puppeteer.puppeteernode.md) when you import or require <code>puppeteer</code>. That class extends <code>Puppeteer</code>, so has all the methods documented below as well as all that are defined on [PuppeteerNode](./puppeteer.puppeteernode.md).</p> |
@ -70,8 +69,6 @@ sidebar_label: API
| [BoxModel](./puppeteer.boxmodel.md) | |
| [BrowserConnectOptions](./puppeteer.browserconnectoptions.md) | Generic browser options that can be passed when launching any browser or when connecting to an existing browser instance. |
| [BrowserContextOptions](./puppeteer.browsercontextoptions.md) | BrowserContext options. |
| [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | |
| [BrowserFetcherRevisionInfo](./puppeteer.browserfetcherrevisioninfo.md) | |
| [BrowserLaunchArgumentOptions](./puppeteer.browserlaunchargumentoptions.md) | Launcher options that only apply to Chrome. |
| [CDPSessionOnMessageObject](./puppeteer.cdpsessiononmessageobject.md) | |
| [ClickOptions](./puppeteer.clickoptions.md) | |
@ -86,7 +83,6 @@ sidebar_label: API
| [CSSCoverageOptions](./puppeteer.csscoverageoptions.md) | Set of configurable options for CSS coverage. |
| [CustomQueryHandler](./puppeteer.customqueryhandler.md) | |
| [Device](./puppeteer.device.md) | |
| [ExperimentsConfiguration](./puppeteer.experimentsconfiguration.md) | <p>Defines experiment options for Puppeteer.</p><p>See individual properties for more information.</p> |
| [FrameAddScriptTagOptions](./puppeteer.frameaddscripttagoptions.md) | |
| [FrameAddStyleTagOptions](./puppeteer.frameaddstyletagoptions.md) | |
| [FrameWaitForFunctionOptions](./puppeteer.framewaitforfunctionoptions.md) | |
@ -129,7 +125,6 @@ sidebar_label: API
| Variable | Description |
| --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| [connect](./puppeteer.connect.md) | |
| [createBrowserFetcher](./puppeteer.createbrowserfetcher.md) | |
| [DEFAULT_INTERCEPT_RESOLUTION_PRIORITY](./puppeteer.default_intercept_resolution_priority.md) | The default cooperative request interception resolution priority |
| [defaultArgs](./puppeteer.defaultargs.md) | |
| [devices](./puppeteer.devices.md) | |
@ -157,6 +152,7 @@ sidebar_label: API
| [EvaluateFunc](./puppeteer.evaluatefunc.md) | |
| [EvaluateFuncWith](./puppeteer.evaluatefuncwith.md) | |
| [EventType](./puppeteer.eventtype.md) | |
| [ExperimentsConfiguration](./puppeteer.experimentsconfiguration.md) | <p>Defines experiment options for Puppeteer.</p><p>See individual properties for more information.</p> |
| [FlattenHandle](./puppeteer.flattenhandle.md) | |
| [HandleFor](./puppeteer.handlefor.md) | |
| [HandleOr](./puppeteer.handleor.md) | |
@ -169,7 +165,6 @@ sidebar_label: API
| [NodeFor](./puppeteer.nodefor.md) | |
| [PaperFormat](./puppeteer.paperformat.md) | All the valid paper format types when printing a PDF. |
| [Permission](./puppeteer.permission.md) | |
| [Platform](./puppeteer.platform.md) | Supported platforms. |
| [Product](./puppeteer.product.md) | Supported products. |
| [ProtocolLifeCycleEvent](./puppeteer.protocollifecycleevent.md) | |
| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) | |

View File

@ -4,7 +4,7 @@ sidebar_label: Accessibility
# Accessibility class
The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access).
The Accessibility class provides methods for inspecting the browser's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access).
#### Signature:

View File

@ -28,7 +28,7 @@ An AXNode object representing the snapshot.
## Remarks
**NOTE** The Chromium accessibility tree contains nodes that go unused on most platforms and by most screen readers. Puppeteer will discard them as well for an easier to process tree, unless `interestingOnly` is set to `false`.
**NOTE** The Chrome accessibility tree contains nodes that go unused on most platforms and by most screen readers. Puppeteer will discard them as well for an easier to process tree, unless `interestingOnly` is set to `false`.
## Example 1

View File

@ -4,7 +4,7 @@ sidebar_label: Browser.close
# Browser.close() method
Closes Chromium and all of its pages (if any were opened). The [Browser](./puppeteer.browser.md) object itself is considered to be disposed and cannot be used anymore.
Closes the browser and all of its pages (if any were opened). The [Browser](./puppeteer.browser.md) object itself is considered to be disposed and cannot be used anymore.
#### Signature:

View File

@ -4,7 +4,7 @@ sidebar_label: Browser.disconnect
# Browser.disconnect() method
Disconnects Puppeteer from the browser, but leaves the Chromium process running. After calling `disconnect`, the [Browser](./puppeteer.browser.md) object is considered disposed and cannot be used anymore.
Disconnects Puppeteer from the browser, but leaves the browser process running. After calling `disconnect`, the [Browser](./puppeteer.browser.md) object is considered disposed and cannot be used anymore.
#### Signature:

View File

@ -4,7 +4,7 @@ sidebar_label: Browser
# Browser class
A Browser is created when Puppeteer connects to a Chromium instance, either through [PuppeteerNode.launch()](./puppeteer.puppeteernode.launch.md) or [Puppeteer.connect()](./puppeteer.puppeteer.connect.md).
A Browser is created when Puppeteer connects to a browser instance, either through [PuppeteerNode.launch()](./puppeteer.puppeteernode.launch.md) or [Puppeteer.connect()](./puppeteer.puppeteer.connect.md).
#### Signature:
@ -44,34 +44,34 @@ import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
// Store the endpoint to be able to reconnect to Chromium
// Store the endpoint to be able to reconnect to the browser.
const browserWSEndpoint = browser.wsEndpoint();
// Disconnect puppeteer from Chromium
// Disconnect puppeteer from the browser.
browser.disconnect();
// Use the endpoint to reestablish a connection
const browser2 = await puppeteer.connect({browserWSEndpoint});
// Close Chromium
// Close the browser.
await browser2.close();
})();
```
## Methods
| Method | Modifiers | Description |
| ---------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [browserContexts()](./puppeteer.browser.browsercontexts.md) | | Returns an array of all open browser contexts. In a newly created browser, this will return a single instance of [BrowserContext](./puppeteer.browsercontext.md). |
| [close()](./puppeteer.browser.close.md) | | Closes Chromium and all of its pages (if any were opened). The [Browser](./puppeteer.browser.md) object itself is considered to be disposed and cannot be used anymore. |
| [createIncognitoBrowserContext(options)](./puppeteer.browser.createincognitobrowsercontext.md) | | Creates a new incognito browser context. This won't share cookies/cache with other browser contexts. |
| [defaultBrowserContext()](./puppeteer.browser.defaultbrowsercontext.md) | | Returns the default browser context. The default browser context cannot be closed. |
| [disconnect()](./puppeteer.browser.disconnect.md) | | Disconnects Puppeteer from the browser, but leaves the Chromium process running. After calling <code>disconnect</code>, the [Browser](./puppeteer.browser.md) object is considered disposed and cannot be used anymore. |
| [isConnected()](./puppeteer.browser.isconnected.md) | | Indicates that the browser is connected. |
| [newPage()](./puppeteer.browser.newpage.md) | | Promise which resolves to a new [Page](./puppeteer.page.md) object. The Page is created in a default browser context. |
| [pages()](./puppeteer.browser.pages.md) | | An array of all open pages inside the Browser. |
| [process()](./puppeteer.browser.process.md) | | The spawned browser process. Returns <code>null</code> if the browser instance was created with [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
| [target()](./puppeteer.browser.target.md) | | The target associated with the browser. |
| [targets()](./puppeteer.browser.targets.md) | | All active targets inside the Browser. In case of multiple browser contexts, returns an array with all the targets in all browser contexts. |
| [userAgent()](./puppeteer.browser.useragent.md) | | The browser's original user agent. Pages can override the browser user agent with [Page.setUserAgent()](./puppeteer.page.setuseragent.md). |
| [version()](./puppeteer.browser.version.md) | | A string representing the browser name and version. |
| [waitForTarget(predicate, options)](./puppeteer.browser.waitfortarget.md) | | Searches for a target in all browser contexts. |
| [wsEndpoint()](./puppeteer.browser.wsendpoint.md) | | The browser websocket endpoint which can be used as an argument to [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
| Method | Modifiers | Description |
| ---------------------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [browserContexts()](./puppeteer.browser.browsercontexts.md) | | Returns an array of all open browser contexts. In a newly created browser, this will return a single instance of [BrowserContext](./puppeteer.browsercontext.md). |
| [close()](./puppeteer.browser.close.md) | | Closes the browser and all of its pages (if any were opened). The [Browser](./puppeteer.browser.md) object itself is considered to be disposed and cannot be used anymore. |
| [createIncognitoBrowserContext(options)](./puppeteer.browser.createincognitobrowsercontext.md) | | Creates a new incognito browser context. This won't share cookies/cache with other browser contexts. |
| [defaultBrowserContext()](./puppeteer.browser.defaultbrowsercontext.md) | | Returns the default browser context. The default browser context cannot be closed. |
| [disconnect()](./puppeteer.browser.disconnect.md) | | Disconnects Puppeteer from the browser, but leaves the browser process running. After calling <code>disconnect</code>, the [Browser](./puppeteer.browser.md) object is considered disposed and cannot be used anymore. |
| [isConnected()](./puppeteer.browser.isconnected.md) | | Indicates that the browser is connected. |
| [newPage()](./puppeteer.browser.newpage.md) | | Promise which resolves to a new [Page](./puppeteer.page.md) object. The Page is created in a default browser context. |
| [pages()](./puppeteer.browser.pages.md) | | An array of all open pages inside the Browser. |
| [process()](./puppeteer.browser.process.md) | | The spawned browser process. Returns <code>null</code> if the browser instance was created with [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
| [target()](./puppeteer.browser.target.md) | | The target associated with the browser. |
| [targets()](./puppeteer.browser.targets.md) | | All active targets inside the Browser. In case of multiple browser contexts, returns an array with all the targets in all browser contexts. |
| [userAgent()](./puppeteer.browser.useragent.md) | | The browser's original user agent. Pages can override the browser user agent with [Page.setUserAgent()](./puppeteer.page.setuseragent.md). |
| [version()](./puppeteer.browser.version.md) | | A string representing the browser name and version. |
| [waitForTarget(predicate, options)](./puppeteer.browser.waitfortarget.md) | | Searches for a target in all browser contexts. |
| [wsEndpoint()](./puppeteer.browser.wsendpoint.md) | | The browser websocket endpoint which can be used as an argument to [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |

View File

@ -20,6 +20,6 @@ Promise&lt;string&gt;
## Remarks
For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For non-headless, this is similar to `Chrome/61.0.3153.0`.
For headless browser, this is similar to `HeadlessChrome/61.0.3153.0`. For non-headless, this is similar to `Chrome/61.0.3153.0`.
The format of browser.version() might change with future releases of Chromium.
The format of browser.version() might change with future releases of browsers.

View File

@ -16,7 +16,7 @@ export declare const enum BrowserEmittedEvents
| Member | Value | Description |
| --------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Disconnected | <code>&quot;disconnected&quot;</code> | <p>Emitted when Puppeteer gets disconnected from the Chromium instance. This might happen because of one of the following:</p><p>- Chromium is closed or crashed</p><p>- The [browser.disconnect](./puppeteer.browser.disconnect.md) method was called.</p> |
| Disconnected | <code>&quot;disconnected&quot;</code> | <p>Emitted when Puppeteer gets disconnected from the browser instance. This might happen because of one of the following:</p><p>- browser is closed or crashed</p><p>- The [browser.disconnect](./puppeteer.browser.disconnect.md) method was called.</p> |
| TargetChanged | <code>&quot;targetchanged&quot;</code> | Emitted when the url of a target changes. Contains a [Target](./puppeteer.target.md) instance. |
| TargetCreated | <code>&quot;targetcreated&quot;</code> | <p>Emitted when a target is created, for example when a new page is opened by [window.open](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) or by [browser.newPage](./puppeteer.browser.newpage.md)</p><p>Contains a [Target](./puppeteer.target.md) instance.</p> |
| TargetDestroyed | <code>&quot;targetdestroyed&quot;</code> | Emitted when a target is destroyed, for example when a page is closed. Contains a [Target](./puppeteer.target.md) instance. |

View File

@ -1,21 +0,0 @@
---
sidebar_label: BrowserFetcher.(constructor)
---
# BrowserFetcher.(constructor)
Constructs a browser fetcher for the given options.
#### Signature:
```typescript
class BrowserFetcher {
constructor(options: BrowserFetcherOptions);
}
```
## Parameters
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------- | ----------- |
| options | [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | |

View File

@ -1,31 +0,0 @@
---
sidebar_label: BrowserFetcher.canDownload
---
# BrowserFetcher.canDownload() method
Initiates a HEAD request to check if the revision is available.
#### Signature:
```typescript
class BrowserFetcher {
canDownload(revision: string): Promise<boolean>;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | ------ | --------------------------------------- |
| revision | string | The revision to check availability for. |
**Returns:**
Promise&lt;boolean&gt;
A promise that resolves to `true` if the revision could be downloaded from the host.
## Remarks
This method is affected by the current `product`.

View File

@ -1,35 +0,0 @@
---
sidebar_label: BrowserFetcher.download
---
# BrowserFetcher.download() method
Initiates a GET request to download the revision from the host.
#### Signature:
```typescript
class BrowserFetcher {
download(
revision: string,
progressCallback?: (x: number, y: number) => void
): Promise<BrowserFetcherRevisionInfo | undefined>;
}
```
## Parameters
| Parameter | Type | Description |
| ---------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| revision | string | The revision to download. |
| progressCallback | (x: number, y: number) =&gt; void | _(Optional)_ A function that will be called with two arguments: How many bytes have been downloaded and the total number of bytes of the download. |
**Returns:**
Promise&lt;[BrowserFetcherRevisionInfo](./puppeteer.browserfetcherrevisioninfo.md) \| undefined&gt;
A promise with revision information when the revision is downloaded and extracted.
## Remarks
This method is affected by the current `product`.

View File

@ -1,19 +0,0 @@
---
sidebar_label: BrowserFetcher.host
---
# BrowserFetcher.host() method
The download host being used.
#### Signature:
```typescript
class BrowserFetcher {
host(): string;
}
```
**Returns:**
string

View File

@ -1,23 +0,0 @@
---
sidebar_label: BrowserFetcher.localRevisions
---
# BrowserFetcher.localRevisions() method
#### Signature:
```typescript
class BrowserFetcher {
localRevisions(): string[];
}
```
**Returns:**
string\[\]
A list of all revision strings (for the current `product`) available locally on disk.
## Remarks
This method is affected by the current `product`.

View File

@ -1,48 +0,0 @@
---
sidebar_label: BrowserFetcher
---
# BrowserFetcher class
BrowserFetcher can download and manage different versions of Chromium and Firefox.
#### Signature:
```typescript
export declare class BrowserFetcher
```
## Remarks
BrowserFetcher is not designed to work concurrently with other instances of BrowserFetcher that share the same downloads directory.
## Example
An example of using BrowserFetcher to download a specific version of Chromium and running Puppeteer against it:
```ts
const browserFetcher = new BrowserFetcher({path: 'path/to/download/folder'});
const revisionInfo = await browserFetcher.download('533271');
const browser = await puppeteer.launch({
executablePath: revisionInfo.executablePath,
});
```
## Constructors
| Constructor | Modifiers | Description |
| --------------------------------------------------------------------- | --------- | --------------------------------------------------- |
| [(constructor)(options)](./puppeteer.browserfetcher._constructor_.md) | | Constructs a browser fetcher for the given options. |
## Methods
| Method | Modifiers | Description |
| ------------------------------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| [canDownload(revision)](./puppeteer.browserfetcher.candownload.md) | | Initiates a HEAD request to check if the revision is available. |
| [download(revision, progressCallback)](./puppeteer.browserfetcher.download.md) | | Initiates a GET request to download the revision from the host. |
| [host()](./puppeteer.browserfetcher.host.md) | | The download host being used. |
| [localRevisions()](./puppeteer.browserfetcher.localrevisions.md) | | |
| [platform()](./puppeteer.browserfetcher.platform.md) | | Returns the current <code>Platform</code>, which is one of <code>mac</code>, <code>linux</code>, <code>win32</code> or <code>win64</code>. |
| [product()](./puppeteer.browserfetcher.product.md) | | Returns the current <code>Product</code>, which is one of <code>chrome</code> or <code>firefox</code>. |
| [remove(revision)](./puppeteer.browserfetcher.remove.md) | | |
| [revisionInfo(revision)](./puppeteer.browserfetcher.revisioninfo.md) | | |

View File

@ -1,19 +0,0 @@
---
sidebar_label: BrowserFetcher.platform
---
# BrowserFetcher.platform() method
Returns the current `Platform`, which is one of `mac`, `linux`, `win32` or `win64`.
#### Signature:
```typescript
class BrowserFetcher {
platform(): Platform;
}
```
**Returns:**
[Platform](./puppeteer.platform.md)

View File

@ -1,19 +0,0 @@
---
sidebar_label: BrowserFetcher.product
---
# BrowserFetcher.product() method
Returns the current `Product`, which is one of `chrome` or `firefox`.
#### Signature:
```typescript
class BrowserFetcher {
product(): Product;
}
```
**Returns:**
[Product](./puppeteer.product.md)

View File

@ -1,29 +0,0 @@
---
sidebar_label: BrowserFetcher.remove
---
# BrowserFetcher.remove() method
#### Signature:
```typescript
class BrowserFetcher {
remove(revision: string): Promise<void>;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | ------ | ---------------------------------------------------------- |
| revision | string | A revision to remove for the current <code>product</code>. |
**Returns:**
Promise&lt;void&gt;
A promise that resolves when the revision has been removed or throws if the revision has not been downloaded.
## Remarks
This method is affected by the current `product`.

View File

@ -1,25 +0,0 @@
---
sidebar_label: BrowserFetcher.revisionInfo
---
# BrowserFetcher.revisionInfo() method
#### Signature:
```typescript
class BrowserFetcher {
revisionInfo(revision: string): BrowserFetcherRevisionInfo;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | ------ | ----------------------------- |
| revision | string | The revision to get info for. |
**Returns:**
[BrowserFetcherRevisionInfo](./puppeteer.browserfetcherrevisioninfo.md)
The revision info for the given revision.

View File

@ -1,21 +0,0 @@
---
sidebar_label: BrowserFetcherOptions
---
# BrowserFetcherOptions interface
#### Signature:
```typescript
export interface BrowserFetcherOptions
```
## Properties
| Property | Modifiers | Type | Description | Default |
| ----------------- | --------------------- | ----------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| host | <code>optional</code> | string | Determines the host that will be used for downloading. | <p>Either</p><p>- https://storage.googleapis.com or - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central</p> |
| path | | string | Determines the path to download browsers to. | |
| platform | <code>optional</code> | [Platform](./puppeteer.platform.md) | Determines which platform the browser will be suited for. | **Auto-detected.** |
| product | <code>optional</code> | 'chrome' \| 'firefox' | Determines which product the [BrowserFetcher](./puppeteer.browserfetcher.md) is for. | <code>chrome</code> |
| useMacOSARMBinary | <code>optional</code> | boolean | Enables the use of the Chromium binary for macOS ARM. | |

View File

@ -1,22 +0,0 @@
---
sidebar_label: BrowserFetcherRevisionInfo
---
# BrowserFetcherRevisionInfo interface
#### Signature:
```typescript
export interface BrowserFetcherRevisionInfo
```
## Properties
| Property | Modifiers | Type | Description | Default |
| -------------- | --------- | ------- | ----------- | ------- |
| executablePath | | string | | |
| folderPath | | string | | |
| local | | boolean | | |
| product | | string | | |
| revision | | string | | |
| url | | string | | |

View File

@ -21,7 +21,7 @@ export interface Configuration
| browserRevision | <code>optional</code> | string | <p>Specifies a certain version of the browser you'd like Puppeteer to use.</p><p>Can be overridden by <code>PUPPETEER_BROWSER_REVISION</code>.</p><p>See [puppeteer.launch](./puppeteer.puppeteernode.launch.md) on how executable path is inferred.</p> | A compatible-revision of the browser. |
| cacheDirectory | <code>optional</code> | string | <p>Defines the directory to be used by Puppeteer for caching.</p><p>Can be overridden by <code>PUPPETEER_CACHE_DIR</code>.</p> | <code>path.join(os.homedir(), '.cache', 'puppeteer')</code> |
| defaultProduct | <code>optional</code> | [Product](./puppeteer.product.md) | <p>Specifies which browser you'd like Puppeteer to use.</p><p>Can be overridden by <code>PUPPETEER_PRODUCT</code>.</p> | <code>chrome</code> |
| downloadHost | <code>optional</code> | string | <p>Specifies the URL prefix that is used to download Chromium.</p><p>Can be overridden by <code>PUPPETEER_DOWNLOAD_HOST</code>.</p> | Either https://storage.googleapis.com or https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central, depending on the product. |
| downloadHost | <code>optional</code> | string | <p>Specifies the URL prefix that is used to download the browser.</p><p>Can be overridden by <code>PUPPETEER_DOWNLOAD_HOST</code>.</p> | Either https://storage.googleapis.com or https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central, depending on the product. |
| downloadPath | <code>optional</code> | string | <p>Specifies the path for the downloads folder.</p><p>Can be overridden by <code>PUPPETEER_DOWNLOAD_PATH</code>.</p> | <code>&lt;cache&gt;/&lt;product&gt;</code> where <code>&lt;cache&gt;</code> is Puppeteer's cache directory and <code>&lt;product&gt;</code> is the name of the browser. |
| executablePath | <code>optional</code> | string | <p>Specifies an executable path to be used in [puppeteer.launch](./puppeteer.puppeteernode.launch.md).</p><p>Can be overridden by <code>PUPPETEER_EXECUTABLE_PATH</code>.</p> | **Auto-computed.** |
| experiments | <code>optional</code> | [ExperimentsConfiguration](./puppeteer.experimentsconfiguration.md) | Defines experimental options for Puppeteer. | |

View File

@ -1,17 +0,0 @@
---
sidebar_label: createBrowserFetcher
---
# createBrowserFetcher variable
#### Signature:
```typescript
createBrowserFetcher: (
options?:
| Partial<
import('puppeteer-core/internal/puppeteer-core.js').BrowserFetcherOptions
>
| undefined
) => import('puppeteer-core/internal/puppeteer-core.js').BrowserFetcher;
```

View File

@ -2,7 +2,7 @@
sidebar_label: ExperimentsConfiguration
---
# ExperimentsConfiguration interface
# ExperimentsConfiguration type
Defines experiment options for Puppeteer.
@ -11,11 +11,5 @@ See individual properties for more information.
#### Signature:
```typescript
export interface ExperimentsConfiguration
export type ExperimentsConfiguration = Record<string, never>;
```
## Properties
| Property | Modifiers | Type | Description | Default |
| --------------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| macArmChromiumEnabled | <code>optional</code> | boolean | <p>Require Puppeteer to download Chromium for Apple M1.</p><p>On Apple M1 devices Puppeteer by default downloads the version for Intel's processor which runs via Rosetta. It works without any problems, however, with this option, you should get more efficient resource usage (CPU and RAM) that could lead to a faster execution time.</p><p>Can be overridden by <code>PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM</code>.</p> | <code>false</code> |

View File

@ -4,7 +4,7 @@ sidebar_label: Page
# Page class
Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium.
Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in the browser.
:::note
@ -63,14 +63,14 @@ page.off('request', logRequest);
## Properties
| Property | Modifiers | Type | Description |
| ------------- | --------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| accessibility | <code>readonly</code> | [Accessibility](./puppeteer.accessibility.md) | The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access). |
| coverage | <code>readonly</code> | [Coverage](./puppeteer.coverage.md) | The Coverage class provides methods to gather information about parts of JavaScript and CSS that were used by the page. |
| keyboard | <code>readonly</code> | [Keyboard](./puppeteer.keyboard.md) | Keyboard provides an api for managing a virtual keyboard. The high level api is [Keyboard.type()](./puppeteer.keyboard.type.md), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page. |
| mouse | <code>readonly</code> | [Mouse](./puppeteer.mouse.md) | The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport. |
| touchscreen | <code>readonly</code> | [Touchscreen](./puppeteer.touchscreen.md) | The Touchscreen class exposes touchscreen events. |
| tracing | <code>readonly</code> | [Tracing](./puppeteer.tracing.md) | The Tracing class exposes the tracing audit interface. |
| Property | Modifiers | Type | Description |
| ------------- | --------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| accessibility | <code>readonly</code> | [Accessibility](./puppeteer.accessibility.md) | The Accessibility class provides methods for inspecting the browser's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access). |
| coverage | <code>readonly</code> | [Coverage](./puppeteer.coverage.md) | The Coverage class provides methods to gather information about parts of JavaScript and CSS that were used by the page. |
| keyboard | <code>readonly</code> | [Keyboard](./puppeteer.keyboard.md) | Keyboard provides an api for managing a virtual keyboard. The high level api is [Keyboard.type()](./puppeteer.keyboard.type.md), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page. |
| mouse | <code>readonly</code> | [Mouse](./puppeteer.mouse.md) | The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport. |
| touchscreen | <code>readonly</code> | [Touchscreen](./puppeteer.touchscreen.md) | The Touchscreen class exposes touchscreen events. |
| tracing | <code>readonly</code> | [Tracing](./puppeteer.tracing.md) | The Tracing class exposes the tracing audit interface. |
## Methods

View File

@ -32,7 +32,7 @@ Promise&lt;[FileChooser](./puppeteer.filechooser.md)&gt;
## Remarks
In non-headless Chromium, this method results in the native file picker dialog `not showing up` for the user.
In the "headful" browser, this method results in the native file picker dialog `not showing up` for the user.
## Example

View File

@ -1,13 +0,0 @@
---
sidebar_label: Platform
---
# Platform type
Supported platforms.
#### Signature:
```typescript
export type Platform = 'linux' | 'mac' | 'mac_arm' | 'win32' | 'win64';
```

View File

@ -1,31 +0,0 @@
---
sidebar_label: PuppeteerNode.createBrowserFetcher
---
# PuppeteerNode.createBrowserFetcher() method
#### Signature:
```typescript
class PuppeteerNode {
createBrowserFetcher(
options?: Partial<BrowserFetcherOptions>
): BrowserFetcher;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| options | Partial&lt;[BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md)&gt; | _(Optional)_ Set of configurable options to specify the settings of the BrowserFetcher. |
**Returns:**
[BrowserFetcher](./puppeteer.browserfetcher.md)
A new BrowserFetcher instance.
## Remarks
If you are using `puppeteer-core`, do not use this method. Just construct [BrowserFetcher](./puppeteer.browserfetcher.md) manually.

View File

@ -28,7 +28,7 @@ Promise&lt;[Browser](./puppeteer.browser.md)&gt;
## Remarks
Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chromium downloaded by default by Puppeteer. There is no guarantee it will work with any other version. If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested. See [this article](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [This article](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chrome for Testing downloaded by default. There is no guarantee it will work with any other version. If Google Chrome (rather than Chrome for Testing) is preferred, a [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested. See [this article](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [This article](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. See [this doc](https://goo.gle/chrome-for-testing) for the description of Chrome for Testing.
## Example

View File

@ -52,10 +52,9 @@ Once you have created a `page` you have access to a large API to interact with t
## Methods
| Method | Modifiers | Description |
| ---------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [connect(options)](./puppeteer.puppeteernode.connect.md) | | This method attaches Puppeteer to an existing browser instance. |
| [createBrowserFetcher(options)](./puppeteer.puppeteernode.createbrowserfetcher.md) | | |
| [defaultArgs(options)](./puppeteer.puppeteernode.defaultargs.md) | | |
| [executablePath(channel)](./puppeteer.puppeteernode.executablepath.md) | | The default executable path. |
| [launch(options)](./puppeteer.puppeteernode.launch.md) | | <p>Launches a browser instance with given arguments and options when specified.</p><p>When using with <code>puppeteer-core</code>, [options.executablePath](./puppeteer.launchoptions.md) or [options.channel](./puppeteer.launchoptions.md) must be provided.</p> |
| Method | Modifiers | Description |
| ---------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [connect(options)](./puppeteer.puppeteernode.connect.md) | | This method attaches Puppeteer to an existing browser instance. |
| [defaultArgs(options)](./puppeteer.puppeteernode.defaultargs.md) | | |
| [executablePath(channel)](./puppeteer.puppeteernode.executablepath.md) | | The default executable path. |
| [launch(options)](./puppeteer.puppeteernode.launch.md) | | <p>Launches a browser instance with given arguments and options when specified.</p><p>When using with <code>puppeteer-core</code>, [options.executablePath](./puppeteer.launchoptions.md) or [options.channel](./puppeteer.launchoptions.md) must be provided.</p> |

View File

@ -48,23 +48,6 @@ again.
npm test
```
### macOS ARM and custom executables.
- To run experimental Chromium macOS ARM tests, firstly ensure you have correct
Chromium version installed locally (you only need to do this once, not on
every test run) and then you can run the tests:
```bash
PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM=1 npm install
PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM=1 npm run test
```
- To run tests with custom browser executable:
```bash
BINARY=<path-to-chrome/firefox-executable> npm run test:chrome # Or npm run test:firefox
```
## Building a single package
To build a single package, you can run:
@ -280,18 +263,15 @@ npm run build --workspace @puppeteer-test/test
# For Project Maintainers
## Rolling new Chromium version
## Rolling new Chrome version
The following steps are needed to update the Chromium version.
The following steps are needed to update the Chrome version.
1. Find a suitable Chromium revision. Not all revisions have builds for all
platforms, so we need to find one that does. The easiest way is to run
`tools/check_availability.js -rd` to find the latest suitable `dev` Chromium
revision (see `tools/check_availability.js -help` for more options).
2. Update `packages/puppeteer-core/src/revisions.ts` with the found revision
1. Find a suitable Chrome revision via https://mathiasbynens.github.io/chrome-for-testing/ or https://chromiumdash.appspot.com/.
2. Update `packages/puppeteer-core/src/revisions.ts` with the found version
number.
3. Update `versions.js` with the new Chromium-to-Puppeteer version mapping and
update `lastMaintainedChromiumVersion` with the latest stable Chrome version.
3. Update `versions.js` with the new Chrome-to-Puppeteer version mapping and
update `lastMaintainedChromeVersion` with the latest stable Chrome version.
You can find the corresponding version by going to [omahaproxy.appspot.com](https://omahaproxy.appspot.com/) then
searching in `Find Releases` for `r<revision>`.
4. Run `npm run check`. If it fails, update
@ -304,26 +284,13 @@ The following steps are needed to update the Chromium version.
change) or work around the changes in Puppeteer (if its not desirable to
change Puppeteers observable behavior).
7. Commit and push your changes and open a pull request. The commit message must
contain the version in `Chromium <version> (r<revision>)` format to ensure
contain the version in `Chrome <version> (r<revision>)` format to ensure
that [pptr.dev](https://pptr.dev/) can parse it correctly, e.g.
`'feat(chromium): roll to Chromium 90.0.4427.0 (r856583)'`.
`'feat(chrome): roll to Chrome 90.0.4427.0 (r856583)'`.
### Bisecting upstream changes
Sometimes, performing a Chromium roll causes tests to fail. To figure out the
cause, you need to bisect Chromium revisions to figure out the earliest possible
revision that changed the behavior. The `bisect` script can be helpful here.
Given a pattern for one or more unit tests, it will automatically bisect the
current range:
```bash
npm run bisect -- --good 686378 --bad 706915 script.js
npm run bisect -- --unit-test Response.fromCache
```
By default, it will use the Chromium revision in
`packages/puppeteer-core/src/revisions.ts` from the `main` branch and from the
working tree to determine the range to bisect.
For bisecting Chrome/Chromium changes use https://www.chromium.org/developers/bisect-builds-py/.
## Releasing to npm

View File

@ -97,19 +97,10 @@ taking place in the Chromium repository. Heres a typical story:
- Once the upstream fix is landed, we roll updated Chromium into Puppeteer:
https://github.com/puppeteer/puppeteer/pull/2769
## Q: Which Chromium version does Puppeteer use?
## Q: Which Chrome version does Puppeteer use?
Find the version using one of the following ways:
- Look for the `chromium` entry in
[revisions.ts](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/revisions.ts).
To find the corresponding Chromium commit and version number, search for the
revision prefixed by an `r` in [OmahaProxy](https://omahaproxy.appspot.com/)'s
"Find Releases" section.
- Look for the `versionsPerRelease` map in
[versions.js](https://github.com/puppeteer/puppeteer/blob/main/versions.js)
which contains mapping between Chromium and the smallest Puppeteer version
that supports it.
Look for the `chrome` entry in
[revisions.ts](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/revisions.ts).
## Q: Which Firefox version does Puppeteer use?
@ -175,7 +166,7 @@ that incorporate audio and video. (For example,
[video playback/screenshots is likely to fail](https://github.com/puppeteer/puppeteer/issues/291).)
There are two reasons for this:
- Puppeteer is bundled with Chromium — not Chrome — and so by default, it
- Puppeteer is bundled with [Chrome for Testing](https://goo.gle/chrome-for-testing) — not the regular Chrome — and so by default, it
inherits all of
[Chromium's media-related limitations](https://www.chromium.org/audio-video).
This means that Puppeteer does not support licensed formats such as AAC or
@ -195,30 +186,6 @@ We have a
[troubleshooting](https://pptr.dev/troubleshooting)
guide for various operating systems that lists the required dependencies.
#### Q: Chromium gets downloaded on every `npm ci` run. How can I cache the download?
The default download path is `node_modules/puppeteer/.local-chromium`. However,
you can change that path with the `PUPPETEER_DOWNLOAD_PATH` environment
variable.
Puppeteer uses that variable to resolve the Chromium executable location during
launch, so you dont need to specify `PUPPETEER_EXECUTABLE_PATH` as well.
For example, if you wish to keep the Chromium download in `~/.npm/chromium`:
```bash
export PUPPETEER_DOWNLOAD_PATH=~/.npm/chromium
npm ci
# by default the Chromium executable path is inferred
# from the download path
npm test
# a new run of npm ci will check for the existence of
# Chromium in ~/.npm/chromium
npm ci
```
#### Q: I have more questions! Where do I ask?
There are many ways to get help on Puppeteer:

View File

@ -78,49 +78,6 @@ about the ambient environment is needed (in this case, `__dirname`).
:::
#### Enabling experiments
By default, experiments are turned off, but they can be individually turned on
using the [`experiments`](../api/puppeteer.configuration) key.
For example, if you want to enable ARM-native macOS chromium, you can use
<Tabs>
<TabItem value="CommonJS">
```js title=".puppeteerrc.cjs"
/**
* @type {import("puppeteer").Configuration}
*/
module.exports = {
experiments: {
macArmChromiumEnabled: true,
},
};
```
</TabItem>
<TabItem value="JSON">
```json title=".puppeteerrc.json"
{
"experiments": {
"macArmChromiumEnabled": true
}
}
```
</TabItem>
<TabItem value="YAML">
```yaml title=".puppeteerrc.yaml"
experiments:
macArmChromiumEnabled: true
```
</TabItem>
</Tabs>
## Environment variables
Along with configuration files, Puppeteer looks for certain

View File

@ -1,6 +1,6 @@
# Docker
Puppeteer offers a Docker image that includes Chromium along with the required
Puppeteer offers a Docker image that includes [Chrome for Testing](https://goo.gle/chrome-for-testing) along with the required
dependencies and a pre-installed Puppeteer version. The image is available via
the
[GitHub Container Registry](https://github.com/puppeteer/puppeteer/pkgs/container/puppeteer).

View File

@ -12,7 +12,7 @@
> [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
> Puppeteer runs in
> [headless](https://developer.chrome.com/articles/new-headless/)
> mode by default, but can be configured to run in full (non-headless)
> mode by default, but can be configured to run in full ("headful")
> Chrome/Chromium.
#### What can I do?
@ -46,7 +46,7 @@ pnpm i puppeteer
```
When you install Puppeteer, it automatically downloads a recent version of
Chromium (~170MB macOS, ~282MB Linux, ~280MB Windows) that is [guaranteed to
[Chrome for Testing](https://goo.gle/chrome-for-testing) (~170MB macOS, ~282MB Linux, ~280MB Windows) that is [guaranteed to
work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
with Puppeteer. The browser is downloaded to the `$HOME/.cache/puppeteer` folder
by default (starting with Puppeteer v19.0.0).
@ -94,14 +94,14 @@ Every release since v1.7.0 we publish two packages:
- [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core)
`puppeteer` is a _product_ for browser automation. When installed, it downloads
a version of Chromium, which it then drives using `puppeteer-core`. Being an
a version of Chrome, which it then drives using `puppeteer-core`. Being an
end-user product, `puppeteer` automates several workflows using reasonable
defaults [that can be customized](https://pptr.dev/guides/configuration).
`puppeteer-core` is a _library_ to help drive anything that supports DevTools
protocol. Being a library, `puppeteer-core` is fully driven through its
programmatic interface implying no defaults are assumed and `puppeteer-core`
will not download Chromium when installed.
will not download Chrome when installed.
You should use `puppeteer-core` if you are
[connecting to a remote browser](https://pptr.dev/api/puppeteer.puppeteer.connect)
@ -176,7 +176,7 @@ import puppeteer from 'puppeteer';
**1. Uses Headless mode**
By default Puppeteer launches Chromium in
By default Puppeteer launches Chrome in
[old Headless mode](https://developer.chrome.com/articles/new-headless/).
```ts
@ -193,17 +193,17 @@ We recommend you try it out before the switch:
const browser = await puppeteer.launch({headless: 'new'});
```
To launch a full version of Chromium, set the
[`headless`](https://pptr.dev/api/puppeteer.browserlaunchargumentoptions) to `true`
To launch a "headful" version of Chrome, set the
[`headless`](https://pptr.dev/api/puppeteer.browserlaunchargumentoptions) to `false`
option when launching a browser:
```ts
const browser = await puppeteer.launch({headless: false});
```
**2. Runs a bundled version of Chromium**
**2. Runs a bundled version of Chrome**
By default, Puppeteer downloads and uses a specific version of Chromium so its
By default, Puppeteer downloads and uses a specific version of Chrome so its
API is guaranteed to work out of the box. To use Puppeteer with a different
version of Chrome or Chromium, pass in the executable's path when creating a
`Browser` instance:

View File

@ -6,7 +6,6 @@
"url": "https://github.com/puppeteer/puppeteer"
},
"scripts": {
"bisect": "tsx tools/bisect.ts",
"build": "wireit",
"build:docs": "wireit",
"check:pinned-deps": "tsx tools/ensure-pinned-deps",

View File

@ -51,12 +51,7 @@ export class Cache {
}
browserRoot(browser: Browser): string {
// Chromium is a special case for backward compatibility: we install it in
// the Chrome folder so that Puppeteer can find it.
return path.join(
this.#rootDir,
browser === Browser.CHROMIUM ? Browser.CHROME : browser
);
return path.join(this.#rootDir, browser);
}
installationDir(

View File

@ -39,7 +39,7 @@ describe('Chromium', () => {
buildId: '123',
cacheDir: 'cache',
}),
path.join('cache', 'chrome', 'linux-123', 'chrome-linux', 'chrome')
path.join('cache', 'chromium', 'linux-123', 'chrome-linux', 'chrome')
);
});

View File

@ -131,10 +131,10 @@ export interface WaitForTargetOptions {
*/
export const enum BrowserEmittedEvents {
/**
* Emitted when Puppeteer gets disconnected from the Chromium instance. This
* Emitted when Puppeteer gets disconnected from the browser instance. This
* might happen because of one of the following:
*
* - Chromium is closed or crashed
* - browser is closed or crashed
*
* - The {@link Browser.disconnect | browser.disconnect } method was called.
*/
@ -173,7 +173,7 @@ export const enum BrowserEmittedEvents {
}
/**
* A Browser is created when Puppeteer connects to a Chromium instance, either through
* A Browser is created when Puppeteer connects to a browser instance, either through
* {@link PuppeteerNode.launch} or {@link Puppeteer.connect}.
*
* @remarks
@ -203,14 +203,14 @@ export const enum BrowserEmittedEvents {
*
* (async () => {
* const browser = await puppeteer.launch();
* // Store the endpoint to be able to reconnect to Chromium
* // Store the endpoint to be able to reconnect to the browser.
* const browserWSEndpoint = browser.wsEndpoint();
* // Disconnect puppeteer from Chromium
* // Disconnect puppeteer from the browser.
* browser.disconnect();
*
* // Use the endpoint to reestablish a connection
* const browser2 = await puppeteer.connect({browserWSEndpoint});
* // Close Chromium
* // Close the browser.
* await browser2.close();
* })();
* ```
@ -404,10 +404,10 @@ export class Browser extends EventEmitter {
*
* @remarks
*
* For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For
* For headless browser, this is similar to `HeadlessChrome/61.0.3153.0`. For
* non-headless, this is similar to `Chrome/61.0.3153.0`.
*
* The format of browser.version() might change with future releases of Chromium.
* The format of browser.version() might change with future releases of browsers.
*/
version(): Promise<string> {
throw new Error('Not implemented');
@ -422,15 +422,16 @@ export class Browser extends EventEmitter {
}
/**
* Closes Chromium and all of its pages (if any were opened). The {@link Browser} object
* itself is considered to be disposed and cannot be used anymore.
* Closes the browser and all of its pages (if any were opened). The
* {@link Browser} object itself is considered to be disposed and cannot be
* used anymore.
*/
close(): Promise<void> {
throw new Error('Not implemented');
}
/**
* Disconnects Puppeteer from the browser, but leaves the Chromium process running.
* Disconnects Puppeteer from the browser, but leaves the browser process running.
* After calling `disconnect`, the {@link Browser} object is considered disposed and
* cannot be used anymore.
*/

View File

@ -388,7 +388,7 @@ export interface PageEventObject {
/**
* Page provides methods to interact with a single tab or
* {@link https://developer.chrome.com/extensions/background_pages | extension background page}
* in Chromium.
* in the browser.
*
* :::note
*
@ -521,7 +521,7 @@ export class Page extends EventEmitter {
* :::
*
* @remarks
* In non-headless Chromium, this method results in the native file picker
* In the "headful" browser, this method results in the native file picker
* dialog `not showing up` for the user.
*
* @example

View File

@ -110,7 +110,7 @@ export interface SnapshotOptions {
}
/**
* The Accessibility class provides methods for inspecting Chromium's
* The Accessibility class provides methods for inspecting the browser's
* accessibility tree. The accessibility tree is used by assistive technology
* such as {@link https://en.wikipedia.org/wiki/Screen_reader | screen readers} or
* {@link https://en.wikipedia.org/wiki/Switch_access | switches}.
@ -147,7 +147,7 @@ export class Accessibility {
*
* @remarks
*
* **NOTE** The Chromium accessibility tree contains nodes that go unused on
* **NOTE** The Chrome accessibility tree contains nodes that go unused on
* most platforms and by most screen readers. Puppeteer will discard them as
* well for an easier to process tree, unless `interestingOnly` is set to
* `false`.

View File

@ -522,16 +522,6 @@ export class CDPBrowser extends BrowserBase {
}, []);
}
/**
* A string representing the browser name and version.
*
* @remarks
*
* For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For
* non-headless, this is similar to `Chrome/61.0.3153.0`.
*
* The format of browser.version() might change with future releases of Chromium.
*/
override async version(): Promise<string> {
const version = await this.#getVersion();
return version.product;
@ -546,21 +536,11 @@ export class CDPBrowser extends BrowserBase {
return version.userAgent;
}
/**
* Closes Chromium and all of its pages (if any were opened). The
* {@link CDPBrowser} object itself is considered to be disposed and cannot be
* used anymore.
*/
override async close(): Promise<void> {
await this.#closeCallback.call(null);
this.disconnect();
}
/**
* Disconnects Puppeteer from the browser, but leaves the Chromium process running.
* After calling `disconnect`, the {@link CDPBrowser} object is considered disposed and
* cannot be used anymore.
*/
override disconnect(): void {
this.#targetManager.dispose();
this.#connection.dispose();

View File

@ -23,21 +23,7 @@ import {Product} from './Product.js';
*
* @public
*/
export interface ExperimentsConfiguration {
/**
* Require Puppeteer to download Chromium for Apple M1.
*
* On Apple M1 devices Puppeteer by default downloads the version for
* Intel's processor which runs via Rosetta. It works without any problems,
* however, with this option, you should get more efficient resource usage
* (CPU and RAM) that could lead to a faster execution time.
*
* Can be overridden by `PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM`.
*
* @defaultValue `false`
*/
macArmChromiumEnabled?: boolean;
}
export type ExperimentsConfiguration = Record<string, never>;
/**
* Defines options to configure Puppeteer's behavior during installation and
@ -68,7 +54,7 @@ export interface Configuration {
*/
cacheDirectory?: string;
/**
* Specifies the URL prefix that is used to download Chromium.
* Specifies the URL prefix that is used to download the browser.
*
* Can be overridden by `PUPPETEER_DOWNLOAD_HOST`.
*

View File

@ -394,11 +394,14 @@ export async function getReadableAsBuffer(
if (path) {
const fs = await importFSPromises();
const fileHandle = await fs.open(path, 'w+');
for await (const chunk of readable) {
buffers.push(chunk);
await fileHandle.writeFile(chunk);
try {
for await (const chunk of readable) {
buffers.push(chunk);
await fileHandle.writeFile(chunk);
}
} finally {
await fileHandle.close();
}
await fileHandle.close();
} else {
for await (const chunk of readable) {
buffers.push(chunk);
@ -433,12 +436,20 @@ export async function getReadableFromProtocolStream(
return;
}
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);
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;
}
},
});

View File

@ -1,724 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {exec as execChildProcess} from 'child_process';
import {createReadStream, createWriteStream, existsSync, readdirSync} from 'fs';
import {chmod, mkdir, readdir, unlink} from 'fs/promises';
import http from 'http';
import https from 'https';
import os from 'os';
import path from 'path';
import URL from 'url';
import {promisify, format} from 'util';
import extractZip from 'extract-zip';
import createHttpsProxyAgent, {
HttpsProxyAgent,
HttpsProxyAgentOptions,
} from 'https-proxy-agent';
import {getProxyForUrl} from 'proxy-from-env';
import tar from 'tar-fs';
import bzip from 'unbzip2-stream';
import {debug} from '../common/Debug.js';
import {Product} from '../common/Product.js';
import {assert} from '../util/assert.js';
import {rm} from './util/fs.js';
const debugFetcher = debug('puppeteer:fetcher');
const downloadURLs: Record<Product, Partial<Record<Platform, string>>> = {
chrome: {
linux: '%s/chromium-browser-snapshots/Linux_x64/%d/%s.zip',
mac: '%s/chromium-browser-snapshots/Mac/%d/%s.zip',
mac_arm: '%s/chromium-browser-snapshots/Mac_Arm/%d/%s.zip',
win32: '%s/chromium-browser-snapshots/Win/%d/%s.zip',
win64: '%s/chromium-browser-snapshots/Win_x64/%d/%s.zip',
},
firefox: {
linux: '%s/firefox-%s.en-US.%s-x86_64.tar.bz2',
mac: '%s/firefox-%s.en-US.%s.dmg',
win32: '%s/firefox-%s.en-US.%s.zip',
win64: '%s/firefox-%s.en-US.%s.zip',
},
};
const browserConfig = {
chrome: {
host: 'https://storage.googleapis.com',
},
firefox: {
host: 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central',
},
} as const;
const exec = promisify(execChildProcess);
/**
* Supported platforms.
*
* @public
*/
export type Platform = 'linux' | 'mac' | 'mac_arm' | 'win32' | 'win64';
function archiveName(
product: Product,
platform: Platform,
revision: string
): string {
switch (product) {
case 'chrome':
switch (platform) {
case 'linux':
return 'chrome-linux';
case 'mac_arm':
case 'mac':
return 'chrome-mac';
case 'win32':
case 'win64':
// Windows archive name changed at r591479.
return parseInt(revision, 10) > 591479
? 'chrome-win'
: 'chrome-win32';
}
case 'firefox':
return platform;
}
}
function downloadURL(
product: Product,
platform: Platform,
host: string,
revision: string
): string {
const url = format(
downloadURLs[product][platform],
host,
revision,
archiveName(product, platform, revision)
);
return url;
}
function handleArm64(): void {
let exists = existsSync('/usr/bin/chromium-browser');
if (exists) {
return;
}
exists = existsSync('/usr/bin/chromium');
if (exists) {
return;
}
console.error(
'The chromium binary is not available for arm64.' +
'\nIf you are on Ubuntu, you can install with: ' +
'\n\n sudo apt install chromium\n' +
'\n\n sudo apt install chromium-browser\n'
);
throw new Error();
}
/**
* @public
*/
export interface BrowserFetcherOptions {
/**
* Determines the path to download browsers to.
*/
path: string;
/**
* Determines which platform the browser will be suited for.
*
* @defaultValue **Auto-detected.**
*/
platform?: Platform;
/**
* Determines which product the {@link BrowserFetcher} is for.
*
* @defaultValue `chrome`
*/
product?: 'chrome' | 'firefox';
/**
* Determines the host that will be used for downloading.
*
* @defaultValue Either
*
* - https://storage.googleapis.com or
* - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central
*
*/
host?: string;
/**
* Enables the use of the Chromium binary for macOS ARM.
*
* @experimental
*/
useMacOSARMBinary?: boolean;
}
/**
* @public
*/
export interface BrowserFetcherRevisionInfo {
folderPath: string;
executablePath: string;
url: string;
local: boolean;
revision: string;
product: string;
}
/**
* BrowserFetcher can download and manage different versions of Chromium and
* Firefox.
*
* @remarks
* BrowserFetcher operates on revision strings that specify a precise version of
* Chromium, e.g. `"533271"`. Revision strings can be obtained from
* {@link http://omahaproxy.appspot.com/ | omahaproxy.appspot.com}. For Firefox,
* BrowserFetcher downloads Firefox Nightly and operates on version numbers such
* as `"75"`.
*
* @remarks
* The default constructed fetcher will always be for Chromium unless otherwise
* specified.
*
* @remarks
* BrowserFetcher is not designed to work concurrently with other instances of
* BrowserFetcher that share the same downloads directory.
*
* @example
* An example of using BrowserFetcher to download a specific version of Chromium
* and running Puppeteer against it:
*
* ```ts
* const browserFetcher = new BrowserFetcher({path: 'path/to/download/folder'});
* const revisionInfo = await browserFetcher.download('533271');
* const browser = await puppeteer.launch({
* executablePath: revisionInfo.executablePath,
* });
* ```
*
* @public
*/
export class BrowserFetcher {
#product: Product;
#downloadPath: string;
#downloadHost: string;
#platform: Platform;
/**
* Constructs a browser fetcher for the given options.
*/
constructor(options: BrowserFetcherOptions) {
this.#product = options.product ?? 'chrome';
this.#downloadPath = options.path;
this.#downloadHost = options.host ?? browserConfig[this.#product].host;
if (options.platform) {
this.#platform = options.platform;
} else {
const platform = os.platform();
switch (platform) {
case 'darwin':
switch (this.#product) {
case 'chrome':
this.#platform =
os.arch() === 'arm64' && options.useMacOSARMBinary
? 'mac_arm'
: 'mac';
break;
case 'firefox':
this.#platform = 'mac';
break;
}
break;
case 'linux':
this.#platform = 'linux';
break;
case 'win32':
this.#platform =
os.arch() === 'x64' ||
// Windows 11 for ARM supports x64 emulation
(os.arch() === 'arm64' && isWindows11(os.release()))
? 'win64'
: 'win32';
return;
default:
assert(false, 'Unsupported platform: ' + platform);
}
}
assert(
downloadURLs[this.#product][this.#platform],
'Unsupported platform: ' + this.#platform
);
}
/**
* Returns the current `Platform`, which is one of `mac`, `linux`,
* `win32` or `win64`.
*/
platform(): Platform {
return this.#platform;
}
/**
* Returns the current `Product`, which is one of `chrome` or
* `firefox`.
*/
product(): Product {
return this.#product;
}
/**
* The download host being used.
*/
host(): string {
return this.#downloadHost;
}
/**
* Initiates a HEAD request to check if the revision is available.
* @remarks
* This method is affected by the current `product`.
* @param revision - The revision to check availability for.
* @returns A promise that resolves to `true` if the revision could be downloaded
* from the host.
*/
canDownload(revision: string): Promise<boolean> {
const url = downloadURL(
this.#product,
this.#platform,
this.#downloadHost,
revision
);
return new Promise(resolve => {
const request = httpRequest(
url,
'HEAD',
response => {
resolve(response.statusCode === 200);
},
false
);
request.on('error', error => {
console.error(error);
resolve(false);
});
});
}
/**
* Initiates a GET request to download the revision from the host.
* @remarks
* This method is affected by the current `product`.
* @param revision - The revision to download.
* @param progressCallback - A function that will be called with two arguments:
* How many bytes have been downloaded and the total number of bytes of the download.
* @returns A promise with revision information when the revision is downloaded
* and extracted.
*/
async download(
revision: string,
progressCallback: (x: number, y: number) => void = (): void => {}
): Promise<BrowserFetcherRevisionInfo | undefined> {
const url = downloadURL(
this.#product,
this.#platform,
this.#downloadHost,
revision
);
const fileName = url.split('/').pop();
assert(fileName, `A malformed download URL was found: ${url}.`);
const archivePath = path.join(this.#downloadPath, fileName);
const outputPath = this.#getFolderPath(revision);
if (existsSync(outputPath)) {
return this.revisionInfo(revision);
}
if (!existsSync(this.#downloadPath)) {
await mkdir(this.#downloadPath, {recursive: true});
}
// Use system Chromium builds on Linux ARM devices
if (os.platform() === 'linux' && os.arch() === 'arm64') {
handleArm64();
return;
}
try {
await _downloadFile(url, archivePath, progressCallback);
await install(archivePath, outputPath);
} finally {
if (existsSync(archivePath)) {
await unlink(archivePath);
}
}
const revisionInfo = this.revisionInfo(revision);
if (revisionInfo) {
await chmod(revisionInfo.executablePath, 0o755);
}
return revisionInfo;
}
/**
* @remarks
* This method is affected by the current `product`.
* @returns A list of all revision strings (for the current `product`)
* available locally on disk.
*/
localRevisions(): string[] {
if (!existsSync(this.#downloadPath)) {
return [];
}
const fileNames = readdirSync(this.#downloadPath);
return fileNames
.map(fileName => {
return parseFolderPath(this.#product, fileName);
})
.filter((entry): entry is Exclude<typeof entry, undefined> => {
return (entry && entry.platform === this.#platform) ?? false;
})
.map(entry => {
return entry.revision;
});
}
/**
* @remarks
* This method is affected by the current `product`.
* @param revision - A revision to remove for the current `product`.
* @returns A promise that resolves when the revision has been removed or
* throws if the revision has not been downloaded.
*/
async remove(revision: string): Promise<void> {
const folderPath = this.#getFolderPath(revision);
assert(
existsSync(folderPath),
`Failed to remove: revision ${revision} is not downloaded`
);
await rm(folderPath);
}
/**
* @param revision - The revision to get info for.
* @returns The revision info for the given revision.
*/
revisionInfo(revision: string): BrowserFetcherRevisionInfo {
const folderPath = this.#getFolderPath(revision);
let executablePath = '';
switch (this.#product) {
case 'chrome':
switch (this.#platform) {
case 'mac':
case 'mac_arm':
executablePath = path.join(
folderPath,
archiveName(this.#product, this.#platform, revision),
'Chromium.app',
'Contents',
'MacOS',
'Chromium'
);
break;
case 'linux':
executablePath = path.join(
folderPath,
archiveName(this.#product, this.#platform, revision),
'chrome'
);
break;
case 'win32':
case 'win64':
executablePath = path.join(
folderPath,
archiveName(this.#product, this.#platform, revision),
'chrome.exe'
);
break;
}
break;
case 'firefox':
switch (this.#platform) {
case 'mac':
case 'mac_arm':
executablePath = path.join(
folderPath,
'Firefox Nightly.app',
'Contents',
'MacOS',
'firefox'
);
break;
case 'linux':
executablePath = path.join(folderPath, 'firefox', 'firefox');
break;
case 'win32':
case 'win64':
executablePath = path.join(folderPath, 'firefox', 'firefox.exe');
break;
}
}
const url = downloadURL(
this.#product,
this.#platform,
this.#downloadHost,
revision
);
const local = existsSync(folderPath);
debugFetcher({
revision,
executablePath,
folderPath,
local,
url,
product: this.#product,
});
return {
revision,
executablePath,
folderPath,
local,
url,
product: this.#product,
};
}
#getFolderPath(revision: string): string {
return path.resolve(this.#downloadPath, `${this.#platform}-${revision}`);
}
/**
* @internal
*/
getDownloadPath(): string {
return this.#downloadPath;
}
}
function parseFolderPath(
product: Product,
folderPath: string
): {product: string; platform: string; revision: string} | undefined {
const name = path.basename(folderPath);
const splits = name.split('-');
if (splits.length !== 2) {
return;
}
const [platform, revision] = splits;
if (!revision || !platform || !(platform in downloadURLs[product])) {
return;
}
return {product, platform, revision};
}
/**
* Windows 11 is identified by 10.0.22000 or greater
* @internal
*/
function isWindows11(version: string): boolean {
const parts = version.split('.');
if (parts.length > 2) {
const major = parseInt(parts[0] as string, 10);
const minor = parseInt(parts[1] as string, 10);
const patch = parseInt(parts[2] as string, 10);
return (
major > 10 ||
(major === 10 && minor > 0) ||
(major === 10 && minor === 0 && patch >= 22000)
);
}
return false;
}
/**
* @internal
*/
function _downloadFile(
url: string,
destinationPath: string,
progressCallback?: (x: number, y: number) => void
): Promise<void> {
debugFetcher(`Downloading binary from ${url}`);
let fulfill: (value: void | PromiseLike<void>) => void;
let reject: (err: Error) => void;
const promise = new Promise<void>((x, y) => {
fulfill = x;
reject = y;
});
let downloadedBytes = 0;
let totalBytes = 0;
const request = httpRequest(url, 'GET', response => {
if (response.statusCode !== 200) {
const error = new Error(
`Download failed: server returned code ${response.statusCode}. URL: ${url}`
);
// consume response data to free up memory
response.resume();
reject(error);
return;
}
const file = createWriteStream(destinationPath);
file.on('finish', () => {
return fulfill();
});
file.on('error', error => {
return reject(error);
});
response.pipe(file);
totalBytes = parseInt(response.headers['content-length']!, 10);
if (progressCallback) {
response.on('data', onData);
}
});
request.on('error', error => {
return reject(error);
});
return promise;
function onData(chunk: string): void {
downloadedBytes += chunk.length;
progressCallback!(downloadedBytes, totalBytes);
}
}
async function install(archivePath: string, folderPath: string): Promise<void> {
debugFetcher(`Installing ${archivePath} to ${folderPath}`);
if (archivePath.endsWith('.zip')) {
await extractZip(archivePath, {dir: folderPath});
} else if (archivePath.endsWith('.tar.bz2')) {
await extractTar(archivePath, folderPath);
} else if (archivePath.endsWith('.dmg')) {
await mkdir(folderPath);
await installDMG(archivePath, folderPath);
} else {
throw new Error(`Unsupported archive format: ${archivePath}`);
}
}
/**
* @internal
*/
function extractTar(tarPath: string, folderPath: string): Promise<void> {
return new Promise((fulfill, reject) => {
const tarStream = tar.extract(folderPath);
tarStream.on('error', reject);
tarStream.on('finish', fulfill);
const readStream = createReadStream(tarPath);
readStream.pipe(bzip()).pipe(tarStream);
});
}
/**
* @internal
*/
async function installDMG(dmgPath: string, folderPath: string): Promise<void> {
const {stdout} = await exec(
`hdiutil attach -nobrowse -noautoopen "${dmgPath}"`
);
const volumes = stdout.match(/\/Volumes\/(.*)/m);
if (!volumes) {
throw new Error(`Could not find volume path in ${stdout}`);
}
const mountPath = volumes[0]!;
try {
const fileNames = await readdir(mountPath);
const appName = fileNames.find(item => {
return typeof item === 'string' && item.endsWith('.app');
});
if (!appName) {
throw new Error(`Cannot find app in ${mountPath}`);
}
const mountedPath = path.join(mountPath!, appName);
debugFetcher(`Copying ${mountedPath} to ${folderPath}`);
await exec(`cp -R "${mountedPath}" "${folderPath}"`);
} finally {
debugFetcher(`Unmounting ${mountPath}`);
await exec(`hdiutil detach "${mountPath}" -quiet`);
}
}
function httpRequest(
url: string,
method: string,
response: (x: http.IncomingMessage) => void,
keepAlive = true
): http.ClientRequest {
const urlParsed = URL.parse(url);
type Options = Partial<URL.UrlWithStringQuery> & {
method?: string;
agent?: HttpsProxyAgent;
rejectUnauthorized?: boolean;
headers?: http.OutgoingHttpHeaders | undefined;
};
let options: Options = {
...urlParsed,
method,
headers: keepAlive ? {Connection: 'keep-alive'} : undefined,
};
const proxyURL = getProxyForUrl(url);
if (proxyURL) {
if (url.startsWith('http:')) {
const proxy = URL.parse(proxyURL);
options = {
path: options.href,
host: proxy.hostname,
port: proxy.port,
};
} else {
const parsedProxyURL = URL.parse(proxyURL);
const proxyOptions = {
...parsedProxyURL,
secureProxy: parsedProxyURL.protocol === 'https:',
} as HttpsProxyAgentOptions;
options.agent = createHttpsProxyAgent(proxyOptions);
options.rejectUnauthorized = false;
}
}
const requestCallback = (res: http.IncomingMessage): void => {
if (
res.statusCode &&
res.statusCode >= 300 &&
res.statusCode < 400 &&
res.headers.location
) {
httpRequest(res.headers.location, method, response);
} else {
response(res);
}
};
const request =
options.protocol === 'https:'
? https.request(options, requestCallback)
: http.request(options, requestCallback);
request.end();
return request;
}

View File

@ -179,7 +179,8 @@ export class ChromeLauncher extends ProductLauncher {
'--disable-dev-shm-usage',
'--disable-extensions',
// AcceptCHFrame disabled because of crbug.com/1348106.
'--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints',
// DIPS is disabled because of crbug.com/1439578. TODO: enable after M115.
'--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints,DIPS',
'--disable-hang-monitor',
'--disable-ipc-flooding-protection',
'--disable-popup-blocking',

View File

@ -19,7 +19,13 @@ import {rename, unlink, mkdtemp} from 'fs/promises';
import os from 'os';
import path from 'path';
import {Browser as SupportedBrowsers, createProfile} from '@puppeteer/browsers';
import {
Browser as SupportedBrowsers,
createProfile,
Cache,
detectBrowserPlatform,
Browser,
} from '@puppeteer/browsers';
import {debugError} from '../common/util.js';
import {assert} from '../util/assert.js';
@ -164,13 +170,15 @@ export class FirefoxLauncher extends ProductLauncher {
override executablePath(): string {
// replace 'latest' placeholder with actual downloaded revision
if (this.puppeteer.browserRevision === 'latest') {
const browserFetcher = this.puppeteer.createBrowserFetcher({
product: this.product,
path: this.puppeteer.defaultDownloadPath!,
const cache = new Cache(this.puppeteer.defaultDownloadPath!);
const installedFirefox = cache.getInstalledBrowsers().find(browser => {
return (
browser.platform === detectBrowserPlatform() &&
browser.browser === Browser.FIREFOX
);
});
const localRevisions = browserFetcher.localRevisions();
if (localRevisions[0]) {
this.actualBrowserRevision = localRevisions[0];
if (installedFirefox) {
this.actualBrowserRevision = installedFirefox.buildId;
}
}
return this.resolveExecutablePath();

View File

@ -14,14 +14,16 @@
* limitations under the License.
*/
import {existsSync} from 'fs';
import os, {tmpdir} from 'os';
import {tmpdir} from 'os';
import {join} from 'path';
import {
Browser as InstalledBrowser,
CDP_WEBSOCKET_ENDPOINT_REGEX,
launch,
TimeoutError as BrowsersTimeoutError,
WEBDRIVER_BIDI_WEBSOCKET_ENDPOINT_REGEX,
computeExecutablePath,
} from '@puppeteer/browsers';
import {Browser, BrowserCloseCallback} from '../api/Browser.js';
@ -392,7 +394,7 @@ export class ProductLauncher {
* @internal
*/
protected resolveExecutablePath(): string {
const executablePath = this.puppeteer.configuration.executablePath;
let executablePath = this.puppeteer.configuration.executablePath;
if (executablePath) {
if (!existsSync(executablePath)) {
throw new Error(
@ -402,34 +404,32 @@ export class ProductLauncher {
return executablePath;
}
const ubuntuChromiumPath = '/usr/bin/chromium-browser';
if (
this.product === 'chrome' &&
os.platform() !== 'darwin' &&
os.arch() === 'arm64' &&
existsSync(ubuntuChromiumPath)
) {
return ubuntuChromiumPath;
function productToBrowser(product?: Product) {
switch (product) {
case 'chrome':
return InstalledBrowser.CHROME;
case 'firefox':
return InstalledBrowser.FIREFOX;
}
return InstalledBrowser.CHROME;
}
const browserFetcher = this.puppeteer.createBrowserFetcher({
product: this.product,
path: this.puppeteer.defaultDownloadPath!,
executablePath = computeExecutablePath({
cacheDir: this.puppeteer.defaultDownloadPath!,
browser: productToBrowser(this.product),
buildId: this.puppeteer.browserRevision,
});
const revisionInfo = browserFetcher.revisionInfo(
this.puppeteer.browserRevision
);
if (!revisionInfo.local) {
if (!existsSync(executablePath)) {
if (this.puppeteer.configuration.browserRevision) {
throw new Error(
`Tried to find the browser at the configured path (${revisionInfo.executablePath}) for revision ${this.puppeteer.browserRevision}, but no executable was found.`
`Tried to find the browser at the configured path (${executablePath}) for revision ${this.puppeteer.browserRevision}, but no executable was found.`
);
}
switch (this.product) {
case 'chrome':
throw new Error(
`Could not find Chromium (rev. ${this.puppeteer.browserRevision}). This can occur if either\n` +
`Could not find Chrome (ver. ${this.puppeteer.browserRevision}). This can occur if either\n` +
' 1. you did not perform an installation before running the script (e.g. `npm install`) or\n' +
` 2. your cache path is incorrectly configured (which is: ${this.puppeteer.configuration.cacheDirectory}).\n` +
'For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides/configuration.'
@ -443,6 +443,6 @@ export class ProductLauncher {
);
}
}
return revisionInfo.executablePath;
return executablePath;
}
}

View File

@ -14,8 +14,6 @@
* limitations under the License.
*/
import {join} from 'path';
import {Browser} from '../api/Browser.js';
import {BrowserConnectOptions} from '../common/BrowserConnector.js';
import {Configuration} from '../common/Configuration.js';
@ -27,7 +25,6 @@ import {
} from '../common/Puppeteer.js';
import {PUPPETEER_REVISIONS} from '../revisions.js';
import {BrowserFetcher, BrowserFetcherOptions} from './BrowserFetcher.js';
import {ChromeLauncher} from './ChromeLauncher.js';
import {FirefoxLauncher} from './FirefoxLauncher.js';
import {
@ -116,7 +113,7 @@ export class PuppeteerNode extends Puppeteer {
break;
default:
this.configuration.defaultProduct = 'chrome';
this.defaultBrowserRevision = PUPPETEER_REVISIONS.chromium;
this.defaultBrowserRevision = PUPPETEER_REVISIONS.chrome;
break;
}
@ -124,7 +121,6 @@ export class PuppeteerNode extends Puppeteer {
this.launch = this.launch.bind(this);
this.executablePath = this.executablePath.bind(this);
this.defaultArgs = this.defaultArgs.bind(this);
this.createBrowserFetcher = this.createBrowserFetcher.bind(this);
}
/**
@ -157,9 +153,9 @@ export class PuppeteerNode extends Puppeteer {
*
* @remarks
* Puppeteer can also be used to control the Chrome browser, but it works best
* with the version of Chromium downloaded by default by Puppeteer. There is
* no guarantee it will work with any other version. If Google Chrome (rather
* than Chromium) is preferred, a
* with the version of Chrome for Testing downloaded by default.
* There is no guarantee it will work with any other version. If Google Chrome
* (rather than Chrome for Testing) is preferred, a
* {@link https://www.google.com/chrome/browser/canary.html | Chrome Canary}
* or
* {@link https://www.chromium.org/getting-involved/dev-channel | Dev Channel}
@ -167,7 +163,9 @@ export class PuppeteerNode extends Puppeteer {
* {@link https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/ | this article}
* for a description of the differences between Chromium and Chrome.
* {@link https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md | This article}
* describes some differences for Linux users.
* describes some differences for Linux users. See
* {@link https://goo.gle/chrome-for-testing | this doc} for the description
* of Chrome for Testing.
*
* @param options - Options to configure launching behavior.
*/
@ -189,7 +187,7 @@ export class PuppeteerNode extends Puppeteer {
}
switch (this.lastLaunchedProduct) {
case 'chrome':
this.defaultBrowserRevision = PUPPETEER_REVISIONS.chromium;
this.defaultBrowserRevision = PUPPETEER_REVISIONS.chrome;
this.#_launcher = new ChromeLauncher(this);
break;
case 'firefox':
@ -227,10 +225,7 @@ export class PuppeteerNode extends Puppeteer {
* @internal
*/
get defaultDownloadPath(): string | undefined {
return (
this.configuration.downloadPath ??
join(this.configuration.cacheDirectory!, this.product)
);
return this.configuration.downloadPath ?? this.configuration.cacheDirectory;
}
/**
@ -269,39 +264,4 @@ export class PuppeteerNode extends Puppeteer {
defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] {
return this.#launcher.defaultArgs(options);
}
/**
* @param options - Set of configurable options to specify the settings of the
* BrowserFetcher.
*
* @remarks
* If you are using `puppeteer-core`, do not use this method. Just
* construct {@link BrowserFetcher} manually.
*
* @returns A new BrowserFetcher instance.
*/
createBrowserFetcher(
options: Partial<BrowserFetcherOptions> = {}
): BrowserFetcher {
const downloadPath = this.defaultDownloadPath;
if (!options.path && downloadPath) {
options.path = downloadPath;
}
if (!options.path) {
throw new Error('A `path` must be specified for `puppeteer-core`.');
}
if (
!('useMacOSARMBinary' in options) &&
this.configuration.experiments?.macArmChromiumEnabled
) {
options.useMacOSARMBinary = true;
}
if (!('host' in options) && this.configuration.downloadHost) {
options.host = this.configuration.downloadHost;
}
if (!('product' in options) && this.configuration.defaultProduct) {
options.product = this.configuration.defaultProduct;
}
return new BrowserFetcher(options as BrowserFetcherOptions);
}
}

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
export * from './BrowserFetcher.js';
export * from './ChromeLauncher.js';
export * from './FirefoxLauncher.js';
export * from './LaunchOptions.js';

View File

@ -41,10 +41,6 @@ export const {
* @public
*/
connect,
/**
* @public
*/
createBrowserFetcher,
/**
* @public
*/

View File

@ -18,6 +18,6 @@
* @internal
*/
export const PUPPETEER_REVISIONS = Object.freeze({
chromium: '1108766',
chrome: '112.0.5615.121',
firefox: 'latest',
});

View File

@ -16,21 +16,21 @@
/**
* This script ensures that the pinned version of devtools-protocol in
* package.json is the right version for the current revision of Chromium that
* package.json is the right version for the current revision of Chrome that
* Puppeteer ships with.
*
* The devtools-protocol package publisher runs every hour and checks if there
* are protocol changes. If there are, it will be versioned with the revision
* number of the commit that last changed the .pdl files.
*
* Chromium branches/releases are figured out at a later point in time, so it's
* not true that each Chromium revision will have an exact matching revision
* Chrome branches/releases are figured out at a later point in time, so it's
* not true that each Chrome revision will have an exact matching revision
* version of devtools-protocol. To ensure we're using a devtools-protocol that
* is aligned with our revision, we want to find the largest package number
* that's \<= the revision that Puppeteer is using.
*
* This script uses npm's `view` function to list all versions in a range and
* find the one closest to our Chromium revision.
* find the one closest to our Chrome revision.
*/
// eslint-disable-next-line import/extensions
@ -39,46 +39,59 @@ import {execSync} from 'child_process';
import packageJson from '../package.json';
import {PUPPETEER_REVISIONS} from '../src/revisions.js';
const currentProtocolPackageInstalledVersion =
packageJson.dependencies['devtools-protocol'];
async function main() {
const currentProtocolPackageInstalledVersion =
packageJson.dependencies['devtools-protocol'];
/**
* Ensure that the devtools-protocol version is pinned.
*/
if (/^[^0-9]/.test(currentProtocolPackageInstalledVersion)) {
console.log(
`ERROR: devtools-protocol package is not pinned to a specific version.\n`
/**
* Ensure that the devtools-protocol version is pinned.
*/
if (/^[^0-9]/.test(currentProtocolPackageInstalledVersion)) {
console.log(
`ERROR: devtools-protocol package is not pinned to a specific version.\n`
);
process.exit(1);
}
const chromeVersion = PUPPETEER_REVISIONS.chrome;
// find the right revision for our Chrome version.
const req = await fetch(
`https://chromiumdash.appspot.com/fetch_releases?channel=stable`
);
process.exit(1);
}
const stableReleases = await req.json();
const chromeRevision = stableReleases.find(release => {
return release.version === chromeVersion;
}).chromium_main_branch_position;
console.log(`Revisions for ${chromeVersion}: ${chromeRevision}`);
// find the right revision for our Chromium revision
const command = `npm view "devtools-protocol@<=0.0.${chromeRevision}" version | tail -1`;
const command = `npm view "devtools-protocol@<=0.0.${PUPPETEER_REVISIONS.chromium}" version | tail -1`;
console.log(
'Checking npm for devtools-protocol revisions:\n',
`'${command}'`,
'\n'
);
console.log(
'Checking npm for devtools-protocol revisions:\n',
`'${command}'`,
'\n'
);
const output = execSync(command, {
encoding: 'utf8',
});
const output = execSync(command, {
encoding: 'utf8',
});
const bestRevisionFromNpm = output.split(' ')[1]!.replace(/'|\n/g, '');
const bestRevisionFromNpm = output.split(' ')[1]!.replace(/'|\n/g, '');
if (currentProtocolPackageInstalledVersion !== bestRevisionFromNpm) {
console.log(`ERROR: bad devtools-protocol revision detected:
if (currentProtocolPackageInstalledVersion !== bestRevisionFromNpm) {
console.log(`ERROR: bad devtools-protocol revision detected:
Current Puppeteer Chromium revision: ${PUPPETEER_REVISIONS.chromium}
Current Puppeteer Chrome revision: ${chromeRevision}
Current devtools-protocol version in package.json: ${currentProtocolPackageInstalledVersion}
Expected devtools-protocol version: ${bestRevisionFromNpm}`);
process.exit(1);
process.exit(1);
}
console.log(
`Correct devtools-protocol version found (${bestRevisionFromNpm}).`
);
process.exit(0);
}
console.log(
`Correct devtools-protocol version found (${bestRevisionFromNpm}).`
);
process.exit(0);
void main();

View File

@ -47,16 +47,12 @@ export const getConfiguration = (): Configuration => {
process.env['PUPPETEER_SKIP_DOWNLOAD'] ??
process.env['npm_config_puppeteer_skip_download'] ??
process.env['npm_package_config_puppeteer_skip_download'] ??
process.env['PUPPETEER_SKIP_CHROMIUM_DOWNLOAD'] ??
process.env['npm_config_puppeteer_skip_chromium_download'] ??
process.env['npm_package_config_puppeteer_skip_chromium_download'] ??
configuration.skipDownload
);
// Prepare variables used in browser downloading
if (!configuration.skipDownload) {
configuration.browserRevision =
process.env['PUPPETEER_CHROMIUM_REVISION'] ??
process.env['PUPPETEER_BROWSER_REVISION'] ??
process.env['npm_config_puppeteer_browser_revision'] ??
process.env['npm_package_config_puppeteer_browser_revision'] ??
@ -86,14 +82,6 @@ export const getConfiguration = (): Configuration => {
configuration.temporaryDirectory;
configuration.experiments ??= {};
configuration.experiments.macArmChromiumEnabled = Boolean(
process.env['PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM'] ??
process.env['npm_config_puppeteer_experimental_chromium_mac_arm'] ??
process.env[
'npm_package_config_puppeteer_experimental_chromium_mac_arm'
] ??
configuration.experiments.macArmChromiumEnabled
);
configuration.logLevel = (process.env['PUPPETEER_LOGLEVEL'] ??
process.env['npm_config_LOGLEVEL'] ??

View File

@ -20,7 +20,6 @@ import {
resolveBuildId,
makeProgressCallback,
detectBrowserPlatform,
BrowserPlatform,
} from '@puppeteer/browsers';
import {Product} from 'puppeteer-core';
import {PUPPETEER_REVISIONS} from 'puppeteer-core/internal/revisions.js';
@ -31,8 +30,7 @@ import {getConfiguration} from '../getConfiguration.js';
* @internal
*/
const supportedProducts = {
chromium: 'Chromium',
chrome: 'Chromium',
chrome: 'Chrome',
firefox: 'Firefox Nightly',
} as const;
@ -48,42 +46,18 @@ export async function downloadBrowser(): Promise<void> {
return;
}
let downloadHost = configuration.downloadHost;
const downloadHost = configuration.downloadHost;
let platform = detectBrowserPlatform();
const platform = detectBrowserPlatform();
if (!platform) {
throw new Error('The current platform is not supported.');
}
// TODO: remove once Mac ARM is enabled by default for Puppeteer https://github.com/puppeteer/puppeteer/issues/9630.
if (
platform === BrowserPlatform.MAC_ARM &&
!configuration.experiments?.macArmChromiumEnabled
) {
platform = BrowserPlatform.MAC;
}
const product = configuration.defaultProduct!;
const browser = productToBrowser(product);
// TODO: PUPPETEER_REVISIONS should use Chrome and not Chromium.
const unresolvedBuildId =
configuration.browserRevision ||
PUPPETEER_REVISIONS[product === 'chrome' ? 'chromium' : 'firefox'] ||
'latest';
if (product === 'chrome' && downloadHost) {
// TODO: remove downloadHost in favour of baseDownloadUrl. The "host" of
// Firefox is already a URL and not a host. This would be a breaking change.
if (
!downloadHost.endsWith('/chromium-browser-snapshots') &&
!downloadHost.endsWith('/chromium-browser-snapshots/')
) {
downloadHost += downloadHost.endsWith('/')
? 'chromium-browser-snapshots'
: '/chromium-browser-snapshots';
}
}
configuration.browserRevision || PUPPETEER_REVISIONS[product] || 'latest';
const buildId = await resolveBuildId(browser, platform, unresolvedBuildId);
@ -94,6 +68,8 @@ export async function downloadBrowser(): Promise<void> {
platform,
buildId,
downloadProgressCallback: makeProgressCallback(browser, buildId),
// TODO: remove downloadHost in favour of baseDownloadUrl. The "host" of
// Firefox is already a URL and not a host. This would be a breaking change.
baseUrl: downloadHost,
});
@ -112,11 +88,11 @@ export async function downloadBrowser(): Promise<void> {
function productToBrowser(product?: Product) {
switch (product) {
case 'chrome':
return Browser.CHROMIUM;
return Browser.CHROME;
case 'firefox':
return Browser.FIREFOX;
}
return Browser.CHROMIUM;
return Browser.CHROME;
}
/**

View File

@ -37,10 +37,6 @@ export const {
* @public
*/
connect,
/**
* @public
*/
createBrowserFetcher,
/**
* @public
*/

View File

@ -1,6 +1,5 @@
import puppeteer, {
connect,
createBrowserFetcher,
defaultArgs,
executablePath,
launch,
@ -9,6 +8,5 @@ import {expectType} from 'tsd';
expectType<typeof launch>(puppeteer.launch);
expectType<typeof connect>(puppeteer.connect);
expectType<typeof createBrowserFetcher>(puppeteer.createBrowserFetcher);
expectType<typeof defaultArgs>(puppeteer.defaultArgs);
expectType<typeof executablePath>(puppeteer.executablePath);

View File

@ -299,6 +299,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should work with no default arguments",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL",
"platforms": ["darwin", "linux", "win32"],
@ -1013,18 +1019,6 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable does not exist does not return /usr/bin/chromium-browser",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable exists returns /usr/bin/chromium-browser",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should be able to launch Chrome",
"platforms": ["darwin", "linux", "win32"],

View File

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 436 B

View File

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 276 B

View File

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 428 B

View File

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 448 B

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 81 B

After

Width:  |  Height:  |  Size: 81 B

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 461 B

After

Width:  |  Height:  |  Size: 461 B

View File

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 138 B

View File

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 138 B

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 168 B

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 168 B

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 266 B

After

Width:  |  Height:  |  Size: 266 B

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 119 B

After

Width:  |  Height:  |  Size: 119 B

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 357 B

After

Width:  |  Height:  |  Size: 357 B

View File

@ -14,14 +14,14 @@
* limitations under the License.
*/
import fs from 'fs';
import {mkdtemp, readFile, stat, writeFile} from 'fs/promises';
import {mkdtemp, readFile, writeFile} from 'fs/promises';
import os from 'os';
import path from 'path';
import {TLSSocket} from 'tls';
import {Protocol} from 'devtools-protocol';
import expect from 'expect';
import {BrowserFetcher, TimeoutError} from 'puppeteer';
import {TimeoutError} from 'puppeteer';
import {Page} from 'puppeteer-core/internal/api/Page.js';
import {rmSync} from 'puppeteer-core/internal/node/util/fs.js';
import sinon from 'sinon';
@ -38,93 +38,6 @@ describe('Launcher specs', function () {
}
describe('Puppeteer', function () {
describe('BrowserFetcher', function () {
it('should download and extract chrome linux binary', async () => {
const {server} = getTestState();
const downloadsFolder = await mkdtemp(TMP_FOLDER);
const browserFetcher = new BrowserFetcher({
platform: 'linux',
path: downloadsFolder,
host: server.PREFIX,
});
const expectedRevision = '123456';
let revisionInfo = browserFetcher.revisionInfo(expectedRevision);
server.setRoute(
revisionInfo.url.substring(server.PREFIX.length),
(req, res) => {
server.serveFile(req, res, '/chromium-linux.zip');
}
);
expect(revisionInfo.local).toBe(false);
expect(browserFetcher.platform()).toBe('linux');
expect(browserFetcher.product()).toBe('chrome');
expect(!!browserFetcher.host()).toBe(true);
expect(await browserFetcher.canDownload('100000')).toBe(false);
expect(await browserFetcher.canDownload(expectedRevision)).toBe(true);
revisionInfo = (await browserFetcher.download(expectedRevision))!;
expect(revisionInfo.local).toBe(true);
expect(await readFile(revisionInfo.executablePath, 'utf8')).toBe(
'LINUX BINARY\n'
);
const expectedPermissions = os.platform() === 'win32' ? 0o666 : 0o755;
expect((await stat(revisionInfo.executablePath)).mode & 0o777).toBe(
expectedPermissions
);
expect(browserFetcher.localRevisions()).toEqual([expectedRevision]);
await browserFetcher.remove(expectedRevision);
expect(browserFetcher.localRevisions()).toEqual([]);
rmSync(downloadsFolder);
});
it('should download and extract firefox linux binary', async () => {
const {server} = getTestState();
const downloadsFolder = await mkdtemp(TMP_FOLDER);
const browserFetcher = new BrowserFetcher({
platform: 'linux',
path: downloadsFolder,
host: server.PREFIX,
product: 'firefox',
});
const expectedVersion = '75.0a1';
let revisionInfo = browserFetcher.revisionInfo(expectedVersion);
server.setRoute(
revisionInfo.url.substring(server.PREFIX.length),
(req, res) => {
server.serveFile(
req,
res,
`/firefox-${expectedVersion}.en-US.linux-x86_64.tar.bz2`
);
}
);
expect(revisionInfo.local).toBe(false);
expect(browserFetcher.platform()).toBe('linux');
expect(browserFetcher.product()).toBe('firefox');
expect(await browserFetcher.canDownload('100000')).toBe(false);
expect(await browserFetcher.canDownload(expectedVersion)).toBe(true);
revisionInfo = (await browserFetcher.download(expectedVersion))!;
expect(revisionInfo.local).toBe(true);
expect(await readFile(revisionInfo.executablePath, 'utf8')).toBe(
'FIREFOX LINUX BINARY\n'
);
const expectedPermissions = os.platform() === 'win32' ? 0o666 : 0o755;
expect((await stat(revisionInfo.executablePath)).mode & 0o777).toBe(
expectedPermissions
);
expect(await browserFetcher.localRevisions()).toEqual([
expectedVersion,
]);
await browserFetcher.remove(expectedVersion);
expect(await browserFetcher.localRevisions()).toEqual([]);
rmSync(downloadsFolder);
});
});
describe('Browser.disconnect', function () {
it('should reject navigation when browser closes', async () => {
const {server, puppeteer, defaultBrowserOptions} = getTestState();
@ -910,68 +823,6 @@ describe('Launcher specs', function () {
}
});
});
describe('when the product is chrome, platform is not darwin, and arch is arm64', () => {
describe('and the executable exists', () => {
it('returns /usr/bin/chromium-browser', async () => {
const {puppeteer} = getTestState();
const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64');
const fsExistsStub = sinon.stub(fs, 'existsSync');
fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(true);
const executablePath = puppeteer.executablePath();
expect(executablePath).toEqual('/usr/bin/chromium-browser');
osPlatformStub.restore();
osArchStub.restore();
fsExistsStub.restore();
});
describe('and the executable path is configured', () => {
const sandbox = sinon.createSandbox();
beforeEach(() => {
const {puppeteer} = getTestState();
sandbox
.stub(puppeteer.configuration, 'executablePath')
.value('SOME_CUSTOM_EXECUTABLE');
});
afterEach(() => {
sandbox.restore();
});
it('its value is used', async () => {
const {puppeteer} = getTestState();
try {
puppeteer.executablePath();
} catch (error) {
expect((error as Error).message).toContain(
'SOME_CUSTOM_EXECUTABLE'
);
}
});
});
});
describe('and the executable does not exist', () => {
it('does not return /usr/bin/chromium-browser', async () => {
const {puppeteer} = getTestState();
const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64');
const fsExistsStub = sinon.stub(fs, 'existsSync');
fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false);
expect(() => {
return puppeteer.executablePath();
}).toThrowError();
osPlatformStub.restore();
osArchStub.restore();
fsExistsStub.restore();
});
});
});
});
});

View File

@ -120,11 +120,7 @@ const defaultBrowserOptions = Object.assign(
})();
const setupGoldenAssertions = (): void => {
let suffix = product.toLowerCase();
if (suffix === 'chrome') {
// TODO: to avoid moving golden folders.
suffix = 'chromium';
}
const suffix = product.toLowerCase();
const GOLDEN_DIR = path.join(__dirname, `../golden-${suffix}`);
const OUTPUT_DIR = path.join(__dirname, `../output-${suffix}`);
if (fs.existsSync(OUTPUT_DIR)) {

View File

@ -759,7 +759,7 @@ describe('navigation', function () {
it('should return matching responses', async () => {
const {page, server} = getTestState();
// Disable cache: otherwise, chromium will cache similar requests.
// Disable cache: otherwise, the browser will cache similar requests.
await page.setCacheEnabled(false);
await page.goto(server.EMPTY_PAGE);
// Attach three frames.

View File

@ -1,319 +0,0 @@
#!/usr/bin/env node
/**
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {execSync, fork, spawn} from 'child_process';
import fs from 'fs';
import path from 'path';
import URL from 'url';
import debug from 'debug';
import minimist from 'minimist';
import ProgressBar from 'progress';
import {BrowserFetcher, BrowserFetcherRevisionInfo} from 'puppeteer';
const COLOR_RESET = '\x1b[0m';
const COLOR_RED = '\x1b[31m';
const COLOR_GREEN = '\x1b[32m';
const COLOR_YELLOW = '\x1b[33m';
const argv = minimist(process.argv.slice(2), {});
const help = `
Usage:
node bisect.js --good <revision> --bad <revision> <script>
Parameters:
--good revision that is known to be GOOD, defaults to the chromium revision in src/revision.ts in the main branch
--bad revision that is known to be BAD, defaults to the chromium revision in src/revision.ts
--no-cache do not keep downloaded Chromium revisions
--unit-test pattern that identifies a unit tests that should be checked
--script path to a script that returns non-zero code for BAD revisions and 0 for good
Example:
node tools/bisect.js --unit-test test
node tools/bisect.js --good 577361 --bad 599821 --script simple.js
node tools/bisect.js --good 577361 --bad 599821 --unit-test test
`;
if (argv.h || argv.help) {
console.log(help);
process.exit(0);
}
if (typeof argv.good !== 'number') {
argv.good = getChromiumRevision('main');
if (typeof argv.good !== 'number') {
console.log(
COLOR_RED +
'ERROR: Could not parse current Chromium revision' +
COLOR_RESET
);
console.log(help);
process.exit(1);
}
}
if (typeof argv.bad !== 'number') {
argv.bad = getChromiumRevision();
if (typeof argv.bad !== 'number') {
console.log(
COLOR_RED +
'ERROR: Could not parse Chromium revision in the main branch' +
COLOR_RESET
);
console.log(help);
process.exit(1);
}
}
if (!argv.script && !argv['unit-test']) {
console.log(
COLOR_RED +
'ERROR: Expected to be given a script or a unit test to run' +
COLOR_RESET
);
console.log(help);
process.exit(1);
}
const scriptPath = argv.script && path.resolve(argv.script);
if (scriptPath && !fs.existsSync(scriptPath)) {
console.log(
COLOR_RED +
'ERROR: Expected to be given a path to a script to run' +
COLOR_RESET
);
console.log(help);
process.exit(1);
}
const browserFetcher = new BrowserFetcher();
(async (scriptPath, good, bad, pattern, noCache) => {
const span = Math.abs(good - bad);
console.log(
`Bisecting ${COLOR_YELLOW}${span}${COLOR_RESET} revisions in ${COLOR_YELLOW}~${
span.toString(2).length
}${COLOR_RESET} iterations`
);
while (true) {
const middle = Math.round((good + bad) / 2);
const revision = await findDownloadableRevision(middle, good, bad);
if (!revision || revision === good || revision === bad) {
break;
}
let info: BrowserFetcherRevisionInfo | undefined =
browserFetcher.revisionInfo(revision);
const shouldRemove = noCache && !info.local;
info = await downloadRevision(revision);
const exitCode = await (pattern
? runUnitTest(pattern, info)
: runScript(scriptPath, info));
if (shouldRemove) {
await browserFetcher.remove(revision);
}
let outcome;
if (exitCode) {
bad = revision;
outcome = COLOR_RED + 'BAD' + COLOR_RESET;
} else {
good = revision;
outcome = COLOR_GREEN + 'GOOD' + COLOR_RESET;
}
const span = Math.abs(good - bad);
let fromText = '';
let toText = '';
if (good < bad) {
fromText = COLOR_GREEN + good + COLOR_RESET;
toText = COLOR_RED + bad + COLOR_RESET;
} else {
fromText = COLOR_RED + bad + COLOR_RESET;
toText = COLOR_GREEN + good + COLOR_RESET;
}
console.log(
`- ${COLOR_YELLOW}r${revision}${COLOR_RESET} was ${outcome}. Bisecting [${fromText}, ${toText}] - ${COLOR_YELLOW}${span}${COLOR_RESET} revisions and ${COLOR_YELLOW}~${
span.toString(2).length
}${COLOR_RESET} iterations`
);
}
const [fromSha, toSha] = await Promise.all([
revisionToSha(Math.min(good, bad)),
revisionToSha(Math.max(good, bad)),
]);
console.log(
`RANGE: https://chromium.googlesource.com/chromium/src/+log/${fromSha}..${toSha}`
);
})(scriptPath, argv.good, argv.bad, argv['unit-test'], argv['no-cache']);
function runScript(scriptPath, revisionInfo) {
const log = debug('bisect:runscript');
log('Running script');
const child = fork(scriptPath, [], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
env: {
...process.env,
PUPPETEER_EXECUTABLE_PATH: revisionInfo.executablePath,
},
});
return new Promise((resolve, reject) => {
child.on('error', err => {
return reject(err);
});
child.on('exit', code => {
return resolve(code);
});
});
}
function runUnitTest(pattern, revisionInfo) {
const log = debug('bisect:rununittest');
log('Running unit test');
const child = spawn('npm run test:chrome:headless', ['--', '-g', pattern], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
shell: true,
env: {
...process.env,
PUPPETEER_EXECUTABLE_PATH: revisionInfo.executablePath,
},
});
return new Promise((resolve, reject) => {
child.on('error', err => {
return reject(err);
});
child.on('exit', code => {
return resolve(code);
});
});
}
async function downloadRevision(revision) {
const log = debug('bisect:download');
log(`Downloading ${revision}`);
let progressBar: ProgressBar | undefined;
let lastDownloadedBytes = 0;
return await browserFetcher.download(
revision,
(downloadedBytes, totalBytes) => {
if (!progressBar) {
progressBar = new ProgressBar(
`- downloading Chromium r${revision} - ${toMegabytes(
totalBytes
)} [:bar] :percent :etas `,
{
complete: '=',
incomplete: ' ',
width: 20,
total: totalBytes,
}
);
}
const delta = downloadedBytes - lastDownloadedBytes;
lastDownloadedBytes = downloadedBytes;
progressBar.tick(delta);
}
);
function toMegabytes(bytes) {
const mb = bytes / 1024 / 1024;
return `${Math.round(mb * 10) / 10} Mb`;
}
}
async function findDownloadableRevision(rev, from, to) {
const log = debug('bisect:findrev');
const min = Math.min(from, to);
const max = Math.max(from, to);
log(`Looking around ${rev} from [${min}, ${max}]`);
if (await browserFetcher.canDownload(rev)) {
return rev;
}
let down = rev;
let up = rev;
while (min <= down || up <= max) {
const [downOk, upOk] = await Promise.all([
down > min ? probe(--down) : Promise.resolve(false),
up < max ? probe(++up) : Promise.resolve(false),
]);
if (downOk) {
return down;
}
if (upOk) {
return up;
}
}
return null;
async function probe(rev) {
const result = await browserFetcher.canDownload(rev);
log(` ${rev} - ${result ? 'OK' : 'missing'}`);
return result;
}
}
async function revisionToSha(revision) {
const json = await fetchJSON(
'https://cr-rev.appspot.com/_ah/api/crrev/v1/redirect/' + revision
);
return json.git_sha;
}
async function fetchJSON(url: string): Promise<{git_sha: string}> {
const agent = url.startsWith('https://')
? await import('https')
: await import('http');
return new Promise((resolve, reject) => {
const options = {
...URL.parse(url),
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
};
const req = agent.request(options, function (res) {
let result = '';
res.setEncoding('utf8');
res.on('data', chunk => {
return (result += chunk);
});
res.on('end', () => {
return resolve(JSON.parse(result));
});
});
req.on('error', err => {
return reject(err);
});
req.end();
});
}
function getChromiumRevision(gitRevision?: string) {
const fileName = 'packages/puppeteer-core/src/revisions.ts';
const command = gitRevision
? `git show ${gitRevision}:${fileName}`
: `cat ${fileName}`;
const result = execSync(command, {
encoding: 'utf8',
});
const m = result.match(/chromium: '(\d+)'/);
if (!m) {
return null;
}
return parseInt(m[1], 10);
}

View File

@ -1,361 +0,0 @@
#!/usr/bin/env node
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const assert = require('assert');
const https = require('https');
const BrowserFetcher =
require('puppeteer-core/internal/node/BrowserFetcher.js').BrowserFetcher;
const SUPPORTED_PLATFORMS = ['linux', 'mac', 'mac_arm', 'win32', 'win64'];
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
};
class Table {
/**
* @param {!Array<number>} columnWidths
*/
constructor(columnWidths) {
this.widths = columnWidths;
}
/**
* @param {!Array<string>} values
*/
drawRow(values) {
assert(values.length === this.widths.length);
let row = '';
for (let i = 0; i < values.length; ++i) {
row += padCenter(values[i], this.widths[i]);
}
console.log(row);
}
}
const helpMessage = `
This script checks availability of prebuilt Chromium snapshots.
Usage: node check_availability.js [<options>] [<browser version(s)>]
options
-f full mode checks availability of all the platforms, default mode
-r roll mode checks for the most recent stable Chromium roll candidate
-rb roll mode checks for the most recent beta Chromium roll candidate
-rd roll mode checks for the most recent dev Chromium roll candidate
-p $platform print the latest revision for the given platform (${SUPPORTED_PLATFORMS.join(
','
)}).
-h show this help
browser version(s)
<revision> single revision number means checking for this specific revision
<from> <to> checks all the revisions within a given range, inclusively
Examples
To check Chromium availability of a certain revision
node check_availability.js [revision]
To find a Chromium roll candidate for current stable Linux version
node check_availability.js -r
use -rb for beta and -rd for dev versions.
To check Chromium availability from the latest revision in a descending order
node check_availability.js
`;
function main() {
const args = process.argv.slice(2);
if (args.length > 3) {
console.log(helpMessage);
return;
}
if (args.length === 0) {
checkOmahaProxyAvailability();
return;
}
if (args[0].startsWith('-')) {
const option = args[0].substring(1);
switch (option) {
case 'f':
break;
case 'r':
checkRollCandidate('stable');
return;
case 'rb':
checkRollCandidate('beta');
return;
case 'rd':
checkRollCandidate('dev');
return;
case 'p':
printLatestRevisionForPlatform(args[1]);
return;
default:
console.log(helpMessage);
return;
}
args.splice(0, 1); // remove options arg since we are done with options
}
if (args.length === 1) {
const revision = parseInt(args[0], 10);
checkRangeAvailability({
fromRevision: revision,
toRevision: revision,
stopWhenAllAvailable: false,
});
} else {
const fromRevision = parseInt(args[0], 10);
const toRevision = parseInt(args[1], 10);
checkRangeAvailability({
fromRevision,
toRevision,
stopWhenAllAvailable: false,
});
}
}
async function checkOmahaProxyAvailability() {
const latestRevisions = (
await Promise.all([
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Mac/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Mac_Arm/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Win/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/LAST_CHANGE'
),
])
).map(s => {
return parseInt(s, 10);
});
const from = Math.max(...latestRevisions);
await checkRangeAvailability({
fromRevision: from,
toRevision: 0,
stopWhenAllAvailable: false,
});
}
async function printLatestRevisionForPlatform(platform) {
const latestRevisions = (
await Promise.all([
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Mac/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Mac_Arm/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Win/LAST_CHANGE'
),
fetch(
'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/LAST_CHANGE'
),
])
).map(s => {
return parseInt(s, 10);
});
const from = Math.max(...latestRevisions);
await checkRangeAvailability({
fromRevision: from,
toRevision: 0,
stopWhenAllAvailable: true,
printAsTable: false,
platforms: [platform],
});
}
async function checkRollCandidate(channel) {
const omahaResponse = await fetch(
`https://omahaproxy.appspot.com/all.json?channel=${channel}&os=linux`
);
const linuxInfo = JSON.parse(omahaResponse)[0];
if (!linuxInfo) {
console.error(`no ${channel} linux information available from omahaproxy`);
return;
}
const linuxRevision = parseInt(
linuxInfo.versions[0].branch_base_position,
10
);
const currentRevision = parseInt(
require('puppeteer-core/internal/revisions.js').PUPPETEER_REVISIONS
.chromium,
10
);
checkRangeAvailability({
fromRevision: linuxRevision,
toRevision: currentRevision,
stopWhenAllAvailable: true,
});
}
/**
* @param {*} options
*/
async function checkRangeAvailability({
fromRevision,
toRevision,
stopWhenAllAvailable,
platforms,
printAsTable = true,
}) {
platforms = platforms || SUPPORTED_PLATFORMS;
let table;
if (printAsTable) {
table = new Table([
10,
...platforms.map(() => {
return 7;
}),
]);
table.drawRow([''].concat(platforms));
}
const fetchers = platforms.map(platform => {
return new BrowserFetcher({platform});
});
const inc = fromRevision < toRevision ? 1 : -1;
const revisionToStop = toRevision + inc; // +inc so the range is fully inclusive
for (
let revision = fromRevision;
revision !== revisionToStop;
revision += inc
) {
const promises = fetchers.map(fetcher => {
return fetcher.canDownload(revision);
});
const availability = await Promise.all(promises);
const allAvailable = availability.every(e => {
return !!e;
});
if (table) {
const values = [
' ' +
(allAvailable ? colors.green + revision + colors.reset : revision),
];
for (let i = 0; i < availability.length; ++i) {
const decoration = availability[i] ? '+' : '-';
const color = availability[i] ? colors.green : colors.red;
values.push(color + decoration + colors.reset);
}
table.drawRow(values);
} else {
if (allAvailable) {
console.log(revision);
}
}
if (allAvailable && stopWhenAllAvailable) {
break;
}
}
}
/**
* @param {string} url
* @returns {!Promise<?string>}
*/
function fetch(url) {
let resolve;
const promise = new Promise(x => {
return (resolve = x);
});
https
.get(url, response => {
if (response.statusCode !== 200) {
resolve(null);
return;
}
let body = '';
response.on('data', function (chunk) {
body += chunk;
});
response.on('end', function () {
resolve(body);
});
})
.on('error', function (e) {
// This is okay; the server may just be faster at closing than us after
// the body is fully sent.
if (e.message.includes('ECONNRESET')) {
return;
}
console.error(`Error fetching json from ${url}: ${e}`);
resolve(null);
});
return promise;
}
/**
* @param {number} size
* @returns {string}
*/
function spaceString(size) {
return new Array(size).fill(' ').join('');
}
/**
* @param {string} text
* @returns {string}
*/
function filterOutColors(text) {
for (const colorName in colors) {
const color = colors[colorName];
text = text.replace(color, '');
}
return text;
}
/**
* @param {string} text
* @param {number} length
* @returns {string}
*/
function padCenter(text, length) {
const printableCharacters = filterOutColors(text);
if (printableCharacters.length >= length) {
return text;
}
const left = Math.floor((length - printableCharacters.length) / 2);
const right = Math.ceil((length - printableCharacters.length) / 2);
return spaceString(left) + text + spaceString(right);
}
main();

View File

@ -73,9 +73,17 @@ function spliceIntoSection(
continue;
}
if (versionsArchived.includes(puppeteerVersion.substring(1))) {
buffer.push(
` * Chromium ${chromiumVersion} - [Puppeteer ${puppeteerVersion}](https://github.com/puppeteer/puppeteer/blob/${puppeteerVersion}/docs/api/index.md)`
);
if (semver.gte(puppeteerVersion, '20.0.0')) {
buffer.push(
` * [Chrome for Testing](https://goo.gle/chrome-for-testing) ${chromiumVersion} - [Puppeteer ${puppeteerVersion}](https://pptr.dev/${puppeteerVersion.slice(
1
)})`
);
} else {
buffer.push(
` * Chromium ${chromiumVersion} - [Puppeteer ${puppeteerVersion}](https://github.com/puppeteer/puppeteer/blob/${puppeteerVersion}/docs/api/index.md)`
);
}
} else if (semver.lt(puppeteerVersion, '15.0.0')) {
buffer.push(
` * Chromium ${chromiumVersion} - [Puppeteer ${puppeteerVersion}](https://github.com/puppeteer/puppeteer/blob/${puppeteerVersion}/docs/api.md)`

View File

@ -15,8 +15,9 @@
*/
const versionsPerRelease = new Map([
// This is a mapping from Chromium version => Puppeteer version.
// In Chromium roll patches, use `NEXT` for the Puppeteer version.
// This is a mapping from Chrome version => Puppeteer version.
// In Chrome roll patches, use `NEXT` for the Puppeteer version.
['112.0.5615.121', 'NEXT'],
['112.0.5614.0', 'v19.8.0'],
['111.0.5556.0', 'v19.7.0'],
['110.0.5479.0', 'v19.6.0'],
@ -57,15 +58,15 @@ const versionsPerRelease = new Map([
]);
// Should not be more than 2 major versions behind Chrome Stable (https://chromestatus.com/roadmap).
const lastMaintainedChromiumVersion = '109.0.5412.0';
const lastMaintainedChromeVersion = '109.0.5412.0';
if (!versionsPerRelease.has(lastMaintainedChromiumVersion)) {
if (!versionsPerRelease.has(lastMaintainedChromeVersion)) {
throw new Error(
'lastMaintainedChromiumVersion is missing from versionsPerRelease'
'lastMaintainedChromeVersion is missing from versionsPerRelease'
);
}
module.exports = {
versionsPerRelease,
lastMaintainedChromiumVersion,
lastMaintainedChromeVersion,
};