Skip to content

Commit

Permalink
fix: multiple dock icons when calling dock.show/hide (#25301)
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz committed Sep 3, 2020
1 parent 213f2fc commit 35b348a
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 15 deletions.
1 change: 1 addition & 0 deletions shell/browser/browser.h
Expand Up @@ -309,6 +309,7 @@ class Browser : public WindowListObserver {
base::Value about_panel_options_;
#elif defined(OS_MACOSX)
base::DictionaryValue about_panel_options_;
base::Time last_dock_show_;
#endif

DISALLOW_COPY_AND_ASSIGN(Browser);
Expand Down
15 changes: 15 additions & 0 deletions shell/browser/browser_mac.mm
Expand Up @@ -329,6 +329,20 @@ void RemoveFromLoginItems() {
}

void Browser::DockHide() {
// Transforming application state from UIElement to Foreground is an
// asyncronous operation, and unfortunately there is currently no way to know
// when it is finished.
// So if we call DockHide => DockShow => DockHide => DockShow in a very short
// time, we would triger a bug of macOS that, there would be multiple dock
// icons of the app left in system.
// To work around this, we make sure DockHide does nothing if it is called
// immediately after DockShow. After some experiments, 1 second seems to be
// a proper interval.
if (!last_dock_show_.is_null() &&
base::Time::Now() - last_dock_show_ < base::TimeDelta::FromSeconds(1)) {
return;
}

for (auto* const& window : WindowList::GetWindows())
[window->GetNativeWindow().GetNativeNSWindow() setCanHide:NO];

Expand All @@ -344,6 +358,7 @@ void RemoveFromLoginItems() {
}

v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) {
last_dock_show_ = base::Time::Now();
gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();

Expand Down
33 changes: 18 additions & 15 deletions spec-main/api-app-spec.ts
Expand Up @@ -453,7 +453,7 @@ describe('app module', () => {
await w.loadURL('about:blank');

const promise = emittedOnce(app, 'desktop-capturer-get-sources');
w.webContents.executeJavaScript(`require('electron').desktopCapturer.getSources({ types: ['screen'] })`);
w.webContents.executeJavaScript('require(\'electron\').desktopCapturer.getSources({ types: [\'screen\'] })');

const [, webContents] = await promise;
expect(webContents).to.equal(w.webContents);
Expand All @@ -471,7 +471,7 @@ describe('app module', () => {
await w.loadURL('about:blank');

const promise = emittedOnce(app, 'remote-require');
w.webContents.executeJavaScript(`require('electron').remote.require('test')`);
w.webContents.executeJavaScript('require(\'electron\').remote.require(\'test\')');

const [, webContents, moduleName] = await promise;
expect(webContents).to.equal(w.webContents);
Expand All @@ -488,7 +488,7 @@ describe('app module', () => {
await w.loadURL('about:blank');

const promise = emittedOnce(app, 'remote-get-global');
w.webContents.executeJavaScript(`require('electron').remote.getGlobal('test')`);
w.webContents.executeJavaScript('require(\'electron\').remote.getGlobal(\'test\')');

const [, webContents, globalName] = await promise;
expect(webContents).to.equal(w.webContents);
Expand All @@ -505,7 +505,7 @@ describe('app module', () => {
await w.loadURL('about:blank');

const promise = emittedOnce(app, 'remote-get-builtin');
w.webContents.executeJavaScript(`require('electron').remote.app`);
w.webContents.executeJavaScript('require(\'electron\').remote.app');

const [, webContents, moduleName] = await promise;
expect(webContents).to.equal(w.webContents);
Expand All @@ -522,7 +522,7 @@ describe('app module', () => {
await w.loadURL('about:blank');

const promise = emittedOnce(app, 'remote-get-current-window');
w.webContents.executeJavaScript(`{ require('electron').remote.getCurrentWindow() }`);
w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWindow() }');

const [, webContents] = await promise;
expect(webContents).to.equal(w.webContents);
Expand All @@ -538,7 +538,7 @@ describe('app module', () => {
await w.loadURL('about:blank');

const promise = emittedOnce(app, 'remote-get-current-web-contents');
w.webContents.executeJavaScript(`{ require('electron').remote.getCurrentWebContents() }`);
w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWebContents() }');

const [, webContents] = await promise;
expect(webContents).to.equal(w.webContents);
Expand Down Expand Up @@ -601,7 +601,7 @@ describe('app module', () => {
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
const processStartArgs = [
'--processStart', `"${path.basename(process.execPath)}"`,
'--process-start-args', `"--hidden"`
'--process-start-args', '"--hidden"'
];

before(function () {
Expand Down Expand Up @@ -814,7 +814,7 @@ describe('app module', () => {
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
const processStartArgs = [
'--processStart', `"${path.basename(process.execPath)}"`,
'--process-start-args', `"--hidden"`
'--process-start-args', '"--hidden"'
];

let Winreg: any;
Expand Down Expand Up @@ -1323,6 +1323,16 @@ describe('app module', () => {
});
});

describe('dock.hide', () => {
it('should not throw', () => {
app.dock.hide();
expect(app.dock.isVisible()).to.equal(false);
});
});

// Note that dock.show tests should run after dock.hide tests, to work
// around a bug of macOS.
// See https://github.com/electron/electron/pull/25269 for more.
describe('dock.show', () => {
it('should not throw', () => {
return app.dock.show().then(() => {
Expand All @@ -1338,13 +1348,6 @@ describe('app module', () => {
await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined);
});
});

describe('dock.hide', () => {
it('should not throw', () => {
app.dock.hide();
expect(app.dock.isVisible()).to.equal(false);
});
});
});

describe('whenReady', () => {
Expand Down

0 comments on commit 35b348a

Please sign in to comment.