| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // On Mac, shortcuts can't have command-line arguments. Instead, produce small | 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 | 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 | 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 | 8 // minimal and do as little work as possible (with as much work done on |
| 9 // framework side as possible). | 9 // framework side as possible). |
| 10 | 10 |
| 11 #include <dlfcn.h> | 11 #include <dlfcn.h> |
| 12 #include <stdio.h> | |
| 13 #include <stdlib.h> | |
| 14 #include <string.h> | |
| 15 | 12 |
| 16 #include <CoreFoundation/CoreFoundation.h> | 13 #include <CoreFoundation/CoreFoundation.h> |
| 17 #import <Foundation/Foundation.h> | 14 #import <Foundation/Foundation.h> |
| 18 | 15 |
| 16 #include "base/file_path.h" |
| 17 #include "base/file_util.h" |
| 18 #include "base/logging.h" |
| 19 #include "base/mac/foundation_util.h" |
| 20 #include "base/mac/scoped_nsautorelease_pool.h" |
| 21 #include "base/sys_string_conversions.h" |
| 22 #import "chrome/common/mac/app_mode_chrome_locator.h" |
| 19 #include "chrome/common/mac/app_mode_common.h" | 23 #include "chrome/common/mac/app_mode_common.h" |
| 20 | 24 |
| 21 namespace { | 25 namespace { |
| 22 | 26 |
| 23 // Checks that condition |x| is true. If not, outputs |msg| (together with | 27 void LoadFramework(void** cr_dylib, app_mode::ChromeAppModeInfo* info) { |
| 24 // source filename and line number) and exits. | 28 using base::SysNSStringToUTF8; |
| 25 #define CHECK_MSG(x, msg) if (!(x)) check_msg_helper(__FILE__, __LINE__, msg); | 29 using base::SysNSStringToUTF16; |
| 26 void check_msg_helper(const char* file, int line, const char* msg) { | 30 using base::mac::CFToNSCast; |
| 27 fprintf(stderr, "%s (%d): %s\n", file, line, msg); | 31 using base::mac::CFCastStrict; |
| 28 exit(1); | 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 = [app_bundle |
| 43 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 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() && file_util::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 FilePath version_path; |
| 68 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_versioned_path = version_path; |
| 76 info->app_mode_bundle_path = |
| 77 base::mac::NSStringToFilePath([app_bundle bundlePath]); |
| 78 |
| 79 // Read information about the this app shortcut from the Info.plist. |
| 80 // Don't check for null-ness on optional items. |
| 81 NSDictionary* info_plist = [app_bundle infoDictionary]; |
| 82 CHECK(info_plist) << "couldn't get loader Info.plist"; |
| 83 |
| 84 info->app_mode_id = SysNSStringToUTF8( |
| 85 [info_plist objectForKey:app_mode::kCrAppModeShortcutIDKey]); |
| 86 CHECK(info->app_mode_id.size()) << "couldn't get app shortcut ID"; |
| 87 |
| 88 info->app_mode_short_name = SysNSStringToUTF16( |
| 89 [info_plist objectForKey:app_mode::kCrAppModeShortcutShortNameKey]); |
| 90 |
| 91 info->app_mode_name = SysNSStringToUTF16( |
| 92 [info_plist objectForKey:app_mode::kCrAppModeShortcutNameKey]); |
| 93 |
| 94 info->app_mode_url = SysNSStringToUTF8( |
| 95 [info_plist objectForKey:app_mode::kCrAppModeShortcutURLKey]); |
| 96 CHECK(info->app_mode_url.size()) << "couldn't get app shortcut URL"; |
| 97 |
| 98 // Open the framework. |
| 99 *cr_dylib = dlopen(framework_shlib_path.value().c_str(), RTLD_LAZY); |
| 100 CHECK(cr_dylib) << "couldn't load framework: " << dlerror(); |
| 29 } | 101 } |
| 30 | 102 |
| 31 // Converts an NSString to a UTF8 C string (which is allocated, and may be freed | 103 } // namespace |
| 32 // using |free()|). If |s| is nil or can't produce such a string, this returns | |
| 33 // |NULL|. | |
| 34 char* NSStringToUTF8CString(NSString* s) { | |
| 35 CHECK_MSG([s isKindOfClass:[NSString class]], "expected an NSString"); | |
| 36 const char* cstring = [s UTF8String]; | |
| 37 return cstring ? strdup(cstring) : NULL; | |
| 38 } | |
| 39 | |
| 40 // Converts an NSString to a file-system representation C string (which is | |
| 41 // allocated, and may be freed using |free()|). If |s| is nil or can't produce | |
| 42 // such a string, this returns |NULL|. | |
| 43 char* NSStringToFSCString(NSString* s) { | |
| 44 CHECK_MSG([s isKindOfClass:[NSString class]], "expected an NSString"); | |
| 45 const char* cstring = [s fileSystemRepresentation]; | |
| 46 return cstring ? strdup(cstring) : NULL; | |
| 47 } | |
| 48 | |
| 49 } // namespace | |
| 50 | 104 |
| 51 __attribute__((visibility("default"))) | 105 __attribute__((visibility("default"))) |
| 52 int main(int argc, char** argv) { | 106 int main(int argc, char** argv) { |
| 53 app_mode::ChromeAppModeInfo info; | 107 app_mode::ChromeAppModeInfo info; |
| 54 info.major_version = 0; // v0.1 | 108 |
| 55 info.minor_version = 1; | 109 // Hard coded info parameters. |
| 110 info.major_version = 1; // v1.0 |
| 111 info.minor_version = 0; |
| 56 info.argc = argc; | 112 info.argc = argc; |
| 57 info.argv = argv; | 113 info.argv = argv; |
| 58 | 114 |
| 59 // The Cocoa APIs are a bit more convenient; for this an autorelease pool is | 115 // Load the Chrome framework. |
| 60 // needed. | 116 void *cr_dylib; |
| 61 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 117 LoadFramework(&cr_dylib, &info); |
| 62 | |
| 63 // Get the current main bundle, i.e., that of the app loader that's running. | |
| 64 NSBundle* app_bundle = [NSBundle mainBundle]; | |
| 65 CHECK_MSG(app_bundle, "couldn't get loader bundle"); | |
| 66 | |
| 67 // Get the bundle ID of the browser that created this app bundle. | |
| 68 NSString* cr_bundle_id = [app_bundle | |
| 69 objectForInfoDictionaryKey:app_mode::kBrowserBundleIDKey]; | |
| 70 CHECK_MSG(cr_bundle_id, "couldn't get browser bundle ID"); | |
| 71 | |
| 72 // Get the browser bundle path. | |
| 73 // TODO(viettrungluu): more fun | |
| 74 NSString* cr_bundle_path = [(NSString*)CFPreferencesCopyAppValue( | |
| 75 (CFStringRef)app_mode::kLastRunAppBundlePathPrefsKey, | |
| 76 (CFStringRef)cr_bundle_id) autorelease]; | |
| 77 CHECK_MSG(cr_bundle_path, "couldn't get browser bundle path"); | |
| 78 | |
| 79 // Get the browser bundle. | |
| 80 NSBundle* cr_bundle = [NSBundle bundleWithPath:cr_bundle_path]; | |
| 81 CHECK_MSG(cr_bundle, "couldn't get browser bundle"); | |
| 82 | |
| 83 // Get the current browser version. | |
| 84 NSString* cr_version = | |
| 85 [cr_bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; | |
| 86 CHECK_MSG(cr_version, "couldn't get browser version"); | |
| 87 | |
| 88 // Get the current browser versioned directory. | |
| 89 NSArray* cr_versioned_path_components = | |
| 90 [NSArray arrayWithObjects:cr_bundle_path, | |
| 91 @"Contents", | |
| 92 @"Versions", | |
| 93 cr_version, | |
| 94 nil]; | |
| 95 NSString* cr_versioned_path = | |
| 96 [[NSString pathWithComponents:cr_versioned_path_components] | |
| 97 stringByStandardizingPath]; | |
| 98 CHECK_MSG(cr_versioned_path, "couldn't get browser versioned path"); | |
| 99 // And copy it, since |cr_versioned_path| will go away with the pool. | |
| 100 info.chrome_versioned_path = NSStringToFSCString(cr_versioned_path); | |
| 101 | |
| 102 // Optional, so okay if it's NULL. | |
| 103 info.app_mode_bundle_path = NSStringToFSCString([app_bundle bundlePath]); | |
| 104 | |
| 105 // Read information about the this app shortcut from the Info.plist. | |
| 106 // Don't check for null-ness on optional items. | |
| 107 NSDictionary* info_plist = [app_bundle infoDictionary]; | |
| 108 CHECK_MSG(info_plist, "couldn't get loader Info.plist"); | |
| 109 | |
| 110 info.app_mode_id = NSStringToUTF8CString( | |
| 111 [info_plist objectForKey:app_mode::kCrAppModeShortcutIDKey]); | |
| 112 CHECK_MSG(info.app_mode_id, "couldn't get app shortcut ID"); | |
| 113 | |
| 114 info.app_mode_short_name = NSStringToUTF8CString( | |
| 115 [info_plist objectForKey:app_mode::kCrAppModeShortcutShortNameKey]); | |
| 116 | |
| 117 info.app_mode_name = NSStringToUTF8CString( | |
| 118 [info_plist objectForKey:app_mode::kCrAppModeShortcutNameKey]); | |
| 119 | |
| 120 info.app_mode_url = NSStringToUTF8CString( | |
| 121 [info_plist objectForKey:app_mode::kCrAppModeShortcutURLKey]); | |
| 122 CHECK_MSG(info.app_mode_url, "couldn't get app shortcut URL"); | |
| 123 | |
| 124 // Get the framework path. | |
| 125 NSString* cr_bundle_exe = | |
| 126 [cr_bundle objectForInfoDictionaryKey:@"CFBundleExecutable"]; | |
| 127 NSString* cr_framework_path = | |
| 128 [cr_versioned_path stringByAppendingPathComponent: | |
| 129 [cr_bundle_exe stringByAppendingString:@" Framework.framework"]]; | |
| 130 cr_framework_path = | |
| 131 [cr_framework_path stringByAppendingPathComponent: | |
| 132 [cr_bundle_exe stringByAppendingString:@" Framework"]]; | |
| 133 | |
| 134 // Open the framework. | |
| 135 void* cr_dylib = dlopen([cr_framework_path fileSystemRepresentation], | |
| 136 RTLD_LAZY); | |
| 137 CHECK_MSG(cr_dylib, "couldn't load framework"); | |
| 138 | |
| 139 // Drain the pool as late as possible. | |
| 140 [pool drain]; | |
| 141 | 118 |
| 142 typedef int (*StartFun)(const app_mode::ChromeAppModeInfo*); | 119 typedef int (*StartFun)(const app_mode::ChromeAppModeInfo*); |
| 143 StartFun ChromeAppModeStart = (StartFun)dlsym(cr_dylib, "ChromeAppModeStart"); | 120 StartFun ChromeAppModeStart = (StartFun)dlsym(cr_dylib, "ChromeAppModeStart"); |
| 144 CHECK_MSG(ChromeAppModeStart, "couldn't get entry point"); | 121 CHECK(ChromeAppModeStart) << "couldn't get entry point"; |
| 145 | 122 |
| 146 // Exit instead of returning to avoid the the removal of |main()| from stack | 123 // Exit instead of returning to avoid the the removal of |main()| from stack |
| 147 // backtraces under tail call optimization. | 124 // backtraces under tail call optimization. |
| 148 int rv = ChromeAppModeStart(&info); | 125 int rv = ChromeAppModeStart(&info); |
| 149 exit(rv); | 126 exit(rv); |
| 150 } | 127 } |
| OLD | NEW |