feat: add workingDirectory option to shell.openExternal() (#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.
This commit is contained in:
parent
2d186cb31a
commit
a9475f3590
7 changed files with 42 additions and 35 deletions
|
@ -611,7 +611,7 @@ void OnOpenExternal(const GURL& escaped_url, bool allowed) {
|
||||||
#else
|
#else
|
||||||
escaped_url,
|
escaped_url,
|
||||||
#endif
|
#endif
|
||||||
true);
|
platform_util::OpenExternalOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleExternalProtocolInUI(
|
void HandleExternalProtocolInUI(
|
||||||
|
|
|
@ -60,11 +60,12 @@ bool OpenExternal(
|
||||||
const GURL& url,
|
const GURL& url,
|
||||||
#endif
|
#endif
|
||||||
mate::Arguments* args) {
|
mate::Arguments* args) {
|
||||||
bool activate = true;
|
platform_util::OpenExternalOptions options;
|
||||||
if (args->Length() >= 2) {
|
if (args->Length() >= 2) {
|
||||||
mate::Dictionary options;
|
mate::Dictionary obj;
|
||||||
if (args->GetNext(&options)) {
|
if (args->GetNext(&obj)) {
|
||||||
options.Get("activate", &activate);
|
obj.Get("activate", &options.activate);
|
||||||
|
obj.Get("workingDirectory", &options.working_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,13 +73,13 @@ bool OpenExternal(
|
||||||
base::Callback<void(v8::Local<v8::Value>)> callback;
|
base::Callback<void(v8::Local<v8::Value>)> callback;
|
||||||
if (args->GetNext(&callback)) {
|
if (args->GetNext(&callback)) {
|
||||||
platform_util::OpenExternal(
|
platform_util::OpenExternal(
|
||||||
url, activate,
|
url, options,
|
||||||
base::Bind(&OnOpenExternalFinished, args->isolate(), callback));
|
base::Bind(&OnOpenExternalFinished, args->isolate(), callback));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return platform_util::OpenExternal(url, activate);
|
return platform_util::OpenExternal(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/callback_forward.h"
|
#include "base/callback_forward.h"
|
||||||
|
#include "base/files/file_path.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
@ -16,10 +17,6 @@
|
||||||
|
|
||||||
class GURL;
|
class GURL;
|
||||||
|
|
||||||
namespace base {
|
|
||||||
class FilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace platform_util {
|
namespace platform_util {
|
||||||
|
|
||||||
typedef base::Callback<void(const std::string&)> OpenExternalCallback;
|
typedef base::Callback<void(const std::string&)> OpenExternalCallback;
|
||||||
|
@ -32,6 +29,11 @@ bool ShowItemInFolder(const base::FilePath& full_path);
|
||||||
// Must be called from the UI thread.
|
// Must be called from the UI thread.
|
||||||
bool OpenItem(const base::FilePath& full_path);
|
bool OpenItem(const base::FilePath& full_path);
|
||||||
|
|
||||||
|
struct OpenExternalOptions {
|
||||||
|
bool activate = true;
|
||||||
|
base::FilePath working_dir;
|
||||||
|
};
|
||||||
|
|
||||||
// Open the given external protocol URL in the desktop's default manner.
|
// Open the given external protocol URL in the desktop's default manner.
|
||||||
// (For example, mailto: URLs in the default mail user agent.)
|
// (For example, mailto: URLs in the default mail user agent.)
|
||||||
bool OpenExternal(
|
bool OpenExternal(
|
||||||
|
@ -40,7 +42,7 @@ bool OpenExternal(
|
||||||
#else
|
#else
|
||||||
const GURL& url,
|
const GURL& url,
|
||||||
#endif
|
#endif
|
||||||
bool activate);
|
const OpenExternalOptions& options);
|
||||||
|
|
||||||
// The asynchronous version of OpenExternal.
|
// The asynchronous version of OpenExternal.
|
||||||
void OpenExternal(
|
void OpenExternal(
|
||||||
|
@ -49,7 +51,7 @@ void OpenExternal(
|
||||||
#else
|
#else
|
||||||
const GURL& url,
|
const GURL& url,
|
||||||
#endif
|
#endif
|
||||||
bool activate,
|
const OpenExternalOptions& options,
|
||||||
const OpenExternalCallback& callback);
|
const OpenExternalCallback& callback);
|
||||||
|
|
||||||
// Move a file to trash.
|
// Move a file to trash.
|
||||||
|
|
|
@ -80,7 +80,7 @@ bool OpenItem(const base::FilePath& full_path) {
|
||||||
return XDGOpen(full_path.value(), false);
|
return XDGOpen(full_path.value(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenExternal(const GURL& url, bool activate) {
|
bool OpenExternal(const GURL& url, const OpenExternalOptions& options) {
|
||||||
// Don't wait for exit, since we don't want to wait for the browser/email
|
// Don't wait for exit, since we don't want to wait for the browser/email
|
||||||
// client window to close before returning
|
// client window to close before returning
|
||||||
if (url.SchemeIs("mailto"))
|
if (url.SchemeIs("mailto"))
|
||||||
|
@ -90,10 +90,10 @@ bool OpenExternal(const GURL& url, bool activate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenExternal(const GURL& url,
|
void OpenExternal(const GURL& url,
|
||||||
bool activate,
|
const OpenExternalOptions& options,
|
||||||
const OpenExternalCallback& callback) {
|
const OpenExternalCallback& callback) {
|
||||||
// TODO(gabriel): Implement async open if callback is specified
|
// TODO(gabriel): Implement async open if callback is specified
|
||||||
callback.Run(OpenExternal(url, activate) ? "" : "Failed to open");
|
callback.Run(OpenExternal(url, options) ? "" : "Failed to open");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoveItemToTrash(const base::FilePath& full_path) {
|
bool MoveItemToTrash(const base::FilePath& full_path) {
|
||||||
|
|
|
@ -139,16 +139,16 @@ bool OpenItem(const base::FilePath& full_path) {
|
||||||
launchIdentifiers:NULL];
|
launchIdentifiers:NULL];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenExternal(const GURL& url, bool activate) {
|
bool OpenExternal(const GURL& url, const OpenExternalOptions& options) {
|
||||||
DCHECK([NSThread isMainThread]);
|
DCHECK([NSThread isMainThread]);
|
||||||
NSURL* ns_url = net::NSURLWithGURL(url);
|
NSURL* ns_url = net::NSURLWithGURL(url);
|
||||||
if (ns_url)
|
if (ns_url)
|
||||||
return OpenURL(ns_url, activate).empty();
|
return OpenURL(ns_url, options.activate).empty();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenExternal(const GURL& url,
|
void OpenExternal(const GURL& url,
|
||||||
bool activate,
|
const OpenExternalOptions& options,
|
||||||
const OpenExternalCallback& callback) {
|
const OpenExternalCallback& callback) {
|
||||||
NSURL* ns_url = net::NSURLWithGURL(url);
|
NSURL* ns_url = net::NSURLWithGURL(url);
|
||||||
if (!ns_url) {
|
if (!ns_url) {
|
||||||
|
@ -157,13 +157,13 @@ void OpenExternal(const GURL& url,
|
||||||
}
|
}
|
||||||
|
|
||||||
__block OpenExternalCallback c = callback;
|
__block OpenExternalCallback c = callback;
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
dispatch_async(
|
||||||
^{
|
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
__block std::string error = OpenURL(ns_url, activate);
|
__block std::string error = OpenURL(ns_url, options.activate);
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
c.Run(error);
|
c.Run(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoveItemToTrash(const base::FilePath& full_path) {
|
bool MoveItemToTrash(const base::FilePath& full_path) {
|
||||||
|
|
|
@ -294,15 +294,18 @@ bool OpenItem(const base::FilePath& full_path) {
|
||||||
return ui::win::OpenFileViaShell(full_path);
|
return ui::win::OpenFileViaShell(full_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenExternal(const base::string16& url, bool activate) {
|
bool OpenExternal(const base::string16& url,
|
||||||
|
const OpenExternalOptions& options) {
|
||||||
// Quote the input scheme to be sure that the command does not have
|
// Quote the input scheme to be sure that the command does not have
|
||||||
// parameters unexpected by the external program. This url should already
|
// parameters unexpected by the external program. This url should already
|
||||||
// have been escaped.
|
// have been escaped.
|
||||||
base::string16 escaped_url = L"\"" + url + L"\"";
|
base::string16 escaped_url = L"\"" + url + L"\"";
|
||||||
|
auto working_dir = options.working_dir.value();
|
||||||
|
|
||||||
if (reinterpret_cast<ULONG_PTR>(ShellExecuteW(
|
if (reinterpret_cast<ULONG_PTR>(
|
||||||
NULL, L"open", escaped_url.c_str(), NULL, NULL, SW_SHOWNORMAL)) <=
|
ShellExecuteW(nullptr, L"open", escaped_url.c_str(), nullptr,
|
||||||
32) {
|
working_dir.empty() ? nullptr : working_dir.c_str(),
|
||||||
|
SW_SHOWNORMAL)) <= 32) {
|
||||||
// We fail to execute the call. We could display a message to the user.
|
// We fail to execute the call. We could display a message to the user.
|
||||||
// TODO(nsylvain): we should also add a dialog to warn on errors. See
|
// TODO(nsylvain): we should also add a dialog to warn on errors. See
|
||||||
// bug 1136923.
|
// bug 1136923.
|
||||||
|
@ -312,10 +315,10 @@ bool OpenExternal(const base::string16& url, bool activate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenExternal(const base::string16& url,
|
void OpenExternal(const base::string16& url,
|
||||||
bool activate,
|
const OpenExternalOptions& options,
|
||||||
const OpenExternalCallback& callback) {
|
const OpenExternalCallback& callback) {
|
||||||
// TODO(gabriel): Implement async open if callback is specified
|
// TODO(gabriel): Implement async open if callback is specified
|
||||||
callback.Run(OpenExternal(url, activate) ? "" : "Failed to open");
|
callback.Run(OpenExternal(url, options) ? "" : "Failed to open");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoveItemToTrash(const base::FilePath& path) {
|
bool MoveItemToTrash(const base::FilePath& path) {
|
||||||
|
|
|
@ -37,9 +37,10 @@ Open the given file in the desktop's default manner.
|
||||||
### `shell.openExternal(url[, options, callback])`
|
### `shell.openExternal(url[, options, callback])`
|
||||||
|
|
||||||
* `url` String - Max 2081 characters on windows, or the function returns false.
|
* `url` String - Max 2081 characters on windows, or the function returns false.
|
||||||
* `options` Object (optional) _macOS_
|
* `options` Object (optional)
|
||||||
* `activate` Boolean - `true` to bring the opened application to the
|
* `activate` Boolean (optional) - `true` to bring the opened application to the
|
||||||
foreground. The default is `true`.
|
foreground. The default is `true`. _macOS_
|
||||||
|
* `workingDirectory` String (optional) - The working directory. _Windows_
|
||||||
* `callback` Function (optional) _macOS_ - If specified will perform the open asynchronously.
|
* `callback` Function (optional) _macOS_ - If specified will perform the open asynchronously.
|
||||||
* `error` Error
|
* `error` Error
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue