mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore(agnostification): split up launcher class (#6484)
The `Launcher` class was serving two purposes: 1. Launch browsers 2. Connect to browsers Number 1) only needs to be done in Node land, but 2) is agnostic; in a browser version of Puppeteer we'll need the ability to connect over a websocket to send commands back and forth. As part of the agnostification work we needed to split the `Launcher` up so that the connection part can be made agnostic. Additionally, I removed dependencies on `https`, `http` and `URL` from Node, instead leaning on fetch (via `node-fetch` if in Node land) and the browser `URL` API (which was added to Node in Node 10).
This commit is contained in:
parent
1ed38af79a
commit
4846b8723c
@ -1,22 +0,0 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [puppeteer](./puppeteer.md) > [ProductLauncher](./puppeteer.productlauncher.md) > [connect](./puppeteer.productlauncher.connect.md)
|
||||
|
||||
## ProductLauncher.connect() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
connect(object: any): any;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| object | any | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
any
|
||||
|
@ -17,13 +17,12 @@ export interface ProductLauncher
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [executablePath](./puppeteer.productlauncher.executablepath.md) | () => string | |
|
||||
| [product](./puppeteer.productlauncher.product.md) | string | |
|
||||
| [product](./puppeteer.productlauncher.product.md) | [Product](./puppeteer.product.md) | |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [connect(object)](./puppeteer.productlauncher.connect.md) | |
|
||||
| [defaultArgs(object)](./puppeteer.productlauncher.defaultargs.md) | |
|
||||
| [launch(object)](./puppeteer.productlauncher.launch.md) | |
|
||||
|
||||
|
@ -7,5 +7,5 @@
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
product: string;
|
||||
product: Product;
|
||||
```
|
||||
|
@ -13,7 +13,7 @@ connect(options: BrowserOptions & {
|
||||
browserWSEndpoint?: string;
|
||||
browserURL?: string;
|
||||
transport?: ConnectionTransport;
|
||||
product?: string;
|
||||
product?: Product;
|
||||
}): Promise<Browser>;
|
||||
```
|
||||
|
||||
@ -21,7 +21,7 @@ connect(options: BrowserOptions & {
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| options | [BrowserOptions](./puppeteer.browseroptions.md) & { browserWSEndpoint?: string; browserURL?: string; transport?: ConnectionTransport; product?: string; } | Set of configurable options to set on the browser. |
|
||||
| options | [BrowserOptions](./puppeteer.browseroptions.md) & { browserWSEndpoint?: string; browserURL?: string; transport?: ConnectionTransport; product?: [Product](./puppeteer.product.md)<!-- -->; } | Set of configurable options to set on the browser. |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
"devtools-protocol": "0.0.809251",
|
||||
"extract-zip": "^2.0.0",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"pkg-dir": "^4.2.0",
|
||||
"progress": "^2.0.1",
|
||||
"proxy-from-env": "^1.0.0",
|
||||
|
@ -41,9 +41,11 @@ export * from './common/FileChooser.js';
|
||||
export * from './common/FrameManager.js';
|
||||
export * from './common/Input.js';
|
||||
export * from './common/Page.js';
|
||||
export * from './common/Product.js';
|
||||
export * from './common/Puppeteer.js';
|
||||
export * from './node/LaunchOptions.js';
|
||||
export * from './common/BrowserConnector.js';
|
||||
export * from './node/Launcher.js';
|
||||
export * from './node/LaunchOptions.js';
|
||||
export * from './common/HTTPRequest.js';
|
||||
export * from './common/HTTPResponse.js';
|
||||
export * from './common/SecurityDetails.js';
|
||||
|
109
src/common/BrowserConnector.ts
Normal file
109
src/common/BrowserConnector.ts
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright 2020 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 { ConnectionTransport } from './ConnectionTransport.js';
|
||||
import { Browser } from './Browser.js';
|
||||
import { assert } from './assert.js';
|
||||
import { debugError } from '../common/helper.js';
|
||||
import { Connection } from './Connection.js';
|
||||
import { WebSocketTransport } from './WebSocketTransport.js';
|
||||
import { getFetch } from './fetch.js';
|
||||
import { Viewport } from './PuppeteerViewport.js';
|
||||
|
||||
/**
|
||||
* Generic browser options that can be passed when launching any browser.
|
||||
* @public
|
||||
*/
|
||||
export interface BrowserOptions {
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
defaultViewport?: Viewport;
|
||||
slowMo?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Users should never call this directly; it's called when calling
|
||||
* `puppeteer.connect`.
|
||||
* @internal
|
||||
*/
|
||||
export const connectToBrowser = async (
|
||||
options: BrowserOptions & {
|
||||
browserWSEndpoint?: string;
|
||||
browserURL?: string;
|
||||
transport?: ConnectionTransport;
|
||||
}
|
||||
) => {
|
||||
const {
|
||||
browserWSEndpoint,
|
||||
browserURL,
|
||||
ignoreHTTPSErrors = false,
|
||||
defaultViewport = { width: 800, height: 600 },
|
||||
transport,
|
||||
slowMo = 0,
|
||||
} = options;
|
||||
|
||||
assert(
|
||||
Number(!!browserWSEndpoint) + Number(!!browserURL) + Number(!!transport) ===
|
||||
1,
|
||||
'Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect'
|
||||
);
|
||||
|
||||
let connection = null;
|
||||
if (transport) {
|
||||
connection = new Connection('', transport, slowMo);
|
||||
} else if (browserWSEndpoint) {
|
||||
const connectionTransport = await WebSocketTransport.create(
|
||||
browserWSEndpoint
|
||||
);
|
||||
connection = new Connection(browserWSEndpoint, connectionTransport, slowMo);
|
||||
} else if (browserURL) {
|
||||
const connectionURL = await getWSEndpoint(browserURL);
|
||||
const connectionTransport = await WebSocketTransport.create(connectionURL);
|
||||
connection = new Connection(connectionURL, connectionTransport, slowMo);
|
||||
}
|
||||
|
||||
const { browserContextIds } = await connection.send(
|
||||
'Target.getBrowserContexts'
|
||||
);
|
||||
return Browser.create(
|
||||
connection,
|
||||
browserContextIds,
|
||||
ignoreHTTPSErrors,
|
||||
defaultViewport,
|
||||
null,
|
||||
() => connection.send('Browser.close').catch(debugError)
|
||||
);
|
||||
};
|
||||
|
||||
async function getWSEndpoint(browserURL: string): Promise<string> {
|
||||
const endpointURL = new URL('/json/version', browserURL);
|
||||
|
||||
const fetch = await getFetch();
|
||||
try {
|
||||
const result = await fetch(endpointURL.toString(), {
|
||||
method: 'GET',
|
||||
});
|
||||
if (!result.ok) {
|
||||
throw new Error(`HTTP ${result.statusText}`);
|
||||
}
|
||||
const data = await result.json();
|
||||
return data.webSocketDebuggerUrl;
|
||||
} catch (error) {
|
||||
error.message =
|
||||
`Failed to fetch browser webSocket URL from ${endpointURL}: ` +
|
||||
error.message;
|
||||
throw error;
|
||||
}
|
||||
}
|
21
src/common/Product.ts
Normal file
21
src/common/Product.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Supported products.
|
||||
* @public
|
||||
*/
|
||||
export type Product = 'chrome' | 'firefox';
|
@ -14,11 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Launcher from '../node/Launcher.js';
|
||||
import {
|
||||
LaunchOptions,
|
||||
ChromeArgOptions,
|
||||
BrowserOptions,
|
||||
} from '../node/LaunchOptions.js';
|
||||
import { LaunchOptions, ChromeArgOptions } from '../node/LaunchOptions.js';
|
||||
import { ProductLauncher } from '../node/Launcher.js';
|
||||
import {
|
||||
BrowserFetcher,
|
||||
@ -36,6 +32,8 @@ import {
|
||||
CustomQueryHandler,
|
||||
} from './QueryHandler.js';
|
||||
import { PUPPETEER_REVISIONS } from '../revisions.js';
|
||||
import { Product } from './Product.js';
|
||||
import { connectToBrowser, BrowserOptions } from './BrowserConnector.js';
|
||||
|
||||
/**
|
||||
* The main Puppeteer class. Provides the {@link Puppeteer.launch | launch}
|
||||
@ -141,11 +139,11 @@ export class Puppeteer {
|
||||
browserWSEndpoint?: string;
|
||||
browserURL?: string;
|
||||
transport?: ConnectionTransport;
|
||||
product?: string;
|
||||
product?: Product;
|
||||
}
|
||||
): Promise<Browser> {
|
||||
if (options.product) this._productName = options.product;
|
||||
return this._launcher.connect(options);
|
||||
return connectToBrowser(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
22
src/common/fetch.ts
Normal file
22
src/common/fetch.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright 2020 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 { isNode } from '../environment.js';
|
||||
|
||||
/* Use the global version if we're in the browser, else load the node-fetch module. */
|
||||
export const getFetch = async (): Promise<typeof fetch> => {
|
||||
return isNode ? await import('node-fetch') : globalThis.fetch;
|
||||
};
|
@ -22,6 +22,7 @@ import * as childProcess from 'child_process';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
|
||||
import { Product } from '../common/Product.js';
|
||||
import extractZip from 'extract-zip';
|
||||
import { debug } from '../common/Debug.js';
|
||||
import { promisify } from 'util';
|
||||
@ -65,11 +66,6 @@ const browserConfig = {
|
||||
* @public
|
||||
*/
|
||||
export type Platform = 'linux' | 'mac' | 'win32' | 'win64';
|
||||
/**
|
||||
* Supported products.
|
||||
* @public
|
||||
*/
|
||||
export type Product = 'chrome' | 'firefox';
|
||||
|
||||
function archiveName(
|
||||
product: Product,
|
||||
|
@ -13,9 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Viewport } from '../common/PuppeteerViewport.js';
|
||||
|
||||
/**
|
||||
* Launcher options that only apply to Chrome.
|
||||
*
|
||||
@ -43,13 +40,3 @@ export interface LaunchOptions {
|
||||
env?: Record<string, string | undefined>;
|
||||
pipe?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic browser options that can be passed when launching any browser.
|
||||
* @public
|
||||
*/
|
||||
export interface BrowserOptions {
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
defaultViewport?: Viewport;
|
||||
slowMo?: number;
|
||||
}
|
||||
|
@ -15,29 +15,19 @@
|
||||
*/
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as URL from 'url';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { BrowserFetcher } from './BrowserFetcher.js';
|
||||
import { Connection } from '../common/Connection.js';
|
||||
import { Browser } from '../common/Browser.js';
|
||||
import { assert } from '../common/assert.js';
|
||||
import { debugError } from '../common/helper.js';
|
||||
import { ConnectionTransport } from '../common/ConnectionTransport.js';
|
||||
import { WebSocketTransport } from '../common/WebSocketTransport.js';
|
||||
import { BrowserRunner } from './BrowserRunner.js';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const mkdtempAsync = promisify(fs.mkdtemp);
|
||||
const writeFileAsync = promisify(fs.writeFile);
|
||||
|
||||
import {
|
||||
ChromeArgOptions,
|
||||
LaunchOptions,
|
||||
BrowserOptions,
|
||||
} from './LaunchOptions.js';
|
||||
import { ChromeArgOptions, LaunchOptions } from './LaunchOptions.js';
|
||||
import { BrowserOptions } from '../common/BrowserConnector.js';
|
||||
import { Product } from '../common/Product.js';
|
||||
|
||||
/**
|
||||
* Describes a launcher - a class that is able to create and launch a browser instance.
|
||||
@ -45,10 +35,9 @@ import {
|
||||
*/
|
||||
export interface ProductLauncher {
|
||||
launch(object);
|
||||
connect(object);
|
||||
executablePath: () => string;
|
||||
defaultArgs(object);
|
||||
product: string;
|
||||
product: Product;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,66 +204,9 @@ class ChromeLauncher implements ProductLauncher {
|
||||
return resolveExecutablePath(this).executablePath;
|
||||
}
|
||||
|
||||
get product(): string {
|
||||
get product(): Product {
|
||||
return 'chrome';
|
||||
}
|
||||
|
||||
async connect(
|
||||
options: BrowserOptions & {
|
||||
browserWSEndpoint?: string;
|
||||
browserURL?: string;
|
||||
transport?: ConnectionTransport;
|
||||
}
|
||||
): Promise<Browser> {
|
||||
const {
|
||||
browserWSEndpoint,
|
||||
browserURL,
|
||||
ignoreHTTPSErrors = false,
|
||||
defaultViewport = { width: 800, height: 600 },
|
||||
transport,
|
||||
slowMo = 0,
|
||||
} = options;
|
||||
|
||||
assert(
|
||||
Number(!!browserWSEndpoint) +
|
||||
Number(!!browserURL) +
|
||||
Number(!!transport) ===
|
||||
1,
|
||||
'Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect'
|
||||
);
|
||||
|
||||
let connection = null;
|
||||
if (transport) {
|
||||
connection = new Connection('', transport, slowMo);
|
||||
} else if (browserWSEndpoint) {
|
||||
const connectionTransport = await WebSocketTransport.create(
|
||||
browserWSEndpoint
|
||||
);
|
||||
connection = new Connection(
|
||||
browserWSEndpoint,
|
||||
connectionTransport,
|
||||
slowMo
|
||||
);
|
||||
} else if (browserURL) {
|
||||
const connectionURL = await getWSEndpoint(browserURL);
|
||||
const connectionTransport = await WebSocketTransport.create(
|
||||
connectionURL
|
||||
);
|
||||
connection = new Connection(connectionURL, connectionTransport, slowMo);
|
||||
}
|
||||
|
||||
const { browserContextIds } = await connection.send(
|
||||
'Target.getBrowserContexts'
|
||||
);
|
||||
return Browser.create(
|
||||
connection,
|
||||
browserContextIds,
|
||||
ignoreHTTPSErrors,
|
||||
defaultViewport,
|
||||
null,
|
||||
() => connection.send('Browser.close').catch(debugError)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -392,63 +324,6 @@ class FirefoxLauncher implements ProductLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
async connect(
|
||||
options: BrowserOptions & {
|
||||
browserWSEndpoint?: string;
|
||||
browserURL?: string;
|
||||
transport?: ConnectionTransport;
|
||||
}
|
||||
): Promise<Browser> {
|
||||
const {
|
||||
browserWSEndpoint,
|
||||
browserURL,
|
||||
ignoreHTTPSErrors = false,
|
||||
defaultViewport = { width: 800, height: 600 },
|
||||
transport,
|
||||
slowMo = 0,
|
||||
} = options;
|
||||
|
||||
assert(
|
||||
Number(!!browserWSEndpoint) +
|
||||
Number(!!browserURL) +
|
||||
Number(!!transport) ===
|
||||
1,
|
||||
'Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect'
|
||||
);
|
||||
|
||||
let connection = null;
|
||||
if (transport) {
|
||||
connection = new Connection('', transport, slowMo);
|
||||
} else if (browserWSEndpoint) {
|
||||
const connectionTransport = await WebSocketTransport.create(
|
||||
browserWSEndpoint
|
||||
);
|
||||
connection = new Connection(
|
||||
browserWSEndpoint,
|
||||
connectionTransport,
|
||||
slowMo
|
||||
);
|
||||
} else if (browserURL) {
|
||||
const connectionURL = await getWSEndpoint(browserURL);
|
||||
const connectionTransport = await WebSocketTransport.create(
|
||||
connectionURL
|
||||
);
|
||||
connection = new Connection(connectionURL, connectionTransport, slowMo);
|
||||
}
|
||||
|
||||
const { browserContextIds } = await connection.send(
|
||||
'Target.getBrowserContexts'
|
||||
);
|
||||
return Browser.create(
|
||||
connection,
|
||||
browserContextIds,
|
||||
ignoreHTTPSErrors,
|
||||
defaultViewport,
|
||||
null,
|
||||
() => connection.send('Browser.close').catch(debugError)
|
||||
);
|
||||
}
|
||||
|
||||
executablePath(): string {
|
||||
return resolveExecutablePath(this).executablePath;
|
||||
}
|
||||
@ -464,7 +339,7 @@ class FirefoxLauncher implements ProductLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
get product(): string {
|
||||
get product(): Product {
|
||||
return 'firefox';
|
||||
}
|
||||
|
||||
@ -705,42 +580,6 @@ class FirefoxLauncher implements ProductLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
function getWSEndpoint(browserURL: string): Promise<string> {
|
||||
let resolve, reject;
|
||||
const promise = new Promise<string>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
|
||||
const endpointURL = URL.resolve(browserURL, '/json/version');
|
||||
const protocol = endpointURL.startsWith('https') ? https : http;
|
||||
const requestOptions = Object.assign(URL.parse(endpointURL), {
|
||||
method: 'GET',
|
||||
});
|
||||
const request = protocol.request(requestOptions, (res) => {
|
||||
let data = '';
|
||||
if (res.statusCode !== 200) {
|
||||
// Consume response data to free up memory.
|
||||
res.resume();
|
||||
reject(new Error('HTTP ' + res.statusCode));
|
||||
return;
|
||||
}
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', (chunk) => (data += chunk));
|
||||
res.on('end', () => resolve(JSON.parse(data).webSocketDebuggerUrl));
|
||||
});
|
||||
|
||||
request.on('error', reject);
|
||||
request.end();
|
||||
|
||||
return promise.catch((error) => {
|
||||
error.message =
|
||||
`Failed to fetch browser webSocket url from ${endpointURL}: ` +
|
||||
error.message;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
function resolveExecutablePath(
|
||||
launcher: ChromeLauncher | FirefoxLauncher
|
||||
): { executablePath: string; missingText?: string } {
|
||||
|
@ -84,7 +84,7 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () {
|
||||
.connect({ browserURL })
|
||||
.catch((error_) => (error = error_));
|
||||
expect(error.message).toContain(
|
||||
'Failed to fetch browser webSocket url from'
|
||||
'Failed to fetch browser webSocket URL from'
|
||||
);
|
||||
originalBrowser.close();
|
||||
});
|
||||
|
@ -803,6 +803,13 @@ function compareDocumentations(actual, expected) {
|
||||
expectedName: 'Array<Object>',
|
||||
},
|
||||
],
|
||||
[
|
||||
'Method Puppeteer.connect() options.product',
|
||||
{
|
||||
actualName: 'string',
|
||||
expectedName: 'Product',
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const expectedForSource = expectedNamingMismatches.get(source);
|
||||
|
Loading…
Reference in New Issue
Block a user