// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // On Mac, shortcuts can't have command-line arguments. Instead, produce small // app bundles which locate the Chromium framework and load it, passing the // appropriate data. This is the code for such an app bundle. It should be kept // minimal and do as little work as possible (with as much work done on // framework side as possible). #include #include #include #include #include #import #include "chrome/common/app_mode_common_mac.h" namespace { // Checks that condition |x| is true. If not, outputs |msg| (together with // source filename and line number) and exits. #define CHECK_MSG(x, msg) if (!(x)) check_msg_helper(__FILE__, __LINE__, msg); void check_msg_helper(const char* file, int line, const char* msg) { fprintf(stderr, "%s (%d): %s\n", file, line, msg); exit(1); } // Converts an NSString to a UTF8 C string (which is allocated, and may be freed // using |free()|). If |s| is nil or can't produce such a string, this returns // |NULL|. char* NSStringToUTF8CString(NSString* s) { CHECK_MSG([s isKindOfClass:[NSString class]], "expected an NSString"); const char* cstring = [s UTF8String]; return cstring ? strdup(cstring) : NULL; } // Converts an NSString to a file-system representation C string (which is // allocated, and may be freed using |free()|). If |s| is nil or can't produce // such a string, this returns |NULL|. char* NSStringToFSCString(NSString* s) { CHECK_MSG([s isKindOfClass:[NSString class]], "expected an NSString"); const char* cstring = [s fileSystemRepresentation]; return cstring ? strdup(cstring) : NULL; } } // namespace __attribute__((visibility("default"))) int main(int argc, char** argv) { app_mode::ChromeAppModeInfo info; info.major_version = 0; // v0.1 info.minor_version = 1; info.argc = argc; info.argv = argv; // The Cocoa APIs are a bit more convenient; for this an autorelease pool is // needed. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // Get the browser bundle path. // TODO(viettrungluu): more fun NSString* cr_bundle_path = [(NSString*)CFPreferencesCopyAppValue( app_mode::kLastRunAppBundlePathPrefsKey, app_mode::kAppPrefsID) autorelease]; CHECK_MSG(cr_bundle_path, "couldn't get browser bundle path"); // Get the browser bundle. NSBundle* cr_bundle = [NSBundle bundleWithPath:cr_bundle_path]; CHECK_MSG(cr_bundle, "couldn't get browser bundle"); // Get the current browser version. NSString* cr_version = [cr_bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; CHECK_MSG(cr_version, "couldn't get browser version"); // Get the current browser versioned directory. NSArray* cr_versioned_path_components = [NSArray arrayWithObjects:cr_bundle_path, @"Contents", @"Versions", cr_version, nil]; NSString* cr_versioned_path = [[NSString pathWithComponents:cr_versioned_path_components] stringByStandardizingPath]; CHECK_MSG(cr_versioned_path, "couldn't get browser versioned path"); // And copy it, since |cr_versioned_path| will go away with the pool. info.chrome_versioned_path = NSStringToFSCString(cr_versioned_path); // Get the current main bundle, i.e., that of the app loader that's running. NSBundle* app_bundle = [NSBundle mainBundle]; CHECK_MSG(app_bundle, "couldn't get loader bundle"); // Optional, so okay if it's NULL. info.app_mode_bundle_path = NSStringToFSCString([app_bundle bundlePath]); // Read information about the this app shortcut from the Info.plist. // Don't check for null-ness on optional items. NSDictionary* info_plist = [app_bundle infoDictionary]; CHECK_MSG(info_plist, "couldn't get loader Info.plist"); info.app_mode_id = NSStringToUTF8CString( [info_plist objectForKey:@"CrAppModeShortcutID"]); CHECK_MSG(info.app_mode_id, "couldn't get app shortcut ID"); info.app_mode_short_name = NSStringToUTF8CString( [info_plist objectForKey:@"CrAppModeShortcutShortName"]); info.app_mode_name = NSStringToUTF8CString( [info_plist objectForKey:@"CrAppModeShortcutName"]); info.app_mode_url = NSStringToUTF8CString( [info_plist objectForKey:@"CrAppModeShortcutURL"]); CHECK_MSG(info.app_mode_url, "couldn't get app shortcut URL"); // Get the framework path. NSString* cr_bundle_exe = [cr_bundle objectForInfoDictionaryKey:@"CFBundleExecutable"]; NSString* cr_framework_path = [cr_versioned_path stringByAppendingPathComponent: [cr_bundle_exe stringByAppendingString:@" Framework.framework"]]; cr_framework_path = [cr_framework_path stringByAppendingPathComponent: [cr_bundle_exe stringByAppendingString:@" Framework"]]; // Open the framework. void* cr_dylib = dlopen([cr_framework_path fileSystemRepresentation], RTLD_LAZY); CHECK_MSG(cr_dylib, "couldn't load framework"); // Drain the pool as late as possible. [pool drain]; typedef int (*StartFun)(const app_mode::ChromeAppModeInfo*); StartFun ChromeAppModeStart = (StartFun)dlsym(cr_dylib, "ChromeAppModeStart"); CHECK_MSG(ChromeAppModeStart, "couldn't get entry point"); // Exit instead of returning to avoid the the removal of |main()| from stack // backtraces under tail call optimization. int rv = ChromeAppModeStart(&info); exit(rv); }