Source code
Revision control
Copy as Markdown
Other Tools
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {
Browser as browsers_SupportedBrowser,
resolveBuildId,
detectBrowserPlatform,
getInstalledBrowsers,
uninstall,
} from '@puppeteer/browsers';
import type {Browser} from '../api/Browser.js';
import type {Configuration} from '../common/Configuration.js';
import type {
ConnectOptions,
BrowserConnectOptions,
} from '../common/ConnectOptions.js';
import {type CommonPuppeteerSettings, Puppeteer} from '../common/Puppeteer.js';
import type {SupportedBrowser} from '../common/SupportedBrowser.js';
import {PUPPETEER_REVISIONS} from '../revisions.js';
import type {BrowserLauncher} from './BrowserLauncher.js';
import {ChromeLauncher} from './ChromeLauncher.js';
import {FirefoxLauncher} from './FirefoxLauncher.js';
import type {
BrowserLaunchArgumentOptions,
ChromeReleaseChannel,
LaunchOptions,
} from './LaunchOptions.js';
/**
* @public
*/
export interface PuppeteerLaunchOptions
extends LaunchOptions,
BrowserLaunchArgumentOptions,
BrowserConnectOptions {
browser?: SupportedBrowser;
extraPrefsFirefox?: Record<string, unknown>;
}
/**
* Extends the main {@link Puppeteer} class with Node specific behaviour for
* fetching and downloading browsers.
*
* If you're using Puppeteer in a Node environment, this is the class you'll get
* when you run `require('puppeteer')` (or the equivalent ES `import`).
*
* @remarks
* The most common method to use is {@link PuppeteerNode.launch | launch}, which
* is used to launch and connect to a new browser instance.
*
* See {@link Puppeteer | the main Puppeteer class} for methods common to all
* environments, such as {@link Puppeteer.connect}.
*
* @example
* The following is a typical example of using Puppeteer to drive automation:
*
* ```ts
* import puppeteer from 'puppeteer';
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* // other actions...
* await browser.close();
* })();
* ```
*
* Once you have created a `page` you have access to a large API to interact
* with the page, navigate, or find certain elements in that page.
* The {@link Page | `page` documentation} lists all the available methods.
*
* @public
*/
export class PuppeteerNode extends Puppeteer {
#_launcher?: BrowserLauncher;
#lastLaunchedBrowser?: SupportedBrowser;
/**
* @internal
*/
defaultBrowserRevision: string;
/**
* @internal
*/
configuration: Configuration = {};
/**
* @internal
*/
constructor(
settings: {
configuration?: Configuration;
} & CommonPuppeteerSettings
) {
const {configuration, ...commonSettings} = settings;
super(commonSettings);
if (configuration) {
this.configuration = configuration;
}
switch (this.configuration.defaultBrowser) {
case 'firefox':
this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox;
break;
default:
this.configuration.defaultBrowser = 'chrome';
this.defaultBrowserRevision = PUPPETEER_REVISIONS.chrome;
break;
}
this.connect = this.connect.bind(this);
this.launch = this.launch.bind(this);
this.executablePath = this.executablePath.bind(this);
this.defaultArgs = this.defaultArgs.bind(this);
this.trimCache = this.trimCache.bind(this);
}
/**
* This method attaches Puppeteer to an existing browser instance.
*
* @param options - Set of configurable options to set on the browser.
* @returns Promise which resolves to browser instance.
*/
override connect(options: ConnectOptions): Promise<Browser> {
return super.connect(options);
}
/**
* Launches a browser instance with given arguments and options when
* specified.
*
* When using with `puppeteer-core`,
* {@link LaunchOptions.executablePath | options.executablePath} or
* {@link LaunchOptions.channel | options.channel} must be provided.
*
* @example
* You can use {@link LaunchOptions.ignoreDefaultArgs | options.ignoreDefaultArgs}
* to filter out `--mute-audio` from default arguments:
*
* ```ts
* const browser = await puppeteer.launch({
* ignoreDefaultArgs: ['--mute-audio'],
* });
* ```
*
* @remarks
* 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
* or
* build is suggested. See
* {@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. See
* of Chrome for Testing.
*
* @param options - Options to configure launching behavior.
*/
launch(options: PuppeteerLaunchOptions = {}): Promise<Browser> {
const {browser: product = this.defaultBrowser} = options;
this.#lastLaunchedBrowser = product;
return this.#launcher.launch(options);
}
/**
* @internal
*/
get #launcher(): BrowserLauncher {
if (
this.#_launcher &&
this.#_launcher.browser === this.lastLaunchedBrowser
) {
return this.#_launcher;
}
switch (this.lastLaunchedBrowser) {
case 'chrome':
this.defaultBrowserRevision = PUPPETEER_REVISIONS.chrome;
this.#_launcher = new ChromeLauncher(this);
break;
case 'firefox':
this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox;
this.#_launcher = new FirefoxLauncher(this);
break;
default:
throw new Error(`Unknown product: ${this.#lastLaunchedBrowser}`);
}
return this.#_launcher;
}
/**
* The default executable path.
*/
executablePath(channel?: ChromeReleaseChannel): string {
return this.#launcher.executablePath(channel);
}
/**
* @internal
*/
get browserVersion(): string {
return (
this.configuration?.[this.lastLaunchedBrowser]?.version ??
this.defaultBrowserRevision!
);
}
/**
* The default download path for puppeteer. For puppeteer-core, this
* code should never be called as it is never defined.
*
* @internal
*/
get defaultDownloadPath(): string | undefined {
return this.configuration.cacheDirectory;
}
/**
* The name of the browser that was last launched.
*/
get lastLaunchedBrowser(): SupportedBrowser {
return this.#lastLaunchedBrowser ?? this.defaultBrowser;
}
/**
* The name of the browser that will be launched by default. For
* `puppeteer`, this is influenced by your configuration. Otherwise, it's
* `chrome`.
*/
get defaultBrowser(): SupportedBrowser {
return this.configuration.defaultBrowser ?? 'chrome';
}
/**
* @deprecated Do not use as this field as it does not take into account
* multiple browsers of different types. Use
* {@link PuppeteerNode.defaultBrowser | defaultBrowser} or
* {@link PuppeteerNode.lastLaunchedBrowser | lastLaunchedBrowser}.
*
* @returns The name of the browser that is under automation.
*/
get product(): string {
return this.#launcher.browser;
}
/**
* @param options - Set of configurable options to set on the browser.
*
* @returns The default flags that Chromium will be launched with.
*/
defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] {
return this.#launcher.defaultArgs(options);
}
/**
* Removes all non-current Firefox and Chrome binaries in the cache directory
* identified by the provided Puppeteer configuration. The current browser
* version is determined by resolving PUPPETEER_REVISIONS from Puppeteer
* unless `configuration.browserRevision` is provided.
*
* @remarks
*
* Note that the method does not check if any other Puppeteer versions
* installed on the host that use the same cache directory require the
* non-current binaries.
*
* @public
*/
async trimCache(): Promise<void> {
const platform = detectBrowserPlatform();
if (!platform) {
throw new Error('The current platform is not supported.');
}
const cacheDir = this.configuration.cacheDirectory!;
const installedBrowsers = await getInstalledBrowsers({
cacheDir,
});
const puppeteerBrowsers: Array<{
product: SupportedBrowser;
browser: browsers_SupportedBrowser;
currentBuildId: string;
}> = [
{
product: 'chrome',
browser: browsers_SupportedBrowser.CHROME,
currentBuildId: '',
},
{
product: 'firefox',
browser: browsers_SupportedBrowser.FIREFOX,
currentBuildId: '',
},
];
// Resolve current buildIds.
for (const item of puppeteerBrowsers) {
const tag =
this.configuration?.[item.product]?.version ??
PUPPETEER_REVISIONS[item.product];
item.currentBuildId = await resolveBuildId(item.browser, platform, tag);
}
const currentBrowserBuilds = new Set(
puppeteerBrowsers.map(browser => {
return `${browser.browser}_${browser.currentBuildId}`;
})
);
const currentBrowsers = new Set(
puppeteerBrowsers.map(browser => {
return browser.browser;
})
);
for (const installedBrowser of installedBrowsers) {
// Don't uninstall browsers that are not managed by Puppeteer yet.
if (!currentBrowsers.has(installedBrowser.browser)) {
continue;
}
// Keep the browser build used by the current Puppeteer installation.
if (
currentBrowserBuilds.has(
`${installedBrowser.browser}_${installedBrowser.buildId}`
)
) {
continue;
}
await uninstall({
browser: installedBrowser.browser,
platform,
cacheDir,
buildId: installedBrowser.buildId,
});
}
}
}