| 
									
										
										
										
											2014-10-31 11:17:05 -07:00
										 |  |  |  | // Copyright (c) 2013 GitHub, Inc. | 
					
						
							| 
									
										
										
										
											2014-04-25 17:49:37 +08:00
										 |  |  |  | // Use of this source code is governed by the MIT license that can be | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | // found in the LICENSE file. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-16 08:30:26 +08:00
										 |  |  |  | #include "atom/common/platform_util.h" | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | #include <Carbon/Carbon.h> | 
					
						
							|  |  |  |  | #import <Cocoa/Cocoa.h> | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-13 13:28:11 -07:00
										 |  |  |  | #include "base/cancelable_callback.h" | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | #include "base/files/file_path.h" | 
					
						
							| 
									
										
										
										
											2016-06-20 21:14:17 +09:00
										 |  |  |  | #include "base/files/file_util.h" | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | #include "base/logging.h" | 
					
						
							|  |  |  |  | #include "base/mac/mac_logging.h" | 
					
						
							|  |  |  |  | #include "base/mac/scoped_aedesc.h" | 
					
						
							|  |  |  |  | #include "base/strings/sys_string_conversions.h" | 
					
						
							| 
									
										
										
										
											2015-12-24 12:43:07 +08:00
										 |  |  |  | #include "net/base/mac/url_conversions.h" | 
					
						
							| 
									
										
										
										
											2013-12-11 15:48:19 +08:00
										 |  |  |  | #include "url/gurl.h" | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  | namespace { | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  | NSError *PlatformError(NSString* message) { | 
					
						
							|  |  |  |  |   return [NSError errorWithDomain:@"Electron" | 
					
						
							|  |  |  |  |                   code:0 | 
					
						
							|  |  |  |  |                   userInfo:@{NSLocalizedDescriptionKey: message}]; | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 16:26:30 -07:00
										 |  |  |  | NSString *MessageForOSStatus(OSStatus status, NSString* defaultMessage) { | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  |   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."; | 
					
						
							| 
									
										
										
										
											2016-11-02 16:26:30 -07:00
										 |  |  |  |     default: return [NSString stringWithFormat:@"%@ (%@)", defaultMessage, @(status)]; | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  | // 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) { | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  |   CFURLRef openingApp = NULL; | 
					
						
							|  |  |  |  |   OSStatus status = LSGetApplicationForURL((CFURLRef)ns_url, | 
					
						
							|  |  |  |  |                                            kLSRolesAll, | 
					
						
							|  |  |  |  |                                            NULL, | 
					
						
							|  |  |  |  |                                            &openingApp); | 
					
						
							|  |  |  |  |   if (status != noErr) { | 
					
						
							| 
									
										
										
										
											2016-11-02 16:26:30 -07:00
										 |  |  |  |     return PlatformError(MessageForOSStatus(status, @"Failed to open")); | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  |   CFRelease(openingApp);  // NOT A BUG; LSGetApplicationForURL retains for us | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   NSUInteger launchOptions = NSWorkspaceLaunchDefault; | 
					
						
							|  |  |  |  |   if (!activate) | 
					
						
							|  |  |  |  |     launchOptions |= NSWorkspaceLaunchWithoutActivation; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  |   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<v8::Value> ConvertNSError(v8::Isolate* isolate, NSError* platformError) { | 
					
						
							|  |  |  |  |   if (!platformError) { | 
					
						
							|  |  |  |  |     return v8::Null(isolate); | 
					
						
							|  |  |  |  |   } else { | 
					
						
							|  |  |  |  |     v8::Local<v8::String> error_message = | 
					
						
							|  |  |  |  |       v8::String::NewFromUtf8(isolate, platformError.localizedDescription.UTF8String); | 
					
						
							|  |  |  |  |     return v8::Exception::Error(error_message); | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-10-25 15:19:34 -07:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | }  // namespace | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | namespace platform_util { | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-12 14:22:29 -07:00
										 |  |  |  | bool ShowItemInFolder(const base::FilePath& path) { | 
					
						
							| 
									
										
										
										
											2016-06-20 21:14:17 +09:00
										 |  |  |  |   // The API only takes absolute path. | 
					
						
							|  |  |  |  |   base::FilePath full_path = | 
					
						
							|  |  |  |  |       path.IsAbsolute() ? path : base::MakeAbsoluteFilePath(path); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   DCHECK([NSThread isMainThread]); | 
					
						
							|  |  |  |  |   NSString* path_string = base::SysUTF8ToNSString(full_path.value()); | 
					
						
							|  |  |  |  |   if (!path_string || ![[NSWorkspace sharedWorkspace] selectFile:path_string | 
					
						
							| 
									
										
										
										
											2016-09-12 14:22:29 -07:00
										 |  |  |  |                                         inFileViewerRootedAtPath:@""]) { | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |     LOG(WARNING) << "NSWorkspace failed to select file " << full_path.value(); | 
					
						
							| 
									
										
										
										
											2016-09-12 14:22:29 -07:00
										 |  |  |  |     return false; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // This function opens a file.  This doesn't use LaunchServices or NSWorkspace | 
					
						
							|  |  |  |  | // because of two bugs: | 
					
						
							|  |  |  |  | //  1. Incorrect app activation with com.apple.quarantine: | 
					
						
							|  |  |  |  | //     http://crbug.com/32921 | 
					
						
							|  |  |  |  | //  2. Silent no-op for unassociated file types: http://crbug.com/50263 | 
					
						
							|  |  |  |  | // Instead, an AppleEvent is constructed to tell the Finder to open the | 
					
						
							|  |  |  |  | // document. | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  | bool OpenItem(const base::FilePath& full_path) { | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   DCHECK([NSThread isMainThread]); | 
					
						
							|  |  |  |  |   NSString* path_string = base::SysUTF8ToNSString(full_path.value()); | 
					
						
							|  |  |  |  |   if (!path_string) | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Create the target of this AppleEvent, the Finder. | 
					
						
							|  |  |  |  |   base::mac::ScopedAEDesc<AEAddressDesc> address; | 
					
						
							|  |  |  |  |   const OSType finderCreatorCode = 'MACS'; | 
					
						
							|  |  |  |  |   OSErr status = AECreateDesc(typeApplSignature,  // type | 
					
						
							|  |  |  |  |                               &finderCreatorCode,  // data | 
					
						
							|  |  |  |  |                               sizeof(finderCreatorCode),  // dataSize | 
					
						
							|  |  |  |  |                               address.OutPointer());  // result | 
					
						
							|  |  |  |  |   if (status != noErr) { | 
					
						
							|  |  |  |  |     OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE target"; | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Build the AppleEvent data structure that instructs Finder to open files. | 
					
						
							|  |  |  |  |   base::mac::ScopedAEDesc<AppleEvent> theEvent; | 
					
						
							|  |  |  |  |   status = AECreateAppleEvent(kCoreEventClass,  // theAEEventClass | 
					
						
							|  |  |  |  |                               kAEOpenDocuments,  // theAEEventID | 
					
						
							|  |  |  |  |                               address,  // target | 
					
						
							|  |  |  |  |                               kAutoGenerateReturnID,  // returnID | 
					
						
							|  |  |  |  |                               kAnyTransactionID,  // transactionID | 
					
						
							|  |  |  |  |                               theEvent.OutPointer());  // result | 
					
						
							|  |  |  |  |   if (status != noErr) { | 
					
						
							|  |  |  |  |     OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE event"; | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Create the list of files (only ever one) to open. | 
					
						
							|  |  |  |  |   base::mac::ScopedAEDesc<AEDescList> fileList; | 
					
						
							|  |  |  |  |   status = AECreateList(NULL,  // factoringPtr | 
					
						
							|  |  |  |  |                         0,  // factoredSize | 
					
						
							|  |  |  |  |                         false,  // isRecord | 
					
						
							|  |  |  |  |                         fileList.OutPointer());  // resultList | 
					
						
							|  |  |  |  |   if (status != noErr) { | 
					
						
							|  |  |  |  |     OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE file list"; | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Add the single path to the file list.  C-style cast to avoid both a | 
					
						
							|  |  |  |  |   // static_cast and a const_cast to get across the toll-free bridge. | 
					
						
							|  |  |  |  |   CFURLRef pathURLRef = (CFURLRef)[NSURL fileURLWithPath:path_string]; | 
					
						
							|  |  |  |  |   FSRef pathRef; | 
					
						
							|  |  |  |  |   if (CFURLGetFSRef(pathURLRef, &pathRef)) { | 
					
						
							|  |  |  |  |     status = AEPutPtr(fileList.OutPointer(),  // theAEDescList | 
					
						
							|  |  |  |  |                       0,  // index | 
					
						
							|  |  |  |  |                       typeFSRef,  // typeCode | 
					
						
							|  |  |  |  |                       &pathRef,  // dataPtr | 
					
						
							|  |  |  |  |                       sizeof(pathRef));  // dataSize | 
					
						
							|  |  |  |  |     if (status != noErr) { | 
					
						
							|  |  |  |  |       OSSTATUS_LOG(WARNING, status) | 
					
						
							|  |  |  |  |           << "Could not add file path to AE list in OpenItem()"; | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } else { | 
					
						
							|  |  |  |  |     LOG(WARNING) << "Could not get FSRef for path URL in OpenItem()"; | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Attach the file list to the AppleEvent. | 
					
						
							|  |  |  |  |   status = AEPutParamDesc(theEvent.OutPointer(),  // theAppleEvent | 
					
						
							|  |  |  |  |                           keyDirectObject,  // theAEKeyword | 
					
						
							|  |  |  |  |                           fileList);  // theAEDesc | 
					
						
							|  |  |  |  |   if (status != noErr) { | 
					
						
							|  |  |  |  |     OSSTATUS_LOG(WARNING, status) | 
					
						
							|  |  |  |  |         << "Could not put the AE file list the path in OpenItem()"; | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Send the actual event.  Do not care about the reply. | 
					
						
							|  |  |  |  |   base::mac::ScopedAEDesc<AppleEvent> reply; | 
					
						
							|  |  |  |  |   status = AESend(theEvent,  // theAppleEvent | 
					
						
							|  |  |  |  |                   reply.OutPointer(),  // reply | 
					
						
							|  |  |  |  |                   kAENoReply + kAEAlwaysInteract,  // sendMode | 
					
						
							|  |  |  |  |                   kAENormalPriority,  // sendPriority | 
					
						
							|  |  |  |  |                   kAEDefaultTimeout,  // timeOutInTicks | 
					
						
							|  |  |  |  |                   NULL, // idleProc | 
					
						
							|  |  |  |  |                   NULL);  // filterProc | 
					
						
							|  |  |  |  |   if (status != noErr) { | 
					
						
							|  |  |  |  |     OSSTATUS_LOG(WARNING, status) | 
					
						
							|  |  |  |  |         << "Could not send AE to Finder in OpenItem()"; | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-09-12 14:29:06 -07:00
										 |  |  |  |   return status == noErr; | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-13 13:28:11 -07:00
										 |  |  |  | bool OpenExternal(const GURL& url, bool activate) { | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  |   DCHECK([NSThread isMainThread]); | 
					
						
							|  |  |  |  |   NSURL* ns_url = net::NSURLWithGURL(url); | 
					
						
							|  |  |  |  |   if (!ns_url) { | 
					
						
							|  |  |  |  |     return false; | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-11-02 16:23:37 -07:00
										 |  |  |  |   NSError *error = OpenURL(ns_url, activate); | 
					
						
							|  |  |  |  |   return !error; | 
					
						
							| 
									
										
										
										
											2016-10-13 13:28:11 -07:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  | 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; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-13 13:28:11 -07:00
										 |  |  |  |   __block OpenExternalCallback callback = c; | 
					
						
							| 
									
										
										
										
											2016-11-02 16:20:26 -07:00
										 |  |  |  |   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)); | 
					
						
							| 
									
										
										
										
											2016-10-13 13:28:11 -07:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-27 18:31:57 +05:30
										 |  |  |  | bool MoveItemToTrash(const base::FilePath& full_path) { | 
					
						
							| 
									
										
										
										
											2013-04-29 21:57:05 +08:00
										 |  |  |  |   NSString* path_string = base::SysUTF8ToNSString(full_path.value()); | 
					
						
							| 
									
										
										
										
											2015-06-18 13:09:02 +08:00
										 |  |  |  |   BOOL status = [[NSFileManager defaultManager] | 
					
						
							|  |  |  |  |                 trashItemAtURL:[NSURL fileURLWithPath:path_string] | 
					
						
							|  |  |  |  |                 resultingItemURL:nil | 
					
						
							|  |  |  |  |                 error:nil]; | 
					
						
							|  |  |  |  |   if (!path_string || !status) | 
					
						
							| 
									
										
										
										
											2013-04-29 21:57:05 +08:00
										 |  |  |  |     LOG(WARNING) << "NSWorkspace failed to move file " << full_path.value() | 
					
						
							|  |  |  |  |                  << " to trash"; | 
					
						
							| 
									
										
										
										
											2015-04-08 19:31:46 +05:30
										 |  |  |  |   return status; | 
					
						
							| 
									
										
										
										
											2013-04-29 21:57:05 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-29 22:10:03 +08:00
										 |  |  |  | void Beep() { | 
					
						
							|  |  |  |  |   NSBeep(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-29 20:41:11 +08:00
										 |  |  |  | }  // namespace platform_util |