Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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/file_path.h"
     12 #include "base/logging.h"
     13 #include "base/mac/scoped_aedesc.h"
     14 #include "base/sys_string_conversions.h"
     15 #include "googleurl/src/gurl.h"
     16 #include "grit/generated_resources.h"
     17 #include "ui/base/l10n/l10n_util.h"
     18 #include "ui/base/l10n/l10n_util_mac.h"
     19 
     20 namespace platform_util {
     21 
     22 void ShowItemInFolder(const FilePath& full_path) {
     23   DCHECK_EQ([NSThread currentThread], [NSThread mainThread]);
     24   NSString* path_string = base::SysUTF8ToNSString(full_path.value());
     25   if (!path_string || ![[NSWorkspace sharedWorkspace] selectFile:path_string
     26                                         inFileViewerRootedAtPath:nil])
     27     LOG(WARNING) << "NSWorkspace failed to select file " << full_path.value();
     28 }
     29 
     30 // This function opens a file.  This doesn't use LaunchServices or NSWorkspace
     31 // because of two bugs:
     32 //  1. Incorrect app activation with com.apple.quarantine:
     33 //     http://crbug.com/32921
     34 //  2. Silent no-op for unassociated file types: http://crbug.com/50263
     35 // Instead, an AppleEvent is constructed to tell the Finder to open the
     36 // document.
     37 void OpenItem(const FilePath& full_path) {
     38   DCHECK_EQ([NSThread currentThread], [NSThread mainThread]);
     39   NSString* path_string = base::SysUTF8ToNSString(full_path.value());
     40   if (!path_string)
     41     return;
     42 
     43   OSErr status;
     44 
     45   // Create the target of this AppleEvent, the Finder.
     46   base::mac::ScopedAEDesc<AEAddressDesc> address;
     47   const OSType finderCreatorCode = 'MACS';
     48   status = AECreateDesc(typeApplSignature,  // type
     49                         &finderCreatorCode,  // data
     50                         sizeof(finderCreatorCode),  // dataSize
     51                         address.OutPointer());  // result
     52   if (status != noErr) {
     53     LOG(WARNING) << "Could not create OpenItem() AE target";
     54     return;
     55   }
     56 
     57   // Build the AppleEvent data structure that instructs Finder to open files.
     58   base::mac::ScopedAEDesc<AppleEvent> theEvent;
     59   status = AECreateAppleEvent(kCoreEventClass,  // theAEEventClass
     60                               kAEOpenDocuments,  // theAEEventID
     61                               address,  // target
     62                               kAutoGenerateReturnID,  // returnID
     63                               kAnyTransactionID,  // transactionID
     64                               theEvent.OutPointer());  // result
     65   if (status != noErr) {
     66     LOG(WARNING) << "Could not create OpenItem() AE event";
     67     return;
     68   }
     69 
     70   // Create the list of files (only ever one) to open.
     71   base::mac::ScopedAEDesc<AEDescList> fileList;
     72   status = AECreateList(NULL,  // factoringPtr
     73                         0,  // factoredSize
     74                         false,  // isRecord
     75                         fileList.OutPointer());  // resultList
     76   if (status != noErr) {
     77     LOG(WARNING) << "Could not create OpenItem() AE file list";
     78     return;
     79   }
     80 
     81   // Add the single path to the file list.  C-style cast to avoid both a
     82   // static_cast and a const_cast to get across the toll-free bridge.
     83   CFURLRef pathURLRef = (CFURLRef)[NSURL fileURLWithPath:path_string];
     84   FSRef pathRef;
     85   if (CFURLGetFSRef(pathURLRef, &pathRef)) {
     86     status = AEPutPtr(fileList.OutPointer(),  // theAEDescList
     87                       0,  // index
     88                       typeFSRef,  // typeCode
     89                       &pathRef,  // dataPtr
     90                       sizeof(pathRef));  // dataSize
     91     if (status != noErr) {
     92       LOG(WARNING) << "Could not add file path to AE list in OpenItem()";
     93       return;
     94     }
     95   } else {
     96     LOG(WARNING) << "Could not get FSRef for path URL in OpenItem()";
     97     return;
     98   }
     99 
    100   // Attach the file list to the AppleEvent.
    101   status = AEPutParamDesc(theEvent.OutPointer(),  // theAppleEvent
    102                           keyDirectObject,  // theAEKeyword
    103                           fileList);  // theAEDesc
    104   if (status != noErr) {
    105     LOG(WARNING) << "Could not put the AE file list the path in OpenItem()";
    106     return;
    107   }
    108 
    109   // Send the actual event.  Do not care about the reply.
    110   base::mac::ScopedAEDesc<AppleEvent> reply;
    111   status = AESend(theEvent,  // theAppleEvent
    112                   reply.OutPointer(),  // reply
    113                   kAENoReply + kAEAlwaysInteract,  // sendMode
    114                   kAENormalPriority,  // sendPriority
    115                   kAEDefaultTimeout,  // timeOutInTicks
    116                   NULL, // idleProc
    117                   NULL);  // filterProc
    118   if (status != noErr) {
    119     LOG(WARNING) << "Could not send AE to Finder in OpenItem()";
    120   }
    121 }
    122 
    123 void OpenExternal(const GURL& url) {
    124   DCHECK_EQ([NSThread currentThread], [NSThread mainThread]);
    125   NSString* url_string = base::SysUTF8ToNSString(url.spec());
    126   NSURL* ns_url = [NSURL URLWithString:url_string];
    127   if (!ns_url || ![[NSWorkspace sharedWorkspace] openURL:ns_url])
    128     LOG(WARNING) << "NSWorkspace failed to open URL " << url;
    129 }
    130 
    131 gfx::NativeWindow GetTopLevel(gfx::NativeView view) {
    132   return [view window];
    133 }
    134 
    135 gfx::NativeView GetParent(gfx::NativeView view) {
    136   return nil;
    137 }
    138 
    139 bool IsWindowActive(gfx::NativeWindow window) {
    140   return [window isKeyWindow] || [window isMainWindow];
    141 }
    142 
    143 void ActivateWindow(gfx::NativeWindow window) {
    144   [window makeKeyAndOrderFront:nil];
    145 }
    146 
    147 bool IsVisible(gfx::NativeView view) {
    148   // A reasonable approximation of how you'd expect this to behave.
    149   return (view &&
    150           ![view isHiddenOrHasHiddenAncestor] &&
    151           [view window] &&
    152           [[view window] isVisible]);
    153 }
    154 
    155 void SimpleErrorBox(gfx::NativeWindow parent,
    156                     const string16& title,
    157                     const string16& message) {
    158   // Ignore the title; it's the window title on other platforms and ignorable.
    159   NSAlert* alert = [[[NSAlert alloc] init] autorelease];
    160   [alert addButtonWithTitle:l10n_util::GetNSString(IDS_OK)];
    161   [alert setMessageText:base::SysUTF16ToNSString(message)];
    162   [alert setAlertStyle:NSWarningAlertStyle];
    163   [alert runModal];
    164 }
    165 
    166 bool SimpleYesNoBox(gfx::NativeWindow parent,
    167                     const string16& title,
    168                     const string16& message) {
    169   // Ignore the title; it's the window title on other platforms and ignorable.
    170   NSAlert* alert = [[[NSAlert alloc] init] autorelease];
    171   [alert setMessageText:base::SysUTF16ToNSString(message)];
    172   [alert setAlertStyle:NSWarningAlertStyle];
    173 
    174   [alert addButtonWithTitle:
    175       l10n_util::GetNSString(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL)];
    176   [alert addButtonWithTitle:
    177       l10n_util::GetNSString(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL)];
    178 
    179   NSInteger result = [alert runModal];
    180   return result == NSAlertFirstButtonReturn;
    181 }
    182 
    183 std::string GetVersionStringModifier() {
    184 #if defined(GOOGLE_CHROME_BUILD)
    185   // Use the main application bundle and not the framework bundle. Keystone
    186   // keys don't live in the framework.
    187   NSBundle* bundle = [NSBundle mainBundle];
    188   NSString* channel = [bundle objectForInfoDictionaryKey:@"KSChannelID"];
    189 
    190   // Only ever return "", "unknown", "beta", "dev", or "canary" in a branded
    191   // build.
    192   if (![bundle objectForInfoDictionaryKey:@"KSProductID"]) {
    193     // This build is not Keystone-enabled, it can't have a channel.
    194     channel = @"unknown";
    195   } else if (!channel) {
    196     // For the stable channel, KSChannelID is not set.
    197     channel = @"";
    198   } else if ([channel isEqual:@"beta"] ||
    199              [channel isEqual:@"dev"] ||
    200              [channel isEqual:@"canary"]) {
    201     // do nothing.
    202   } else {
    203     channel = @"unknown";
    204   }
    205 
    206   return base::SysNSStringToUTF8(channel);
    207 #else
    208   return std::string();
    209 #endif
    210 }
    211 
    212 bool CanSetAsDefaultBrowser() {
    213   return GetVersionStringModifier().compare("canary") != 0;
    214 }
    215 
    216 }  // namespace platform_util
    217