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 18namespace platform_util { 19 20void 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. 35void 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 122void 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 130gfx::NativeWindow GetTopLevel(gfx::NativeView view) { 131 return [view window]; 132} 133 134gfx::NativeView GetParent(gfx::NativeView view) { 135 return nil; 136} 137 138bool IsWindowActive(gfx::NativeWindow window) { 139 return [window isKeyWindow] || [window isMainWindow]; 140} 141 142void ActivateWindow(gfx::NativeWindow window) { 143 [window makeKeyAndOrderFront:nil]; 144} 145 146bool 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 154bool IsSwipeTrackingFromScrollEventsEnabled() { 155 SEL selector = @selector(isSwipeTrackingFromScrollEventsEnabled); 156 return [NSEvent respondsToSelector:selector] 157 && [NSEvent performSelector:selector]; 158} 159 160} // namespace platform_util 161