From 3dc14eddaf2902c62e442aaa9304452efbb7fac8 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 6 Apr 2020 19:49:33 -0700 Subject: [PATCH] fix(colorScheme): make light scheme default on all browsers (#1668) --- docs/api.md | 2 ++ package.json | 2 +- src/browserContext.ts | 1 + src/chromium/crPage.ts | 10 ++++---- src/firefox/ffBrowser.ts | 6 +++++ src/firefox/ffPage.ts | 7 +++--- src/page.ts | 4 ++-- src/webkit/wkPage.ts | 7 +++--- test/emulation.spec.js | 50 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 76 insertions(+), 13 deletions(-) diff --git a/docs/api.md b/docs/api.md index 6bd9f550a9..4fd4939627 100644 --- a/docs/api.md +++ b/docs/api.md @@ -215,6 +215,7 @@ Indicates that the browser is connected. - `httpCredentials` <[Object]> Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). - `username` <[string]> - `password` <[string]> + - `colorScheme` <"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. - returns: <[Promise]<[BrowserContext]>> Creates a new browser context. It won't share cookies/cache with other browser contexts. @@ -255,6 +256,7 @@ Creates a new browser context. It won't share cookies/cache with other browser c - `httpCredentials` <[Object]> Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). - `username` <[string]> - `password` <[string]> + - `colorScheme` <"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. - returns: <[Promise]<[Page]>> Creates a new page in a new browser context. Closing this page will close the context as well. diff --git a/package.json b/package.json index 27da27c184..645099c697 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "main": "index.js", "playwright": { "chromium_revision": "754895", - "firefox_revision": "1071", + "firefox_revision": "1072", "webkit_revision": "1188" }, "scripts": { diff --git a/src/browserContext.ts b/src/browserContext.ts index a270af4285..42750411cc 100644 --- a/src/browserContext.ts +++ b/src/browserContext.ts @@ -40,6 +40,7 @@ export type BrowserContextOptions = { deviceScaleFactor?: number, isMobile?: boolean, hasTouch?: boolean, + colorScheme?: types.ColorScheme, acceptDownloads?: boolean }; diff --git a/src/chromium/crPage.ts b/src/chromium/crPage.ts index c65c0f1d1d..359593e384 100644 --- a/src/chromium/crPage.ts +++ b/src/chromium/crPage.ts @@ -150,8 +150,8 @@ export class CRPage implements PageDelegate { await this._mainFrameSession._updateViewport(); } - async setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise { - await this._forAllFrameSessions(frame => frame._setEmulateMedia(mediaType, colorScheme)); + async updateEmulateMedia(): Promise { + await this._forAllFrameSessions(frame => frame._updateEmulateMedia()); } async updateRequestInterception(): Promise { @@ -422,6 +422,7 @@ class FrameSession { promises.push(this._updateRequestInterception()); promises.push(this._updateOffline()); promises.push(this._updateHttpCredentials()); + promises.push(this._updateEmulateMedia()); for (const binding of this._crPage._browserContext._pageBindings.values()) promises.push(this._initBinding(binding)); for (const source of this._crPage._browserContext._evaluateOnNewDocumentSources) @@ -687,9 +688,10 @@ class FrameSession { await Promise.all(promises); } - async _setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise { + async _updateEmulateMedia(): Promise { + const colorScheme = this._page._state.colorScheme || this._crPage._browserContext._options.colorScheme || 'light'; const features = colorScheme ? [{ name: 'prefers-color-scheme', value: colorScheme }] : []; - await this._client.send('Emulation.setEmulatedMedia', { media: mediaType || '', features }); + await this._client.send('Emulation.setEmulatedMedia', { media: this._page._state.mediaType || '', features }); } async _updateRequestInterception(): Promise { diff --git a/src/firefox/ffBrowser.ts b/src/firefox/ffBrowser.ts index 5df38ab58c..f14e69c3b7 100644 --- a/src/firefox/ffBrowser.ts +++ b/src/firefox/ffBrowser.ts @@ -164,6 +164,8 @@ export class FFBrowserContext extends BrowserContextBase { await this.setGeolocation(this._options.geolocation); if (this._options.offline) await this.setOffline(this._options.offline); + if (this._options.colorScheme) + await this._setColorScheme(this._options.colorScheme); } _ffPages(): FFPage[] { @@ -259,6 +261,10 @@ export class FFBrowserContext extends BrowserContextBase { await this._browser._connection.send('Browser.setOnlineOverride', { browserContextId: this._browserContextId || undefined, override: offline ? 'offline' : 'online' }); } + async _setColorScheme(colorScheme?: types.ColorScheme): Promise { + await this._browser._connection.send('Browser.setColorScheme', { browserContextId: this._browserContextId || undefined, colorScheme }); + } + async setHTTPCredentials(httpCredentials: types.Credentials | null): Promise { this._options.httpCredentials = httpCredentials || undefined; await this._browser._connection.send('Browser.setHTTPCredentials', { browserContextId: this._browserContextId || undefined, credentials: httpCredentials }); diff --git a/src/firefox/ffPage.ts b/src/firefox/ffPage.ts index 4bca44a1bc..0474d6ea3d 100644 --- a/src/firefox/ffPage.ts +++ b/src/firefox/ffPage.ts @@ -284,10 +284,11 @@ export class FFPage implements PageDelegate { }); } - async setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise { + async updateEmulateMedia(): Promise { + const colorScheme = this._page._state.colorScheme || this._browserContext._options.colorScheme || 'light'; await this._session.send('Page.setEmulatedMedia', { - type: mediaType === null ? undefined : mediaType, - colorScheme: colorScheme === null ? undefined : colorScheme + type: this._page._state.mediaType === null ? undefined : this._page._state.mediaType, + colorScheme }); } diff --git a/src/page.ts b/src/page.ts index 27851f0ec2..75a7f9a054 100644 --- a/src/page.ts +++ b/src/page.ts @@ -48,7 +48,7 @@ export interface PageDelegate { updateExtraHTTPHeaders(): Promise; setViewportSize(viewportSize: types.Size): Promise; - setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise; + updateEmulateMedia(): Promise; updateRequestInterception(): Promise; setFileChooserIntercepted(enabled: boolean): Promise; @@ -357,7 +357,7 @@ export class Page extends ExtendedEventEmitter { this._state.mediaType = options.media; if (options.colorScheme !== undefined) this._state.colorScheme = options.colorScheme; - await this._delegate.setEmulateMedia(this._state.mediaType, this._state.colorScheme); + await this._delegate.updateEmulateMedia(); } async setViewportSize(viewportSize: types.Size) { diff --git a/src/webkit/wkPage.ts b/src/webkit/wkPage.ts index d5b1a938e6..a3cfd59d2b 100644 --- a/src/webkit/wkPage.ts +++ b/src/webkit/wkPage.ts @@ -162,7 +162,7 @@ export class WKPage implements PageDelegate { height: this._page._state.viewportSize.height, })); } - + promises.push(this.updateEmulateMedia()); promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() })); if (contextOptions.offline) promises.push(session.send('Network.setEmulateOfflineState', { offline: true })); @@ -492,8 +492,9 @@ export class WKPage implements PageDelegate { return headers; } - async setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise { - await this._forAllSessions(session => WKPage._setEmulateMedia(session, mediaType, colorScheme)); + async updateEmulateMedia(): Promise { + const colorScheme = this._page._state.colorScheme || this._browserContext._options.colorScheme || 'light'; + await this._forAllSessions(session => WKPage._setEmulateMedia(session, this._page._state.mediaType, colorScheme)); } async setViewportSize(viewportSize: types.Size): Promise { diff --git a/test/emulation.spec.js b/test/emulation.spec.js index 8aa3e746cb..668d41852e 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -15,6 +15,8 @@ * limitations under the License. */ +const utils = require('./utils'); + /** * @type {PageTestSuite} */ @@ -236,6 +238,21 @@ module.exports.describe = function({testRunner, expect, playwright, headless, FF expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(true); } }); + it('should default to light', async({page, server}) => { + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false); + + await page.emulateMedia({ colorScheme: 'dark' }); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false); + + await page.emulateMedia({ colorScheme: null }); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false); + }); it('should throw in case of bad argument', async({page, server}) => { let error = null; await page.emulateMedia({ colorScheme: 'bad' }).catch(e => error = e); @@ -253,6 +270,39 @@ module.exports.describe = function({testRunner, expect, playwright, headless, FF await navigated; expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true); }); + it('should work in popup', async({browser, server}) => { + { + const context = await browser.newContext({ colorScheme: 'dark' }); + const page = await context.newPage(); + await page.goto(server.EMPTY_PAGE); + const [popup] = await Promise.all([ + page.waitForEvent('popup'), + page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE), + ]); + expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false); + expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true); + await context.close(); + } + { + const page = await browser.newPage({ colorScheme: 'light' }); + await page.goto(server.EMPTY_PAGE); + const [popup] = await Promise.all([ + page.waitForEvent('popup'), + page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE), + ]); + expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); + expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false); + await page.close(); + } + }); + it('should work in cross-process iframe', async({browser, server}) => { + const page = await browser.newPage({ colorScheme: 'dark' }); + await page.goto(server.EMPTY_PAGE); + await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html'); + const frame = page.frames()[1]; + expect(await frame.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true); + await page.close(); + }); }); describe('BrowserContext({timezoneId})', function() {