diff --git a/atom/common/api/atom_api_shell.cc b/atom/common/api/atom_api_shell.cc index 6b3919159672..39e6aea7baf8 100644 --- a/atom/common/api/atom_api_shell.cc +++ b/atom/common/api/atom_api_shell.cc @@ -42,6 +42,16 @@ struct Converter { namespace { +void OnOpenExternalFinished( + v8::Isolate* isolate, + const base::Callback)>& callback, + const std::string& error) { + if (error.empty()) + callback.Run(v8::Null(isolate)); + else + callback.Run(v8::String::NewFromUtf8(isolate, error.c_str())); +} + bool OpenExternal( #if defined(OS_WIN) const base::string16& url, @@ -58,9 +68,11 @@ bool OpenExternal( } if (args->Length() >= 3) { - platform_util::OpenExternalCallback callback; + base::Callback)> callback; if (args->GetNext(&callback)) { - platform_util::OpenExternal(url, activate, callback); + platform_util::OpenExternal( + url, activate, + base::Bind(&OnOpenExternalFinished, args->isolate(), callback)); return true; } } diff --git a/atom/common/platform_util.h b/atom/common/platform_util.h index e3153cb082ce..dc4b4723589b 100644 --- a/atom/common/platform_util.h +++ b/atom/common/platform_util.h @@ -5,9 +5,10 @@ #ifndef ATOM_COMMON_PLATFORM_UTIL_H_ #define ATOM_COMMON_PLATFORM_UTIL_H_ +#include + #include "base/callback_forward.h" #include "build/build_config.h" -#include "v8/include/v8.h" #if defined(OS_WIN) #include "base/strings/string16.h" @@ -21,6 +22,8 @@ class FilePath; namespace platform_util { +typedef base::Callback OpenExternalCallback; + // Show the given file in a file manager. If possible, select the file. // Must be called from the UI thread. bool ShowItemInFolder(const base::FilePath& full_path); @@ -29,8 +32,6 @@ bool ShowItemInFolder(const base::FilePath& full_path); // Must be called from the UI thread. bool OpenItem(const base::FilePath& full_path); -typedef base::Callback error)> 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( @@ -41,6 +42,7 @@ bool OpenExternal( #endif bool activate); +// The asynchronous version of OpenExternal. void OpenExternal( #if defined(OS_WIN) const base::string16& url, diff --git a/atom/common/platform_util_mac.mm b/atom/common/platform_util_mac.mm index 5e2d948ad4cc..aa64678caffe 100644 --- a/atom/common/platform_util_mac.mm +++ b/atom/common/platform_util_mac.mm @@ -4,82 +4,97 @@ #include "atom/common/platform_util.h" -#include +#import #import -#include "base/cancelable_callback.h" +#include "base/callback.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/mac/mac_logging.h" #include "base/mac/scoped_aedesc.h" +#include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "net/base/mac/url_conversions.h" #include "url/gurl.h" namespace { -NSError *PlatformError(NSString* message) { - return [NSError errorWithDomain:@"Electron" - code:0 - userInfo:@{NSLocalizedDescriptionKey: message}]; -} - -NSString *MessageForOSStatus(OSStatus status, NSString* defaultMessage) { +std::string MessageForOSStatus(OSStatus status, const char* default_message) { switch (status) { - case kLSAppInTrashErr: return @"The application cannot be run because it is inside a Trash folder."; - case kLSUnknownErr: return @"An unknown error has occurred."; - case kLSNotAnApplicationErr: return @"The item to be registered is not an application."; - case kLSNotInitializedErr: return @"Formerly returned by LSInit on initialization failure; no longer used."; - case kLSDataUnavailableErr: return @"Data of the desired type is not available (for example, there is no kind string)."; - case kLSApplicationNotFoundErr: return @"No application in the Launch Services database matches the input criteria."; - case kLSDataErr: return @"Data is structured improperly (for example, an item’s information property list is malformed). Not used in macOS 10.4."; - case kLSLaunchInProgressErr: return @"A launch of the application is already in progress."; - case kLSServerCommunicationErr: return @"There is a problem communicating with the server process that maintains the Launch Services database."; - case kLSCannotSetInfoErr: return @"The filename extension to be hidden cannot be hidden."; - case kLSIncompatibleSystemVersionErr: return @"The application to be launched cannot run on the current Mac OS version."; - case kLSNoLaunchPermissionErr: return @"The user does not have permission to launch the application (on a managed network)."; - case kLSNoExecutableErr: return @"The executable file is missing or has an unusable format."; - case kLSNoClassicEnvironmentErr: return @"The Classic emulation environment was required but is not available."; - case kLSMultipleSessionsNotSupportedErr: return @"The application to be launched cannot run simultaneously in two different user sessions."; - default: return [NSString stringWithFormat:@"%@ (%@)", defaultMessage, @(status)]; + case kLSAppInTrashErr: + return "The application cannot be run because it is inside a Trash " + "folder."; + case kLSUnknownErr: + return "An unknown error has occurred."; + case kLSNotAnApplicationErr: + return "The item to be registered is not an application."; + case kLSNotInitializedErr: + return "Formerly returned by LSInit on initialization failure; " + "no longer used."; + case kLSDataUnavailableErr: + return "Data of the desired type is not available (for example, there is " + "no kind string)."; + case kLSApplicationNotFoundErr: + return "No application in the Launch Services database matches the input " + "criteria."; + case kLSDataErr: + return "Data is structured improperly (for example, an item’s " + "information property list is malformed). Not used in macOS 10.4."; + case kLSLaunchInProgressErr: + return "A launch of the application is already in progress."; + case kLSServerCommunicationErr: + return "There is a problem communicating with the server process that " + "maintains the Launch Services database."; + case kLSCannotSetInfoErr: + return "The filename extension to be hidden cannot be hidden."; + case kLSIncompatibleSystemVersionErr: + return "The application to be launched cannot run on the current Mac OS " + "version."; + case kLSNoLaunchPermissionErr: + return "The user does not have permission to launch the application (on a" + "managed network)."; + case kLSNoExecutableErr: + return "The executable file is missing or has an unusable format."; + case kLSNoClassicEnvironmentErr: + return "The Classic emulation environment was required but is not " + "available."; + case kLSMultipleSessionsNotSupportedErr: + return "The application to be launched cannot run simultaneously in two " + "different user sessions."; + default: + return base::StringPrintf("%s (%d)", default_message, status); } } -// This may be called from a global dispatch queue, the methods used here are thread safe, -// including LSGetApplicationForURL (> 10.2) and NSWorkspace#openURLs. -NSError* OpenURL(NSURL* ns_url, bool activate) { +// This may be called from a global dispatch queue, the methods used here are +// thread safe, including LSGetApplicationForURL (> 10.2) and +// NSWorkspace#openURLs. +std::string OpenURL(NSURL* ns_url, bool activate) { CFURLRef openingApp = NULL; OSStatus status = LSGetApplicationForURL((CFURLRef)ns_url, kLSRolesAll, NULL, &openingApp); - if (status != noErr) { - return PlatformError(MessageForOSStatus(status, @"Failed to open")); - } + if (status != noErr) + return MessageForOSStatus(status, "Failed to open"); + CFRelease(openingApp); // NOT A BUG; LSGetApplicationForURL retains for us NSUInteger launchOptions = NSWorkspaceLaunchDefault; if (!activate) launchOptions |= NSWorkspaceLaunchWithoutActivation; - bool opened = [[NSWorkspace sharedWorkspace] openURLs: @[ns_url] - withAppBundleIdentifier: nil - options: launchOptions - additionalEventParamDescriptor: nil - launchIdentifiers: nil]; - if (!opened) return PlatformError(@"Failed to open URL"); - return nil; -} + bool opened = [[NSWorkspace sharedWorkspace] + openURLs:@[ns_url] + withAppBundleIdentifier:nil + options:launchOptions + additionalEventParamDescriptor:nil + launchIdentifiers:nil]; + if (!opened) + return "Failed to open URL"; -v8::Local ConvertNSError(v8::Isolate* isolate, NSError* platformError) { - if (!platformError) { - return v8::Null(isolate); - } else { - v8::Local error_message = - v8::String::NewFromUtf8(isolate, platformError.localizedDescription.UTF8String); - return v8::Exception::Error(error_message); - } + return ""; } } // namespace @@ -199,27 +214,24 @@ bool OpenItem(const base::FilePath& full_path) { bool OpenExternal(const GURL& url, bool activate) { DCHECK([NSThread isMainThread]); NSURL* ns_url = net::NSURLWithGURL(url); - if (!ns_url) { - return false; - } - NSError *error = OpenURL(ns_url, activate); - return !error; + if (ns_url) + return OpenURL(ns_url, activate).empty(); + return false; } -void OpenExternal(const GURL& url, bool activate, const OpenExternalCallback& c) { +void OpenExternal(const GURL& url, bool activate, + const OpenExternalCallback& callback) { NSURL* ns_url = net::NSURLWithGURL(url); if (!ns_url) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - c.Run(ConvertNSError(isolate, PlatformError(@"Invalid URL"))); + callback.Run("Invalid URL"); return; } - __block OpenExternalCallback callback = c; + __block OpenExternalCallback c = callback; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *openError = OpenURL(ns_url, activate); + __block std::string error = OpenURL(ns_url, activate); dispatch_async(dispatch_get_main_queue(), ^{ - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - callback.Run(ConvertNSError(isolate, openError)); + c.Run(error); }); }); }