Merge pull request #4700 from ArekSredzki/async-document-hidden
Cache browser visibility state & emit visibilitychange event on change
This commit is contained in:
commit
abd466ee4a
13 changed files with 173 additions and 58 deletions
|
@ -191,6 +191,14 @@ void Window::OnWindowFocus() {
|
||||||
Emit("focus");
|
Emit("focus");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::OnWindowShow() {
|
||||||
|
Emit("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::OnWindowHide() {
|
||||||
|
Emit("hide");
|
||||||
|
}
|
||||||
|
|
||||||
void Window::OnWindowMaximize() {
|
void Window::OnWindowMaximize() {
|
||||||
Emit("maximize");
|
Emit("maximize");
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,8 @@ class Window : public mate::TrackableObject<Window>,
|
||||||
void OnWindowClosed() override;
|
void OnWindowClosed() override;
|
||||||
void OnWindowBlur() override;
|
void OnWindowBlur() override;
|
||||||
void OnWindowFocus() override;
|
void OnWindowFocus() override;
|
||||||
|
void OnWindowShow() override;
|
||||||
|
void OnWindowHide() override;
|
||||||
void OnWindowMaximize() override;
|
void OnWindowMaximize() override;
|
||||||
void OnWindowUnmaximize() override;
|
void OnWindowUnmaximize() override;
|
||||||
void OnWindowMinimize() override;
|
void OnWindowMinimize() override;
|
||||||
|
|
|
@ -428,6 +428,14 @@ void NativeWindow::NotifyWindowFocus() {
|
||||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowFocus());
|
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowFocus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindow::NotifyWindowShow() {
|
||||||
|
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowShow());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindow::NotifyWindowHide() {
|
||||||
|
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowHide());
|
||||||
|
}
|
||||||
|
|
||||||
void NativeWindow::NotifyWindowMaximize() {
|
void NativeWindow::NotifyWindowMaximize() {
|
||||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowMaximize());
|
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowMaximize());
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,6 +210,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
void NotifyWindowClosed();
|
void NotifyWindowClosed();
|
||||||
void NotifyWindowBlur();
|
void NotifyWindowBlur();
|
||||||
void NotifyWindowFocus();
|
void NotifyWindowFocus();
|
||||||
|
void NotifyWindowShow();
|
||||||
|
void NotifyWindowHide();
|
||||||
void NotifyWindowMaximize();
|
void NotifyWindowMaximize();
|
||||||
void NotifyWindowUnmaximize();
|
void NotifyWindowUnmaximize();
|
||||||
void NotifyWindowMinimize();
|
void NotifyWindowMinimize();
|
||||||
|
|
|
@ -79,6 +79,21 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)windowDidChangeOcclusionState:(NSNotification *)notification {
|
||||||
|
// notification.object is the window that changed its state.
|
||||||
|
// It's safe to use self.window instead if you don't assign one delegate to many windows
|
||||||
|
NSWindow *window = notification.object;
|
||||||
|
|
||||||
|
// check occlusion binary flag
|
||||||
|
if (window.occlusionState & NSWindowOcclusionStateVisible) {
|
||||||
|
// The app is visible
|
||||||
|
shell_->NotifyWindowShow();
|
||||||
|
} else {
|
||||||
|
// The app is not visible
|
||||||
|
shell_->NotifyWindowHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)windowDidBecomeMain:(NSNotification*)notification {
|
- (void)windowDidBecomeMain:(NSNotification*)notification {
|
||||||
content::WebContents* web_contents = shell_->web_contents();
|
content::WebContents* web_contents = shell_->web_contents();
|
||||||
if (!web_contents)
|
if (!web_contents)
|
||||||
|
|
|
@ -42,6 +42,12 @@ class NativeWindowObserver {
|
||||||
// Called when window gains focus.
|
// Called when window gains focus.
|
||||||
virtual void OnWindowFocus() {}
|
virtual void OnWindowFocus() {}
|
||||||
|
|
||||||
|
// Called when window is shown.
|
||||||
|
virtual void OnWindowShow() {}
|
||||||
|
|
||||||
|
// Called when window is hidden.
|
||||||
|
virtual void OnWindowHide() {}
|
||||||
|
|
||||||
// Called when window state changed.
|
// Called when window state changed.
|
||||||
virtual void OnWindowMaximize() {}
|
virtual void OnWindowMaximize() {}
|
||||||
virtual void OnWindowUnmaximize() {}
|
virtual void OnWindowUnmaximize() {}
|
||||||
|
|
|
@ -302,6 +302,8 @@ bool NativeWindowViews::IsFocused() {
|
||||||
void NativeWindowViews::Show() {
|
void NativeWindowViews::Show() {
|
||||||
window_->native_widget_private()->ShowWithWindowState(GetRestoredState());
|
window_->native_widget_private()->ShowWithWindowState(GetRestoredState());
|
||||||
|
|
||||||
|
NotifyWindowShow();
|
||||||
|
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
if (global_menu_bar_)
|
if (global_menu_bar_)
|
||||||
global_menu_bar_->OnWindowMapped();
|
global_menu_bar_->OnWindowMapped();
|
||||||
|
@ -311,6 +313,8 @@ void NativeWindowViews::Show() {
|
||||||
void NativeWindowViews::ShowInactive() {
|
void NativeWindowViews::ShowInactive() {
|
||||||
window_->ShowInactive();
|
window_->ShowInactive();
|
||||||
|
|
||||||
|
NotifyWindowShow();
|
||||||
|
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
if (global_menu_bar_)
|
if (global_menu_bar_)
|
||||||
global_menu_bar_->OnWindowMapped();
|
global_menu_bar_->OnWindowMapped();
|
||||||
|
@ -320,6 +324,8 @@ void NativeWindowViews::ShowInactive() {
|
||||||
void NativeWindowViews::Hide() {
|
void NativeWindowViews::Hide() {
|
||||||
window_->Hide();
|
window_->Hide();
|
||||||
|
|
||||||
|
NotifyWindowHide();
|
||||||
|
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
if (global_menu_bar_)
|
if (global_menu_bar_)
|
||||||
global_menu_bar_->OnWindowUnmapped();
|
global_menu_bar_->OnWindowUnmapped();
|
||||||
|
|
|
@ -245,6 +245,14 @@ Emitted when the window loses focus.
|
||||||
|
|
||||||
Emitted when the window gains focus.
|
Emitted when the window gains focus.
|
||||||
|
|
||||||
|
### Event: 'show'
|
||||||
|
|
||||||
|
Emitted when the window is shown.
|
||||||
|
|
||||||
|
### Event: 'hide'
|
||||||
|
|
||||||
|
Emitted when the window is hidden.
|
||||||
|
|
||||||
### Event: 'maximize'
|
### Event: 'maximize'
|
||||||
|
|
||||||
Emitted when window is maximized.
|
Emitted when window is maximized.
|
||||||
|
|
|
@ -20,7 +20,7 @@ BrowserWindow.prototype._init = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make new windows requested by links behave like "window.open"
|
// Make new windows requested by links behave like "window.open"
|
||||||
this.webContents.on('-new-window', function(event, url, frameName) {
|
this.webContents.on('-new-window', (event, url, frameName) => {
|
||||||
var options;
|
var options;
|
||||||
options = {
|
options = {
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -32,27 +32,21 @@ BrowserWindow.prototype._init = function() {
|
||||||
|
|
||||||
// window.resizeTo(...)
|
// window.resizeTo(...)
|
||||||
// window.moveTo(...)
|
// window.moveTo(...)
|
||||||
this.webContents.on('move', (function(_this) {
|
this.webContents.on('move', (event, size) => {
|
||||||
return function(event, size) {
|
return this.setBounds(size);
|
||||||
return _this.setBounds(size);
|
});
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
|
|
||||||
// Hide the auto-hide menu when webContents is focused.
|
// Hide the auto-hide menu when webContents is focused.
|
||||||
this.webContents.on('activate', (function(_this) {
|
this.webContents.on('activate', () => {
|
||||||
return function() {
|
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
|
||||||
if (process.platform !== 'darwin' && _this.isMenuBarAutoHide() && _this.isMenuBarVisible()) {
|
return this.setMenuBarVisibility(false);
|
||||||
return _this.setMenuBarVisibility(false);
|
}
|
||||||
}
|
});
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
|
|
||||||
// Forward the crashed event.
|
// Forward the crashed event.
|
||||||
this.webContents.on('crashed', (function(_this) {
|
this.webContents.on('crashed', () => {
|
||||||
return function() {
|
return this.emit('crashed');
|
||||||
return _this.emit('crashed');
|
});
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
|
|
||||||
// Change window title to page title.
|
// Change window title to page title.
|
||||||
this.webContents.on('page-title-updated', (event, title) => {
|
this.webContents.on('page-title-updated', (event, title) => {
|
||||||
|
@ -77,36 +71,40 @@ BrowserWindow.prototype._init = function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Redirect focus/blur event to app instance too.
|
// Redirect focus/blur event to app instance too.
|
||||||
this.on('blur', (function(_this) {
|
this.on('blur', (event) => {
|
||||||
return function(event) {
|
return app.emit('browser-window-blur', event, this);
|
||||||
return app.emit('browser-window-blur', event, _this);
|
});
|
||||||
};
|
this.on('focus', (event) => {
|
||||||
})(this));
|
return app.emit('browser-window-focus', event, this);
|
||||||
this.on('focus', (function(_this) {
|
});
|
||||||
return function(event) {
|
|
||||||
return app.emit('browser-window-focus', event, _this);
|
// Evented visibilityState changes
|
||||||
};
|
this.on('show', () => {
|
||||||
})(this));
|
return this.webContents.send('ATOM_RENDERER_WINDOW_VISIBILITY_CHANGE', this.isVisible(), this.isMinimized());
|
||||||
|
});
|
||||||
|
this.on('hide', () => {
|
||||||
|
return this.webContents.send('ATOM_RENDERER_WINDOW_VISIBILITY_CHANGE', this.isVisible(), this.isMinimized());
|
||||||
|
});
|
||||||
|
this.on('minimize', () => {
|
||||||
|
return this.webContents.send('ATOM_RENDERER_WINDOW_VISIBILITY_CHANGE', this.isVisible(), this.isMinimized());
|
||||||
|
});
|
||||||
|
this.on('restore', () => {
|
||||||
|
return this.webContents.send('ATOM_RENDERER_WINDOW_VISIBILITY_CHANGE', this.isVisible(), this.isMinimized());
|
||||||
|
});
|
||||||
|
|
||||||
// Notify the creation of the window.
|
// Notify the creation of the window.
|
||||||
app.emit('browser-window-created', {}, this);
|
app.emit('browser-window-created', {}, this);
|
||||||
|
|
||||||
// Be compatible with old APIs.
|
// Be compatible with old APIs.
|
||||||
this.webContents.on('devtools-focused', (function(_this) {
|
this.webContents.on('devtools-focused', () => {
|
||||||
return function() {
|
return this.emit('devtools-focused');
|
||||||
return _this.emit('devtools-focused');
|
});
|
||||||
};
|
this.webContents.on('devtools-opened', () => {
|
||||||
})(this));
|
return this.emit('devtools-opened');
|
||||||
this.webContents.on('devtools-opened', (function(_this) {
|
});
|
||||||
return function() {
|
this.webContents.on('devtools-closed', () => {
|
||||||
return _this.emit('devtools-opened');
|
return this.emit('devtools-closed');
|
||||||
};
|
});
|
||||||
})(this));
|
|
||||||
this.webContents.on('devtools-closed', (function(_this) {
|
|
||||||
return function() {
|
|
||||||
return _this.emit('devtools-closed');
|
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
return Object.defineProperty(this, 'devToolsWebContents', {
|
return Object.defineProperty(this, 'devToolsWebContents', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
|
|
|
@ -3,7 +3,7 @@ const v8Util = process.atomBinding('v8_util');
|
||||||
|
|
||||||
var slice = [].slice;
|
var slice = [].slice;
|
||||||
|
|
||||||
// Created by init.coffee.
|
// Created by init.js.
|
||||||
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc');
|
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc');
|
||||||
|
|
||||||
ipcRenderer.send = function() {
|
ipcRenderer.send = function() {
|
||||||
|
|
|
@ -3,7 +3,7 @@ window.onload = function() {
|
||||||
InspectorFrontendHost.showContextMenuAtPoint = createMenu;
|
InspectorFrontendHost.showContextMenuAtPoint = createMenu;
|
||||||
|
|
||||||
// Use dialog API to override file chooser dialog.
|
// Use dialog API to override file chooser dialog.
|
||||||
return WebInspector.createFileSelectorElement = createFileSelectorElement;
|
return (WebInspector.createFileSelectorElement = createFileSelectorElement);
|
||||||
};
|
};
|
||||||
|
|
||||||
var convertToMenuTemplate = function(items) {
|
var convertToMenuTemplate = function(items) {
|
||||||
|
|
|
@ -3,6 +3,16 @@ const remote = require('electron').remote;
|
||||||
|
|
||||||
var slice = [].slice;
|
var slice = [].slice;
|
||||||
|
|
||||||
|
// Cache browser window visibility
|
||||||
|
var _isVisible = true;
|
||||||
|
var _isMinimized = false;
|
||||||
|
(function() {
|
||||||
|
var currentWindow;
|
||||||
|
currentWindow = remote.getCurrentWindow();
|
||||||
|
_isVisible = currentWindow.isVisible();
|
||||||
|
_isMinimized = currentWindow.isMinimized();
|
||||||
|
})();
|
||||||
|
|
||||||
// Helper function to resolve relative url.
|
// Helper function to resolve relative url.
|
||||||
var a = window.top.document.createElement('a');
|
var a = window.top.document.createElement('a');
|
||||||
|
|
||||||
|
@ -30,7 +40,7 @@ var BrowserWindowProxy = (function() {
|
||||||
ipcRenderer.once("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + this.guestId, (function(_this) {
|
ipcRenderer.once("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + this.guestId, (function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
BrowserWindowProxy.remove(_this.guestId);
|
BrowserWindowProxy.remove(_this.guestId);
|
||||||
return _this.closed = true;
|
return (_this.closed = true);
|
||||||
};
|
};
|
||||||
})(this));
|
})(this));
|
||||||
}
|
}
|
||||||
|
@ -87,7 +97,9 @@ window.open = function(url, frameName, features) {
|
||||||
ref1 = features.split(/,\s*/);
|
ref1 = features.split(/,\s*/);
|
||||||
for (i = 0, len = ref1.length; i < len; i++) {
|
for (i = 0, len = ref1.length; i < len; i++) {
|
||||||
feature = ref1[i];
|
feature = ref1[i];
|
||||||
ref2 = feature.split(/\s*=/), name = ref2[0], value = ref2[1];
|
ref2 = feature.split(/\s*=/);
|
||||||
|
name = ref2[0];
|
||||||
|
value = ref2[1];
|
||||||
options[name] = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : value;
|
options[name] = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : value;
|
||||||
}
|
}
|
||||||
if (options.left) {
|
if (options.left) {
|
||||||
|
@ -168,6 +180,17 @@ if (process.openerId != null) {
|
||||||
window.opener = BrowserWindowProxy.getOrCreate(process.openerId);
|
window.opener = BrowserWindowProxy.getOrCreate(process.openerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipcRenderer.on('ATOM_RENDERER_WINDOW_VISIBILITY_CHANGE', function (event, isVisible, isMinimized) {
|
||||||
|
var hasChanged = _isVisible != isVisible || _isMinimized != isMinimized;
|
||||||
|
|
||||||
|
if (hasChanged) {
|
||||||
|
_isVisible = isVisible;
|
||||||
|
_isMinimized = isMinimized;
|
||||||
|
|
||||||
|
document.dispatchEvent(new Event('visibilitychange'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcRenderer.on('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', function(event, sourceId, message, sourceOrigin) {
|
ipcRenderer.on('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', function(event, sourceId, message, sourceOrigin) {
|
||||||
// Manually dispatch event instead of using postMessage because we also need to
|
// Manually dispatch event instead of using postMessage because we also need to
|
||||||
// set event.source.
|
// set event.source.
|
||||||
|
@ -212,19 +235,17 @@ Object.defineProperty(window.history, 'length', {
|
||||||
|
|
||||||
// Make document.hidden and document.visibilityState return the correct value.
|
// Make document.hidden and document.visibilityState return the correct value.
|
||||||
Object.defineProperty(document, 'hidden', {
|
Object.defineProperty(document, 'hidden', {
|
||||||
get: function() {
|
get: function () {
|
||||||
var currentWindow;
|
return _isMinimized || !_isVisible;
|
||||||
currentWindow = remote.getCurrentWindow();
|
|
||||||
return currentWindow.isMinimized() || !currentWindow.isVisible();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(document, 'visibilityState', {
|
Object.defineProperty(document, 'visibilityState', {
|
||||||
get: function() {
|
get: function() {
|
||||||
if (document.hidden) {
|
if (_isVisible && !_isMinimized) {
|
||||||
return "hidden";
|
|
||||||
} else {
|
|
||||||
return "visible";
|
return "visible";
|
||||||
|
} else {
|
||||||
|
return "hidden";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -120,14 +120,55 @@ describe('browser-window module', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BrowserWindow.show()', function() {
|
describe('BrowserWindow.show()', function() {
|
||||||
it('should focus on window', function() {
|
if (isCI) {
|
||||||
if (isCI) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
it('should focus on window', function() {
|
||||||
w.show();
|
w.show();
|
||||||
assert(w.isFocused());
|
assert(w.isFocused());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should make the window visible', function() {
|
||||||
|
w.show();
|
||||||
|
assert(w.isVisible());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits when window is shown', function(done) {
|
||||||
|
this.timeout(10000);
|
||||||
|
w.once('show', function() {
|
||||||
|
assert.equal(w.isVisible(), true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
w.show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('BrowserWindow.hide()', function() {
|
||||||
|
if (isCI) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should defocus on window', function() {
|
||||||
|
w.hide();
|
||||||
|
assert(!w.isFocused());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make the window not visible', function() {
|
||||||
|
w.show();
|
||||||
|
w.hide();
|
||||||
|
assert(!w.isVisible());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits when window is hidden', function(done) {
|
||||||
|
this.timeout(10000);
|
||||||
|
w.show();
|
||||||
|
w.once('hide', function() {
|
||||||
|
assert.equal(w.isVisible(), false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
w.hide();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BrowserWindow.showInactive()', function() {
|
describe('BrowserWindow.showInactive()', function() {
|
||||||
|
|
Loading…
Reference in a new issue