Browse Source

fix: multiple dock icons when calling dock.show/hide (#25301)

Cheng Zhao 4 years ago
parent
commit
35b348a7d7
3 changed files with 34 additions and 15 deletions
  1. 1 0
      shell/browser/browser.h
  2. 15 0
      shell/browser/browser_mac.mm
  3. 18 15
      spec-main/api-app-spec.ts

+ 1 - 0
shell/browser/browser.h

@@ -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);

+ 15 - 0
shell/browser/browser_mac.mm

@@ -329,6 +329,20 @@ std::string Browser::DockGetBadgeText() {
 }
 
 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];
 
@@ -344,6 +358,7 @@ bool Browser::DockIsVisible() {
 }
 
 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();
 

+ 18 - 15
spec-main/api-app-spec.ts

@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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 () {
@@ -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;
@@ -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(() => {
@@ -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', () => {