feat(context): remove the notion of incognito context (#299)

This commit is contained in:
Pavel Feldman 2019-12-18 16:44:02 -08:00 committed by Dmitry Gozman
parent da051a72a8
commit 0e3328218d
6 changed files with 42 additions and 59 deletions

View file

@ -44,7 +44,6 @@
* [browserContext.clearCookies()](#browsercontextclearcookies) * [browserContext.clearCookies()](#browsercontextclearcookies)
* [browserContext.close()](#browsercontextclose) * [browserContext.close()](#browsercontextclose)
* [browserContext.cookies([...urls])](#browsercontextcookiesurls) * [browserContext.cookies([...urls])](#browsercontextcookiesurls)
* [browserContext.isIncognito()](#browsercontextisincognito)
* [browserContext.newPage()](#browsercontextnewpage) * [browserContext.newPage()](#browsercontextnewpage)
* [browserContext.overrides](#browsercontextoverrides) * [browserContext.overrides](#browsercontextoverrides)
* [browserContext.pages()](#browsercontextpages) * [browserContext.pages()](#browsercontextpages)
@ -736,12 +735,6 @@ will be closed.
If no URLs are specified, this method returns all cookies. If no URLs are specified, this method returns all cookies.
If URLs are specified, only cookies that affect those URLs are returned. If URLs are specified, only cookies that affect those URLs are returned.
#### browserContext.isIncognito()
- returns: <[boolean]>
Returns whether BrowserContext is incognito.
The default browser context is the only non-incognito browser context.
> **NOTE** the default browser context cannot be closed. > **NOTE** the default browser context cannot be closed.
#### browserContext.newPage() #### browserContext.newPage()

View file

@ -15,19 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { assert } from './helper';
import { Page } from './page'; import { Page } from './page';
import * as input from './input'; import * as input from './input';
import * as network from './network'; import * as network from './network';
import * as types from './types'; import * as types from './types';
export interface BrowserDelegate { export interface BrowserContextDelegate {
contextPages(): Promise<Page[]>; pages(): Promise<Page[]>;
createPageInContext(): Promise<Page>; newPage(): Promise<Page>;
closeContext(): Promise<void>; close(): Promise<void>;
getContextCookies(): Promise<network.NetworkCookie[]>; cookies(): Promise<network.NetworkCookie[]>;
clearContextCookies(): Promise<void>; clearCookies(): Promise<void>;
setContextCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>; setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
} }
export type BrowserContextOptions = { export type BrowserContextOptions = {
@ -42,34 +41,28 @@ export type BrowserContextOptions = {
}; };
export class BrowserContext { export class BrowserContext {
private readonly _delegate: BrowserDelegate; private readonly _delegate: BrowserContextDelegate;
private readonly _isIncognito: boolean;
readonly _options: BrowserContextOptions; readonly _options: BrowserContextOptions;
private _closed = false; private _closed = false;
constructor(delegate: BrowserDelegate, isIncognito: boolean, options: BrowserContextOptions) { constructor(delegate: BrowserContextDelegate, options: BrowserContextOptions) {
this._delegate = delegate; this._delegate = delegate;
this._isIncognito = isIncognito;
this._options = options; this._options = options;
if (!options.viewport && options.viewport !== null) if (!options.viewport && options.viewport !== null)
options.viewport = { width: 800, height: 600 }; options.viewport = { width: 800, height: 600 };
} }
async pages(): Promise<Page[]> { async pages(): Promise<Page[]> {
return this._delegate.contextPages(); return this._delegate.pages();
}
isIncognito(): boolean {
return this._isIncognito;
} }
async newPage(): Promise<Page> { async newPage(): Promise<Page> {
return this._delegate.createPageInContext(); return this._delegate.newPage();
} }
async _createOwnerPage(): Promise<Page> { async _createOwnerPage(): Promise<Page> {
try { try {
const page = await this._delegate.createPageInContext(); const page = await this._delegate.newPage();
page._isContextOwner = true; page._isContextOwner = true;
return page; return page;
} catch (e) { } catch (e) {
@ -79,22 +72,21 @@ export class BrowserContext {
} }
async cookies(...urls: string[]): Promise<network.NetworkCookie[]> { async cookies(...urls: string[]): Promise<network.NetworkCookie[]> {
return network.filterCookies(await this._delegate.getContextCookies(), urls); return network.filterCookies(await this._delegate.cookies(), urls);
} }
async clearCookies() { async clearCookies() {
await this._delegate.clearContextCookies(); await this._delegate.clearCookies();
} }
async setCookies(cookies: network.SetNetworkCookieParam[]) { async setCookies(cookies: network.SetNetworkCookieParam[]) {
await this._delegate.setContextCookies(network.rewriteCookies(cookies)); await this._delegate.setCookies(network.rewriteCookies(cookies));
} }
async close() { async close() {
if (this._closed) if (this._closed)
return; return;
assert(this._isIncognito, 'Non-incognito profiles cannot be closed!'); await this._delegate.close();
await this._delegate.closeContext();
this._closed = true; this._closed = true;
} }
} }

View file

@ -73,16 +73,15 @@ export class Browser extends EventEmitter {
} }
_createBrowserContext(contextId: string | null, options: BrowserContextOptions): BrowserContext { _createBrowserContext(contextId: string | null, options: BrowserContextOptions): BrowserContext {
const isIncognito = !!contextId;
let overrides: Overrides | null = null; let overrides: Overrides | null = null;
const context = new BrowserContext({ const context = new BrowserContext({
contextPages: async (): Promise<Page[]> => { pages: async (): Promise<Page[]> => {
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page'); const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
const pages = await Promise.all(targets.map(target => target.page())); const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page); return pages.filter(page => !!page);
}, },
createPageInContext: async (): Promise<Page> => { newPage: async (): Promise<Page> => {
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined }); const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
const target = this._targets.get(targetId); const target = this._targets.get(targetId);
assert(await target._initializedPromise, 'Failed to create target for page'); assert(await target._initializedPromise, 'Failed to create target for page');
@ -109,12 +108,13 @@ export class Browser extends EventEmitter {
return page; return page;
}, },
closeContext: async (): Promise<void> => { close: async (): Promise<void> => {
assert(contextId, 'Non-incognito profiles cannot be closed!');
await this._client.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined}); await this._client.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
this._contexts.delete(contextId); this._contexts.delete(contextId);
}, },
getContextCookies: async (): Promise<network.NetworkCookie[]> => { cookies: async (): Promise<network.NetworkCookie[]> => {
const { cookies } = await this._client.send('Storage.getCookies', { browserContextId: contextId || undefined }); const { cookies } = await this._client.send('Storage.getCookies', { browserContextId: contextId || undefined });
return cookies.map(c => { return cookies.map(c => {
const copy: any = { sameSite: 'None', ...c }; const copy: any = { sameSite: 'None', ...c };
@ -124,14 +124,14 @@ export class Browser extends EventEmitter {
}); });
}, },
clearContextCookies: async (): Promise<void> => { clearCookies: async (): Promise<void> => {
await this._client.send('Storage.clearCookies', { browserContextId: contextId || undefined }); await this._client.send('Storage.clearCookies', { browserContextId: contextId || undefined });
}, },
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => { setCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
await this._client.send('Storage.setCookies', { cookies, browserContextId: contextId || undefined }); await this._client.send('Storage.setCookies', { cookies, browserContextId: contextId || undefined });
}, },
}, isIncognito, options); }, options);
overrides = new Overrides(context); overrides = new Overrides(context);
(context as any).permissions = new Permissions(this._client, contextId); (context as any).permissions = new Permissions(this._client, contextId);
(context as any).overrides = overrides; (context as any).overrides = overrides;

View file

@ -16,7 +16,7 @@
*/ */
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { helper, RegisteredListener } from '../helper'; import { helper, RegisteredListener, assert } from '../helper';
import { Connection, ConnectionEvents, JugglerSessionEvents } from './Connection'; import { Connection, ConnectionEvents, JugglerSessionEvents } from './Connection';
import { Events } from './events'; import { Events } from './events';
import { Events as CommonEvents } from '../events'; import { Events as CommonEvents } from '../events';
@ -175,15 +175,14 @@ export class Browser extends EventEmitter {
} }
_createBrowserContext(browserContextId: string | null, options: BrowserContextOptions): BrowserContext { _createBrowserContext(browserContextId: string | null, options: BrowserContextOptions): BrowserContext {
const isIncognito = !!browserContextId;
const context = new BrowserContext({ const context = new BrowserContext({
contextPages: async (): Promise<Page[]> => { pages: async (): Promise<Page[]> => {
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page'); const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
const pages = await Promise.all(targets.map(target => target.page())); const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page); return pages.filter(page => !!page);
}, },
createPageInContext: async (): Promise<Page> => { newPage: async (): Promise<Page> => {
const {targetId} = await this._connection.send('Target.newPage', { const {targetId} = await this._connection.send('Target.newPage', {
browserContextId: browserContextId || undefined browserContextId: browserContextId || undefined
}); });
@ -205,12 +204,13 @@ export class Browser extends EventEmitter {
return page; return page;
}, },
closeContext: async (): Promise<void> => { close: async (): Promise<void> => {
assert(browserContextId, 'Non-incognito profiles cannot be closed!');
await this._connection.send('Target.removeBrowserContext', { browserContextId }); await this._connection.send('Target.removeBrowserContext', { browserContextId });
this._contexts.delete(browserContextId); this._contexts.delete(browserContextId);
}, },
getContextCookies: async (): Promise<network.NetworkCookie[]> => { cookies: async (): Promise<network.NetworkCookie[]> => {
const { cookies } = await this._connection.send('Browser.getCookies', { browserContextId: browserContextId || undefined }); const { cookies } = await this._connection.send('Browser.getCookies', { browserContextId: browserContextId || undefined });
return cookies.map(c => { return cookies.map(c => {
const copy: any = { ... c }; const copy: any = { ... c };
@ -219,14 +219,14 @@ export class Browser extends EventEmitter {
}); });
}, },
clearContextCookies: async (): Promise<void> => { clearCookies: async (): Promise<void> => {
await this._connection.send('Browser.clearCookies', { browserContextId: browserContextId || undefined }); await this._connection.send('Browser.clearCookies', { browserContextId: browserContextId || undefined });
}, },
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => { setCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
await this._connection.send('Browser.setCookies', { browserContextId: browserContextId || undefined, cookies }); await this._connection.send('Browser.setCookies', { browserContextId: browserContextId || undefined, cookies });
}, },
}, isIncognito, options); }, options);
(context as any).permissions = new Permissions(this._connection, browserContextId); (context as any).permissions = new Permissions(this._connection, browserContextId);
return context; return context;
} }

View file

@ -17,7 +17,7 @@
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { helper, RegisteredListener, debugError } from '../helper'; import { helper, RegisteredListener, debugError, assert } from '../helper';
import * as network from '../network'; import * as network from '../network';
import { Connection, ConnectionEvents, TargetSession } from './Connection'; import { Connection, ConnectionEvents, TargetSession } from './Connection';
import { Page } from '../page'; import { Page } from '../page';
@ -209,26 +209,26 @@ export class Browser extends EventEmitter {
} }
_createBrowserContext(browserContextId: string | undefined, options: BrowserContextOptions): BrowserContext { _createBrowserContext(browserContextId: string | undefined, options: BrowserContextOptions): BrowserContext {
const isIncognito = !!browserContextId;
const context = new BrowserContext({ const context = new BrowserContext({
contextPages: async (): Promise<Page[]> => { pages: async (): Promise<Page[]> => {
const targets = this.targets().filter(target => target._browserContext === context && target._type === 'page'); const targets = this.targets().filter(target => target._browserContext === context && target._type === 'page');
const pages = await Promise.all(targets.map(target => target.page())); const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page); return pages.filter(page => !!page);
}, },
createPageInContext: async (): Promise<Page> => { newPage: async (): Promise<Page> => {
const { targetId } = await this._connection.send('Browser.createPage', { browserContextId }); const { targetId } = await this._connection.send('Browser.createPage', { browserContextId });
const target = this._targets.get(targetId); const target = this._targets.get(targetId);
return await target.page(); return await target.page();
}, },
closeContext: async (): Promise<void> => { close: async (): Promise<void> => {
assert(browserContextId, 'Non-incognito profiles cannot be closed!');
await this._connection.send('Browser.deleteContext', { browserContextId }); await this._connection.send('Browser.deleteContext', { browserContextId });
this._contexts.delete(browserContextId); this._contexts.delete(browserContextId);
}, },
getContextCookies: async (): Promise<network.NetworkCookie[]> => { cookies: async (): Promise<network.NetworkCookie[]> => {
const { cookies } = await this._connection.send('Browser.getAllCookies', { browserContextId }); const { cookies } = await this._connection.send('Browser.getAllCookies', { browserContextId });
return cookies.map((c: network.NetworkCookie) => ({ return cookies.map((c: network.NetworkCookie) => ({
...c, ...c,
@ -236,15 +236,15 @@ export class Browser extends EventEmitter {
})); }));
}, },
clearContextCookies: async (): Promise<void> => { clearCookies: async (): Promise<void> => {
await this._connection.send('Browser.deleteAllCookies', { browserContextId }); await this._connection.send('Browser.deleteAllCookies', { browserContextId });
}, },
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => { setCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined })) as Protocol.Browser.SetCookieParam[]; const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined })) as Protocol.Browser.SetCookieParam[];
await this._connection.send('Browser.setCookies', { cookies: cc, browserContextId }); await this._connection.send('Browser.setCookies', { cookies: cc, browserContextId });
}, },
}, isIncognito, options); }, options);
return context; return context;
} }
} }

