Repository: electron/electron
Pull request: fix: update shell.openExternal to correctly focus on external window

#### Description of Change
This PR fixes bug [identified in Slack](https://electronhq.slack.com/archives/CB6CG54DB/p1729185377925139) where Electron 33 doesn't focus the browser window when opening links when using shell.openExternal.

**Current behavior:**
- If the URL was successfully opened and the options.activate flag is set to true, the method calls [[NSApp activateIgnoringOtherApps:YES].](https://developer.apple.com/documentation/appkit/nsapplication/1428468-activateignoringotherapps) This call forces the Electron app to become the active application, bringing it to the foreground and giving it focus, regardless of whether another application is currently active.

**Updated behavior:**
- The URL is opened with [`openURL:configuration:completionHandler`](https://developer.apple.com/documentation/appkit/nsworkspace/3172701-openurl) with configuration [`activates`](https://developer.apple.com/documentation/appkit/nsworkspaceopenconfiguration/3172704-activates) which ensures that the external application is activated (brought to the foreground) when the URL is opened.


### Before
https://github.com/user-attachments/assets/1162bada-3128-49db-b211-ea26c124c26b 


### After
https://github.com/user-attachments/assets/706da9af-9f6b-436a-a84a-4714a879fa49 







<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING.md
-->

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [ ] relevant documentation, tutorials, templates and examples are changed or added
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fix external window focus when using shell.openExternal

Changed files:
filename: shell/common/platform_util_mac.mm
patch: @@ -19,6 +19,7 @@
 #include "base/logging.h"
 #include "base/mac/scoped_aedesc.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "electron/mas.h"
@@ -147,11 +148,27 @@ void OpenExternal(const GURL& url,
     return;
   }
 
-  bool success = [[NSWorkspace sharedWorkspace] openURL:ns_url];
-  if (success && options.activate)
-    [NSApp activateIgnoringOtherApps:YES];
+  NSWorkspaceOpenConfiguration* configuration =
+      [NSWorkspaceOpenConfiguration configuration];
+  configuration.activates = options.activate;
+
+  __block OpenCallback copied_callback = std::move(callback);
+  scoped_refptr<base::SequencedTaskRunner> runner =
+      base::SequencedTaskRunner::GetCurrentDefault();
 
-  std::move(callback).Run(success ? "" : "Failed to open URL");
+  [[NSWorkspace sharedWorkspace]
+                openURL:ns_url
+          configuration:configuration
+      completionHandler:^(NSRunningApplication* _Nullable app,
+                          NSError* _Nullable error) {
+        if (error) {
+          runner->PostTask(FROM_HERE, base::BindOnce(std::move(copied_callback),
+                                                     "Failed to open URL"));
+        } else {
+          runner->PostTask(FROM_HERE,
+                           base::BindOnce(std::move(copied_callback), ""));
+        }
+      }];
 }
 
 bool MoveItemToTrashWithError(const base::FilePath& full_path,

filename: spec/api-shell-spec.ts
patch: @@ -76,6 +76,21 @@ describe('shell module', () => {
         requestReceived
       ]);
     });
+
+    ifit(process.platform === 'darwin')('removes focus from the electron window after opening an external link', async () => {
+      const url = 'http://127.0.0.1';
+      const w = new BrowserWindow({ show: true });
+
+      await once(w, 'focus');
+      expect(w.isFocused()).to.be.true();
+
+      await Promise.all<void>([
+        shell.openExternal(url),
+        once(w, 'blur') as Promise<any>
+      ]);
+
+      expect(w.isFocused()).to.be.false();
+    });
   });
 
   describe('shell.trashItem()', () => {