Skip to content

Commit a9475f3

Browse files
miniakalexeykuzmin
authored andcommitted
feat: add workingDirectory option to shell.openExternal() (electron#15065)
Allows passing `workingDirectory` to the underlying `ShellExecuteW` API on Windows. the motivation is that by default `ShellExecute` would use the current working directory, which would get locked on Windows and can prevent autoUpdater from working correctly. We need to be able specify a different `workingDirectory` to prevent this situation.
1 parent 2d186cb commit a9475f3

File tree

7 files changed

+42
-35
lines changed

7 files changed

+42
-35
lines changed

atom/browser/atom_browser_client.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ void OnOpenExternal(const GURL& escaped_url, bool allowed) {
611611
#else
612612
escaped_url,
613613
#endif
614-
true);
614+
platform_util::OpenExternalOptions());
615615
}
616616

617617
void HandleExternalProtocolInUI(

atom/common/api/atom_api_shell.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,26 @@ bool OpenExternal(
6060
const GURL& url,
6161
#endif
6262
mate::Arguments* args) {
63-
bool activate = true;
63+
platform_util::OpenExternalOptions options;
6464
if (args->Length() >= 2) {
65-
mate::Dictionary options;
66-
if (args->GetNext(&options)) {
67-
options.Get("activate", &activate);
65+
mate::Dictionary obj;
66+
if (args->GetNext(&obj)) {
67+
obj.Get("activate", &options.activate);
68+
obj.Get("workingDirectory", &options.working_dir);
6869
}
6970
}
7071

7172
if (args->Length() >= 3) {
7273
base::Callback<void(v8::Local<v8::Value>)> callback;
7374
if (args->GetNext(&callback)) {
7475
platform_util::OpenExternal(
75-
url, activate,
76+
url, options,
7677
base::Bind(&OnOpenExternalFinished, args->isolate(), callback));
7778
return true;
7879
}
7980
}
8081

81-
return platform_util::OpenExternal(url, activate);
82+
return platform_util::OpenExternal(url, options);
8283
}
8384

8485
#if defined(OS_WIN)

atom/common/platform_util.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <string>
99

1010
#include "base/callback_forward.h"
11+
#include "base/files/file_path.h"
1112
#include "build/build_config.h"
1213

1314
#if defined(OS_WIN)
@@ -16,10 +17,6 @@
1617

1718
class GURL;
1819

19-
namespace base {
20-
class FilePath;
21-
}
22-
2320
namespace platform_util {
2421

2522
typedef base::Callback<void(const std::string&)> OpenExternalCallback;
@@ -32,6 +29,11 @@ bool ShowItemInFolder(const base::FilePath& full_path);
3229
// Must be called from the UI thread.
3330
bool OpenItem(const base::FilePath& full_path);
3431

32+
struct OpenExternalOptions {
33+
bool activate = true;
34+
base::FilePath working_dir;
35+
};
36+
3537
// Open the given external protocol URL in the desktop's default manner.
3638
// (For example, mailto: URLs in the default mail user agent.)
3739
bool OpenExternal(
@@ -40,7 +42,7 @@ bool OpenExternal(
4042
#else
4143
const GURL& url,
4244
#endif
43-
bool activate);
45+
const OpenExternalOptions& options);
4446

4547
// The asynchronous version of OpenExternal.
4648
void OpenExternal(
@@ -49,7 +51,7 @@ void OpenExternal(
4951
#else
5052
const GURL& url,
5153
#endif
52-
bool activate,
54+
const OpenExternalOptions& options,
5355
const OpenExternalCallback& callback);
5456

5557
// Move a file to trash.

atom/common/platform_util_linux.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ bool OpenItem(const base::FilePath& full_path) {
8080
return XDGOpen(full_path.value(), false);
8181
}
8282

83-
bool OpenExternal(const GURL& url, bool activate) {
83+
bool OpenExternal(const GURL& url, const OpenExternalOptions& options) {
8484
// Don't wait for exit, since we don't want to wait for the browser/email
8585
// client window to close before returning
8686
if (url.SchemeIs("mailto"))
@@ -90,10 +90,10 @@ bool OpenExternal(const GURL& url, bool activate) {
9090
}
9191

9292
void OpenExternal(const GURL& url,
93-
bool activate,
93+
const OpenExternalOptions& options,
9494
const OpenExternalCallback& callback) {
9595
// TODO(gabriel): Implement async open if callback is specified
96-
callback.Run(OpenExternal(url, activate) ? "" : "Failed to open");
96+
callback.Run(OpenExternal(url, options) ? "" : "Failed to open");
9797
}
9898

9999
bool MoveItemToTrash(const base::FilePath& full_path) {

atom/common/platform_util_mac.mm

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,16 @@ bool OpenItem(const base::FilePath& full_path) {
139139
launchIdentifiers:NULL];
140140
}
141141

142-
bool OpenExternal(const GURL& url, bool activate) {
142+
bool OpenExternal(const GURL& url, const OpenExternalOptions& options) {
143143
DCHECK([NSThread isMainThread]);
144144
NSURL* ns_url = net::NSURLWithGURL(url);
145145
if (ns_url)
146-
return OpenURL(ns_url, activate).empty();
146+
return OpenURL(ns_url, options.activate).empty();
147147
return false;
148148
}
149149

150150
void OpenExternal(const GURL& url,
151-
bool activate,
151+
const OpenExternalOptions& options,
152152
const OpenExternalCallback& callback) {
153153
NSURL* ns_url = net::NSURLWithGURL(url);
154154
if (!ns_url) {
@@ -157,13 +157,13 @@ void OpenExternal(const GURL& url,
157157
}
158158

159159
__block OpenExternalCallback c = callback;
160-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
161-
^{
162-
__block std::string error = OpenURL(ns_url, activate);
163-
dispatch_async(dispatch_get_main_queue(), ^{
164-
c.Run(error);
165-
});
166-
});
160+
dispatch_async(
161+
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
162+
__block std::string error = OpenURL(ns_url, options.activate);
163+
dispatch_async(dispatch_get_main_queue(), ^{
164+
c.Run(error);
165+
});
166+
});
167167
}
168168

169169
bool MoveItemToTrash(const base::FilePath& full_path) {

atom/common/platform_util_win.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,15 +294,18 @@ bool OpenItem(const base::FilePath& full_path) {
294294
return ui::win::OpenFileViaShell(full_path);
295295
}
296296

297-
bool OpenExternal(const base::string16& url, bool activate) {
297+
bool OpenExternal(const base::string16& url,
298+
const OpenExternalOptions& options) {
298299
// Quote the input scheme to be sure that the command does not have
299300
// parameters unexpected by the external program. This url should already
300301
// have been escaped.
301302
base::string16 escaped_url = L"\"" + url + L"\"";
303+
auto working_dir = options.working_dir.value();
302304

303-
if (reinterpret_cast<ULONG_PTR>(ShellExecuteW(
304-
NULL, L"open", escaped_url.c_str(), NULL, NULL, SW_SHOWNORMAL)) <=
305-
32) {
305+
if (reinterpret_cast<ULONG_PTR>(
306+
ShellExecuteW(nullptr, L"open", escaped_url.c_str(), nullptr,
307+
working_dir.empty() ? nullptr : working_dir.c_str(),
308+
SW_SHOWNORMAL)) <= 32) {
306309
// We fail to execute the call. We could display a message to the user.
307310
// TODO(nsylvain): we should also add a dialog to warn on errors. See
308311
// bug 1136923.
@@ -312,10 +315,10 @@ bool OpenExternal(const base::string16& url, bool activate) {
312315
}
313316

314317
void OpenExternal(const base::string16& url,
315-
bool activate,
318+
const OpenExternalOptions& options,
316319
const OpenExternalCallback& callback) {
317320
// TODO(gabriel): Implement async open if callback is specified
318-
callback.Run(OpenExternal(url, activate) ? "" : "Failed to open");
321+
callback.Run(OpenExternal(url, options) ? "" : "Failed to open");
319322
}
320323

321324
bool MoveItemToTrash(const base::FilePath& path) {

docs/api/shell.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ Open the given file in the desktop's default manner.
3737
### `shell.openExternal(url[, options, callback])`
3838

3939
* `url` String - Max 2081 characters on windows, or the function returns false.
40-
* `options` Object (optional) _macOS_
41-
* `activate` Boolean - `true` to bring the opened application to the
42-
foreground. The default is `true`.
40+
* `options` Object (optional)
41+
* `activate` Boolean (optional) - `true` to bring the opened application to the
42+
foreground. The default is `true`. _macOS_
43+
* `workingDirectory` String (optional) - The working directory. _Windows_
4344
* `callback` Function (optional) _macOS_ - If specified will perform the open asynchronously.
4445
* `error` Error
4546

0 commit comments

Comments
 (0)