View file

@ -26,7 +26,6 @@ module.exports.addTests = function({testRunner, expect, playwright, WEBKIT}) {
it('should have default context', async function({browser, server}) { it('should have default context', async function({browser, server}) {
expect(browser.browserContexts().length).toBe(1); expect(browser.browserContexts().length).toBe(1);
const defaultContext = browser.browserContexts()[0]; const defaultContext = browser.browserContexts()[0];
expect(defaultContext.isIncognito()).toBe(false);
let error = null; let error = null;
await defaultContext.close().catch(e => error = e); await defaultContext.close().catch(e => error = e);
expect(browser.defaultContext()).toBe(defaultContext); expect(browser.defaultContext()).toBe(defaultContext);
@ -35,7 +34,6 @@ module.exports.addTests = function({testRunner, expect, playwright, WEBKIT}) {
it('should create new incognito context', async function({browser, newContext}) { it('should create new incognito context', async function({browser, newContext}) {
expect(browser.browserContexts().length).toBe(1); expect(browser.browserContexts().length).toBe(1);
const context = await newContext(); const context = await newContext();
expect(context.isIncognito()).toBe(true);
expect(browser.browserContexts().length).toBe(2); expect(browser.browserContexts().length).toBe(2);
expect(browser.browserContexts().indexOf(context) !== -1).toBe(true); expect(browser.browserContexts().indexOf(context) !== -1).toBe(true);
await context.close(); await context.close();