diff --git a/docs/api.md b/docs/api.md
index a3ec8be2df..7658d8ae7a 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -144,6 +144,7 @@
* [page.$x(expression)](#pagexexpression)
* [page.addScriptTag(options)](#pageaddscripttagoptions)
* [page.addStyleTag(options)](#pageaddstyletagoptions)
+ * [page.authenticate(credentials)](#pageauthenticatecredentials)
* [page.browserContext()](#pagebrowsercontext)
* [page.click(selector[, options])](#pageclickselector-options)
* [page.close([options])](#pagecloseoptions)
@@ -173,6 +174,7 @@
* [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout)
* [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout)
* [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
+ * [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
* [page.setRequestInterception(enabled)](#pagesetrequestinterceptionenabled)
* [page.setViewport(viewport)](#pagesetviewportviewport)
* [page.title()](#pagetitle)
@@ -233,9 +235,6 @@
* [chromiumCoverage.startJSCoverage([options])](#chromiumcoveragestartjscoverageoptions)
* [chromiumCoverage.stopCSSCoverage()](#chromiumcoveragestopcsscoverage)
* [chromiumCoverage.stopJSCoverage()](#chromiumcoveragestopjscoverage)
-- [class: ChromiumInterception](#class-chromiuminterception)
- * [chromiumInterception.authenticate(credentials)](#chromiuminterceptionauthenticatecredentials)
- * [chromiumInterception.setOfflineMode(enabled)](#chromiuminterceptionsetofflinemodeenabled)
- [class: ChromiumOverrides](#class-chromiumoverrides)
* [chromiumOverrides.setGeolocation(options)](#chromiumoverridessetgeolocationoptions)
- [class: ChromiumPlaywright](#class-chromiumplaywright)
@@ -1916,6 +1915,16 @@ Adds a `` tag into the page with the desired url or a `
+ - `username` <[string]>
+ - `password` <[string]>
+- returns: <[Promise]>
+
+Provide credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
+
+To disable authentication, pass `null`.
+
#### page.browserContext()
- returns: <[BrowserContext]>
@@ -2402,6 +2411,10 @@ The extra HTTP headers will be sent with every request the page initiates.
> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
+#### page.setOfflineMode(enabled)
+- `enabled` <[boolean]> When `true`, enables offline mode for the page.
+- returns: <[Promise]>
+
#### page.setRequestInterception(enabled)
- `enabled` <[boolean]> Whether to enable request interception.
- returns: <[Promise]>
@@ -3134,22 +3147,6 @@ _To output coverage in a form consumable by [Istanbul](https://github.com/istanb
> **NOTE** JavaScript Coverage doesn't include anonymous scripts by default. However, scripts with sourceURLs are
reported.
-### class: ChromiumInterception
-
-#### chromiumInterception.authenticate(credentials)
-- `credentials` [Object]>
- - `username` <[string]>
- - `password` <[string]>
-- returns: <[Promise]>
-
-Provide credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
-
-To disable authentication, pass `null`.
-
-#### chromiumInterception.setOfflineMode(enabled)
-- `enabled` <[boolean]> When `true`, enables offline mode for the page.
-- returns: <[Promise]>
-
### class: ChromiumOverrides
#### chromiumOverrides.setGeolocation(options)
diff --git a/package.json b/package.json
index 15af0df881..17f31f7b24 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"playwright": {
"chromium_revision": "724623",
"firefox_revision": "1008",
- "webkit_revision": "1053"
+ "webkit_revision": "1055"
},
"scripts": {
"unit": "node test/test.js",
diff --git a/src/chromium/crApi.ts b/src/chromium/crApi.ts
index 7732e2325f..f9647824ad 100644
--- a/src/chromium/crApi.ts
+++ b/src/chromium/crApi.ts
@@ -8,6 +8,5 @@ export { CRPlaywright as ChromiumPlaywright } from './crPlaywright';
export { CRTarget as ChromiumTarget } from './crTarget';
export { CRAccessibility as ChromiumAccessibility } from './features/crAccessibility';
export { CRCoverage as ChromiumCoverage } from './features/crCoverage';
-export { CRInterception as ChromiumInterception } from './features/crInterception';
export { CROverrides as ChromiumOverrides } from './features/crOverrides';
export { CRWorker as ChromiumWorker } from './features/crWorkers';
diff --git a/src/chromium/crNetworkManager.ts b/src/chromium/crNetworkManager.ts
index a9f85507c3..94a4a2bcdf 100644
--- a/src/chromium/crNetworkManager.ts
+++ b/src/chromium/crNetworkManager.ts
@@ -21,6 +21,7 @@ import { assert, debugError, helper, RegisteredListener } from '../helper';
import { Protocol } from './protocol';
import * as network from '../network';
import * as frames from '../frames';
+import { Credentials } from '../types';
export class CRNetworkManager {
private _client: CRSession;
@@ -58,14 +59,12 @@ export class CRNetworkManager {
helper.removeEventListeners(this._eventListeners);
}
- async authenticate(credentials: { username: string; password: string; } | null) {
+ async authenticate(credentials: Credentials | null) {
this._credentials = credentials;
await this._updateProtocolRequestInterception();
}
async setOfflineMode(value: boolean) {
- if (this._offline === value)
- return;
this._offline = value;
await this._client.send('Network.emulateNetworkConditions', {
offline: this._offline,
diff --git a/src/chromium/crPage.ts b/src/chromium/crPage.ts
index 27112ceac3..33dec12721 100644
--- a/src/chromium/crPage.ts
+++ b/src/chromium/crPage.ts
@@ -33,7 +33,6 @@ import { CRAccessibility } from './features/crAccessibility';
import { CRCoverage } from './features/crCoverage';
import { CRPDF, PDFOptions } from './features/crPdf';
import { CRWorkers, CRWorker } from './features/crWorkers';
-import { CRInterception } from './features/crInterception';
import { CRBrowser } from './crBrowser';
import { BrowserContext } from '../browserContext';
import * as types from '../types';
@@ -302,6 +301,14 @@ export class CRPage implements PageDelegate {
await this._networkManager.setRequestInterception(enabled);
}
+ async setOfflineMode(value: boolean) {
+ await this._networkManager.setOfflineMode(value);
+ }
+
+ async authenticate(credentials: types.Credentials | null) {
+ await this._networkManager.authenticate(credentials);
+ }
+
async reload(): Promise {
await this._client.send('Page.reload');
}
@@ -456,7 +463,6 @@ export class CRPage implements PageDelegate {
export class ChromiumPage extends Page {
readonly accessibility: CRAccessibility;
readonly coverage: CRCoverage;
- readonly interception: CRInterception;
private _pdf: CRPDF;
private _workers: CRWorkers;
_networkManager: CRNetworkManager;
@@ -468,7 +474,6 @@ export class ChromiumPage extends Page {
this._pdf = new CRPDF(client);
this._workers = new CRWorkers(client, this, this._addConsoleMessage.bind(this), error => this.emit(Events.Page.PageError, error));
this._networkManager = new CRNetworkManager(client, this);
- this.interception = new CRInterception(this._networkManager);
}
async pdf(options?: PDFOptions): Promise {
diff --git a/src/chromium/features/crInterception.ts b/src/chromium/features/crInterception.ts
deleted file mode 100644
index 2731bf1d69..0000000000
--- a/src/chromium/features/crInterception.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import { CRNetworkManager } from '../crNetworkManager';
-
-export class CRInterception {
- private _networkManager: CRNetworkManager;
-
- constructor(networkManager: CRNetworkManager) {
- this._networkManager = networkManager;
- }
-
- setOfflineMode(enabled: boolean) {
- return this._networkManager.setOfflineMode(enabled);
- }
-
- async authenticate(credentials: { username: string; password: string; } | null) {
- return this._networkManager.authenticate(credentials);
- }
-}
diff --git a/src/firefox/ffPage.ts b/src/firefox/ffPage.ts
index 9db8f0252d..bc8330b545 100644
--- a/src/firefox/ffPage.ts
+++ b/src/firefox/ffPage.ts
@@ -216,6 +216,14 @@ export class FFPage implements PageDelegate {
await this._networkManager.setRequestInterception(enabled);
}
+ async setOfflineMode(enabled: boolean): Promise {
+ throw new Error('Offline mode not implemented in Firefox');
+ }
+
+ async authenticate(credentials: types.Credentials): Promise {
+ throw new Error('Offline mode not implemented in Firefox');
+ }
+
async reload(): Promise {
await this._session.send('Page.reload', { frameId: this._page.mainFrame()._id });
}
diff --git a/src/page.ts b/src/page.ts
index 8f9ab144eb..55c2ec00d9 100644
--- a/src/page.ts
+++ b/src/page.ts
@@ -49,6 +49,8 @@ export interface PageDelegate {
setEmulateMedia(mediaType: input.MediaType | null, colorScheme: input.ColorScheme | null): Promise;
setCacheEnabled(enabled: boolean): Promise;
setRequestInterception(enabled: boolean): Promise;
+ setOfflineMode(enabled: boolean): Promise;
+ authenticate(credentials: types.Credentials | null): Promise;
getBoundingBoxForScreenshot(handle: dom.ElementHandle): Promise;
canScreenshotOutsideViewport(): boolean;
@@ -73,6 +75,8 @@ type PageState = {
extraHTTPHeaders: network.Headers | null;
cacheEnabled: boolean | null;
interceptNetwork: boolean | null;
+ offlineMode: boolean | null;
+ credentials: types.Credentials | null;
};
export type FileChooser = {
@@ -109,7 +113,9 @@ export class Page extends EventEmitter {
colorScheme: browserContext._options.colorScheme || null,
extraHTTPHeaders: null,
cacheEnabled: null,
- interceptNetwork: null
+ interceptNetwork: null,
+ offlineMode: null,
+ credentials: null
};
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
this.mouse = new input.Mouse(delegate.rawMouse, this.keyboard);
@@ -401,6 +407,18 @@ export class Page extends EventEmitter {
await this._delegate.setRequestInterception(enabled);
}
+ async setOfflineMode(enabled: boolean) {
+ if (this._state.offlineMode === enabled)
+ return;
+ this._state.offlineMode = enabled;
+ await this._delegate.setOfflineMode(enabled);
+ }
+
+ async authenticate(credentials: types.Credentials | null) {
+ this._state.credentials = credentials;
+ await this._delegate.authenticate(credentials);
+ }
+
async screenshot(options?: types.ScreenshotOptions): Promise {
return this._screenshotter.screenshotPage(options);
}
diff --git a/src/types.ts b/src/types.ts
index 53ebcb15b3..d6c94d714e 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -51,3 +51,8 @@ export type Viewport = {
};
export type URLMatch = string | RegExp | ((url: kurl.URL) => boolean);
+
+export type Credentials = {
+ username: string;
+ password: string;
+}
diff --git a/src/webkit/wkNetworkManager.ts b/src/webkit/wkNetworkManager.ts
index c28c58920f..707904125c 100644
--- a/src/webkit/wkNetworkManager.ts
+++ b/src/webkit/wkNetworkManager.ts
@@ -21,6 +21,7 @@ import { helper, RegisteredListener, assert } from '../helper';
import { Protocol } from './protocol';
import * as network from '../network';
import * as frames from '../frames';
+import * as types from '../types';
export class WKNetworkManager {
private _session: WKTargetSession;
@@ -45,11 +46,15 @@ export class WKNetworkManager {
];
}
- async initializeSession(session: WKTargetSession, enableInterception: boolean) {
+ async initializeSession(session: WKTargetSession, interceptNetwork: boolean | null, offlineMode: boolean | null, credentials: types.Credentials | null) {
const promises = [];
promises.push(session.send('Network.enable'));
- if (enableInterception)
+ if (interceptNetwork)
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
+ if (offlineMode)
+ promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
+ if (credentials)
+ promises.push(session.send('Emulation.setAuthCredentials', { ...credentials }));
await Promise.all(promises);
}
@@ -151,12 +156,12 @@ export class WKNetworkManager {
this._page._frameManager.requestFailed(request.request, event.errorText.includes('cancelled'));
}
- authenticate(credentials: { username: string; password: string; }) {
- throw new Error('Not implemented');
+ async authenticate(credentials: types.Credentials | null) {
+ await this._session.send('Emulation.setAuthCredentials', { ...(credentials || {}) });
}
- setOfflineMode(enabled: boolean) {
- throw new Error('Not implemented');
+ async setOfflineMode(value: boolean): Promise {
+ await this._session.send('Network.setEmulateOfflineState', { offline: value });
}
}
diff --git a/src/webkit/wkPage.ts b/src/webkit/wkPage.ts
index 952b3e94b6..ddc167bcc6 100644
--- a/src/webkit/wkPage.ts
+++ b/src/webkit/wkPage.ts
@@ -85,7 +85,7 @@ export class WKPage implements PageDelegate {
session.send('Runtime.enable').then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
session.send('Console.enable'),
session.send('Page.setInterceptFileChooserDialog', { enabled: true }),
- this._networkManager.initializeSession(session, this._page._state.interceptNetwork),
+ this._networkManager.initializeSession(session, this._page._state.interceptNetwork, this._page._state.offlineMode, this._page._state.credentials),
];
if (!session.isProvisional()) {
// FIXME: move dialog agent to web process.
@@ -309,6 +309,14 @@ export class WKPage implements PageDelegate {
return this._networkManager.setRequestInterception(enabled);
}
+ async setOfflineMode(value: boolean) {
+ await this._networkManager.setOfflineMode(value);
+ }
+
+ async authenticate(credentials: types.Credentials | null) {
+ await this._networkManager.authenticate(credentials);
+ }
+
async reload(): Promise {
await this._session.send('Page.reload');
}
diff --git a/test/interception.spec.js b/test/interception.spec.js
index 10109ae4e3..8aee868cc1 100644
--- a/test/interception.spec.js
+++ b/test/interception.spec.js
@@ -495,12 +495,12 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
});
});
- describe.skip(FFOX || WEBKIT)('Interception.authenticate', function() {
+ describe.skip(FFOX)('Interception.authenticate', function() {
it('should work', async({page, server}) => {
server.setAuth('/empty.html', 'user', 'pass');
let response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(401);
- await page.interception.authenticate({
+ await page.authenticate({
username: 'user',
password: 'pass'
});
@@ -510,7 +510,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
it('should fail if wrong credentials', async({page, server}) => {
// Use unique user/password since Chrome caches credentials per origin.
server.setAuth('/empty.html', 'user2', 'pass2');
- await page.interception.authenticate({
+ await page.authenticate({
username: 'foo',
password: 'bar'
});
@@ -520,34 +520,34 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
it('should allow disable authentication', async({page, server}) => {
// Use unique user/password since Chrome caches credentials per origin.
server.setAuth('/empty.html', 'user3', 'pass3');
- await page.interception.authenticate({
+ await page.authenticate({
username: 'user3',
password: 'pass3'
});
let response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(200);
- await page.interception.authenticate(null);
+ await page.authenticate(null);
// Navigate to a different origin to bust Chrome's credential caching.
response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(response.status()).toBe(401);
});
});
- describe.skip(FFOX || WEBKIT)('Interception.setOfflineMode', function() {
+ describe.skip(FFOX)('Interception.setOfflineMode', function() {
it('should work', async({page, server}) => {
- await page.interception.setOfflineMode(true);
+ await page.setOfflineMode(true);
let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy();
- await page.interception.setOfflineMode(false);
- const response = await page.reload();
+ await page.setOfflineMode(false);
+ const response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(200);
});
it('should emulate navigator.onLine', async({page, server}) => {
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
- await page.interception.setOfflineMode(true);
+ await page.setOfflineMode(true);
expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
- await page.interception.setOfflineMode(false);
+ await page.setOfflineMode(false);
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
});
});
diff --git a/test/screenshot.spec.js b/test/screenshot.spec.js
index 75e37ab85c..7e3664018b 100644
--- a/test/screenshot.spec.js
+++ b/test/screenshot.spec.js
@@ -201,7 +201,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROME, W
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
});
// Fails on GTK due to async setViewport.
- it.skip(WEBKIT)('should capture full element when larger than viewport', async({page, server}) => {
+ it('should capture full element when larger than viewport', async({page, server}) => {
await page.setViewport({width: 500, height: 500});
await page.setContent(`