1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/platform_util.h" 6 7 #include <Carbon/Carbon.h> 8 #import <Cocoa/Cocoa.h> 9 #include <CoreServices/CoreServices.h> 10 11 #include "base/files/file_path.h" 12 #include "base/logging.h" 13 #include "base/mac/mac_logging.h" 14 #include "base/mac/scoped_aedesc.h" 15 #include "base/strings/sys_string_conversions.h" 16 #include "url/gurl.h" 17 18 namespace platform_util { 19 20 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) { 21 DCHECK([NSThread isMainThread]); 22 NSString* path_string = base::SysUTF8ToNSString(full_path.value()); 23 if (!path_string || ![[NSWorkspace sharedWorkspace] selectFile:path_string 24 inFileViewerRootedAtPath:nil]) 25 LOG(WARNING) << "NSWorkspace failed to select file " << full_path.value(); 26 } 27 28 // This function opens a file. This doesn't use LaunchServices or NSWorkspace 29 // because of two bugs: 30 // 1. Incorrect app activation with com.apple.quarantine: 31 // http://crbug.com/32921 32 // 2. Silent no-op for unassociated file types: http://crbug.com/50263 33 // Instead, an AppleEvent is constructed to tell the Finder to open the 34 // document. 35 void OpenItem(Profile* profile, const base::FilePath& full_path) { 36 DCHECK([NSThread isMainThread]); 37 NSString* path_string = base::SysUTF8ToNSString(full_path.value()); 38 if (!path_string) 39 return; 40 41 // Create the target of this AppleEvent, the Finder. 42 base::mac::ScopedAEDesc<AEAddressDesc> address; 43 const OSType finderCreatorCode = 'MACS'; 44 OSErr status = AECreateDesc(typeApplSignature, // type 45 &finderCreatorCode, // data 46 sizeof(finderCreatorCode), // dataSize 47 address.OutPointer()); // result 48 if (status != noErr) { 49 OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE target"; 50 return; 51 } 52 53 // Build the AppleEvent data structure that instructs Finder to open files. 54 base::mac::ScopedAEDesc<AppleEvent> theEvent; 55 status = AECreateAppleEvent(kCoreEventClass, // theAEEventClass 56 kAEOpenDocuments, // theAEEventID 57 address, // target 58 kAutoGenerateReturnID, // returnID 59 kAnyTransactionID, // transactionID 60 theEvent.OutPointer()); // result 61 if (status != noErr) { 62 OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE event"; 63 return; 64 } 65 66 // Create the list of files (only ever one) to open. 67 base::mac::ScopedAEDesc<AEDescList> fileList; 68 status = AECreateList(NULL, // factoringPtr 69 0, // factoredSize 70 false, // isRecord 71 fileList.OutPointer()); // resultList 72 if (status != noErr) { 73 OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE file list"; 74 return; 75 } 76 77 // Add the single path to the file list. C-style cast to avoid both a 78 // static_cast and a const_cast to get across the toll-free bridge. 79 CFURLRef pathURLRef = (CFURLRef)[NSURL fileURLWithPath:path_string]; 80 FSRef pathRef; 81 if (CFURLGetFSRef(pathURLRef, &pathRef)) { 82 status = AEPutPtr(fileList.OutPointer(), // theAEDescList 83 0, // index 84 typeFSRef, // typeCode 85 &pathRef, // dataPtr 86 sizeof(pathRef)); // dataSize 87 if (status != noErr) { 88 OSSTATUS_LOG(WARNING, status) 89 << "Could not add file path to AE list in OpenItem()"; 90 return; 91 } 92 } else { 93 LOG(WARNING) << "Could not get FSRef for path URL in OpenItem()"; 94 return; 95 } 96 97 // Attach the file list to the AppleEvent. 98 status = AEPutParamDesc(theEvent.OutPointer(), // theAppleEvent 99 keyDirectObject, // theAEKeyword 100 fileList); // theAEDesc 101 if (status != noErr) { 102 OSSTATUS_LOG(WARNING, status) 103 << "Could not put the AE file list the path in OpenItem()"; 104 return; 105 } 106 107 // Send the actual event. Do not care about the reply. 108 base::mac::ScopedAEDesc<AppleEvent> reply; 109 status = AESend(theEvent, // theAppleEvent 110 reply.OutPointer(), // reply 111 kAENoReply + kAEAlwaysInteract, // sendMode 112 kAENormalPriority, // sendPriority 113 kAEDefaultTimeout, // timeOutInTicks 114 NULL, // idleProc 115 NULL); // filterProc 116 if (status != noErr) { 117 OSSTATUS_LOG(WARNING, status) 118 << "Could not send AE to Finder in OpenItem()"; 119 } 120 } 121 122 void OpenExternal(Profile* profile, const GURL& url) { 123 DCHECK([NSThread isMainThread]); 124 NSString* url_string = base::SysUTF8ToNSString(url.spec()); 125 NSURL* ns_url = [NSURL URLWithString:url_string]; 126 if (!ns_url || ![[NSWorkspace sharedWorkspace] openURL:ns_url]) 127 LOG(WARNING) << "NSWorkspace failed to open URL " << url; 128 } 129 130 gfx::NativeWindow GetTopLevel(gfx::NativeView view) { 131 return [view window]; 132 } 133 134 gfx::NativeView GetParent(gfx::NativeView view) { 135 return nil; 136 } 137 138 bool IsWindowActive(gfx::NativeWindow window) { 139 return [window isKeyWindow] || [window isMainWindow]; 140 } 141 142 void ActivateWindow(gfx::NativeWindow window) { 143 [window makeKeyAndOrderFront:nil]; 144 } 145 146 bool IsVisible(gfx::NativeView view) { 147 // A reasonable approximation of how you'd expect this to behave. 148 return (view && 149 ![view isHiddenOrHasHiddenAncestor] && 150 [view window] && 151 [[view window] isVisible]); 152 } 153 154 bool IsSwipeTrackingFromScrollEventsEnabled() { 155 SEL selector = @selector(isSwipeTrackingFromScrollEventsEnabled); 156 return [NSEvent respondsToSelector:selector] 157 && [NSEvent performSelector:selector]; 158 } 159 160 } // namespace platform_util 161