Skip to content

Commit

Permalink
feat: add option to filter targets (#7192)
Browse files Browse the repository at this point in the history
* feat: add option to filter targets

Co-authored-by: Mathias Bynens <mathias@qiwi.be>
  • Loading branch information
jschfflr and mathiasbynens committed May 3, 2021
1 parent a293b96 commit ec3fc2e
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/api.md
Expand Up @@ -516,6 +516,7 @@ Clears all registered handlers.
- `slowMo` <[number]> Slows down Puppeteer operations by the specified amount of milliseconds. Useful so that you can see what is going on.
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Puppeteer to use.
- `product` <[string]> Possible values are: `chrome`, `firefox`. Defaults to `chrome`.
- `targetFilter` <?[function]\([Protocol.Target.TargetInfo]\):[Promise]<[boolean]>|[boolean]> Use this function to decide if Puppeteer should connect to the given target. If a `targetFilter` is provided, Puppeteer only connects to targets for which `targetFilter` returns `true`. By default, Puppeteer connects to all available targets.
- returns: <[Promise]<[Browser]>>

This methods attaches Puppeteer to an existing browser instance.
Expand Down Expand Up @@ -623,6 +624,7 @@ try {
- `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
- `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
- `extraPrefsFirefox` <[Object]> Additional [preferences](https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/Preference_reference) that can be passed to Firefox (see `PUPPETEER_PRODUCT`)
- `targetFilter` <?[function]\([Protocol.Target.TargetInfo]\):[Promise]<[boolean]>|[boolean]> Use this function to decide if Puppeteer should connect to the given target. If a `targetFilter` is provided, Puppeteer only connects to targets for which `targetFilter` returns `true`. By default, Puppeteer connects to all available targets.
- returns: <[Promise]<[Browser]>> Promise which resolves to browser instance.

You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments:
Expand Down
23 changes: 20 additions & 3 deletions src/common/Browser.ts
Expand Up @@ -29,6 +29,13 @@ import { Viewport } from './PuppeteerViewport.js';
*/
export type BrowserCloseCallback = () => Promise<void> | void;

/**
* @public
*/
export type TargetFilterCallback = (
target: Protocol.Target.TargetInfo
) => Promise<boolean> | boolean;

const WEB_PERMISSION_TO_PROTOCOL_PERMISSION = new Map<
Permission,
Protocol.Browser.PermissionType
Expand Down Expand Up @@ -189,15 +196,17 @@ export class Browser extends EventEmitter {
ignoreHTTPSErrors: boolean,
defaultViewport?: Viewport | null,
process?: ChildProcess,
closeCallback?: BrowserCloseCallback
closeCallback?: BrowserCloseCallback,
targetFilterCallback?: TargetFilterCallback
): Promise<Browser> {
const browser = new Browser(
connection,
contextIds,
ignoreHTTPSErrors,
defaultViewport,
process,
closeCallback
closeCallback,
targetFilterCallback
);
await connection.send('Target.setDiscoverTargets', { discover: true });
return browser;
Expand All @@ -207,6 +216,7 @@ export class Browser extends EventEmitter {
private _process?: ChildProcess;
private _connection: Connection;
private _closeCallback: BrowserCloseCallback;
private _targetFilterCallback: TargetFilterCallback;
private _defaultContext: BrowserContext;
private _contexts: Map<string, BrowserContext>;
/**
Expand All @@ -224,14 +234,16 @@ export class Browser extends EventEmitter {
ignoreHTTPSErrors: boolean,
defaultViewport?: Viewport | null,
process?: ChildProcess,
closeCallback?: BrowserCloseCallback
closeCallback?: BrowserCloseCallback,
targetFilterCallback?: TargetFilterCallback
) {
super();
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._defaultViewport = defaultViewport;
this._process = process;
this._connection = connection;
this._closeCallback = closeCallback || function (): void {};
this._targetFilterCallback = targetFilterCallback || ((): boolean => true);

this._defaultContext = new BrowserContext(this._connection, this, null);
this._contexts = new Map();
Expand Down Expand Up @@ -330,6 +342,11 @@ export class Browser extends EventEmitter {
? this._contexts.get(browserContextId)
: this._defaultContext;

const shouldAttachToTarget = await this._targetFilterCallback(targetInfo);
if (!shouldAttachToTarget) {
return;
}

const target = new Target(
targetInfo,
context,
Expand Down
10 changes: 8 additions & 2 deletions src/common/BrowserConnector.ts
Expand Up @@ -15,7 +15,7 @@
*/

import { ConnectionTransport } from './ConnectionTransport.js';
import { Browser } from './Browser.js';
import { Browser, TargetFilterCallback } from './Browser.js';
import { assert } from './assert.js';
import { debugError } from '../common/helper.js';
import { Connection } from './Connection.js';
Expand Down Expand Up @@ -43,6 +43,10 @@ export interface BrowserConnectOptions {
* aid debugging.
*/
slowMo?: number;
/**
* Callback to decide if Puppeteer should connect to a given target or not.
*/
targetFilter?: TargetFilterCallback;
}

const getWebSocketTransportClass = async () => {
Expand Down Expand Up @@ -71,6 +75,7 @@ export const connectToBrowser = async (
defaultViewport = { width: 800, height: 600 },
transport,
slowMo = 0,
targetFilter,
} = options;

assert(
Expand Down Expand Up @@ -106,7 +111,8 @@ export const connectToBrowser = async (
ignoreHTTPSErrors,
defaultViewport,
null,
() => connection.send('Browser.close').catch(debugError)
() => connection.send('Browser.close').catch(debugError),
targetFilter
);
};

Expand Down
30 changes: 30 additions & 0 deletions test/launcher.spec.ts
Expand Up @@ -18,6 +18,7 @@ import os from 'os';
import path from 'path';
import sinon from 'sinon';
import { promisify } from 'util';
import Protocol from 'devtools-protocol';
import {
getTestState,
itFailsFirefox,
Expand Down Expand Up @@ -538,6 +539,35 @@ describe('Launcher specs', function () {
await page.close();
await browser.close();
});
it('should support targetFilter option', async () => {
const { server, puppeteer, defaultBrowserOptions } = getTestState();

const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browserWSEndpoint = originalBrowser.wsEndpoint();

const page1 = await originalBrowser.newPage();
await page1.goto(server.EMPTY_PAGE);

const page2 = await originalBrowser.newPage();
await page2.goto(server.EMPTY_PAGE + '?should-be-ignored');

const browser = await puppeteer.connect({
browserWSEndpoint,
targetFilter: (targetInfo: Protocol.Target.TargetInfo) =>
!targetInfo.url.includes('should-be-ignored'),
});

const pages = await browser.pages();

await page2.close();
await page1.close();
await browser.close();

expect(pages.map((p: Page) => p.url()).sort()).toEqual([
'about:blank',
server.EMPTY_PAGE,
]);
});
itFailsFirefox(
'should be able to reconnect to a disconnected browser',
async () => {
Expand Down

0 comments on commit ec3fc2e

Please sign in to comment.