From 7d2226e05e593109e88ab83ae80995ef7216c464 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Mon, 22 May 2017 21:10:10 +0300 Subject: [PATCH 1/7] Let Chromium manage `document.visibilityState` and `document.hidden` Chromium already includes the necessary plumbing to manage the visibility properties and `visibilitychange` event so this gets rid of most of our custom logic for `BrowserWindow` and `BrowserView`. Note that `webview` remains unchanged and is still affected by the issues listed below. User facing changes: - The `document` visibility properties and `visibilitychange` event are now also updated/fired in response to occlusion changes on macOS. In other words, `document.visibilityState` will now be `hidden` on macOS if the window is occluded by another window. - Previously, `visibilitychange` was also fired by *both* Electron and Chromium in some cases (e.g. when hiding the window). Now it is only fired by Chromium so you no longer get duplicate events. - The visiblity state of `BrowserWindow`s created with `{ show: false }` is now initially `visible` until the window is shown and hidden. - The visibility state of `BrowserWindow`s with `backgroundThrottling` disabled is now permanently `visible`. This should also fix #6860 (but not for `webview`). --- atom/browser/web_contents_preferences.cc | 23 +++-- docs/api/browser-window.md | 23 ++++- docs/api/webview-tag.md | 15 ++-- lib/browser/api/browser-window.js | 1 - lib/browser/guest-view-manager.js | 2 +- lib/renderer/window-setup.js | 48 +++++----- spec/api-browser-window-spec.js | 109 ++++++++++++++++++++++- spec/chromium-spec.js | 29 ------ spec/fixtures/api/isolated.html | 4 +- spec/fixtures/pages/document-hidden.html | 7 -- spec/webview-spec.js | 4 +- 11 files changed, 175 insertions(+), 90 deletions(-) delete mode 100644 spec/fixtures/pages/document-hidden.html diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 6c706199fb9f..30213b098fde 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -186,24 +186,21 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( command_line->AppendSwitchASCII(::switches::kDisableBlinkFeatures, disable_blink_features); - // The initial visibility state. - NativeWindow* window = NativeWindow::FromWebContents(web_contents); - - // Use embedder window for webviews - if (guest_instance_id && !window) { + if (guest_instance_id) { + // Webview `document.visibilityState` tracks window visibility so we need + // to let it know if the window happens to be hidden right now. auto manager = WebViewManager::GetWebViewManager(web_contents); if (manager) { auto embedder = manager->GetEmbedder(guest_instance_id); - if (embedder) - window = NativeWindow::FromWebContents(embedder); + if (embedder) { + auto window = NativeWindow::FromWebContents(embedder); + const bool visible = window->IsVisible() && !window->IsMinimized(); + if (!visible) { + command_line->AppendSwitch(switches::kHiddenPage); + } + } } } - - if (window) { - bool visible = window->IsVisible() && !window->IsMinimized(); - if (!visible) // Default state is visible. - command_line->AppendSwitch(switches::kHiddenPage); - } } bool WebContentsPreferences::IsPreferenceEnabled( diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 69407ec2a712..468cdd292575 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -97,6 +97,25 @@ child.once('ready-to-show', () => { }) ``` +### Page visibility + +The [Page Visibility API][page-visibility-api] works as follows: + +* On all platforms, the visibility state tracks whether the window is + hidden/minimized or not. +* Additionally, on macOS, the visibility state also tracks the window + occlusion state. If the window is occluded (i.e. fully covered) by another + window, the visibility state will be `hidden`. On other platforms, the + visibility state will be `hidden` only when the window is minimized or + explicitly hidden with `win.hide()`. +* If a `BrowserWindow` is created with `show: false`, the initial visibility + state will be `visible` despite the window actually being hidden. +* If `backgroundThrottling` is disabled, the visibility state will remain + `visible` even if the window is minimized, occluded, or hidden. + +It is recommended that you pause expensive operations when the visibility +state is `hidden` in order to minimize power consumption. + ### Platform notices * On macOS modal windows will be displayed as sheets attached to the parent window. @@ -294,7 +313,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. * `minimumFontSize` Integer (optional) - Defaults to `0`. * `defaultEncoding` String (optional) - Defaults to `ISO-8859-1`. * `backgroundThrottling` Boolean (optional) - Whether to throttle animations and timers - when the page becomes background. Defaults to `true`. + when the page becomes background. This also affects the + [Page Visibility API][#page-visibility]. Defaults to `true`. * `offscreen` Boolean (optional) - Whether to enable offscreen rendering for the browser window. Defaults to `false`. See the [offscreen rendering tutorial](../tutorial/offscreen-rendering.md) for @@ -1326,6 +1346,7 @@ removed in future Electron releases. removed in future Electron releases. [blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5?l=62 +[page-visibility-api]: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API [quick-look]: https://en.wikipedia.org/wiki/Quick_Look [vibrancy-docs]: https://developer.apple.com/reference/appkit/nsvisualeffectview?language=objc [window-levels]: https://developer.apple.com/reference/appkit/nswindow/1664726-window_levels diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index b13011fc2a72..0fcb0f8ad50b 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -60,12 +60,11 @@ container when used with traditional and flexbox layouts (since v0.36.11). Pleas do not overwrite the default `display:flex;` CSS property, unless specifying `display:inline-flex;` for inline layout. -`webview` has issues being hidden using the `hidden` attribute or using `display: none;`. -It can cause unusual rendering behaviour within its child `browserplugin` object -and the web page is reloaded, when the `webview` is un-hidden, as opposed to just -becoming visible again. The recommended approach is to hide the `webview` using -CSS by zeroing the `width` & `height` and allowing the element to shrink to the 0px -dimensions via `flex`. +`webview` has issues being hidden using the `hidden` attribute or using +`display: none;`. It can cause unusual rendering behaviour within its child +`browserplugin` object and the web page is reloaded when the `webview` is +un-hidden. The recommended approach is to hide the `webview` using +`visibility: hidden`. ```html ``` diff --git a/lib/browser/api/browser-window.js b/lib/browser/api/browser-window.js index c6931c854a06..2226f7f03aa2 100644 --- a/lib/browser/api/browser-window.js +++ b/lib/browser/api/browser-window.js @@ -109,7 +109,6 @@ BrowserWindow.prototype._init = function () { if (isVisible !== newState) { isVisible = newState const visibilityState = isVisible ? 'visible' : 'hidden' - this.webContents.send('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', visibilityState) this.webContents.emit('-window-visibility-change', visibilityState) } } diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index 45244b1b4466..0bf1f6db7e81 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -281,7 +281,7 @@ const watchEmbedder = function (embedder) { for (const guestInstanceId of Object.keys(guestInstances)) { const guestInstance = guestInstances[guestInstanceId] if (guestInstance.embedder === embedder) { - guestInstance.guest.send('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', visibilityState) + guestInstance.guest.send('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState) } } } diff --git a/lib/renderer/window-setup.js b/lib/renderer/window-setup.js index b1187e96f18f..31767a290d4d 100644 --- a/lib/renderer/window-setup.js +++ b/lib/renderer/window-setup.js @@ -175,27 +175,35 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNative } }) - // The initial visibilityState. - let cachedVisibilityState = hiddenPage ? 'hidden' : 'visible' + if (guestInstanceId != null) { + // Webview `document.visibilityState` tracks window visibility (and ignores + // the actual element visibility) for backwards compatibility. + // See discussion in #9178. + // + // Note that this results in duplicate visibilitychange events (since + // Chromium also fires them) and potentially incorrect visibility change. + // We should reconsider this decision for Electron 2.0. + let cachedVisibilityState = hiddenPage ? 'hidden' : 'visible' - // Subscribe to visibilityState changes. - ipcRenderer.on('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', function (event, visibilityState) { - if (cachedVisibilityState !== visibilityState) { - cachedVisibilityState = visibilityState - document.dispatchEvent(new Event('visibilitychange')) - } - }) + // Subscribe to visibilityState changes. + ipcRenderer.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (event, visibilityState) { + if (cachedVisibilityState !== visibilityState) { + cachedVisibilityState = visibilityState + document.dispatchEvent(new Event('visibilitychange')) + } + }) - // Make document.hidden and document.visibilityState return the correct value. - defineProperty(document, 'hidden', { - get: function () { - return cachedVisibilityState !== 'visible' - } - }) + // Make document.hidden and document.visibilityState return the correct value. + defineProperty(document, 'hidden', { + get: function () { + return cachedVisibilityState !== 'visible' + } + }) - defineProperty(document, 'visibilityState', { - get: function () { - return cachedVisibilityState - } - }) + defineProperty(document, 'visibilityState', { + get: function () { + return cachedVisibilityState + } + }) + } } diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index e6e4661f7bbf..81183ffaa679 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1412,6 +1412,111 @@ describe('BrowserWindow module', function () { }) }) + describe('document.visibilityState', function () { + afterEach(function () { + ipcMain.removeAllListeners('pong') + }) + + function onNextVisibleEvent (callback) { + ipcMain.on('pong', function listener (event, visibilityState, hidden) { + if (visibilityState === 'visible' && hidden === false) { + ipcMain.removeListener('pong', listener) + callback() + } + }) + } + + function onNextHiddenEvent (callback) { + ipcMain.on('pong', function listener (event, visibilityState, hidden) { + if (visibilityState === 'hidden' && hidden === true) { + ipcMain.removeListener('pong', listener) + callback() + } + }) + } + + it('visibilityState is initially visible despite window being hidden', function (done) { + w.destroy() + w = new BrowserWindow({ show: false, width: 100, height: 100 }) + + let readyToShow = false + w.on('ready-to-show', function () { + readyToShow = true + }) + + ipcMain.once('pong', function (event, visibilityState, hidden) { + assert.ok(!readyToShow) + assert.equal(visibilityState, 'visible') + assert.equal(hidden, false) + + ipcMain.once('pong', function (event, visibilityState, hidden) { + assert.ok(false) + }) + + setTimeout(done, 1000) + w.show() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) + }) + + it('visibilityState changes when window is shown and hidden', function (done) { + w.destroy() + w = new BrowserWindow({ + width: 100, + height: 100 + }) + + onNextVisibleEvent(() => { + onNextHiddenEvent(() => { + onNextVisibleEvent(() => { + onNextHiddenEvent(done) + w.minimize() + }) + w.show() + w.focus() + }) + w.hide() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) + }) + + it('visibilityState remains visible if backgroundThrottling is disabled', function (done) { + w.destroy() + w = new BrowserWindow({ + show: false, + width: 100, + height: 100, + webPreferences: { + backgroundThrottling: false + } + }) + + onNextVisibleEvent(() => { + onNextVisibleEvent(() => { + done(new Error('Unexpected visibility change event to visible')) + }) + onNextHiddenEvent(() => { + done(new Error('Unexpected visibility change event to hidden')) + }) + + w.once('show', () => { + w.once('hide', () => { + w.once('show', () => { + done() + }) + w.show() + }) + w.hide() + }) + w.show() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) + }) + }) + describe('new-window event', function () { if (isCI && process.platform === 'darwin') { return @@ -2318,9 +2423,7 @@ describe('BrowserWindow module', function () { typeofArrayPush: 'number', typeofFunctionApply: 'boolean', typeofPreloadExecuteJavaScriptProperty: 'number', - typeofOpenedWindow: 'object', - documentHidden: true, - documentVisibilityState: 'hidden' + typeofOpenedWindow: 'object' } } diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index f33fb284a47b..0e06668cdc9a 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -48,35 +48,6 @@ describe('chromium feature', function () { }) }) - describe('document.hidden', function () { - var url = 'file://' + fixtures + '/pages/document-hidden.html' - - it('is set correctly when window is not shown', function (done) { - w = new BrowserWindow({ - show: false - }) - w.webContents.once('ipc-message', function (event, args) { - assert.deepEqual(args, ['hidden', true]) - done() - }) - w.loadURL(url) - }) - - it('is set correctly when window is inactive', function (done) { - if (isCI && process.platform === 'win32') return done() - - w = new BrowserWindow({ - show: false - }) - w.webContents.once('ipc-message', function (event, args) { - assert.deepEqual(args, ['hidden', false]) - done() - }) - w.showInactive() - w.loadURL(url) - }) - }) - xdescribe('navigator.webkitGetUserMedia', function () { it('calls its callbacks', function (done) { navigator.webkitGetUserMedia({ diff --git a/spec/fixtures/api/isolated.html b/spec/fixtures/api/isolated.html index 25269b35e984..562bf01b7c10 100644 --- a/spec/fixtures/api/isolated.html +++ b/spec/fixtures/api/isolated.html @@ -19,9 +19,7 @@ typeofArrayPush: typeof Array.prototype.push, typeofFunctionApply: typeof Function.prototype.apply, typeofPreloadExecuteJavaScriptProperty: typeof window.preloadExecuteJavaScriptProperty, - typeofOpenedWindow: typeof opened, - documentHidden: document.hidden, - documentVisibilityState: document.visibilityState + typeofOpenedWindow: typeof opened }, '*') diff --git a/spec/fixtures/pages/document-hidden.html b/spec/fixtures/pages/document-hidden.html deleted file mode 100644 index 8b157e05549e..000000000000 --- a/spec/fixtures/pages/document-hidden.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 51600d4711d3..b4bf7ddad7de 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -485,9 +485,7 @@ describe(' tag', function () { typeofArrayPush: 'number', typeofFunctionApply: 'boolean', typeofPreloadExecuteJavaScriptProperty: 'number', - typeofOpenedWindow: 'object', - documentHidden: isCI, - documentVisibilityState: isCI ? 'hidden' : 'visible' + typeofOpenedWindow: 'object' } }) done() From d2d28c42f17342c956d586cffaa124c48771bcb3 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 6 Jun 2017 15:19:29 -0700 Subject: [PATCH 2/7] Remove unused constant --- spec/webview-spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/webview-spec.js b/spec/webview-spec.js index b4bf7ddad7de..f6aa3d18c5ea 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -6,7 +6,6 @@ const {ipcRenderer, remote} = require('electron') const {app, session, getGuestWebContents, ipcMain, BrowserWindow, webContents} = remote const {closeWindow} = require('./window-helpers') -const isCI = remote.getGlobal('isCi') const nativeModulesEnabled = remote.getGlobal('nativeModulesEnabled') describe(' tag', function () { From 28cd973805373727e41957463dd8ed7158c3fc07 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 6 Jun 2017 15:20:08 -0700 Subject: [PATCH 3/7] Mention document.hidden in spec description --- spec/api-browser-window-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 81183ffaa679..13063e295118 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1412,7 +1412,7 @@ describe('BrowserWindow module', function () { }) }) - describe('document.visibilityState', function () { + describe('document.visibilityState/hidden', function () { afterEach(function () { ipcMain.removeAllListeners('pong') }) From 52137485f2041e2b363f12dacfd988c80e4000a6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 6 Jun 2017 16:22:07 -0700 Subject: [PATCH 4/7] Add separate specs for hide/show/minimize visibilitychange events --- spec/api-browser-window-spec.js | 149 +++++++++++++++++++------------- 1 file changed, 90 insertions(+), 59 deletions(-) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 13063e295118..1bb10b84d853 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1413,77 +1413,108 @@ describe('BrowserWindow module', function () { }) describe('document.visibilityState/hidden', function () { + beforeEach(function () { + w.destroy() + }) + + function onVisibilityChange (callback) { + ipcMain.on('pong', function (event, visibilityState, hidden) { + if (event.sender.id === w.webContents.id) { + callback(visibilityState, hidden) + } + }) + } + + function onNextVisibilityChange (callback) { + ipcMain.once('pong', function (event, visibilityState, hidden) { + if (event.sender.id === w.webContents.id) { + callback(visibilityState, hidden) + } + }) + } + afterEach(function () { ipcMain.removeAllListeners('pong') }) - function onNextVisibleEvent (callback) { - ipcMain.on('pong', function listener (event, visibilityState, hidden) { - if (visibilityState === 'visible' && hidden === false) { - ipcMain.removeListener('pong', listener) - callback() - } - }) - } - - function onNextHiddenEvent (callback) { - ipcMain.on('pong', function listener (event, visibilityState, hidden) { - if (visibilityState === 'hidden' && hidden === true) { - ipcMain.removeListener('pong', listener) - callback() - } - }) - } - it('visibilityState is initially visible despite window being hidden', function (done) { - w.destroy() w = new BrowserWindow({ show: false, width: 100, height: 100 }) let readyToShow = false - w.on('ready-to-show', function () { + w.once('ready-to-show', function () { readyToShow = true }) - ipcMain.once('pong', function (event, visibilityState, hidden) { - assert.ok(!readyToShow) + onNextVisibilityChange(function (visibilityState, hidden) { + assert.equal(readyToShow, false) assert.equal(visibilityState, 'visible') assert.equal(hidden, false) - ipcMain.once('pong', function (event, visibilityState, hidden) { - assert.ok(false) - }) - - setTimeout(done, 1000) - w.show() + done() }) w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) }) - it('visibilityState changes when window is shown and hidden', function (done) { - w.destroy() - w = new BrowserWindow({ - width: 100, - height: 100 - }) + it('visibilityState changes when window is hidden', function (done) { + w = new BrowserWindow({width: 100, height: 100}) - onNextVisibleEvent(() => { - onNextHiddenEvent(() => { - onNextVisibleEvent(() => { - onNextHiddenEvent(done) - w.minimize() - }) - w.show() - w.focus() + onNextVisibilityChange(function (visibilityState, hidden) { + assert.equal(visibilityState, 'visible') + assert.equal(hidden, false) + + onNextVisibilityChange(function (visibilityState, hidden) { + assert.equal(visibilityState, 'hidden') + assert.equal(hidden, true) + + done() }) + w.hide() }) w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) }) + it('visibilityState changes when window is shown', function (done) { + w = new BrowserWindow({width: 100, height: 100}) + + onNextVisibilityChange(function (visibilityState, hidden) { + onVisibilityChange(function (visibilityState, hidden) { + if (!hidden) { + assert.equal(visibilityState, 'visible') + done() + } + }) + + w.hide() + w.show() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) + }) + + it('visibilityState changes when window is minimized', function (done) { + w = new BrowserWindow({width: 100, height: 100}) + + onNextVisibilityChange(function (visibilityState, hidden) { + assert.equal(visibilityState, 'visible') + assert.equal(hidden, false) + + onNextVisibilityChange(function (visibilityState, hidden) { + assert.equal(visibilityState, 'hidden') + assert.equal(hidden, true) + + done() + }) + + w.minimize() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) + }) + it('visibilityState remains visible if backgroundThrottling is disabled', function (done) { - w.destroy() w = new BrowserWindow({ show: false, width: 100, @@ -1493,26 +1524,26 @@ describe('BrowserWindow module', function () { } }) - onNextVisibleEvent(() => { - onNextVisibleEvent(() => { - done(new Error('Unexpected visibility change event to visible')) - }) - onNextHiddenEvent(() => { - done(new Error('Unexpected visibility change event to hidden')) - }) + onNextVisibilityChange(function (visibilityState, hidden) { + assert.equal(visibilityState, 'visible') + assert.equal(hidden, false) - w.once('show', () => { - w.once('hide', () => { - w.once('show', () => { - done() - }) - w.show() - }) - w.hide() + onNextVisibilityChange(function (visibilityState, hidden) { + done(new Error(`Unexpected visibility change event. visibilityState: ${visibilityState} hidden: ${hidden}`)) }) - w.show() }) + w.once('show', () => { + w.once('hide', () => { + w.once('show', () => { + done() + }) + w.show() + }) + w.hide() + }) + w.show() + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) }) }) From a53e98c0f3cf736572e04cc804540d35831d9347 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 6 Jun 2017 16:45:46 -0700 Subject: [PATCH 5/7] Add visibilitystate test for showInactive --- spec/api-browser-window-spec.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 1bb10b84d853..07bc5c89b00c 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1494,6 +1494,26 @@ describe('BrowserWindow module', function () { w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) }) + it('visibilityState changes when window is shown inactive', function (done) { + if (isCI && process.platform === 'win32') return done() + + w = new BrowserWindow({width: 100, height: 100}) + + onNextVisibilityChange(function (visibilityState, hidden) { + onVisibilityChange(function (visibilityState, hidden) { + if (!hidden) { + assert.equal(visibilityState, 'visible') + done() + } + }) + + w.hide() + w.showInactive() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'visibilitychange.html')) + }) + it('visibilityState changes when window is minimized', function (done) { w = new BrowserWindow({width: 100, height: 100}) From c60a9d242e19e94551c18b3f609674608a10f636 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 6 Jun 2017 16:58:28 -0700 Subject: [PATCH 6/7] Disable minimize spec on Linux CI --- spec/api-browser-window-spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 07bc5c89b00c..83da2d093c63 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1515,6 +1515,8 @@ describe('BrowserWindow module', function () { }) it('visibilityState changes when window is minimized', function (done) { + if (isCI && process.platform === 'linux') return done() + w = new BrowserWindow({width: 100, height: 100}) onNextVisibilityChange(function (visibilityState, hidden) { From dc5221d551ba8d526776adcd07a9b8429e8fa373 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 6 Jun 2017 17:02:23 -0700 Subject: [PATCH 7/7] Guard against null window like before --- atom/browser/web_contents_preferences.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 30213b098fde..b2d8bc50855d 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -194,9 +194,11 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( auto embedder = manager->GetEmbedder(guest_instance_id); if (embedder) { auto window = NativeWindow::FromWebContents(embedder); - const bool visible = window->IsVisible() && !window->IsMinimized(); - if (!visible) { - command_line->AppendSwitch(switches::kHiddenPage); + if (window) { + const bool visible = window->IsVisible() && !window->IsMinimized(); + if (!visible) { + command_line->AppendSwitch(switches::kHiddenPage); + } } } }