| Index: chrome/app/app_mode_loader_mac.mm
|
| diff --git a/chrome/app/app_mode_loader_mac.mm b/chrome/app/app_mode_loader_mac.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9ffb6975a0f9fde853c437d23f098d74113c0801
|
| --- /dev/null
|
| +++ b/chrome/app/app_mode_loader_mac.mm
|
| @@ -0,0 +1,145 @@
|
| +// 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 <dlfcn.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +
|
| +#include <CoreFoundation/CoreFoundation.h>
|
| +#import <Foundation/Foundation.h>
|
| +
|
| +#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);
|
| +}
|
|
|