Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / platform_util_mac.mm
blobe156ad0422adefc7591df6d3f3a576b952d1d5f7
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.
5 #include "chrome/browser/platform_util.h"
7 #include <Carbon/Carbon.h>
8 #import <Cocoa/Cocoa.h>
9 #include <CoreServices/CoreServices.h>
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/mac/mac_logging.h"
16 #import "base/mac/mac_util.h"
17 #include "base/mac/scoped_aedesc.h"
18 #import "base/mac/sdk_forward_declarations.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "chrome/browser/platform_util_internal.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "url/gurl.h"
24 namespace platform_util {
26 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) {
27   DCHECK([NSThread isMainThread]);
28   NSString* path_string = base::SysUTF8ToNSString(full_path.value());
29   if (!path_string || ![[NSWorkspace sharedWorkspace] selectFile:path_string
30                                         inFileViewerRootedAtPath:nil])
31     LOG(WARNING) << "NSWorkspace failed to select file " << full_path.value();
34 void OpenFileOnMainThread(const base::FilePath& full_path) {
35   DCHECK([NSThread isMainThread]);
36   NSString* path_string = base::SysUTF8ToNSString(full_path.value());
37   if (!path_string)
38     return;
40   // On Mavericks or later, NSWorkspaceLaunchWithErrorPresentation will
41   // properly handle Finder activation for quarantined files
42   // (http://crbug.com/32921) and unassociated file types
43   // (http://crbug.com/50263).
44   if (base::mac::IsOSMavericksOrLater()) {
45     NSURL* url = [NSURL fileURLWithPath:path_string];
46     if (!url)
47       return;
49     const NSWorkspaceLaunchOptions launch_options =
50         NSWorkspaceLaunchAsync | NSWorkspaceLaunchWithErrorPresentation;
51     [[NSWorkspace sharedWorkspace] openURLs:@[ url ]
52                     withAppBundleIdentifier:nil
53                                     options:launch_options
54              additionalEventParamDescriptor:nil
55                           launchIdentifiers:NULL];
56     return;
57   }
59   // On older OSes, both LaunchServices and NSWorkspace will fail silently for
60   // the two cases described above. On those platforms, use an AppleEvent to
61   // instruct the Finder to open the file.
63   // Create the target of this AppleEvent, the Finder.
64   base::mac::ScopedAEDesc<AEAddressDesc> address;
65   const OSType finderCreatorCode = 'MACS';
66   OSErr status = AECreateDesc(typeApplSignature,  // type
67                               &finderCreatorCode,  // data
68                               sizeof(finderCreatorCode),  // dataSize
69                               address.OutPointer());  // result
70   if (status != noErr) {
71     OSSTATUS_LOG(WARNING, status) << "Could not create OpenFile() AE target";
72     return;
73   }
75   // Build the AppleEvent data structure that instructs Finder to open files.
76   base::mac::ScopedAEDesc<AppleEvent> theEvent;
77   status = AECreateAppleEvent(kCoreEventClass,  // theAEEventClass
78                               kAEOpenDocuments,  // theAEEventID
79                               address,  // target
80                               kAutoGenerateReturnID,  // returnID
81                               kAnyTransactionID,  // transactionID
82                               theEvent.OutPointer());  // result
83   if (status != noErr) {
84     OSSTATUS_LOG(WARNING, status) << "Could not create OpenFile() AE event";
85     return;
86   }
88   // Create the list of files (only ever one) to open.
89   base::mac::ScopedAEDesc<AEDescList> fileList;
90   status = AECreateList(NULL,  // factoringPtr
91                         0,  // factoredSize
92                         false,  // isRecord
93                         fileList.OutPointer());  // resultList
94   if (status != noErr) {
95     OSSTATUS_LOG(WARNING, status) << "Could not create OpenFile() AE file list";
96     return;
97   }
99   // Add the single path to the file list.  C-style cast to avoid both a
100   // static_cast and a const_cast to get across the toll-free bridge.
101   CFURLRef pathURLRef = (CFURLRef)[NSURL fileURLWithPath:path_string];
102   FSRef pathRef;
103   if (CFURLGetFSRef(pathURLRef, &pathRef)) {
104     status = AEPutPtr(fileList.OutPointer(),  // theAEDescList
105                       0,  // index
106                       typeFSRef,  // typeCode
107                       &pathRef,  // dataPtr
108                       sizeof(pathRef));  // dataSize
109     if (status != noErr) {
110       OSSTATUS_LOG(WARNING, status)
111           << "Could not add file path to AE list in OpenFile()";
112       return;
113     }
114   } else {
115     LOG(WARNING) << "Could not get FSRef for path URL in OpenFile()";
116     return;
117   }
119   // Attach the file list to the AppleEvent.
120   status = AEPutParamDesc(theEvent.OutPointer(),  // theAppleEvent
121                           keyDirectObject,  // theAEKeyword
122                           fileList);  // theAEDesc
123   if (status != noErr) {
124     OSSTATUS_LOG(WARNING, status)
125         << "Could not put the AE file list the path in OpenFile()";
126     return;
127   }
129   // Send the actual event.  Do not care about the reply.
130   base::mac::ScopedAEDesc<AppleEvent> reply;
131   status = AESend(theEvent,  // theAppleEvent
132                   reply.OutPointer(),  // reply
133                   kAENoReply + kAEAlwaysInteract,  // sendMode
134                   kAENormalPriority,  // sendPriority
135                   kAEDefaultTimeout,  // timeOutInTicks
136                   NULL, // idleProc
137                   NULL);  // filterProc
138   if (status != noErr) {
139     OSSTATUS_LOG(WARNING, status)
140         << "Could not send AE to Finder in OpenFile()";
141   }
144 namespace internal {
146 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) {
147   switch (type) {
148     case OPEN_FILE:
149       content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
150                                        base::Bind(&OpenFileOnMainThread, path));
151       return;
152     case OPEN_FOLDER:
153       NSString* path_string = base::SysUTF8ToNSString(path.value());
154       if (!path_string)
155         return;
156       // Note that there exists a TOCTOU race between the time that |path| was
157       // verified as being a directory and when NSWorkspace invokes Finder (or
158       // alternative) to open |path_string|.
159       [[NSWorkspace sharedWorkspace] openFile:path_string];
160       return;
161   }
164 }  // namespace internal
166 void OpenExternal(Profile* profile, const GURL& url) {
167   DCHECK([NSThread isMainThread]);
168   NSString* url_string = base::SysUTF8ToNSString(url.spec());
169   NSURL* ns_url = [NSURL URLWithString:url_string];
170   if (!ns_url || ![[NSWorkspace sharedWorkspace] openURL:ns_url])
171     LOG(WARNING) << "NSWorkspace failed to open URL " << url;
174 gfx::NativeWindow GetTopLevel(gfx::NativeView view) {
175   return [view window];
178 gfx::NativeView GetViewForWindow(gfx::NativeWindow window) {
179   DCHECK(window);
180   DCHECK([window contentView]);
181   return [window contentView];
184 gfx::NativeView GetParent(gfx::NativeView view) {
185   return nil;
188 bool IsWindowActive(gfx::NativeWindow window) {
189   return [window isKeyWindow] || [window isMainWindow];
192 void ActivateWindow(gfx::NativeWindow window) {
193   [window makeKeyAndOrderFront:nil];
196 bool IsVisible(gfx::NativeView view) {
197   // A reasonable approximation of how you'd expect this to behave.
198   return (view &&
199           ![view isHiddenOrHasHiddenAncestor] &&
200           [view window] &&
201           [[view window] isVisible]);
204 bool IsSwipeTrackingFromScrollEventsEnabled() {
205   SEL selector = @selector(isSwipeTrackingFromScrollEventsEnabled);
206   return [NSEvent respondsToSelector:selector]
207       && [NSEvent performSelector:selector];
210 }  // namespace platform_util