From 99a5258999cabb2c8e65306d05c3355b43b9f644 Mon Sep 17 00:00:00 2001 From: Gabriel Handford Date: Wed, 2 Nov 2016 16:20:26 -0700 Subject: [PATCH] Callback uses (platform specific) error, with message --- atom/common/api/atom_api_shell.cc | 3 +- atom/common/platform_util.h | 5 ++- atom/common/platform_util_linux.cc | 10 +++-- atom/common/platform_util_mac.mm | 90 +++++++++++++++++++++++++++----------- atom/common/platform_util_win.cc | 10 +++-- 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/atom/common/api/atom_api_shell.cc b/atom/common/api/atom_api_shell.cc index 894c85e..eb91e53 100644 --- a/atom/common/api/atom_api_shell.cc +++ b/atom/common/api/atom_api_shell.cc @@ -62,7 +62,8 @@ bool OpenExternal( platform_util::OpenExternalCallback callback; if (mate::Converter::FromV8( args->isolate(), peek, &callback)) { - return platform_util::OpenExternal(url, activate, callback); + platform_util::OpenExternal(url, activate, callback); + return true; } return false; } diff --git a/atom/common/platform_util.h b/atom/common/platform_util.h index d7bb920..e3153cb 100644 --- a/atom/common/platform_util.h +++ b/atom/common/platform_util.h @@ -7,6 +7,7 @@ #include "base/callback_forward.h" #include "build/build_config.h" +#include "v8/include/v8.h" #if defined(OS_WIN) #include "base/strings/string16.h" @@ -28,7 +29,7 @@ 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; +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.) @@ -40,7 +41,7 @@ bool OpenExternal( #endif bool activate); -bool OpenExternal( +void OpenExternal( #if defined(OS_WIN) const base::string16& url, #else diff --git a/atom/common/platform_util_linux.cc b/atom/common/platform_util_linux.cc index ee08d28..039e26e 100644 --- a/atom/common/platform_util_linux.cc +++ b/atom/common/platform_util_linux.cc @@ -89,12 +89,16 @@ bool OpenExternal(const GURL& url, bool activate) { return XDGOpen(url.spec(), false); } -bool OpenExternal(const GURL& url, bool activate, +void OpenExternal(const GURL& url, bool activate, const OpenExternalCallback& callback) { // TODO(gabriel): Implement async open if callback is specified bool opened = OpenExternal(url, activate); - callback.Run(opened); - return opened; + if (!opened) { + callback.Run(v8::Exception::Error( + v8::String::NewFromUtf8(isolate, @"Failed to open"))); + } else { + callback.Run(v8::Null(isolate);) + } } bool MoveItemToTrash(const base::FilePath& full_path) { diff --git a/atom/common/platform_util_mac.mm b/atom/common/platform_util_mac.mm index b88bd53..0bd902a 100644 --- a/atom/common/platform_util_mac.mm +++ b/atom/common/platform_util_mac.mm @@ -19,30 +19,43 @@ namespace { -bool OpenURLInWorkspace(NSURL* ns_url, NSUInteger launchOptions) { - return [[NSWorkspace sharedWorkspace] openURLs: @[ns_url] - withAppBundleIdentifier: nil - options: launchOptions - additionalEventParamDescriptor: NULL - launchIdentifiers: NULL]; +NSError *PlatformError(NSString* message) { + return [NSError errorWithDomain:@"Electron" + code:0 + userInfo:@{NSLocalizedDescriptionKey: message}]; } -typedef bool(^OpenExternalBlock)(NSURL* ns_url, NSUInteger launchOptions); - -bool OpenExternalWithBlock(const GURL& url, bool activate, OpenExternalBlock open) { - DCHECK([NSThread isMainThread]); - NSURL* ns_url = net::NSURLWithGURL(url); - if (!ns_url) { - return false; +NSString *MessageForOSStatus(OSStatus status) { + 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:@"Failed to open (%@)", @(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) { CFURLRef openingApp = NULL; OSStatus status = LSGetApplicationForURL((CFURLRef)ns_url, kLSRolesAll, NULL, &openingApp); if (status != noErr) { - return false; + return PlatformError(MessageForOSStatus(status)); } CFRelease(openingApp); // NOT A BUG; LSGetApplicationForURL retains for us @@ -50,7 +63,23 @@ bool OpenExternalWithBlock(const GURL& url, bool activate, OpenExternalBlock ope if (!activate) launchOptions |= NSWorkspaceLaunchWithoutActivation; - return open(ns_url, launchOptions); + 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; +} + +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); + } } } // namespace @@ -168,21 +197,30 @@ bool OpenItem(const base::FilePath& full_path) { } bool OpenExternal(const GURL& url, bool activate) { - return OpenExternalWithBlock(url, activate, ^bool(NSURL* ns_url, NSUInteger launchOptions) { - return OpenURLInWorkspace(ns_url, launchOptions); - }); + DCHECK([NSThread isMainThread]); + NSURL* ns_url = net::NSURLWithGURL(url); + if (!ns_url) { + return false; + } + NSError *error = OpenURL(ns_url, activate); + return error ? false : true; } -bool OpenExternal(const GURL& url, bool activate, const OpenExternalCallback& c) { +void OpenExternal(const GURL& url, bool activate, const OpenExternalCallback& c) { + NSURL* ns_url = net::NSURLWithGURL(url); + if (!ns_url) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + c.Run(ConvertNSError(isolate, PlatformError(@"Invalid URL"))); + return; + } + __block OpenExternalCallback callback = c; - return OpenExternalWithBlock(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); - }); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSError *openError = OpenURL(ns_url, activate); + dispatch_async(dispatch_get_main_queue(), ^{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + callback.Run(ConvertNSError(isolate, openError)); }); - return YES; }); } diff --git a/atom/common/platform_util_win.cc b/atom/common/platform_util_win.cc index d2f2b1c..acf663b 100644 --- a/atom/common/platform_util_win.cc +++ b/atom/common/platform_util_win.cc @@ -316,12 +316,16 @@ bool OpenExternal(const base::string16& url, bool activate) { return true; } -bool OpenExternal(const base::string16& url, bool activate, +void OpenExternal(const base::string16& url, bool activate, const OpenExternalCallback& callback) { // TODO(gabriel): Implement async open if callback is specified bool opened = OpenExternal(url, activate); - callback.Run(opened); - return opened; + if (!opened) { + callback.Run(v8::Exception::Error( + v8::String::NewFromUtf8(isolate, @"Failed to open"))); + } else { + callback.Run(v8::Null(isolate);) + } } bool MoveItemToTrash(const base::FilePath& path) { -- 2.7.4