feat: move permissions back into the context (#320)

This commit is contained in:
Pavel Feldman 2019-12-20 13:07:14 -08:00 committed by GitHub
parent dd6ba432ab
commit ad22a46fde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 294 additions and 395 deletions

View File

@ -19,6 +19,14 @@
* [chromiumPlaywright.errors](#chromiumplaywrighterrors) * [chromiumPlaywright.errors](#chromiumplaywrighterrors)
* [chromiumPlaywright.executablePath()](#chromiumplaywrightexecutablepath) * [chromiumPlaywright.executablePath()](#chromiumplaywrightexecutablepath)
* [chromiumPlaywright.launch([options])](#chromiumplaywrightlaunchoptions) * [chromiumPlaywright.launch([options])](#chromiumplaywrightlaunchoptions)
- [class: Browser](#class-browser)
* [event: 'disconnected'](#event-disconnected)
* [browser.browserContexts()](#browserbrowsercontexts)
* [browser.close()](#browserclose)
* [browser.defaultContext()](#browserdefaultcontext)
* [browser.disconnect()](#browserdisconnect)
* [browser.isConnected()](#browserisconnected)
* [browser.newContext(options)](#browsernewcontextoptions)
- [class: BrowserFetcher](#class-browserfetcher) - [class: BrowserFetcher](#class-browserfetcher)
* [browserFetcher.canDownload(revision)](#browserfetchercandownloadrevision) * [browserFetcher.canDownload(revision)](#browserfetchercandownloadrevision)
* [browserFetcher.download(revision[, progressCallback])](#browserfetcherdownloadrevision-progresscallback) * [browserFetcher.download(revision[, progressCallback])](#browserfetcherdownloadrevision-progresscallback)
@ -32,18 +40,9 @@
* [browserServer.process()](#browserserverprocess) * [browserServer.process()](#browserserverprocess)
* [browserServer.wsEndpoint()](#browserserverwsendpoint) * [browserServer.wsEndpoint()](#browserserverwsendpoint)
- [class: ChromiumBrowser](#class-chromiumbrowser) - [class: ChromiumBrowser](#class-chromiumbrowser)
* [event: 'disconnected'](#event-disconnected)
* [event: 'targetchanged'](#event-targetchanged) * [event: 'targetchanged'](#event-targetchanged)
* [event: 'targetcreated'](#event-targetcreated) * [event: 'targetcreated'](#event-targetcreated)
* [event: 'targetdestroyed'](#event-targetdestroyed) * [event: 'targetdestroyed'](#event-targetdestroyed)
* [chromiumBrowser.browserContexts()](#chromiumbrowserbrowsercontexts)
* [chromiumBrowser.chromium](#chromiumbrowserchromium)
* [chromiumBrowser.close()](#chromiumbrowserclose)
* [chromiumBrowser.defaultContext()](#chromiumbrowserdefaultcontext)
* [chromiumBrowser.disconnect()](#chromiumbrowserdisconnect)
* [chromiumBrowser.isConnected()](#chromiumbrowserisconnected)
* [chromiumBrowser.newContext(options)](#chromiumbrowsernewcontextoptions)
* [chromiumBrowser.process()](#chromiumbrowserprocess)
* [chromiumBrowser.browserTarget()](#chromiumbrowserbrowsertarget) * [chromiumBrowser.browserTarget()](#chromiumbrowserbrowsertarget)
* [chromiumBrowser.pageTarget(page)](#chromiumbrowserpagetargetpage) * [chromiumBrowser.pageTarget(page)](#chromiumbrowserpagetargetpage)
* [chromiumBrowser.serviceWorker(target)](#chromiumbrowserserviceworkertarget) * [chromiumBrowser.serviceWorker(target)](#chromiumbrowserserviceworkertarget)
@ -53,16 +52,15 @@
* [chromiumBrowser.waitForTarget(predicate[, options])](#chromiumbrowserwaitfortargetpredicate-options) * [chromiumBrowser.waitForTarget(predicate[, options])](#chromiumbrowserwaitfortargetpredicate-options)
- [class: BrowserContext](#class-browsercontext) - [class: BrowserContext](#class-browsercontext)
* [browserContext.clearCookies()](#browsercontextclearcookies) * [browserContext.clearCookies()](#browsercontextclearcookies)
* [browserContext.clearPermissions()](#browsercontextclearpermissions)
* [browserContext.close()](#browsercontextclose) * [browserContext.close()](#browsercontextclose)
* [browserContext.cookies([...urls])](#browsercontextcookiesurls) * [browserContext.cookies([...urls])](#browsercontextcookiesurls)
* [browserContext.newPage()](#browsercontextnewpage) * [browserContext.newPage()](#browsercontextnewpage)
* [browserContext.pages()](#browsercontextpages) * [browserContext.pages()](#browsercontextpages)
* [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies) * [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
* [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
- [class: ChromiumOverrides](#class-chromiumoverrides) - [class: ChromiumOverrides](#class-chromiumoverrides)
* [chromiumOverrides.setGeolocation(options)](#chromiumoverridessetgeolocationoptions) * [chromiumOverrides.setGeolocation(options)](#chromiumoverridessetgeolocationoptions)
- [class: ChromiumPermissions](#class-chromiumpermissions)
* [chromiumPermissions.clearOverrides()](#chromiumpermissionsclearoverrides)
* [chromiumPermissions.override(origin, permissions)](#chromiumpermissionsoverrideorigin-permissions)
- [class: Page](#class-page) - [class: Page](#class-page)
* [event: 'close'](#event-close) * [event: 'close'](#event-close)
* [event: 'console'](#event-console) * [event: 'console'](#event-console)
@ -159,7 +157,7 @@
- [class: ChromiumPDF](#class-chromiumpdf) - [class: ChromiumPDF](#class-chromiumpdf)
* [chromiumPDF.generate([options])](#chromiumpdfgenerateoptions) * [chromiumPDF.generate([options])](#chromiumpdfgenerateoptions)
- [class: FirefoxBrowser](#class-firefoxbrowser) - [class: FirefoxBrowser](#class-firefoxbrowser)
* [firefoxBrowser.wsEndpoint()](#firefoxbrowserwsendpoint) - [class: WebKitBrowser](#class-webkitbrowser)
- [class: Dialog](#class-dialog) - [class: Dialog](#class-dialog)
* [dialog.accept([promptText])](#dialogacceptprompttext) * [dialog.accept([promptText])](#dialogacceptprompttext)
* [dialog.defaultValue()](#dialogdefaultvalue) * [dialog.defaultValue()](#dialogdefaultvalue)
@ -227,22 +225,14 @@
* [elementHandle.$$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args) * [elementHandle.$$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args)
* [elementHandle.$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args-1) * [elementHandle.$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args-1)
* [elementHandle.$x(expression)](#elementhandlexexpression) * [elementHandle.$x(expression)](#elementhandlexexpression)
* [elementHandle.asElement()](#elementhandleaselement)
* [elementHandle.boundingBox()](#elementhandleboundingbox) * [elementHandle.boundingBox()](#elementhandleboundingbox)
* [elementHandle.click([options])](#elementhandleclickoptions) * [elementHandle.click([options])](#elementhandleclickoptions)
* [elementHandle.contentFrame()](#elementhandlecontentframe) * [elementHandle.contentFrame()](#elementhandlecontentframe)
* [elementHandle.dblclick([options])](#elementhandledblclickoptions) * [elementHandle.dblclick([options])](#elementhandledblclickoptions)
* [elementHandle.dispose()](#elementhandledispose)
* [elementHandle.evaluate(pageFunction[, ...args])](#elementhandleevaluatepagefunction-args)
* [elementHandle.evaluateHandle(pageFunction[, ...args])](#elementhandleevaluatehandlepagefunction-args)
* [elementHandle.executionContext()](#elementhandleexecutioncontext)
* [elementHandle.fill(value)](#elementhandlefillvalue) * [elementHandle.fill(value)](#elementhandlefillvalue)
* [elementHandle.focus()](#elementhandlefocus) * [elementHandle.focus()](#elementhandlefocus)
* [elementHandle.getProperties()](#elementhandlegetproperties)
* [elementHandle.getProperty(propertyName)](#elementhandlegetpropertypropertyname)
* [elementHandle.hover([options])](#elementhandlehoveroptions) * [elementHandle.hover([options])](#elementhandlehoveroptions)
* [elementHandle.isIntersectingViewport()](#elementhandleisintersectingviewport) * [elementHandle.isIntersectingViewport()](#elementhandleisintersectingviewport)
* [elementHandle.jsonValue()](#elementhandlejsonvalue)
* [elementHandle.press(key[, options])](#elementhandlepresskey-options) * [elementHandle.press(key[, options])](#elementhandlepresskey-options)
* [elementHandle.screenshot([options])](#elementhandlescreenshotoptions) * [elementHandle.screenshot([options])](#elementhandlescreenshotoptions)
* [elementHandle.select(...values)](#elementhandleselectvalues) * [elementHandle.select(...values)](#elementhandleselectvalues)
@ -454,6 +444,102 @@ const browser = await playwright.launch({
> >
> See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. > See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
### class: Browser
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
A Browser is created when Playwright connects to a browser instance, either through [`playwright.launch`](#playwrightlaunchoptions) or [`playwright.connect`](#playwrightconnectoptions).
An example of using a [Browser] to create a [Page]:
```js
const playwright = require('playwright');
(async () => {
const browser = await playwright.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com');
await browser.close();
})();
```
An example of disconnecting from and reconnecting to a [Browser]:
```js
const playwright = require('playwright');
(async () => {
const browserServer = await playwright.launchServer();
const browserWSEndpoint = browserServer.wsEndpoint();
// Use the endpoint to establish a connection
const browser = await playwright.connect({browserWSEndpoint});
// Close Chromium
await browser.close();
})();
```
#### event: 'disconnected'
Emitted when Playwright gets disconnected from the Chromium instance. This might happen because of one of the following:
- Chromium is closed or crashed
- The [`browser.disconnect`](#browserdisconnect) method was called
#### browser.browserContexts()
- returns: <[Array]<[BrowserContext]>>
Returns an array of all open browser contexts. In a newly created browser, this will return
a single instance of [BrowserContext].
#### browser.close()
- returns: <[Promise]>
Closes Chromium and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
#### browser.defaultContext()
- returns: <[BrowserContext]>
Returns the default browser context. The default browser context can not be closed.
#### browser.disconnect()
Disconnects Playwright from the browser, but leaves the Chromium process running. After calling `disconnect`, the [Browser] object is considered disposed and cannot be used anymore.
#### browser.isConnected()
- returns: <[boolean]>
Indicates that the browser is connected.
#### browser.newContext(options)
- `options` <[Object]>
- `ignoreHTTPSErrors` <?[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
- `bypassCSP` <?[boolean]> Toggles bypassing page's Content-Security-Policy.
- `viewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport.
- `width` <[number]> page width in pixels.
- `height` <[number]> page height in pixels.
- `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
- `userAgent` <?[string]> Specific user agent to use in this page
- `mediaType` <?[string]> Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation.
- `colorScheme` <?"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the page. Defaults to true.
- `timezoneId` <?[string]> Changes the timezone of the page. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
- returns: <[Promise]<[BrowserContext]>>
Creates a new browser context. It won't share cookies/cache with other browser contexts.
```js
(async () => {
const browser = await playwright.launch();
// Create a new incognito browser context.
const context = await browser.newContext();
// Create a new page in a pristine context.
const page = await context.newPage();
// Do stuff
await page.goto('https://example.com');
})();
```
### class: BrowserFetcher ### class: BrowserFetcher
BrowserFetcher can download and manage different versions of Chromium. BrowserFetcher can download and manage different versions of Chromium.
@ -535,51 +621,17 @@ Learn more about the [devtools protocol](https://chromedevtools.github.io/devtoo
### class: ChromiumBrowser ### class: ChromiumBrowser
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) * extends: [Browser]
A Browser is created when Playwright connects to a Chromium instance, either through [`playwright.launch`](#playwrightlaunchoptions) or [`playwright.connect`](#playwrightconnectoptions).
An example of using a [Browser] to create a [Page]:
```js
const playwright = require('playwright');
(async () => {
const browser = await playwright.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com');
await browser.close();
})();
```
An example of disconnecting from and reconnecting to a [Browser]:
```js
const playwright = require('playwright');
(async () => {
const browserServer = await playwright.launchServer();
const browserWSEndpoint = browserServer.wsEndpoint();
// Use the endpoint to establish a connection
const browser = await playwright.connect({browserWSEndpoint});
// Close Chromium
await browser.close();
})();
```
Chromium-specific features including Tracing, service worker support, etc. Chromium-specific features including Tracing, service worker support, etc.
You can use [`chromium.startTracing`](#chromiumstarttracingpage-options) and [`chromium.stopTracing`](#chromiumstoptracing) to create a trace file which can be opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/). You can use [`chromiumBrowser.startTracing`](#chromiumbrowserstarttracingpage-options) and [`chromiumBrowser.stopTracing`](#chromiumbrowserstoptracing) to create a trace file which can be opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/).
```js ```js
await browser.chromium.startTracing(page, {path: 'trace.json'}); await browser.startTracing(page, {path: 'trace.json'});
await page.goto('https://www.google.com'); await page.goto('https://www.google.com');
await browser.chromium.stopTracing(); await browser.stopTracing();
``` ```
#### event: 'disconnected'
Emitted when Playwright gets disconnected from the Chromium instance. This might happen because of one of the following:
- Chromium is closed or crashed
- The [`browser.disconnect`](#browserdisconnect) method was called
#### event: 'targetchanged' #### event: 'targetchanged'
- <[Target]> - <[Target]>
@ -602,71 +654,6 @@ Emitted when a target is destroyed, for example when a page is closed.
> **NOTE** This includes target destructions in incognito browser contexts. > **NOTE** This includes target destructions in incognito browser contexts.
#### chromiumBrowser.browserContexts()
- returns: <[Array]<[BrowserContext]>>
Returns an array of all open browser contexts. In a newly created browser, this will return
a single instance of [BrowserContext].
#### chromiumBrowser.chromium
- returns: <[Chromium]>
#### chromiumBrowser.close()
- returns: <[Promise]>
Closes Chromium and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
#### chromiumBrowser.defaultContext()
- returns: <[BrowserContext]>
Returns the default browser context. The default browser context can not be closed.
#### chromiumBrowser.disconnect()
Disconnects Playwright from the browser, but leaves the Chromium process running. After calling `disconnect`, the [Browser] object is considered disposed and cannot be used anymore.
#### chromiumBrowser.isConnected()
- returns: <[boolean]>
Indicates that the browser is connected.
#### chromiumBrowser.newContext(options)
- `options` <[Object]>
- `ignoreHTTPSErrors` <?[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
- `bypassCSP` <?[boolean]> Toggles bypassing page's Content-Security-Policy.
- `viewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport.
- `width` <[number]> page width in pixels.
- `height` <[number]> page height in pixels.
- `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
- `userAgent` <?[string]> Specific user agent to use in this page
- `mediaType` <?[string]> Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation.
- `colorScheme` <?"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the page. Defaults to true.
- `timezoneId` <?[string]> Changes the timezone of the page. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
- returns: <[Promise]<[BrowserContext]>>
Creates a new browser context. It won't share cookies/cache with other browser contexts.
```js
(async () => {
const browser = await playwright.launch();
// Create a new incognito browser context.
const context = await browser.newContext();
// Create a new page in a pristine context.
const page = await context.newPage();
// Do stuff
await page.goto('https://example.com');
})();
```
#### chromiumBrowser.process()
- returns: <?[ChildProcess]> Spawned browser process. Returns `null` if the browser instance was created with [`playwright.connect`](#playwrightconnectoptions) method.
#### chromiumBrowser.browserTarget() #### chromiumBrowser.browserTarget()
- returns: <[Target]> - returns: <[Target]>
@ -744,6 +731,18 @@ await context.close();
Clears context bookies. Clears context bookies.
#### browserContext.clearPermissions()
- returns: <[Promise]>
Clears all permission overrides for the browser context.
```js
const context = browser.defaultContext();
context.setPermissions('https://example.com', ['clipboard-read']);
// do stuff ..
context.clearPermissions();
```
#### browserContext.close() #### browserContext.close()
- returns: <[Promise]> - returns: <[Promise]>
@ -798,38 +797,7 @@ An array of all pages inside the browser context.
await browserContext.setCookies([cookieObject1, cookieObject2]); await browserContext.setCookies([cookieObject1, cookieObject2]);
``` ```
### class: ChromiumOverrides #### browserContext.setPermissions(origin, permissions[])
#### chromiumOverrides.setGeolocation(options)
- `options` <[Object]>
- `latitude` <[number]> Latitude between -90 and 90.
- `longitude` <[number]> Longitude between -180 and 180.
- `accuracy` <[number]> Optional non-negative accuracy value.
- returns: <[Promise]>
Sets the page's geolocation.
```js
await browserContext.overrides.setGeolocation({latitude: 59.95, longitude: 30.31667});
```
> **NOTE** Consider using [browserContext.permissions.override](#permissionsoverrideorigin-permissions) to grant permissions for the page to read its geolocation.
### class: ChromiumPermissions
#### chromiumPermissions.clearOverrides()
- returns: <[Promise]>
Clears all permission overrides for the browser context.
```js
const context = browser.defaultContext();
context.permissions.override('https://example.com', ['clipboard-read']);
// do stuff ..
context.permissions.clearOverrides();
```
#### chromiumPermissions.override(origin, permissions)
- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com". - `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: - `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'` - `'geolocation'`
@ -853,9 +821,26 @@ context.permissions.clearOverrides();
```js ```js
const context = browser.defaultContext(); const context = browser.defaultContext();
await context.permissions.override('https://html5demos.com', ['geolocation']); await context.setPermissions('https://html5demos.com', ['geolocation']);
``` ```
### class: ChromiumOverrides
#### chromiumOverrides.setGeolocation(options)
- `options` <[Object]>
- `latitude` <[number]> Latitude between -90 and 90.
- `longitude` <[number]> Longitude between -180 and 180.
- `accuracy` <[number]> Optional non-negative accuracy value.
- returns: <[Promise]>
Sets the page's geolocation.
```js
await browserContext.overrides.setGeolocation({latitude: 59.95, longitude: 30.31667});
```
> **NOTE** Consider using [browserContext.setPermissions](#browsercontextsetpermissions-permissions) to grant permissions for the page to read its geolocation.
### class: Page ### class: Page
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) * extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
@ -2180,12 +2165,15 @@ The `format` options are:
### class: FirefoxBrowser ### class: FirefoxBrowser
* extends: [Browser]
Firefox-specific features. Firefox-specific features.
#### firefoxBrowser.wsEndpoint() ### class: WebKitBrowser
- returns: <[string]> Browser websocket url.
Browser websocket endpoint which can be used as an argument to [playwright.connect](#playwrightconnectoptions). * extends: [Browser]
WebKit-specific features.
### class: Dialog ### class: Dialog
@ -3045,9 +3033,6 @@ expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
The method evaluates the XPath expression relative to the elementHandle. If there are no such elements, the method will resolve to an empty array. The method evaluates the XPath expression relative to the elementHandle. If there are no such elements, the method will resolve to an empty array.
#### elementHandle.asElement()
- returns: <[ElementHandle]>
#### elementHandle.boundingBox() #### elementHandle.boundingBox()
- returns: <[Promise]<?[Object]>> - returns: <[Promise]<?[Object]>>
- x <[number]> the x coordinate of the element in pixels. - x <[number]> the x coordinate of the element in pixels.
@ -3091,42 +3076,6 @@ Bear in mind that if the first click of the `dblclick()` triggers a navigation e
> **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event. > **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.
#### elementHandle.dispose()
- returns: <[Promise]> Promise which resolves when the element handle is successfully disposed.
The `elementHandle.dispose` method stops referencing the element handle.
#### elementHandle.evaluate(pageFunction[, ...args])
- `pageFunction` <[function]\([Object]\)> Function to be evaluated in browser context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
This method passes this handle as the first argument to `pageFunction`.
If `pageFunction` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its value.
Examples:
```js
const tweetHandle = await page.$('.tweet .retweets');
expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10');
```
#### elementHandle.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
This method passes this handle as the first argument to `pageFunction`.
The only difference between `evaluateHandle.evaluate` and `evaluateHandle.evaluateHandle` is that `executionContext.evaluateHandle` returns in-page object (JSHandle).
If the function passed to the `evaluateHandle.evaluateHandle` returns a [Promise], then `evaluateHandle.evaluateHandle` would wait for the promise to resolve and return its value.
See [Page.evaluateHandle](#pageevaluatehandlepagefunction-args) for more details.
#### elementHandle.executionContext()
- returns: <[ExecutionContext]>
#### elementHandle.fill(value) #### elementHandle.fill(value)
- `value` <[string]> Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element. - `value` <[string]> Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.
- returns: <[Promise]> Promise which resolves when the element is successfully filled. - returns: <[Promise]> Promise which resolves when the element is successfully filled.
@ -3139,29 +3088,6 @@ If element is not a text `<input>`, `<textarea>` or `[contenteditable]` element,
Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element. Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
#### elementHandle.getProperties()
- returns: <[Promise]<[Map]<[string], [JSHandle]>>>
The method returns a map with property names as keys and JSHandle instances for the property values.
```js
const listHandle = await page.evaluateHandle(() => document.body.children);
const properties = await listHandle.getProperties();
const children = [];
for (const property of properties.values()) {
const element = property.asElement();
if (element)
children.push(element);
}
children; // holds elementHandles to all children of document.body
```
#### elementHandle.getProperty(propertyName)
- `propertyName` <[string]> property to get
- returns: <[Promise]<[JSHandle]>>
Fetches a single property from the objectHandle.
#### elementHandle.hover([options]) #### elementHandle.hover([options])
- `options` <[Object]> - `options` <[Object]>
- `relativePoint` <[Object]> A point to hover relative to the top-left corner of element padding box. If not specified, hovers over some visible point of the element. - `relativePoint` <[Object]> A point to hover relative to the top-left corner of element padding box. If not specified, hovers over some visible point of the element.
@ -3176,13 +3102,6 @@ If the element is detached from DOM, the method throws an error.
#### elementHandle.isIntersectingViewport() #### elementHandle.isIntersectingViewport()
- returns: <[Promise]<[boolean]>> Resolves to true if the element is visible in the current viewport. - returns: <[Promise]<[boolean]>> Resolves to true if the element is visible in the current viewport.
#### elementHandle.jsonValue()
- returns: <[Promise]<[Object]>>
Returns a JSON representation of the object. The JSON is generated by running [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) on the object in page and consequent [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) in playwright.
> **NOTE** The method will throw if the referenced object is not stringifiable.
#### elementHandle.press(key[, options]) #### elementHandle.press(key[, options])
- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names. - `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names.
- `options` <[Object]> - `options` <[Object]>

View File

@ -3,15 +3,16 @@
import { BrowserContext, BrowserContextOptions } from './browserContext'; import { BrowserContext, BrowserContextOptions } from './browserContext';
import { ChildProcess } from 'child_process'; import { ChildProcess } from 'child_process';
import { EventEmitter } from 'events';
export interface Browser { export class Browser extends EventEmitter {
newContext(options?: BrowserContextOptions): Promise<BrowserContext>; newContext(options?: BrowserContextOptions): Promise<BrowserContext> { throw new Error('Not implemented'); }
disconnect(): void; browserContexts(): BrowserContext[] { throw new Error('Not implemented'); }
isConnected(): boolean; defaultContext(): BrowserContext { throw new Error('Not implemented'); }
browserContexts(): BrowserContext[]; disconnect(): void { throw new Error('Not implemented'); }
defaultContext(): BrowserContext; isConnected(): boolean { throw new Error('Not implemented'); }
close(): Promise<void>; close(): Promise<void> { throw new Error('Not implemented'); }
} }
export class BrowserServer<T extends Browser> { export class BrowserServer<T extends Browser> {

View File

@ -24,9 +24,13 @@ export interface BrowserContextDelegate {
pages(): Promise<Page[]>; pages(): Promise<Page[]>;
newPage(): Promise<Page>; newPage(): Promise<Page>;
close(): Promise<void>; close(): Promise<void>;
cookies(): Promise<network.NetworkCookie[]>; cookies(): Promise<network.NetworkCookie[]>;
clearCookies(): Promise<void>;
setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>; setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
clearCookies(): Promise<void>;
setPermissions(origin: string, permissions: string[]): Promise<void>;
clearPermissions(): Promise<void>;
} }
export type BrowserContextOptions = { export type BrowserContextOptions = {
@ -64,12 +68,20 @@ export class BrowserContext {
return network.filterCookies(await this._delegate.cookies(), urls); return network.filterCookies(await this._delegate.cookies(), urls);
} }
async setCookies(cookies: network.SetNetworkCookieParam[]) {
await this._delegate.setCookies(network.rewriteCookies(cookies));
}
async clearCookies() { async clearCookies() {
await this._delegate.clearCookies(); await this._delegate.clearCookies();
} }
async setCookies(cookies: network.SetNetworkCookieParam[]) { async setPermissions(origin: string, permissions: string[]): Promise<void> {
await this._delegate.setCookies(network.rewriteCookies(cookies)); await this._delegate.setPermissions(origin, permissions);
}
async clearPermissions() {
await this._delegate.clearPermissions();
} }
async close() { async close() {

View File

@ -10,5 +10,4 @@ export { CRCoverage as ChromiumCoverage } from './features/crCoverage';
export { CRInterception as ChromiumInterception } from './features/crInterception'; export { CRInterception as ChromiumInterception } from './features/crInterception';
export { CROverrides as ChromiumOverrides } from './features/crOverrides'; export { CROverrides as ChromiumOverrides } from './features/crOverrides';
export { CRPDF as ChromiumPDF } from './features/crPdf'; export { CRPDF as ChromiumPDF } from './features/crPdf';
export { CRPermissions as ChromiumPermissions } from './features/crPermissions';
export { CRWorker as ChromiumWorker, CRWorkers as ChromiumWorkers } from './features/crWorkers'; export { CRWorker as ChromiumWorker, CRWorkers as ChromiumWorkers } from './features/crWorkers';

View File

@ -27,13 +27,12 @@ import { Protocol } from './protocol';
import { CRFrameManager } from './crFrameManager'; import { CRFrameManager } from './crFrameManager';
import * as browser from '../browser'; import * as browser from '../browser';
import * as network from '../network'; import * as network from '../network';
import { CRPermissions } from './features/crPermissions';
import { CROverrides } from './features/crOverrides'; import { CROverrides } from './features/crOverrides';
import { CRWorker } from './features/crWorkers'; import { CRWorker } from './features/crWorkers';
import { ConnectionTransport } from '../transport'; import { ConnectionTransport } from '../transport';
import { readProtocolStream } from './crProtocolHelper'; import { readProtocolStream } from './crProtocolHelper';
export class CRBrowser extends EventEmitter implements browser.Browser { export class CRBrowser extends browser.Browser {
_connection: CRConnection; _connection: CRConnection;
_client: CRSession; _client: CRSession;
private _defaultContext: BrowserContext; private _defaultContext: BrowserContext;
@ -129,9 +128,41 @@ export class CRBrowser extends EventEmitter implements browser.Browser {
setCookies: 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 });
}, },
setPermissions: async (origin: string, permissions: string[]): Promise<void> => {
const webPermissionToProtocol = new Map<string, Protocol.Browser.PermissionType>([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardSanitizedWrite'],
['payment-handler', 'paymentHandler'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._client.send('Browser.grantPermissions', { origin, browserContextId: contextId || undefined, permissions: filtered });
},
clearPermissions: async () => {
await this._client.send('Browser.resetPermissions', { browserContextId: contextId || undefined });
}
}, options); }, options);
overrides = new CROverrides(context); overrides = new CROverrides(context);
(context as any).permissions = new CRPermissions(this._client, contextId);
(context as any).overrides = overrides; (context as any).overrides = overrides;
return context; return context;
} }
@ -161,7 +192,7 @@ export class CRBrowser extends EventEmitter implements browser.Browser {
this._targets.set(event.targetInfo.targetId, target); this._targets.set(event.targetInfo.targetId, target);
if (target._isInitialized || await target._initializedPromise) if (target._isInitialized || await target._initializedPromise)
this.emit(Events.Browser.TargetCreated, target); this.emit(Events.CRBrowser.TargetCreated, target);
} }
async _targetDestroyed(event: { targetId: string; }) { async _targetDestroyed(event: { targetId: string; }) {
@ -170,7 +201,7 @@ export class CRBrowser extends EventEmitter implements browser.Browser {
this._targets.delete(event.targetId); this._targets.delete(event.targetId);
target._didClose(); target._didClose();
if (await target._initializedPromise) if (await target._initializedPromise)
this.emit(Events.Browser.TargetDestroyed, target); this.emit(Events.CRBrowser.TargetDestroyed, target);
} }
_targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) { _targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) {
@ -180,7 +211,7 @@ export class CRBrowser extends EventEmitter implements browser.Browser {
const wasInitialized = target._isInitialized; const wasInitialized = target._isInitialized;
target._targetInfoChanged(event.targetInfo); target._targetInfoChanged(event.targetInfo);
if (wasInitialized && previousURL !== target.url()) if (wasInitialized && previousURL !== target.url())
this.emit(Events.Browser.TargetChanged, target); this.emit(Events.CRBrowser.TargetChanged, target);
} }
async _closePage(page: Page) { async _closePage(page: Page) {
@ -204,15 +235,15 @@ export class CRBrowser extends EventEmitter implements browser.Browser {
return existingTarget; return existingTarget;
let resolve: (target: CRTarget) => void; let resolve: (target: CRTarget) => void;
const targetPromise = new Promise<CRTarget>(x => resolve = x); const targetPromise = new Promise<CRTarget>(x => resolve = x);
this.on(Events.Browser.TargetCreated, check); this.on(Events.CRBrowser.TargetCreated, check);
this.on(Events.Browser.TargetChanged, check); this.on(Events.CRBrowser.TargetChanged, check);
try { try {
if (!timeout) if (!timeout)
return await targetPromise; return await targetPromise;
return await helper.waitWithTimeout(targetPromise, 'target', timeout); return await helper.waitWithTimeout(targetPromise, 'target', timeout);
} finally { } finally {
this.removeListener(Events.Browser.TargetCreated, check); this.removeListener(Events.CRBrowser.TargetCreated, check);
this.removeListener(Events.Browser.TargetChanged, check); this.removeListener(Events.CRBrowser.TargetChanged, check);
} }
function check(target: CRTarget) { function check(target: CRTarget) {

View File

@ -16,13 +16,13 @@
*/ */
export const Events = { export const Events = {
Browser: { CRBrowser: {
TargetCreated: 'targetcreated', TargetCreated: 'targetcreated',
TargetDestroyed: 'targetdestroyed', TargetDestroyed: 'targetdestroyed',
TargetChanged: 'targetchanged', TargetChanged: 'targetchanged',
}, },
Workers: { CRWorkers: {
WorkerCreated: 'workercreated', WorkerCreated: 'workercreated',
WorkerDestroyed: 'workerdestroyed', WorkerDestroyed: 'workerdestroyed',
} }

View File

@ -1,61 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Protocol } from '../protocol';
import { CRSession } from '../crConnection';
export class CRPermissions {
private _client: CRSession;
private _browserContextId: string;
constructor(client: CRSession, browserContextId: string | null) {
this._client = client;
this._browserContextId = browserContextId;
}
async override(origin: string, permissions: string[]) {
const webPermissionToProtocol = new Map<string, Protocol.Browser.PermissionType>([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardSanitizedWrite'],
['payment-handler', 'paymentHandler'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._client.send('Browser.grantPermissions', {origin, browserContextId: this._browserContextId || undefined, permissions: filtered});
}
async clearOverrides() {
await this._client.send('Browser.resetPermissions', {browserContextId: this._browserContextId || undefined});
}
}

View File

@ -41,13 +41,13 @@ export class CRWorkers extends EventEmitter {
const session = CRConnection.fromSession(client).session(event.sessionId); const session = CRConnection.fromSession(client).session(event.sessionId);
const worker = new CRWorker(session, event.targetInfo.url, addToConsole, handleException); const worker = new CRWorker(session, event.targetInfo.url, addToConsole, handleException);
this._workers.set(event.sessionId, worker); this._workers.set(event.sessionId, worker);
this.emit(Events.Workers.WorkerCreated, worker); this.emit(Events.CRWorkers.WorkerCreated, worker);
}); });
client.on('Target.detachedFromTarget', event => { client.on('Target.detachedFromTarget', event => {
const worker = this._workers.get(event.sessionId); const worker = this._workers.get(event.sessionId);
if (!worker) if (!worker)
return; return;
this.emit(Events.Workers.WorkerDestroyed, worker); this.emit(Events.CRWorkers.WorkerDestroyed, worker);
this._workers.delete(event.sessionId); this._workers.delete(event.sessionId);
}); });
} }

View File

@ -1,49 +0,0 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FFConnection } from '../ffConnection';
export class FFPermissions {
private _connection: FFConnection;
private _browserContextId: string;
constructor(connection: FFConnection, browserContextId: string | null) {
this._connection = connection;
this._browserContextId = browserContextId;
}
async override(origin: string, permissions: Array<string>) {
const webPermissionToProtocol = new Map([
['geolocation', 'geo'],
['microphone', 'microphone'],
['camera', 'camera'],
['notifications', 'desktop-notifications'],
]);
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._browserContextId || undefined, permissions});
}
async clearOverrides() {
await this._connection.send('Browser.resetPermissions', {browserContextId: this._browserContextId || undefined});
}
}

View File

@ -2,6 +2,5 @@
// Licensed under the MIT license. // Licensed under the MIT license.
export { FFInterception as FirefoxInterception } from './features/ffInterception'; export { FFInterception as FirefoxInterception } from './features/ffInterception';
export { FFPermissions as FirefoxPermissions } from './features/ffPermissions';
export { FFBrowser as FirefoxBrowser } from './ffBrowser'; export { FFBrowser as FirefoxBrowser } from './ffBrowser';
export { FFPlaywright as Firefox } from './ffPlaywright'; export { FFPlaywright as Firefox } from './ffPlaywright';

View File

@ -15,19 +15,17 @@
* limitations under the License. * limitations under the License.
*/ */
import { EventEmitter } from 'events';
import { helper, RegisteredListener, assert } from '../helper';
import { FFConnection, ConnectionEvents, FFSessionEvents } from './ffConnection';
import { Events } from '../events';
import { FFPermissions } from './features/ffPermissions';
import { Page } from '../page';
import { FFFrameManager } from './ffFrameManager';
import * as browser from '../browser'; import * as browser from '../browser';
import * as network from '../network';
import { BrowserContext, BrowserContextOptions } from '../browserContext'; import { BrowserContext, BrowserContextOptions } from '../browserContext';
import { Events } from '../events';
import { assert, helper, RegisteredListener } from '../helper';
import * as network from '../network';
import { Page } from '../page';
import { ConnectionTransport } from '../transport'; import { ConnectionTransport } from '../transport';
import { ConnectionEvents, FFConnection, FFSessionEvents } from './ffConnection';
import { FFFrameManager } from './ffFrameManager';
export class FFBrowser extends EventEmitter implements browser.Browser { export class FFBrowser extends browser.Browser {
_connection: FFConnection; _connection: FFConnection;
_targets: Map<string, Target>; _targets: Map<string, Target>;
private _defaultContext: BrowserContext; private _defaultContext: BrowserContext;
@ -196,8 +194,28 @@ export class FFBrowser extends EventEmitter implements browser.Browser {
setCookies: 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 });
}, },
setPermissions: async (origin: string, permissions: string[]): Promise<void> => {
const webPermissionToProtocol = new Map([
['geolocation', 'geo'],
['microphone', 'microphone'],
['camera', 'camera'],
['notifications', 'desktop-notifications'],
]);
const filtered = 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: browserContextId || undefined, permissions: filtered});
},
clearPermissions: async () => {
await this._connection.send('Browser.resetPermissions', { browserContextId: browserContextId || undefined });
}
}, options); }, options);
(context as any).permissions = new FFPermissions(this._connection, browserContextId);
return context; return context;
} }
} }

View File

@ -27,7 +27,7 @@ import { Events } from '../events';
import { BrowserContext, BrowserContextOptions } from '../browserContext'; import { BrowserContext, BrowserContextOptions } from '../browserContext';
import { ConnectionTransport } from '../transport'; import { ConnectionTransport } from '../transport';
export class WKBrowser extends EventEmitter implements browser.Browser { export class WKBrowser extends browser.Browser {
readonly _connection: WKConnection; readonly _connection: WKConnection;
private _defaultContext: BrowserContext; private _defaultContext: BrowserContext;
private _contexts = new Map<string, BrowserContext>(); private _contexts = new Map<string, BrowserContext>();
@ -206,6 +206,14 @@ export class WKBrowser extends EventEmitter implements browser.Browser {
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 });
}, },
setPermissions: async (origin: string, permissions: string[]): Promise<void> => {
throw new Error('Permissions are not supported in WebKit');
},
clearPermissions: async () => {
throw new Error('Permissions are not supported in WebKit');
}
}, options); }, options);
return context; return context;
} }

View File

@ -24,7 +24,7 @@ module.exports.describe = function ({ testRunner, expect }) {
// It was removed from WebKit in https://webkit.org/b/126630 // It was removed from WebKit in https://webkit.org/b/126630
describe('Overrides.setGeolocation', function() { describe('Overrides.setGeolocation', function() {
it('should work', async({page, server, context}) => { it('should work', async({page, server, context}) => {
await context.permissions.override(server.PREFIX, ['geolocation']); await context.setPermissions(server.PREFIX, ['geolocation']);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await context.overrides.setGeolocation({longitude: 10, latitude: 10}); await context.overrides.setGeolocation({longitude: 10, latitude: 10});
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => { const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {

View File

@ -35,25 +35,25 @@ module.exports.describe = function({testRunner, expect, WEBKIT}) {
}); });
it.skip(WEBKIT)('should deny permission when not listed', async({page, server, context}) => { it.skip(WEBKIT)('should deny permission when not listed', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await context.permissions.override(server.EMPTY_PAGE, []); await context.setPermissions(server.EMPTY_PAGE, []);
expect(await getPermission(page, 'geolocation')).toBe('denied'); expect(await getPermission(page, 'geolocation')).toBe('denied');
}); });
it.skip(WEBKIT)('should fail when bad permission is given', async({page, server, context}) => { it.skip(WEBKIT)('should fail when bad permission is given', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
let error = {}; let error = {};
await context.permissions.override(server.EMPTY_PAGE, ['foo']).catch(e => error = e); await context.setPermissions(server.EMPTY_PAGE, ['foo']).catch(e => error = e);
expect(error.message).toBe('Unknown permission: foo'); expect(error.message).toBe('Unknown permission: foo');
}); });
it.skip(WEBKIT)('should grant permission when listed', async({page, server, context}) => { it.skip(WEBKIT)('should grant permission when listed', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await context.permissions.override(server.EMPTY_PAGE, ['geolocation']); await context.setPermissions(server.EMPTY_PAGE, ['geolocation']);
expect(await getPermission(page, 'geolocation')).toBe('granted'); expect(await getPermission(page, 'geolocation')).toBe('granted');
}); });
it.skip(WEBKIT)('should reset permissions', async({page, server, context}) => { it.skip(WEBKIT)('should reset permissions', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await context.permissions.override(server.EMPTY_PAGE, ['geolocation']); await context.setPermissions(server.EMPTY_PAGE, ['geolocation']);
expect(await getPermission(page, 'geolocation')).toBe('granted'); expect(await getPermission(page, 'geolocation')).toBe('granted');
await context.permissions.clearOverrides(); await context.clearPermissions();
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
}); });
it.skip(WEBKIT)('should trigger permission onchange', async({page, server, context}) => { it.skip(WEBKIT)('should trigger permission onchange', async({page, server, context}) => {
@ -68,11 +68,11 @@ module.exports.describe = function({testRunner, expect, WEBKIT}) {
}); });
}); });
expect(await page.evaluate(() => window['events'])).toEqual(['prompt']); expect(await page.evaluate(() => window['events'])).toEqual(['prompt']);
await context.permissions.override(server.EMPTY_PAGE, []); await context.setPermissions(server.EMPTY_PAGE, []);
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied']); expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied']);
await context.permissions.override(server.EMPTY_PAGE, ['geolocation']); await context.setPermissions(server.EMPTY_PAGE, ['geolocation']);
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted']); expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted']);
await context.permissions.clearOverrides(); await context.clearPermissions();
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted', 'prompt']); expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted', 'prompt']);
}); });
it.skip(WEBKIT)('should isolate permissions between browser contexs', async({page, server, context, newContext}) => { it.skip(WEBKIT)('should isolate permissions between browser contexs', async({page, server, context, newContext}) => {
@ -83,12 +83,12 @@ module.exports.describe = function({testRunner, expect, WEBKIT}) {
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
expect(await getPermission(otherPage, 'geolocation')).toBe('prompt'); expect(await getPermission(otherPage, 'geolocation')).toBe('prompt');
await context.permissions.override(server.EMPTY_PAGE, []); await context.setPermissions(server.EMPTY_PAGE, []);
await otherContext.permissions.override(server.EMPTY_PAGE, ['geolocation']); await otherContext.setPermissions(server.EMPTY_PAGE, ['geolocation']);
expect(await getPermission(page, 'geolocation')).toBe('denied'); expect(await getPermission(page, 'geolocation')).toBe('denied');
expect(await getPermission(otherPage, 'geolocation')).toBe('granted'); expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
await context.permissions.clearOverrides(); await context.clearPermissions();
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
expect(await getPermission(otherPage, 'geolocation')).toBe('granted'); expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
}); });

View File

@ -37,6 +37,12 @@ Documentation.Class = class {
constructor(name, membersArray, extendsName = null, comment = '') { constructor(name, membersArray, extendsName = null, comment = '') {
this.name = name; this.name = name;
this.membersArray = membersArray; this.membersArray = membersArray;
this.comment = comment;
this.extends = extendsName;
this.index();
}
index() {
/** @type {!Map<string, !Documentation.Member>} */ /** @type {!Map<string, !Documentation.Member>} */
this.members = new Map(); this.members = new Map();
/** @type {!Map<string, !Documentation.Member>} */ /** @type {!Map<string, !Documentation.Member>} */
@ -51,9 +57,8 @@ Documentation.Class = class {
this.events = new Map(); this.events = new Map();
/** @type {!Array<!Documentation.Member>} */ /** @type {!Array<!Documentation.Member>} */
this.eventsArray = []; this.eventsArray = [];
this.comment = comment;
this.extends = extendsName; for (const member of this.membersArray) {
for (const member of membersArray) {
this.members.set(member.name, member); this.members.set(member.name, member);
if (member.kind === 'method') { if (member.kind === 'method') {
this.methods.set(member.name, member); this.methods.set(member.name, member);

View File

@ -27,9 +27,9 @@ module.exports = { checkSources, expandPrefix };
function checkSources(sources) { function checkSources(sources) {
// special treatment for Events.js // special treatment for Events.js
const classEvents = new Map(); const classEvents = new Map();
const eventsSource = sources.find(source => source.name().startsWith('events.')); const eventsSources = sources.filter(source => source.name().startsWith('events.ts'));
if (eventsSource) { for (const eventsSource of eventsSources) {
const {Events} = eventsSource.filePath().endsWith('.js') ? require(eventsSource.filePath()) : require(path.join(eventsSource.filePath(), '..', '..', '..', 'lib', 'chromium', 'events.js')); const {Events} = require(eventsSource.filePath().endsWith('.js') ? eventsSource.filePath() : eventsSource.filePath().replace('/src/', '/lib/').replace('.ts', '.js'));
for (const [className, events] of Object.entries(Events)) for (const [className, events] of Object.entries(Events))
classEvents.set(className, Array.from(Object.values(events)).filter(e => typeof e === 'string').map(e => Documentation.Member.createEvent(e))); classEvents.set(className, Array.from(Object.values(events)).filter(e => typeof e === 'string').map(e => Documentation.Member.createEvent(e)));
} }

View File

@ -299,6 +299,23 @@ module.exports = async function(page, sources) {
errors.push(...outline.errors); errors.push(...outline.errors);
} }
const documentation = new Documentation(classes); const documentation = new Documentation(classes);
// Push base class documentation to derived classes.
for (const [name, clazz] of documentation.classes.entries()) {
if (!clazz.extends || clazz.extends === 'EventEmitter' || clazz.extends === 'Error')
continue;
const superClass = documentation.classes.get(clazz.extends);
if (!superClass) {
errors.push(`Undefined superclass: ${superClass} in ${name}`);
continue;
}
for (const memberName of clazz.members.keys()) {
if (superClass.members.has(memberName))
errors.push(`Member documentation overrides base: ${name}.${memberName} over ${clazz.extends}.${memberName}`);
}
clazz.membersArray = [...clazz.membersArray, ...superClass.membersArray];
clazz.index();
}
return { documentation, errors }; return { documentation, errors };
}; };