feat(browsercontext): implement BrowserContext.overridePermissions (#3159)
Introduce an API to manage permissions per browser context: - BrowserContext.overridePermissions(origin, permissions) - BrowserContext.clearPermissionOverrides() Fixes #846.
This commit is contained in:
parent
df459ba6cd
commit
50d6c2d3c6
50
docs/api.md
50
docs/api.md
@ -35,6 +35,7 @@ Next Release: **Sep 6, 2018**
|
||||
* [browser.browserContexts()](#browserbrowsercontexts)
|
||||
* [browser.close()](#browserclose)
|
||||
* [browser.createIncognitoBrowserContext()](#browsercreateincognitobrowsercontext)
|
||||
* [browser.defaultBrowserContext()](#browserdefaultbrowsercontext)
|
||||
* [browser.disconnect()](#browserdisconnect)
|
||||
* [browser.newPage()](#browsernewpage)
|
||||
* [browser.pages()](#browserpages)
|
||||
@ -48,9 +49,11 @@ Next Release: **Sep 6, 2018**
|
||||
* [event: 'targetcreated'](#event-targetcreated-1)
|
||||
* [event: 'targetdestroyed'](#event-targetdestroyed-1)
|
||||
* [browserContext.browser()](#browsercontextbrowser)
|
||||
* [browserContext.clearPermissionOverrides()](#browsercontextclearpermissionoverrides)
|
||||
* [browserContext.close()](#browsercontextclose)
|
||||
* [browserContext.isIncognito()](#browsercontextisincognito)
|
||||
* [browserContext.newPage()](#browsercontextnewpage)
|
||||
* [browserContext.overridePermissions(origin, permissions)](#browsercontextoverridepermissionsorigin-permissions)
|
||||
* [browserContext.pages()](#browsercontextpages)
|
||||
* [browserContext.targets()](#browsercontexttargets)
|
||||
- [class: Page](#class-page)
|
||||
@ -612,6 +615,11 @@ const page = await context.newPage();
|
||||
await page.goto('https://example.com');
|
||||
```
|
||||
|
||||
#### browser.defaultBrowserContext()
|
||||
- returns: <[BrowserContext]>
|
||||
|
||||
Returns the default browser context. The default browser context can not be closed.
|
||||
|
||||
#### browser.disconnect()
|
||||
|
||||
Disconnects Puppeteer from the browser, but leaves the Chromium process running. After calling `disconnect`, the [Browser] object is considered disposed and cannot be used anymore.
|
||||
@ -698,6 +706,18 @@ Emitted when a target inside the browser context is destroyed, for example when
|
||||
|
||||
The browser this browser context belongs to.
|
||||
|
||||
#### browserContext.clearPermissionOverrides()
|
||||
- returns: <[Promise]>
|
||||
|
||||
Clears all permission overrides for the browser context.
|
||||
|
||||
```js
|
||||
const context = browser.defaultBrowserContext();
|
||||
context.overridePermissions('https://example.com', ['clipboard-read']);
|
||||
// do stuff ..
|
||||
context.clearPermissionOverrides();
|
||||
```
|
||||
|
||||
#### browserContext.close()
|
||||
- returns: <[Promise]>
|
||||
|
||||
@ -719,6 +739,35 @@ The default browser context is the only non-incognito browser context.
|
||||
|
||||
Creates a new page in the browser context.
|
||||
|
||||
|
||||
#### browserContext.overridePermissions(origin, permissions)
|
||||
- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com".
|
||||
- `permissions` <[Array]<[string]>> An array of permissions to grant. All permissions that are not listed here will be automatically denied. Permissions can be one of the following values:
|
||||
- `'geolocation'`
|
||||
- `'midi'`
|
||||
- `'midi-sysex'` (system-exclusive midi)
|
||||
- `'notifications'`
|
||||
- `'push'`
|
||||
- `'camera'`
|
||||
- `'microphone'`
|
||||
- `'background-sync'`
|
||||
- `'ambient-light-sensor'`
|
||||
- `'accelerometer'`
|
||||
- `'gyroscope'`
|
||||
- `'magnetometer'`
|
||||
- `'accessibility-events'`
|
||||
- `'clipboard-read'`
|
||||
- `'clipboard-write'`
|
||||
- `'payment-handler'`
|
||||
- returns: <[Promise]>
|
||||
|
||||
|
||||
```js
|
||||
const context = browser.defaultBrowserContext();
|
||||
await context.overridePermissions('https://html5demos.com', ['geolocation']);
|
||||
```
|
||||
|
||||
|
||||
#### browserContext.pages()
|
||||
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
|
||||
|
||||
@ -3165,6 +3214,7 @@ TimeoutError is emitted whenever certain operations are terminated due to timeou
|
||||
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
|
||||
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
|
||||
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
|
||||
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
|
||||
[Page]: #class-page "Page"
|
||||
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
|
||||
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "String"
|
||||
|
@ -37,11 +37,11 @@ class Browser extends EventEmitter {
|
||||
this._connection = connection;
|
||||
this._closeCallback = closeCallback || new Function();
|
||||
|
||||
this._defaultContext = new BrowserContext(this, null);
|
||||
this._defaultContext = new BrowserContext(this._connection, this, null);
|
||||
/** @type {Map<string, BrowserContext>} */
|
||||
this._contexts = new Map();
|
||||
for (const contextId of contextIds)
|
||||
this._contexts.set(contextId, new BrowserContext(this, contextId));
|
||||
this._contexts.set(contextId, new BrowserContext(this._connection, this, contextId));
|
||||
|
||||
/** @type {Map<string, Target>} */
|
||||
this._targets = new Map();
|
||||
@ -65,7 +65,7 @@ class Browser extends EventEmitter {
|
||||
*/
|
||||
async createIncognitoBrowserContext() {
|
||||
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
|
||||
const context = new BrowserContext(this, browserContextId);
|
||||
const context = new BrowserContext(this._connection, this, browserContextId);
|
||||
this._contexts.set(browserContextId, context);
|
||||
return context;
|
||||
}
|
||||
@ -77,6 +77,13 @@ class Browser extends EventEmitter {
|
||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!BrowserContext}
|
||||
*/
|
||||
defaultBrowserContext() {
|
||||
return this._defaultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?string} contextId
|
||||
*/
|
||||
@ -231,11 +238,13 @@ Browser.Events = {
|
||||
|
||||
class BrowserContext extends EventEmitter {
|
||||
/**
|
||||
* @param {!Puppeteer.Connection} connection
|
||||
* @param {!Browser} browser
|
||||
* @param {?string} contextId
|
||||
*/
|
||||
constructor(browser, contextId) {
|
||||
constructor(connection, browser, contextId) {
|
||||
super();
|
||||
this._connection = connection;
|
||||
this._browser = browser;
|
||||
this._id = contextId;
|
||||
}
|
||||
@ -266,6 +275,43 @@ class BrowserContext extends EventEmitter {
|
||||
return !!this._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} origin
|
||||
* @param {!Array<string>} permissions
|
||||
*/
|
||||
async overridePermissions(origin, permissions) {
|
||||
const webPermissionToProtocol = new Map([
|
||||
['geolocation', 'geolocation'],
|
||||
['midi', 'midi'],
|
||||
['notifications', 'notifications'],
|
||||
['push', 'push'],
|
||||
['camera', 'videoCapture'],
|
||||
['microphone', 'audioCapture'],
|
||||
['background-sync', 'backgroundSync'],
|
||||
['ambient-light-sensor', 'sensors'],
|
||||
['accelerometer', 'sensors'],
|
||||
['gyroscope', 'sensors'],
|
||||
['magnetometer', 'sensors'],
|
||||
['accessibility-events', 'accessibilityEvents'],
|
||||
['clipboard-read', 'clipboardRead'],
|
||||
['clipboard-write', 'clipboardWrite'],
|
||||
['payment-handler', 'paymentHandler'],
|
||||
// chrome-specific permissions we have.
|
||||
['midi-sysex', 'midiSysex'],
|
||||
]);
|
||||
permissions = permissions.map(permission => {
|
||||
const protocolPermission = webPermissionToProtocol.get(permission);
|
||||
if (!protocolPermission)
|
||||
throw new Error('Unknown permission: ' + permission);
|
||||
return protocolPermission;
|
||||
});
|
||||
await this._connection.send('Browser.grantPermissions', {origin, browserContextId: this._id || undefined, permissions});
|
||||
}
|
||||
|
||||
async clearPermissionOverrides() {
|
||||
await this._connection.send('Browser.resetPermissions', {browserContextId: this._id || undefined});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Puppeteer.Page>}
|
||||
*/
|
||||
|
@ -29,6 +29,7 @@ module.exports.addTests = function({testRunner, expect}) {
|
||||
expect(defaultContext.isIncognito()).toBe(false);
|
||||
let error = null;
|
||||
await defaultContext.close().catch(e => error = e);
|
||||
expect(browser.defaultBrowserContext()).toBe(defaultContext);
|
||||
expect(error.message).toContain('cannot be closed');
|
||||
});
|
||||
it('should create new incognito context', async function({browser, server}) {
|
||||
|
@ -75,6 +75,59 @@ module.exports.addTests = function({testRunner, expect, headless}) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.overridePermissions', function() {
|
||||
function getPermission(page, name) {
|
||||
return page.evaluate(name => navigator.permissions.query({name}).then(result => result.state), name);
|
||||
}
|
||||
|
||||
it('should be prompt by default', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
it('should deny permission when not listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.overridePermissions(server.EMPTY_PAGE, []);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('denied');
|
||||
});
|
||||
it('should fail when bad permission is given', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = null;
|
||||
await context.overridePermissions(server.EMPTY_PAGE, ['foo']).catch(e => error = e);
|
||||
expect(error.message).toBe('Unknown permission: foo');
|
||||
});
|
||||
it('should grant permission when listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
});
|
||||
it('should reset permissions', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.clearPermissionOverrides();
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
it('should trigger permission onchange', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window.events = [];
|
||||
return navigator.permissions.query({name: 'clipboard-read'}).then(function(result) {
|
||||
window.events.push(result.state);
|
||||
result.onchange = function() {
|
||||
window.events.push(result.state);
|
||||
};
|
||||
});
|
||||
});
|
||||
expect(await page.evaluate(() => window.events)).toEqual(['prompt']);
|
||||
await context.overridePermissions(server.EMPTY_PAGE, []);
|
||||
expect(await page.evaluate(() => window.events)).toEqual(['prompt', 'denied']);
|
||||
await context.overridePermissions(server.EMPTY_PAGE, ['clipboard-read']);
|
||||
expect(await page.evaluate(() => window.events)).toEqual(['prompt', 'denied', 'granted']);
|
||||
await context.clearPermissionOverrides();
|
||||
expect(await page.evaluate(() => window.events)).toEqual(['prompt', 'denied', 'granted', 'prompt']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.evaluate', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const result = await page.evaluate(() => 7 * 3);
|
||||
|
Loading…
Reference in New Issue
Block a user