Skip to content

Commit

Permalink
feat: Introduce BrowserFetcher class (puppeteer#1983)
Browse files Browse the repository at this point in the history
This patch introduces `BrowserFetcher` class that manages
downloaded versions of products.

This patch:
- shapes Downloader API to be minimal yet usable for our needs. This
  includes removing such methods as `Downloader.supportedPlatforms` and
  `Downloader.defaultRevision`.
- makes most of the fs-related methods in Downloader async. The only
  exception is the `Downloader.revisionInfo`: it has stay sync due to the
  `pptr.executablePath()` method being sync.
- updates `install.js` and `utils/check_availability.js` to use new API
- finally, renames `Downloader` into `BrowserFetcher`

Fixes puppeteer#1748.
  • Loading branch information
aslushnikov authored Feb 7, 2018
1 parent 18c9755 commit a363a73
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 140 deletions.
73 changes: 73 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@
- [Environment Variables](#environment-variables)
- [class: Puppeteer](#class-puppeteer)
* [puppeteer.connect(options)](#puppeteerconnectoptions)
* [puppeteer.createBrowserFetcher([options])](#puppeteercreatebrowserfetcheroptions)
* [puppeteer.defaultArgs()](#puppeteerdefaultargs)
* [puppeteer.executablePath()](#puppeteerexecutablepath)
* [puppeteer.launch([options])](#puppeteerlaunchoptions)
- [class: BrowserFetcher](#class-browserfetcher)
* [browserFetcher.canDownload(revision)](#browserfetchercandownloadrevision)
* [browserFetcher.download(revision[, progressCallback])](#browserfetcherdownloadrevision-progresscallback)
* [browserFetcher.localRevisions()](#browserfetcherlocalrevisions)
* [browserFetcher.platform()](#browserfetcherplatform)
* [browserFetcher.remove(revision)](#browserfetcherremoverevision)
* [browserFetcher.revisionInfo(revision)](#browserfetcherrevisioninforevision)
- [class: Browser](#class-browser)
* [event: 'disconnected'](#event-disconnected)
* [event: 'targetchanged'](#event-targetchanged)
Expand Down Expand Up @@ -273,6 +281,13 @@ puppeteer.launch().then(async browser => {

This methods attaches Puppeteer to an existing Chromium instance.

#### puppeteer.createBrowserFetcher([options])
- `options` <[Object]>
- `host` <[string]> A download host to be used. Defaults to `https://storage.googleapis.com`.
- `path` <[string]> A path for the downloads folder. Defaults to `<root>/.local-chromium`, where `<root>` is puppeteer's package root.
- `platform` <[string]> Possible values are: `mac`, `win32`, `win64`, `linux`. Defaults to the current platform.
- returns: <[BrowserFetcher]>

#### puppeteer.defaultArgs()
- returns: <[Array]<[string]>> The default flags that Chromium will be launched with.

Expand Down Expand Up @@ -307,6 +322,63 @@ If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://
>
> 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/+/lkcr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.

### class: BrowserFetcher

BrowserFetcher can download and manage different versions of Chromium.

BrowserFetcher operates on revision strings that specify a precise version of Chromium, e.g. `"533271"`. Revision strings can be obtained from [omahaproxy.appspot.com](http://omahaproxy.appspot.com/).

Example on how to use BrowserFetcher to download a specific version of Chromium and run
puppeteer against it:

```js
const browserFetcher = puppeteer.createBrowserFetcher();
const revisionInfo = await browserFetcher.download('533271');
const browser = await puppeteer.launch({executablePath: revisionInfo.executablePath})
```

> **NOTE** BrowserFetcher is not designed to work concurrently with other
> instances of BrowserFetcher that share the same downloads directory.
#### browserFetcher.canDownload(revision)
- `revision` <[string]> a revision to check availability.
- returns: <[Promise]<[boolean]>> returns `true` if the revision could be downloaded from the host.

The method initiates a HEAD request to check if the revision is available.

#### browserFetcher.download(revision[, progressCallback])
- `revision` <[string]> a revision to download.
- `progressCallback` <[function]([number], [number])> A function that will be called with two arguments:
- `downloadedBytes` <[number]> how many bytes have been downloaded
- `totalBytes` <[number]> how large is the total download.
- returns: <[Promise]<[Object]>> Resolves with revision information when the revision is downloaded and extracted
- `revision` <[string]> the revision the info was created from
- `folderPath` <[string]> path to the extracted revision folder
- `executablePath` <[string]> path to the revision executable
- `url` <[string]> URL this revision can be downloaded from
- `local` <[boolean]> whether the revision is locally available on disk

The method initiates a GET request to download the revision from the host.

#### browserFetcher.localRevisions()
- returns: <[Promise]<[Array]<[string]>>> A list of all revisions available locally on disk.

#### browserFetcher.platform()
- returns: <[string]> Returns one of `mac`, `linux`, `win32` or `win64`.

#### browserFetcher.remove(revision)
- `revision` <[string]> a revision to remove. The method will throw if the revision has not been downloaded.
- returns: <[Promise]> Resolves when the revision has been removed.

#### browserFetcher.revisionInfo(revision)
- `revision` <[string]> a revision to get info for.
- returns: <[Object]>
- `revision` <[string]> the revision the info was created from
- `folderPath` <[string]> path to the extracted revision folder
- `executablePath` <[string]> path to the revision executable
- `url` <[string]> URL this revision can be downloaded from
- `local` <[boolean]> whether the revision is locally available on disk

### class: Browser

Expand Down Expand Up @@ -2489,6 +2561,7 @@ reported.
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "String"
[stream.Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "stream.Readable"
[CDPSession]: #class-cdpsession "CDPSession"
[BrowserFetcher]: #class-browserfetcher "BrowserFetcher"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[Frame]: #class-frame "Frame"
[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
Expand Down
37 changes: 20 additions & 17 deletions install.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ if (process.env.NPM_CONFIG_PUPPETEER_SKIP_CHROMIUM_DOWNLOAD || process.env.npm_c
return;
}

const Downloader = require('./lib/Downloader');
const downloader = Downloader.createDefault();
const downloadHost = process.env.PUPPETEER_DOWNLOAD_HOST || process.env.npm_config_puppeteer_download_host;

const puppeteer = require('./index');
const browserFetcher = puppeteer.createBrowserFetcher({ host: downloadHost });

const platform = downloader.currentPlatform();
const revision = Downloader.defaultRevision();
const ProgressBar = require('progress');
const revision = require('./package.json').puppeteer.chromium_revision;
const revisionInfo = browserFetcher.revisionInfo(revision);

const revisionInfo = downloader.revisionInfo(platform, revision);
// Do nothing if the revision is already downloaded.
if (revisionInfo.downloaded)
if (revisionInfo.local)
return;

// Override current environment proxy settings with npm configuration, if any.
Expand All @@ -49,21 +49,20 @@ if (NPM_HTTP_PROXY)
if (NPM_NO_PROXY)
process.env.NO_PROXY = NPM_NO_PROXY;

const allRevisions = downloader.downloadedRevisions();
const downloadHost = process.env.PUPPETEER_DOWNLOAD_HOST || process.env.npm_config_puppeteer_download_host;
if (downloadHost)
downloader.setDownloadHost(downloadHost);
downloader.downloadRevision(platform, revision, onProgress)
browserFetcher.download(revisionInfo.revision, onProgress)
.then(() => browserFetcher.localRevisions())
.then(onSuccess)
.catch(onError);

/**
* @param {!Array<string>}
* @return {!Promise}
*/
function onSuccess() {
function onSuccess(localRevisions) {
console.log('Chromium downloaded to ' + revisionInfo.folderPath);
localRevisions = localRevisions.filter(revision => revision !== revisionInfo.revision);
// Remove previous chromium revisions.
const cleanupOldVersions = allRevisions.map(({platform, revision}) => downloader.removeRevision(platform, revision));
const cleanupOldVersions = localRevisions.map(revision => browserFetcher.remove(revision));
return Promise.all(cleanupOldVersions);
}

Expand All @@ -77,15 +76,19 @@ function onError(error) {
}

let progressBar = null;
function onProgress(bytesTotal, delta) {
let lastDownloadedBytes = 0;
function onProgress(downloadedBytes, totalBytes) {
if (!progressBar) {
progressBar = new ProgressBar(`Downloading Chromium r${revision} - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, {
const ProgressBar = require('progress');
progressBar = new ProgressBar(`Downloading Chromium r${revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, {
complete: '=',
incomplete: ' ',
width: 20,
total: bytesTotal,
total: totalBytes,
});
}
const delta = downloadedBytes - lastDownloadedBytes;
lastDownloadedBytes = downloadedBytes;
progressBar.tick(delta);
}

Expand Down
Loading

0 comments on commit a363a73

Please sign in to comment.