Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): move targets from CRBrowser to CRBrowserContext #1089

Merged
merged 1 commit into from
Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 63 additions & 35 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [class: BrowserServer](#class-browserserver)
- [class: BrowserType](#class-browsertype)
- [class: ChromiumBrowser](#class-chromiumbrowser)
- [class: ChromiumBrowserContext](#class-chromiumbrowsercontext)
- [class: ChromiumCoverage](#class-chromiumcoverage)
- [class: ChromiumSession](#class-chromiumsession)
- [class: ChromiumTarget](#class-chromiumtarget)
Expand Down Expand Up @@ -3532,15 +3533,9 @@ await browser.stopTracing();
```

<!-- GEN:toc -->
- [event: 'targetchanged'](#event-targetchanged)
- [event: 'targetcreated'](#event-targetcreated)
- [event: 'targetdestroyed'](#event-targetdestroyed)
- [chromiumBrowser.browserTarget()](#chromiumbrowserbrowsertarget)
- [chromiumBrowser.pageTarget(page)](#chromiumbrowserpagetargetpage)
- [chromiumBrowser.startTracing(page, [options])](#chromiumbrowserstarttracingpage-options)
- [chromiumBrowser.stopTracing()](#chromiumbrowserstoptracing)
- [chromiumBrowser.targets(context)](#chromiumbrowsertargetscontext)
- [chromiumBrowser.waitForTarget(predicate[, options])](#chromiumbrowserwaitfortargetpredicate-options)
<!-- GEN:stop -->
<!-- GEN:toc-extends-Browser -->
- [event: 'disconnected'](#event-disconnected)
Expand All @@ -3551,69 +3546,101 @@ await browser.stopTracing();
- [browser.newPage([options])](#browsernewpageoptions)
<!-- GEN:stop -->

#### chromiumBrowser.browserTarget()
- returns: <[ChromiumTarget]>

Returns browser target.

#### chromiumBrowser.startTracing(page, [options])
- `page` <[Page]> Optional, if specified, tracing includes screenshots of the given page.
- `options` <[Object]>
- `path` <[string]> A path to write the trace file to.
- `screenshots` <[boolean]> captures screenshots in the trace.
- `categories` <[Array]<[string]>> specify custom categories to use instead of default.
- returns: <[Promise]>

Only one trace can be active at a time per browser.

#### chromiumBrowser.stopTracing()
- returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with trace data.

### class: ChromiumBrowserContext

* extends: [BrowserContext]

Chromium-specific features including targets, service worker support, etc.

```js
const backroundPageTarget = await context.waitForTarget(target => target.type() === 'background_page');
const backgroundPage = await backroundPageTarget.page();
```

<!-- GEN:toc -->
- [event: 'targetchanged'](#event-targetchanged)
- [event: 'targetcreated'](#event-targetcreated)
- [event: 'targetdestroyed'](#event-targetdestroyed)
- [chromiumBrowserContext.pageTarget(page)](#chromiumbrowsercontextpagetargetpage)
- [chromiumBrowserContext.targets()](#chromiumbrowsercontexttargets)
- [chromiumBrowserContext.waitForTarget(predicate[, options])](#chromiumbrowsercontextwaitfortargetpredicate-options)
<!-- GEN:stop -->
<!-- GEN:toc-extends-BrowserContext -->
- [event: 'close'](#event-close)
- [browserContext.clearCookies()](#browsercontextclearcookies)
- [browserContext.clearPermissions()](#browsercontextclearpermissions)
- [browserContext.close()](#browsercontextclose)
- [browserContext.cookies([...urls])](#browsercontextcookiesurls)
- [browserContext.newPage()](#browsercontextnewpage)
- [browserContext.pages()](#browsercontextpages)
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
- [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout)
- [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout)
- [browserContext.setGeolocation(geolocation)](#browsercontextsetgeolocationgeolocation)
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
<!-- GEN:stop -->

#### event: 'targetchanged'
- <[ChromiumTarget]>

Emitted when the url of a target changes.

> **NOTE** This includes target changes in incognito browser contexts.
> **NOTE** Only includes targets from this browser context.


#### event: 'targetcreated'
- <[ChromiumTarget]>

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 [`browserContext.newPage`](#browsercontextnewpage).

> **NOTE** This includes target creations in incognito browser contexts.
> **NOTE** Only includes targets from this browser context.

#### event: 'targetdestroyed'
- <[ChromiumTarget]>

Emitted when a target is destroyed, for example when a page is closed.

> **NOTE** This includes target destructions in incognito browser contexts.

#### chromiumBrowser.browserTarget()
- returns: <[ChromiumTarget]>

Returns browser target.
> **NOTE** Only includes targets from this browser context.

#### chromiumBrowser.pageTarget(page)
#### chromiumBrowserContext.pageTarget(page)
- `page` <[Page]> Page to return target for.
- returns: <[ChromiumTarget]> a target given page was created from.

#### chromiumBrowser.startTracing(page, [options])
- `page` <[Page]> Optional, if specified, tracing includes screenshots of the given page.
- `options` <[Object]>
- `path` <[string]> A path to write the trace file to.
- `screenshots` <[boolean]> captures screenshots in the trace.
- `categories` <[Array]<[string]>> specify custom categories to use instead of default.
- returns: <[Promise]>

Only one trace can be active at a time per browser.

#### chromiumBrowser.stopTracing()
- returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with trace data.

#### chromiumBrowser.targets(context)
- `context` <[BrowserContext]> Optional, if specified, only targets from this context are returned.
#### chromiumBrowserContext.targets()
- returns: <[Array]<[ChromiumTarget]>>

An array of all active targets inside the Browser. In case of multiple browser contexts,
the method will return an array with all the targets in all browser contexts.
An array of all active targets inside the browser context.

#### chromiumBrowser.waitForTarget(predicate[, options])
#### chromiumBrowserContext.waitForTarget(predicate[, options])
- `predicate` <[function]\([ChromiumTarget]\):[boolean]> A function to be run for every target
- `options` <[Object]>
- `timeout` <[number]> Maximum wait time in milliseconds. Pass `0` to disable the timeout. Defaults to 30 seconds.
- returns: <[Promise]<[ChromiumTarget]>> Promise which resolves to the first target found that matches the `predicate` function.

This searches for a target in all browser contexts.
This searches for a target in the browser context.

An example of finding a target for a page opened via `window.open`:
```js
await page.evaluate(() => window.open('https://www.example.com/'));
const newWindowTarget = await browser.chromium.waitForTarget(target => target.url() === 'https://www.example.com/');
const newWindowTarget = await page.context().waitForTarget(target => target.url() === 'https://www.example.com/');
```

### class: ChromiumCoverage
Expand Down Expand Up @@ -3875,6 +3902,7 @@ const { chromium } = require('playwright');
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[ChromiumBrowser]: #class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: #class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumSession]: #class-chromiumsession "ChromiumSession"
[ChromiumTarget]: #class-chromiumtarget "ChromiumTarget"
[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
Expand Down
1 change: 1 addition & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { FileChooser, Page, Worker } from './page';
export { Selectors } from './selectors';

export { CRBrowser as ChromiumBrowser } from './chromium/crBrowser';
export { CRBrowserContext as ChromiumBrowserContext } from './chromium/crBrowser';
export { CRCoverage as ChromiumCoverage } from './chromium/crCoverage';
export { CRSession as ChromiumSession } from './chromium/crConnection';
export { CRTarget as ChromiumTarget } from './chromium/crTarget';
Expand Down
75 changes: 36 additions & 39 deletions src/chromium/crBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { TimeoutSettings } from '../timeoutSettings';
export class CRBrowser extends platform.EventEmitter implements Browser {
_connection: CRConnection;
_client: CRSession;
readonly _defaultContext: BrowserContext;
readonly _defaultContext: CRBrowserContext;
readonly _contexts = new Map<string, CRBrowserContext>();
_targets = new Map<string, CRTarget>();

Expand Down Expand Up @@ -93,7 +93,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
this._targets.set(event.targetInfo.targetId, target);

if (target._isInitialized || await target._initializedPromise)
this.emit(Events.CRBrowser.TargetCreated, target);
context.emit(Events.CRBrowserContext.TargetCreated, target);
}

async _targetDestroyed(event: { targetId: string; }) {
Expand All @@ -102,7 +102,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
this._targets.delete(event.targetId);
target._didClose();
if (await target._initializedPromise)
this.emit(Events.CRBrowser.TargetDestroyed, target);
target.context().emit(Events.CRBrowserContext.TargetDestroyed, target);
}

_targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) {
Expand All @@ -112,7 +112,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
const wasInitialized = target._isInitialized;
target._targetInfoChanged(event.targetInfo);
if (wasInitialized && previousURL !== target.url())
this.emit(Events.CRBrowser.TargetChanged, target);
target.context().emit(Events.CRBrowserContext.TargetChanged, target);
}

async _closePage(page: Page) {
Expand All @@ -123,32 +123,6 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
return Array.from(this._targets.values()).filter(target => target._isInitialized);
}

async waitForTarget(predicate: (arg0: CRTarget) => boolean, options: { timeout?: number; } | undefined = {}): Promise<CRTarget> {
const {
timeout = 30000
} = options;
const existingTarget = this._allTargets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve: (target: CRTarget) => void;
const targetPromise = new Promise<CRTarget>(x => resolve = x);
this.on(Events.CRBrowser.TargetCreated, check);
this.on(Events.CRBrowser.TargetChanged, check);
try {
if (!timeout)
return await targetPromise;
return await helper.waitWithTimeout(targetPromise, 'target', timeout);
} finally {
this.removeListener(Events.CRBrowser.TargetCreated, check);
this.removeListener(Events.CRBrowser.TargetChanged, check);
}

function check(target: CRTarget) {
if (predicate(target))
resolve(target);
}
}

async close() {
const disconnected = new Promise(f => this._connection.once(ConnectionEvents.Disconnected, f));
await Promise.all(this.contexts().map(context => context.close()));
Expand Down Expand Up @@ -199,15 +173,6 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
return contentPromise;
}

targets(context?: BrowserContext): CRTarget[] {
const targets = this._allTargets();
return context ? targets.filter(t => t.context() === context) : targets;
}

pageTarget(page: Page): CRTarget {
return CRTarget.fromPage(page);
}

isConnected(): boolean {
return !this._connection._closed;
}
Expand Down Expand Up @@ -339,6 +304,38 @@ export class CRBrowserContext extends platform.EventEmitter implements BrowserCo
this.emit(CommonEvents.BrowserContext.Close);
}

pageTarget(page: Page): CRTarget {
return CRTarget.fromPage(page);
}

targets(): CRTarget[] {
return this._browser._allTargets().filter(t => t.context() === this);
}

async waitForTarget(predicate: (arg0: CRTarget) => boolean, options: { timeout?: number; } = {}): Promise<CRTarget> {
const { timeout = 30000 } = options;
const existingTarget = this._browser._allTargets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve: (target: CRTarget) => void;
const targetPromise = new Promise<CRTarget>(x => resolve = x);
this.on(Events.CRBrowserContext.TargetCreated, check);
this.on(Events.CRBrowserContext.TargetChanged, check);
try {
if (!timeout)
return await targetPromise;
return await helper.waitWithTimeout(targetPromise, 'target', timeout);
} finally {
this.removeListener(Events.CRBrowserContext.TargetCreated, check);
this.removeListener(Events.CRBrowserContext.TargetChanged, check);
}

function check(target: CRTarget) {
if (predicate(target))
resolve(target);
}
}

_browserClosed() {
this._closed = true;
for (const page of this._existingPages())
Expand Down
9 changes: 4 additions & 5 deletions src/chromium/crTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
* limitations under the License.
*/

import { CRBrowser } from './crBrowser';
import { BrowserContext } from '../browserContext';
import { CRBrowser, CRBrowserContext } from './crBrowser';
import { CRSession, CRSessionEvents } from './crConnection';
import { Events } from '../events';
import { Page, Worker } from '../page';
Expand All @@ -30,7 +29,7 @@ const targetSymbol = Symbol('target');
export class CRTarget {
private _targetInfo: Protocol.Target.TargetInfo;
private readonly _browser: CRBrowser;
private readonly _browserContext: BrowserContext;
private readonly _browserContext: CRBrowserContext;
readonly _targetId: string;
private _sessionFactory: () => Promise<CRSession>;
private _pagePromise: Promise<Page> | null = null;
Expand All @@ -47,7 +46,7 @@ export class CRTarget {
constructor(
browser: CRBrowser,
targetInfo: Protocol.Target.TargetInfo,
browserContext: BrowserContext,
browserContext: CRBrowserContext,
sessionFactory: () => Promise<CRSession>) {
this._targetInfo = targetInfo;
this._browser = browser;
Expand Down Expand Up @@ -120,7 +119,7 @@ export class CRTarget {
return 'other';
}

context(): BrowserContext {
context(): CRBrowserContext {
return this._browserContext;
}

Expand Down
2 changes: 1 addition & 1 deletion src/chromium/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

export const Events = {
CRBrowser: {
CRBrowserContext: {
TargetCreated: 'targetcreated',
TargetDestroyed: 'targetdestroyed',
TargetChanged: 'targetchanged',
Expand Down
2 changes: 1 addition & 1 deletion src/server/chromium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class Chromium implements BrowserType {
const { timeout = 30000 } = options || {};
const { browserServer, transport } = await this._launchServer(options, 'persistent', userDataDir);
const browser = await CRBrowser.connect(transport!);
await helper.waitWithTimeout(browser.waitForTarget(t => t.type() === 'page'), 'first page', timeout);
await helper.waitWithTimeout(browser._defaultContext.waitForTarget(t => t.type() === 'page'), 'first page', timeout);
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
const browserContext = browser._defaultContext;
browserContext.close = () => browserServer.close();
Expand Down
4 changes: 2 additions & 2 deletions test/browsercontext.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
const [popupTarget] = await Promise.all([
const [popup] = await Promise.all([
utils.waitEvent(page, 'popup'),
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
]);
expect(popupTarget.context()).toBe(context);
expect(popup.context()).toBe(context);
await context.close();
});
it('should isolate localStorage and cookies', async function({browser, server}) {
Expand Down
Loading