Skip to content

Commit aae5fca

Browse files
authored
feat(api): make browser.newPage own the created context (#930)
1 parent ad9d6cc commit aae5fca

File tree

10 files changed

+32
-49
lines changed

10 files changed

+32
-49
lines changed

docs/api.md

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ See [ChromiumBrowser], [FirefoxBrowser] and [WebKitBrowser] for browser-specific
153153
- [browser.isConnected()](#browserisconnected)
154154
- [browser.newContext(options)](#browsernewcontextoptions)
155155
- [browser.newPage([options])](#browsernewpageoptions)
156-
- [browser.pages()](#browserpages)
157156
<!-- GEN:stop -->
158157

159158
#### event: 'disconnected'
@@ -233,12 +232,9 @@ Creates a new browser context. It won't share cookies/cache with other browser c
233232
- `permissions` <[Object]> A map from origin keys to permissions values. See [browserContext.setPermissions](#browsercontextsetpermissionsorigin-permissions) for more details.
234233
- returns: <[Promise]<[Page]>>
235234

236-
Creates a new page in a new browser context.
235+
Creates a new page in a new browser context. Closing this page will close the context as well.
237236

238-
#### browser.pages()
239-
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages.
240-
241-
An array of all the pages inside all the browser contexts.
237+
This is a convenience API that should only be used for the single-page scenarios and short snippets. Production code and testing frameworks should explicitly create [browser.newContext](#browsernewcontextoptions) followed by the [browserContext.newPage](#browsercontextnewpage) to control their exact life times.
242238

243239
### class: BrowserContext
244240

@@ -3667,7 +3663,6 @@ await browser.stopTracing();
36673663
- [browser.isConnected()](#browserisconnected)
36683664
- [browser.newContext(options)](#browsernewcontextoptions)
36693665
- [browser.newPage([options])](#browsernewpageoptions)
3670-
- [browser.pages()](#browserpages)
36713666
<!-- GEN:stop -->
36723667

36733668
#### event: 'targetchanged'
@@ -3834,7 +3829,6 @@ Firefox browser instance does not expose Firefox-specific features.
38343829
- [browser.isConnected()](#browserisconnected)
38353830
- [browser.newContext(options)](#browsernewcontextoptions)
38363831
- [browser.newPage([options])](#browsernewpageoptions)
3837-
- [browser.pages()](#browserpages)
38383832
<!-- GEN:stop -->
38393833

38403834
### class: WebKitBrowser
@@ -3850,7 +3844,6 @@ WebKit browser instance does not expose WebKit-specific features.
38503844
- [browser.isConnected()](#browserisconnected)
38513845
- [browser.newContext(options)](#browsernewcontextoptions)
38523846
- [browser.newPage([options])](#browsernewpageoptions)
3853-
- [browser.pages()](#browserpages)
38543847
<!-- GEN:stop -->
38553848

38563849
### Working with selectors

src/browser.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { Page } from './page';
2121
export interface Browser extends platform.EventEmitterType {
2222
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
2323
contexts(): BrowserContext[];
24-
pages(): Promise<Page[]>;
2524
newPage(options?: BrowserContextOptions): Promise<Page>;
2625
isConnected(): boolean;
2726
close(): Promise<void>;
@@ -32,14 +31,11 @@ export type ConnectOptions = {
3231
wsEndpoint: string
3332
};
3433

35-
export async function collectPages(browser: Browser): Promise<Page[]> {
36-
const result: Promise<Page[]>[] = [];
37-
for (const browserContext of browser.contexts())
38-
result.push(browserContext.pages());
39-
const pages: Page[] = [];
40-
for (const group of await Promise.all(result))
41-
pages.push(...group);
42-
return pages;
34+
export async function createPageInNewContext(browser: Browser, options?: BrowserContextOptions): Promise<Page> {
35+
const context = await browser.newContext(options);
36+
const page = await context.newPage();
37+
page._ownedContext = context;
38+
return page;
4339
}
4440

4541
export type LaunchType = 'local' | 'server' | 'persistent';

src/browserContext.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ export class BrowserContext extends platform.EventEmitter {
8282
}
8383

8484
async newPage(): Promise<Page> {
85+
const pages = this._delegate.existingPages();
86+
for (const page of pages) {
87+
if (page._ownedContext)
88+
throw new Error('Please use browser.newContext() for multi-page scripts that share the context.');
89+
}
8590
return this._delegate.newPage();
8691
}
8792

src/chromium/crBrowser.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { Page, Worker } from '../page';
2424
import { CRTarget } from './crTarget';
2525
import { Protocol } from './protocol';
2626
import { CRPage } from './crPage';
27-
import { Browser, collectPages } from '../browser';
27+
import { Browser, createPageInNewContext } from '../browser';
2828
import * as network from '../network';
2929
import * as types from '../types';
3030
import * as platform from '../platform';
@@ -168,13 +168,8 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
168168
return Array.from(this._contexts.values());
169169
}
170170

171-
async pages(): Promise<Page[]> {
172-
return collectPages(this);
173-
}
174-
175171
async newPage(options?: BrowserContextOptions): Promise<Page> {
176-
const context = await this.newContext(options);
177-
return context.newPage();
172+
return createPageInNewContext(this, options);
178173
}
179174

180175
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {

src/firefox/ffBrowser.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { Browser, collectPages } from '../browser';
18+
import { Browser, createPageInNewContext } from '../browser';
1919
import { BrowserContext, BrowserContextOptions } from '../browserContext';
2020
import { Events } from '../events';
2121
import { assert, helper, RegisteredListener } from '../helper';
@@ -84,13 +84,8 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
8484
return Array.from(this._contexts.values());
8585
}
8686

87-
async pages(): Promise<Page[]> {
88-
return collectPages(this);
89-
}
90-
9187
async newPage(options?: BrowserContextOptions): Promise<Page> {
92-
const context = await this.newContext(options);
93-
return context.newPage();
88+
return createPageInNewContext(this, options);
9489
}
9590

9691
async _waitForTarget(predicate: (target: Target) => boolean, options: { timeout?: number; } = {}): Promise<Target> {

src/page.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export class Page extends platform.EventEmitter {
114114
readonly pdf: ((options?: types.PDFOptions) => Promise<platform.BufferType>) | undefined;
115115
readonly coverage: Coverage | undefined;
116116
readonly _requestHandlers: { url: types.URLMatch, handler: (request: network.Request) => void }[] = [];
117+
_ownedContext: BrowserContext | undefined;
117118

118119
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
119120
super();
@@ -472,6 +473,8 @@ export class Page extends platform.EventEmitter {
472473
await this._delegate.closePage(runBeforeUnload);
473474
if (!runBeforeUnload)
474475
await this._closedPromise;
476+
if (this._ownedContext)
477+
await this._ownedContext.close();
475478
}
476479

477480
isClosed(): boolean {

src/webkit/wkBrowser.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { Browser, collectPages } from '../browser';
18+
import { Browser, createPageInNewContext } from '../browser';
1919
import { BrowserContext, BrowserContextOptions } from '../browserContext';
2020
import { assert, helper, RegisteredListener } from '../helper';
2121
import * as network from '../network';
@@ -89,13 +89,8 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
8989
return Array.from(this._contexts.values());
9090
}
9191

92-
async pages(): Promise<Page[]> {
93-
return collectPages(this);
94-
}
95-
9692
async newPage(options?: BrowserContextOptions): Promise<Page> {
97-
const context = await this.newContext(options);
98-
return context.newPage();
93+
return createPageInNewContext(this, options);
9994
}
10095

10196
async _waitForFirstPageTarget(timeout: number): Promise<void> {

test/browser.spec.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,25 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
2424
const {it, fit, xit, dit} = testRunner;
2525
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
2626

27-
describe('Browser', function() {
27+
describe('Browser.newPage', function() {
2828
it('should create new page', async function({browser}) {
29-
expect((await browser.pages()).length).toBe(0);
3029
const page1 = await browser.newPage();
31-
expect((await browser.pages()).length).toBe(1);
3230
expect(browser.contexts().length).toBe(1);
3331

3432
const page2 = await browser.newPage();
35-
expect((await browser.pages()).length).toBe(2);
3633
expect(browser.contexts().length).toBe(2);
3734

38-
await page1.context().close();
39-
expect((await browser.pages()).length).toBe(1);
35+
await page1.close();
4036
expect(browser.contexts().length).toBe(1);
4137

42-
await page2.context().close();
43-
expect((await browser.pages()).length).toBe(0);
38+
await page2.close();
4439
expect(browser.contexts().length).toBe(0);
4540
});
41+
it('should throw upon second create new page', async function({browser}) {
42+
const page = await browser.newPage();
43+
let error;
44+
await page.context().newPage().catch(e => error = e);
45+
expect(error.message).toContain('Please use browser.newContext()');
46+
});
4647
});
4748
};

test/chromium/tracing.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
5858
const newPage = await browser.newPage();
5959
let error = null;
6060
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
61-
await newPage.context().close();
61+
await newPage.close();
6262
expect(error).toBeTruthy();
6363
await browser.stopTracing();
6464
});

test/web.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
4646

4747
afterEach(async state => {
4848
await state.page.evaluate(() => teardown());
49-
await state.page.context().close();
49+
await state.page.close();
5050
state.page = null;
5151
});
5252

0 commit comments

Comments
 (0)