Skip to content

Commit 0e77909

Browse files
committed
feat(permissions): make origin optional
1 parent 8401462 commit 0e77909

File tree

8 files changed

+116
-61
lines changed

8 files changed

+116
-61
lines changed

docs/api.md

+31-27
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ Indicates that the browser is connected.
208208
- `longitude` <[number]> Longitude between -180 and 180.
209209
- `accuracy` <[number]> Optional non-negative accuracy value.
210210
- `locale` <?[string]> Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, `Accept-Language` request header value as well as number and date formatting rules.
211-
- `permissions` <[Object]> A map from origin keys to permissions values. See [browserContext.setPermissions](#browsercontextsetpermissionsorigin-permissions) for more details.
211+
- `permissions` <[Array]<[string]>> A list of permissions to grant to all pages in this context. See [browserContext.grantPermissions](#browsercontextgrantpermissionspermissions-options) for more details.
212212
- `extraHTTPHeaders` <[Object]> An object containing additional HTTP headers to be sent with every request. All header values must be strings.
213213
- `offline` <[boolean]> Whether to emulate network being offline. Defaults to `false`.
214214
- `httpCredentials` <[Object]> Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
@@ -246,7 +246,7 @@ Creates a new browser context. It won't share cookies/cache with other browser c
246246
- `longitude` <[number]> Longitude between -180 and 180.
247247
- `accuracy` <[number]> Optional non-negative accuracy value.
248248
- `locale` <?[string]> Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, `Accept-Language` request header value as well as number and date formatting rules.
249-
- `permissions` <[Object]> A map from origin keys to permissions values. See [browserContext.setPermissions](#browsercontextsetpermissionsorigin-permissions) for more details.
249+
- `permissions` <[Array]<[string]>> A list of permissions to grant to all pages in this context. See [browserContext.grantPermissions](#browsercontextgrantpermissionspermissions-options) for more details.
250250
- `extraHTTPHeaders` <[Object]> An object containing additional HTTP headers to be sent with every request. All header values must be strings.
251251
- `offline` <[boolean]> Whether to emulate network being offline. Defaults to `false`.
252252
- `httpCredentials` <[Object]> Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
@@ -290,6 +290,7 @@ await context.close();
290290
- [browserContext.close()](#browsercontextclose)
291291
- [browserContext.cookies([urls])](#browsercontextcookiesurls)
292292
- [browserContext.exposeFunction(name, playwrightFunction)](#browsercontextexposefunctionname-playwrightfunction)
293+
- [browserContext.grantPermissions(permissions[][, options])](#browsercontextgrantpermissionspermissions-options)
293294
- [browserContext.newPage()](#browsercontextnewpage)
294295
- [browserContext.pages()](#browsercontextpages)
295296
- [browserContext.route(url, handler)](#browsercontextrouteurl-handler)
@@ -299,7 +300,6 @@ await context.close();
299300
- [browserContext.setGeolocation(geolocation)](#browsercontextsetgeolocationgeolocation)
300301
- [browserContext.setHTTPCredentials(httpCredentials)](#browsercontextsethttpcredentialshttpcredentials)
301302
- [browserContext.setOffline(offline)](#browsercontextsetofflineoffline)
302-
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
303303
- [browserContext.waitForEvent(event[, optionsOrPredicate])](#browsercontextwaitforeventevent-optionsorpredicate)
304304
<!-- GEN:stop -->
305305

@@ -381,7 +381,7 @@ Clears all permission overrides for the browser context.
381381

382382
```js
383383
const context = await browser.newContext();
384-
context.setPermissions('https://example.com', ['clipboard-read']);
384+
await context.grantPermissions(['clipboard-read']);
385385
// do stuff ..
386386
context.clearPermissions();
387387
```
@@ -447,6 +447,31 @@ const crypto = require('crypto');
447447
})();
448448
```
449449

450+
#### browserContext.grantPermissions(permissions[][, options])
451+
- `permissions` <[Array]<[string]>> A permission or an array of permissions to grant. Permissions can be one of the following values:
452+
- `'*'`
453+
- `'geolocation'`
454+
- `'midi'`
455+
- `'midi-sysex'` (system-exclusive midi)
456+
- `'notifications'`
457+
- `'push'`
458+
- `'camera'`
459+
- `'microphone'`
460+
- `'background-sync'`
461+
- `'ambient-light-sensor'`
462+
- `'accelerometer'`
463+
- `'gyroscope'`
464+
- `'magnetometer'`
465+
- `'accessibility-events'`
466+
- `'clipboard-read'`
467+
- `'clipboard-write'`
468+
- `'payment-handler'`
469+
- `options` <[Object]>
470+
- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com".
471+
- returns: <[Promise]>
472+
473+
Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if specified.
474+
450475
#### browserContext.newPage()
451476
- returns: <[Promise]<[Page]>>
452477

@@ -544,27 +569,6 @@ To disable authentication, pass `null`.
544569
- `offline` <[boolean]> Whether to emulate network being offline for the browser context.
545570
- returns: <[Promise]>
546571

547-
#### browserContext.setPermissions(origin, permissions[])
548-
- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com".
549-
- `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:
550-
- `'geolocation'`
551-
- `'midi'`
552-
- `'midi-sysex'` (system-exclusive midi)
553-
- `'notifications'`
554-
- `'push'`
555-
- `'camera'`
556-
- `'microphone'`
557-
- `'background-sync'`
558-
- `'ambient-light-sensor'`
559-
- `'accelerometer'`
560-
- `'gyroscope'`
561-
- `'magnetometer'`
562-
- `'accessibility-events'`
563-
- `'clipboard-read'`
564-
- `'clipboard-write'`
565-
- `'payment-handler'`
566-
- returns: <[Promise]>
567-
568572
#### browserContext.waitForEvent(event[, optionsOrPredicate])
569573
- `event` <[string]> Event name, same one would pass into `browserContext.on(event)`.
570574
- `optionsOrPredicate` <[Function]|[Object]> Either a predicate that receives an event or an options object.
@@ -577,7 +581,7 @@ is fired.
577581

578582
```js
579583
const context = await browser.newContext();
580-
await context.setPermissions('https://html5demos.com', ['geolocation']);
584+
await context.grantPermissions(['geolocation']);
581585
```
582586

583587
### class: Page
@@ -3824,6 +3828,7 @@ const backgroundPage = await backroundPageTarget.page();
38243828
- [browserContext.close()](#browsercontextclose)
38253829
- [browserContext.cookies([urls])](#browsercontextcookiesurls)
38263830
- [browserContext.exposeFunction(name, playwrightFunction)](#browsercontextexposefunctionname-playwrightfunction)
3831+
- [browserContext.grantPermissions(permissions[][, options])](#browsercontextgrantpermissionspermissions-options)
38273832
- [browserContext.newPage()](#browsercontextnewpage)
38283833
- [browserContext.pages()](#browsercontextpages)
38293834
- [browserContext.route(url, handler)](#browsercontextrouteurl-handler)
@@ -3833,7 +3838,6 @@ const backgroundPage = await backroundPageTarget.page();
38333838
- [browserContext.setGeolocation(geolocation)](#browsercontextsetgeolocationgeolocation)
38343839
- [browserContext.setHTTPCredentials(httpCredentials)](#browsercontextsethttpcredentialshttpcredentials)
38353840
- [browserContext.setOffline(offline)](#browsercontextsetofflineoffline)
3836-
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
38373841
- [browserContext.waitForEvent(event[, optionsOrPredicate])](#browsercontextwaitforeventevent-optionsorpredicate)
38383842
<!-- GEN:stop -->
38393843

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"main": "index.js",
1010
"playwright": {
1111
"chromium_revision": "750417",
12-
"firefox_revision": "1042",
12+
"firefox_revision": "1043",
1313
"webkit_revision": "1179"
1414
},
1515
"scripts": {

src/browserContext.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export type BrowserContextOptions = {
3232
locale?: string,
3333
timezoneId?: string,
3434
geolocation?: types.Geolocation,
35-
permissions?: { [key: string]: string[] },
35+
permissions?: string[],
3636
extraHTTPHeaders?: network.Headers,
3737
offline?: boolean,
3838
httpCredentials?: types.Credentials,
@@ -46,7 +46,7 @@ export interface BrowserContext {
4646
cookies(urls?: string | string[]): Promise<network.NetworkCookie[]>;
4747
addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
4848
clearCookies(): Promise<void>;
49-
setPermissions(origin: string, permissions: string[]): Promise<void>;
49+
grantPermissions(permissions: string[], options?: { origin?: string }): Promise<void>;
5050
clearPermissions(): Promise<void>;
5151
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
5252
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
@@ -67,6 +67,7 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement
6767
_closed = false;
6868
private readonly _closePromise: Promise<Error>;
6969
private _closePromiseFulfill: ((error: Error) => void) | undefined;
70+
private _permissions = new Map<string, string[]>();
7071

7172
constructor(options: BrowserContextOptions) {
7273
super();
@@ -92,8 +93,8 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement
9293
abstract cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
9394
abstract addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
9495
abstract clearCookies(): Promise<void>;
95-
abstract setPermissions(origin: string, permissions: string[]): Promise<void>;
96-
abstract clearPermissions(): Promise<void>;
96+
abstract _doGrantPermissions(origin: string, permissions: string[]): Promise<void>;
97+
abstract _doClearPermissions(): Promise<void>;
9798
abstract setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
9899
abstract setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
99100
abstract setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
@@ -103,6 +104,24 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement
103104
abstract route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>;
104105
abstract close(): Promise<void>;
105106

107+
async grantPermissions(permissions: string[], options?: { origin?: string }) {
108+
let origin = '*';
109+
if (options && options.origin) {
110+
const url = new URL(options.origin);
111+
origin = url.origin;
112+
}
113+
const existing = new Set(this._permissions.get(origin) || []);
114+
permissions.forEach(p => existing.add(p));
115+
const list = [...existing.values()];
116+
this._permissions.set(origin, list);
117+
await this._doGrantPermissions(origin, list);
118+
}
119+
120+
async clearPermissions() {
121+
this._permissions.clear();
122+
await this._doClearPermissions();
123+
}
124+
106125
setDefaultNavigationTimeout(timeout: number) {
107126
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
108127
}

src/chromium/crBrowser.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ export class CRBrowserContext extends BrowserContextBase {
252252
}
253253

254254
async _initialize() {
255-
const entries = Object.entries(this._options.permissions || {});
256-
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
255+
if (this._options.permissions)
256+
await this.grantPermissions(this._options.permissions);
257257
if (this._options.geolocation)
258258
await this.setGeolocation(this._options.geolocation);
259259
if (this._options.offline)
@@ -302,7 +302,7 @@ export class CRBrowserContext extends BrowserContextBase {
302302
await this._browser._session.send('Storage.clearCookies', { browserContextId: this._browserContextId || undefined });
303303
}
304304

305-
async setPermissions(origin: string, permissions: string[]): Promise<void> {
305+
async _doGrantPermissions(origin: string, permissions: string[]) {
306306
const webPermissionToProtocol = new Map<string, Protocol.Browser.PermissionType>([
307307
['geolocation', 'geolocation'],
308308
['midi', 'midi'],
@@ -327,10 +327,10 @@ export class CRBrowserContext extends BrowserContextBase {
327327
throw new Error('Unknown permission: ' + permission);
328328
return protocolPermission;
329329
});
330-
await this._browser._session.send('Browser.grantPermissions', { origin, browserContextId: this._browserContextId || undefined, permissions: filtered });
330+
await this._browser._session.send('Browser.grantPermissions', { origin: origin === '*' ? undefined : origin, browserContextId: this._browserContextId || undefined, permissions: filtered });
331331
}
332332

333-
async clearPermissions() {
333+
async _doClearPermissions() {
334334
await this._browser._session.send('Browser.resetPermissions', { browserContextId: this._browserContextId || undefined });
335335
}
336336

src/firefox/ffBrowser.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ export class FFBrowserContext extends BrowserContextBase {
166166
}
167167

168168
async _initialize() {
169-
const entries = Object.entries(this._options.permissions || {});
170-
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
169+
if (this._options.permissions)
170+
await this.grantPermissions(this._options.permissions);
171171
if (this._options.geolocation)
172172
await this.setGeolocation(this._options.geolocation);
173173
if (this._options.extraHTTPHeaders)
@@ -227,23 +227,23 @@ export class FFBrowserContext extends BrowserContextBase {
227227
await this._browser._connection.send('Browser.clearCookies', { browserContextId: this._browserContextId || undefined });
228228
}
229229

230-
async setPermissions(origin: string, permissions: string[]): Promise<void> {
231-
const webPermissionToProtocol = new Map<string, 'geo' | 'microphone' | 'camera' | 'desktop-notifications'>([
230+
async _doGrantPermissions(origin: string, permissions: string[]) {
231+
const webPermissionToProtocol = new Map<string, 'geo' | 'desktop-notification' | 'persistent-storage' | 'push'>([
232232
['geolocation', 'geo'],
233-
['microphone', 'microphone'],
234-
['camera', 'camera'],
235-
['notifications', 'desktop-notifications'],
233+
['persistent-storage', 'persistent-storage'],
234+
['push', 'push'],
235+
['notifications', 'desktop-notification'],
236236
]);
237237
const filtered = permissions.map(permission => {
238238
const protocolPermission = webPermissionToProtocol.get(permission);
239239
if (!protocolPermission)
240240
throw new Error('Unknown permission: ' + permission);
241241
return protocolPermission;
242242
});
243-
await this._browser._connection.send('Browser.grantPermissions', {origin, browserContextId: this._browserContextId || undefined, permissions: filtered});
243+
await this._browser._connection.send('Browser.grantPermissions', { origin: origin, browserContextId: this._browserContextId || undefined, permissions: filtered});
244244
}
245245

246-
async clearPermissions() {
246+
async _doClearPermissions() {
247247
await this._browser._connection.send('Browser.resetPermissions', { browserContextId: this._browserContextId || undefined });
248248
}
249249

src/webkit/wkBrowser.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ export class WKBrowserContext extends BrowserContextBase {
190190
await this._browser._browserSession.send('Playwright.setIgnoreCertificateErrors', { browserContextId: this._browserContextId, ignore: true });
191191
if (this._options.locale)
192192
await this._browser._browserSession.send('Playwright.setLanguages', { browserContextId: this._browserContextId, languages: [this._options.locale] });
193-
const entries = Object.entries(this._options.permissions || {});
194-
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
193+
if (this._options.permissions)
194+
await this.grantPermissions(this._options.permissions);
195195
if (this._options.geolocation)
196196
await this.setGeolocation(this._options.geolocation);
197197
if (this._options.offline)
@@ -244,7 +244,7 @@ export class WKBrowserContext extends BrowserContextBase {
244244
await this._browser._browserSession.send('Playwright.deleteAllCookies', { browserContextId: this._browserContextId });
245245
}
246246

247-
async setPermissions(origin: string, permissions: string[]): Promise<void> {
247+
async _doGrantPermissions(origin: string, permissions: string[]) {
248248
const webPermissionToProtocol = new Map<string, string>([
249249
['geolocation', 'geolocation'],
250250
]);
@@ -257,7 +257,7 @@ export class WKBrowserContext extends BrowserContextBase {
257257
await this._browser._browserSession.send('Playwright.grantPermissions', { origin, browserContextId: this._browserContextId, permissions: filtered });
258258
}
259259

260-
async clearPermissions() {
260+
async _doClearPermissions() {
261261
await this._browser._browserSession.send('Playwright.resetPermissions', { browserContextId: this._browserContextId });
262262
}
263263

test/geolocation.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports.describe = function ({ testRunner, expect, FFOX, WEBKIT }) {
2525

2626
describe.fail(FFOX)('Overrides.setGeolocation', function() {
2727
it('should work', async({page, server, context}) => {
28-
await context.setPermissions(server.PREFIX, ['geolocation']);
28+
await context.grantPermissions(['geolocation']);
2929
await page.goto(server.EMPTY_PAGE);
3030
await context.setGeolocation({longitude: 10, latitude: 10});
3131
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {

0 commit comments

Comments
 (0)