diff --git a/atom/common/api/atom_api_shell.cc b/atom/common/api/atom_api_shell.cc index 03e8ed32e2..05214531e1 100644 --- a/atom/common/api/atom_api_shell.cc +++ b/atom/common/api/atom_api_shell.cc @@ -4,6 +4,7 @@ #include +#include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" @@ -49,12 +50,22 @@ bool OpenExternal( #endif mate::Arguments* args) { bool activate = true; - if (args->Length() == 2) { + if (args->Length() >= 2) { mate::Dictionary options; if (args->GetNext(&options)) { options.Get("activate", &activate); } } + + if (args->Length() >= 3) { + v8::Local peek = args->PeekNext(); + platform_util::OpenExternalCallback callback; + if (mate::Converter::FromV8(args->isolate(), peek, &callback)) { + return platform_util::OpenExternal(url, activate, callback); + } + return false; + } + return platform_util::OpenExternal(url, activate); } diff --git a/atom/common/platform_util.h b/atom/common/platform_util.h index 520faa48f4..8f2c5b6e52 100644 --- a/atom/common/platform_util.h +++ b/atom/common/platform_util.h @@ -6,6 +6,7 @@ #define ATOM_COMMON_PLATFORM_UTIL_H_ #include "build/build_config.h" +#include "base/callback_forward.h" #if defined(OS_WIN) #include "base/strings/string16.h" @@ -27,6 +28,8 @@ bool ShowItemInFolder(const base::FilePath& full_path); // Must be called from the UI thread. bool OpenItem(const base::FilePath& full_path); +typedef base::Callback OpenExternalCallback; + // Open the given external protocol URL in the desktop's default manner. // (For example, mailto: URLs in the default mail user agent.) bool OpenExternal( @@ -37,6 +40,15 @@ bool OpenExternal( #endif bool activate); +bool OpenExternal( +#if defined(OS_WIN) + const base::string16& url, +#else + const GURL& url, +#endif + bool activate, + const OpenExternalCallback& callback); + // Move a file to trash. bool MoveItemToTrash(const base::FilePath& full_path); diff --git a/atom/common/platform_util_linux.cc b/atom/common/platform_util_linux.cc index 82265901f1..8cfa516038 100644 --- a/atom/common/platform_util_linux.cc +++ b/atom/common/platform_util_linux.cc @@ -79,7 +79,7 @@ bool OpenItem(const base::FilePath& full_path) { return XDGOpen(full_path.value(), true); } -bool OpenExternal(const GURL& url, bool activate) { +bool openExternal(const GURL& url, bool activate) { // Don't wait for exit, since we don't want to wait for the browser/email // client window to close before returning if (url.SchemeIs("mailto")) @@ -88,6 +88,17 @@ bool OpenExternal(const GURL& url, bool activate) { return XDGOpen(url.spec(), false); } +bool OpenExternal(const GURL& url, bool activate) { + return openExternal(url, activate); +} + +bool OpenExternal(const GURL& url, bool activate, const OpenExternalCallback& callback) { + // TODO: Implement async open if callback is specified + bool opened = openExternal(url, activate); + callback(opened); + return opened; +} + bool MoveItemToTrash(const base::FilePath& full_path) { std::string trash; if (getenv(ELECTRON_TRASH) != NULL) { diff --git a/atom/common/platform_util_mac.mm b/atom/common/platform_util_mac.mm index 6d253c99c8..3f6c3470f7 100644 --- a/atom/common/platform_util_mac.mm +++ b/atom/common/platform_util_mac.mm @@ -7,6 +7,7 @@ #include #import +#include "base/cancelable_callback.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" @@ -128,7 +129,17 @@ bool OpenItem(const base::FilePath& full_path) { return status == noErr; } -bool OpenExternal(const GURL& url, bool activate) { +bool openURLInWorkspace(NSURL* ns_url, NSUInteger launchOptions) { + return [[NSWorkspace sharedWorkspace] openURLs: @[ns_url] + withAppBundleIdentifier: nil + options: launchOptions + additionalEventParamDescriptor: NULL + launchIdentifiers: NULL]; +} + +typedef bool(^OpenExternalBlock)(NSURL* ns_url, NSUInteger launchOptions); + +bool openExternal(const GURL& url, bool activate, OpenExternalBlock open) { DCHECK([NSThread isMainThread]); NSURL* ns_url = net::NSURLWithGURL(url); if (!ns_url) { @@ -149,11 +160,26 @@ bool OpenExternal(const GURL& url, bool activate) { if (!activate) launchOptions |= NSWorkspaceLaunchWithoutActivation; - return [[NSWorkspace sharedWorkspace] openURLs: @[ns_url] - withAppBundleIdentifier: nil - options: launchOptions - additionalEventParamDescriptor: NULL - launchIdentifiers: NULL]; + return open(ns_url, launchOptions); +} + +bool OpenExternal(const GURL& url, bool activate) { + return openExternal(url, activate, ^bool(NSURL* ns_url, NSUInteger launchOptions) { + return openURLInWorkspace(ns_url, launchOptions); + }); +} + +bool OpenExternal(const GURL& url, bool activate, const OpenExternalCallback& c) { + __block OpenExternalCallback callback = c; + return openExternal(url, activate, ^bool(NSURL* ns_url, NSUInteger launchOptions) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + bool opened = openURLInWorkspace(ns_url, launchOptions); + dispatch_async(dispatch_get_main_queue(), ^{ + callback.Run(opened); + }); + }); + return YES; + }); } bool MoveItemToTrash(const base::FilePath& full_path) { diff --git a/atom/common/platform_util_win.cc b/atom/common/platform_util_win.cc index 2c98115989..5483c783f1 100644 --- a/atom/common/platform_util_win.cc +++ b/atom/common/platform_util_win.cc @@ -299,7 +299,7 @@ bool OpenItem(const base::FilePath& full_path) { return ui::win::OpenFileViaShell(full_path); } -bool OpenExternal(const base::string16& url, bool activate) { +bool openExternal(const base::string16& url, bool activate) { // Quote the input scheme to be sure that the command does not have // parameters unexpected by the external program. This url should already // have been escaped. @@ -316,6 +316,17 @@ bool OpenExternal(const base::string16& url, bool activate) { return true; } +bool OpenExternal(const base::string16& url, bool activate) { + return openExternal(url, activate); +} + +bool OpenExternal(const base::string16& url, bool activate, const OpenExternalCallback& callback) { + // TODO: Implement async open if callback is specified + bool opened = openExternal(url, activate) + callback(opened); + return opened; +} + bool MoveItemToTrash(const base::FilePath& path) { base::win::ScopedCOMInitializer com_initializer; if (!com_initializer.succeeded()) diff --git a/docs/api/shell.md b/docs/api/shell.md index 89ae954ad6..d96df89153 100644 --- a/docs/api/shell.md +++ b/docs/api/shell.md @@ -34,18 +34,22 @@ Returns `Boolean` - Whether the item was successfully opened. Open the given file in the desktop's default manner. -### `shell.openExternal(url[, options])` +### `shell.openExternal(url[, options, callback])` * `url` String * `options` Object (optional) _macOS_ * `activate` Boolean - `true` to bring the opened application to the foreground. The default is `true`. +* `callback` Function (optional) _macOS_ Returns `Boolean` - Whether an application was available to open the URL. Open the given external protocol URL in the desktop's default manner. (For example, mailto: URLs in the user's default mail agent). +If a `callback` is passed, the API call will be asynchronous and the result +will be passed via `callback(opened)`. + ### `shell.moveItemToTrash(fullPath)` * `fullPath` String