Home | History | Annotate | Download | only in app
      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 // On Mac, shortcuts can't have command-line arguments. Instead, produce small
      6 // app bundles which locate the Chromium framework and load it, passing the
      7 // appropriate data. This is the code for such an app bundle. It should be kept
      8 // minimal and do as little work as possible (with as much work done on
      9 // framework side as possible).
     10 
     11 #include <dlfcn.h>
     12 
     13 #include <CoreFoundation/CoreFoundation.h>
     14 #import <Foundation/Foundation.h>
     15 
     16 #include "base/file_util.h"
     17 #include "base/files/file_path.h"
     18 #include "base/logging.h"
     19 #include "base/mac/foundation_util.h"
     20 #include "base/mac/scoped_nsautorelease_pool.h"
     21 #include "base/strings/sys_string_conversions.h"
     22 #import "chrome/common/mac/app_mode_chrome_locator.h"
     23 #include "chrome/common/mac/app_mode_common.h"
     24 
     25 namespace {
     26 
     27 void LoadFramework(void** cr_dylib, app_mode::ChromeAppModeInfo* info) {
     28   using base::SysNSStringToUTF8;
     29   using base::SysNSStringToUTF16;
     30   using base::mac::CFToNSCast;
     31   using base::mac::CFCastStrict;
     32   using base::mac::NSToCFCast;
     33 
     34   base::mac::ScopedNSAutoreleasePool scoped_pool;
     35 
     36   // Get the current main bundle, i.e., that of the app loader that's running.
     37   NSBundle* app_bundle = [NSBundle mainBundle];
     38   CHECK(app_bundle) << "couldn't get loader bundle";
     39 
     40   // ** 1: Get path to outer Chrome bundle.
     41   // Get the bundle ID of the browser that created this app bundle.
     42   NSString* cr_bundle_id = base::mac::ObjCCast<NSString>(
     43       [app_bundle objectForInfoDictionaryKey:app_mode::kBrowserBundleIDKey]);
     44   CHECK(cr_bundle_id) << "couldn't get browser bundle ID";
     45 
     46   // First check if Chrome exists at the last known location.
     47   base::FilePath cr_bundle_path;
     48   NSString* cr_bundle_path_ns =
     49       [CFToNSCast(CFCastStrict<CFStringRef>(CFPreferencesCopyAppValue(
     50           NSToCFCast(app_mode::kLastRunAppBundlePathPrefsKey),
     51           NSToCFCast(cr_bundle_id)))) autorelease];
     52   cr_bundle_path = base::mac::NSStringToFilePath(cr_bundle_path_ns);
     53   bool found_bundle =
     54       !cr_bundle_path.empty() && base::DirectoryExists(cr_bundle_path);
     55 
     56   if (!found_bundle) {
     57     // If no such bundle path exists, try to search by bundle ID.
     58     if (!app_mode::FindBundleById(cr_bundle_id, &cr_bundle_path)) {
     59       // TODO(jeremy): Display UI to allow user to manually locate the Chrome
     60       // bundle.
     61       LOG(FATAL) << "Failed to locate bundle by identifier";
     62     }
     63   }
     64 
     65   // ** 2: Read information from the Chrome bundle.
     66   string16 raw_version_str;
     67   base::FilePath version_path;
     68   base::FilePath framework_shlib_path;
     69   if (!app_mode::GetChromeBundleInfo(cr_bundle_path, &raw_version_str,
     70           &version_path, &framework_shlib_path)) {
     71     LOG(FATAL) << "Couldn't ready Chrome bundle info";
     72   }
     73 
     74   // ** 3: Fill in ChromeAppModeInfo.
     75   info->chrome_outer_bundle_path = cr_bundle_path;
     76   info->chrome_versioned_path = version_path;
     77   info->app_mode_bundle_path =
     78       base::mac::NSStringToFilePath([app_bundle bundlePath]);
     79 
     80   // Read information about the this app shortcut from the Info.plist.
     81   // Don't check for null-ness on optional items.
     82   NSDictionary* info_plist = [app_bundle infoDictionary];
     83   CHECK(info_plist) << "couldn't get loader Info.plist";
     84 
     85   info->app_mode_id = SysNSStringToUTF8(
     86       [info_plist objectForKey:app_mode::kCrAppModeShortcutIDKey]);
     87   CHECK(info->app_mode_id.size()) << "couldn't get app shortcut ID";
     88 
     89   info->app_mode_name = SysNSStringToUTF16(
     90       [info_plist objectForKey:app_mode::kCrAppModeShortcutNameKey]);
     91 
     92   info->app_mode_url = SysNSStringToUTF8(
     93       [info_plist objectForKey:app_mode::kCrAppModeShortcutURLKey]);
     94 
     95   info->user_data_dir = base::mac::NSStringToFilePath(
     96       [info_plist objectForKey:app_mode::kCrAppModeUserDataDirKey]);
     97 
     98   info->profile_dir = base::mac::NSStringToFilePath(
     99       [info_plist objectForKey:app_mode::kCrAppModeProfileDirKey]);
    100 
    101   // Open the framework.
    102   *cr_dylib = dlopen(framework_shlib_path.value().c_str(), RTLD_LAZY);
    103   CHECK(cr_dylib) << "couldn't load framework: " << dlerror();
    104 }
    105 
    106 } // namespace
    107 
    108 __attribute__((visibility("default")))
    109 int main(int argc, char** argv) {
    110   app_mode::ChromeAppModeInfo info;
    111 
    112   // Hard coded info parameters.
    113   info.major_version = 1;  // v1.0
    114   info.minor_version = 0;
    115   info.argc = argc;
    116   info.argv = argv;
    117 
    118   // Load the Chrome framework.
    119   void *cr_dylib;
    120   LoadFramework(&cr_dylib, &info);
    121 
    122   typedef int (*StartFun)(const app_mode::ChromeAppModeInfo*);
    123   StartFun ChromeAppModeStart = (StartFun)dlsym(cr_dylib, "ChromeAppModeStart");
    124   CHECK(ChromeAppModeStart) << "couldn't get entry point";
    125 
    126   // Exit instead of returning to avoid the the removal of |main()| from stack
    127   // backtraces under tail call optimization.
    128   int rv = ChromeAppModeStart(&info);
    129   exit(rv);
    130 }
    131