summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-18 22:09:40 +0000
committerviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-18 22:09:40 +0000
commitadf0225bfea22dd085a1e0816871af9e8f1fe372 (patch)
tree96fd5362f73388c6a72359db01b33dfbc6d15f81
parent98e94a40052f163889a5d6026a24d750edf28818 (diff)
downloadchromium_src-adf0225bfea22dd085a1e0816871af9e8f1fe372.zip
chromium_src-adf0225bfea22dd085a1e0816871af9e8f1fe372.tar.gz
chromium_src-adf0225bfea22dd085a1e0816871af9e8f1fe372.tar.bz2
Mac: app mode loader (shim).
Define an interface using which Chrome can be loaded from a minimal loader. Produce a binary for this loader (which can be put into a bundle). BUG=13148 TEST=not yet Review URL: http://codereview.chromium.org/2066004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47576 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/app_mode-Info.plist36
-rw-r--r--chrome/app/app_mode_loader_mac.mm145
-rw-r--r--chrome/app/chrome_dll_app_mode_mac.mm56
-rw-r--r--chrome/app/framework.order3
-rw-r--r--chrome/chrome.gyp21
-rw-r--r--chrome/chrome_dll.gypi1
-rw-r--r--chrome/common/app_mode_common_mac.h41
-rw-r--r--chrome/common/app_mode_common_mac.mm4
-rw-r--r--chrome/common/chrome_paths_internal.h7
-rw-r--r--chrome/common/chrome_paths_mac.mm14
10 files changed, 326 insertions, 2 deletions
diff --git a/chrome/app/app_mode-Info.plist b/chrome/app/app_mode-Info.plist
new file mode 100644
index 0000000..611f7c5
--- /dev/null
+++ b/chrome/app/app_mode-Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>@APP_MODE_SHORTCUT_SHORT_NAME@</string>
+ <key>CFBundleExecutable</key>
+ <string>@APP_MODE_SHORTCUT_ID@</string>
+ <key>CFBundleIconFile</key>
+ <string>app.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>????</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>@APP_MODE_SHORTCUT_ID@</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CrAppModeShortcutID</key>
+ <string>@APP_MODE_SHORTCUT_ID@</string>
+ <key>CrAppModeShortcutShortName</key>
+ <string>@APP_MODE_SHORTCUT_SHORT_NAME@</string>
+ <key>CrAppModeShortcutName</key>
+ <string>@APP_MODE_SHORTCUT_NAME@</string>
+ <key>CrAppModeShortcutURL</key>
+ <string>@APP_MODE_SHORTCUT_URL@</string>
+ <key>LSFileQuarantineEnabled</key>
+ <true/>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.5.0</string>
+ <key>NSAppleScriptEnabled</key>
+ <true/>
+</dict>
+</plist>
diff --git a/chrome/app/app_mode_loader_mac.mm b/chrome/app/app_mode_loader_mac.mm
new file mode 100644
index 0000000..9ffb697
--- /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);
+}
diff --git a/chrome/app/chrome_dll_app_mode_mac.mm b/chrome/app/chrome_dll_app_mode_mac.mm
new file mode 100644
index 0000000..678547d
--- /dev/null
+++ b/chrome/app/chrome_dll_app_mode_mac.mm
@@ -0,0 +1,56 @@
+// 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, one can't make shortcuts with command-line arguments. Instead, we
+// produce small app bundles which locate the Chromium framework and load it,
+// passing the appropriate data. This is the entry point into the framework for
+// those app bundles.
+
+#include <string> // TODO(viettrungluu): only needed for temporary hack
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "chrome/common/app_mode_common_mac.h"
+#include "chrome/common/chrome_paths_internal.h"
+
+extern "C" {
+
+// |ChromeAppModeStart()| is the point of entry into the framework from the app
+// mode loader.
+__attribute__((visibility("default")))
+int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info);
+
+// TODO(viettrungluu): put this in a header file somewhere.
+int ChromeMain(int argc, char** argv);
+
+} // extern "C"
+
+int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
+ if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) {
+ RAW_LOG(ERROR, "App Mode Loader too old.");
+ return 1;
+ }
+ if (info->major_version > app_mode::kCurrentChromeAppModeInfoMajorVersion) {
+ RAW_LOG(ERROR, "Browser Framework too old to load App Shortcut.");
+ return 1;
+ }
+
+ RAW_CHECK(info->chrome_versioned_path);
+ FilePath* chrome_versioned_path = new FilePath(info->chrome_versioned_path);
+ RAW_CHECK(!chrome_versioned_path->empty());
+ chrome::SetOverrideVersionedDirectory(chrome_versioned_path);
+
+ // TODO(viettrungluu): do something intelligent with data
+// return ChromeMain(info->argc, info->argv);
+ // For now, a cheesy hack instead.
+ RAW_CHECK(info->app_mode_url);
+ std::string argv1(std::string("--app=") + info->app_mode_url);
+ RAW_CHECK(info->app_mode_id);
+ std::string argv2(std::string("--user-data-dir=/tmp/") + info->app_mode_id);
+ char* argv[] = { info->argv[0],
+ const_cast<char*>(argv1.c_str()),
+ const_cast<char*>(argv2.c_str()) };
+ return ChromeMain(static_cast<int>(arraysize(argv)), argv);
+}
diff --git a/chrome/app/framework.order b/chrome/app/framework.order
index 3ff59b0..c527f6e 100644
--- a/chrome/app/framework.order
+++ b/chrome/app/framework.order
@@ -63,5 +63,8 @@ ___gcov_fork
_NaClSwitch
_NaClSyscallSeg
+# Entry point from the app mode loader.
+_ChromeAppModeStart
+
# _ChromeMain must be listed last. That's the whole point of this file.
_ChromeMain
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 5472fe5..4a46af3 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1147,6 +1147,27 @@
],
}, # target helper_app
{
+ # This produces the app mode loader, but not as a bundle. Chromium
+ # itself is responsible for producing bundles.
+ 'target_name': 'app_mode_app',
+ 'type': 'executable',
+ 'product_name': '<(mac_product_name) App Mode Loader',
+ 'sources': [
+ 'app/app_mode_loader_mac.mm',
+ 'common/app_mode_common_mac.h',
+ 'common/app_mode_common_mac.mm',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ }, # target app_mode_app
+ {
# Convenience target to build a disk image.
'target_name': 'build_app_dmg',
# Don't place this in the 'all' list; most won't want it.
diff --git a/chrome/chrome_dll.gypi b/chrome/chrome_dll.gypi
index a23125a..3884aa0 100644
--- a/chrome/chrome_dll.gypi
+++ b/chrome/chrome_dll.gypi
@@ -170,6 +170,7 @@
'ORDER_FILE': 'app/framework.order',
},
'sources': [
+ 'app/chrome_dll_app_mode_mac.mm',
'app/chrome_dll_main.cc',
'app/chrome_dll_resource.h',
],
diff --git a/chrome/common/app_mode_common_mac.h b/chrome/common/app_mode_common_mac.h
index 0929be5..48d72fe 100644
--- a/chrome/common/app_mode_common_mac.h
+++ b/chrome/common/app_mode_common_mac.h
@@ -20,6 +20,47 @@ extern const CFStringRef kAppPrefsID;
// bundle; this key is recorded under the ID given by |kAppPrefsID|.
extern const CFStringRef kLastRunAppBundlePathPrefsKey;
+// Current major/minor version numbers of |ChromeAppModeInfo| (defined below).
+const unsigned kCurrentChromeAppModeInfoMajorVersion = 0;
+const unsigned kCurrentChromeAppModeInfoMinorVersion = 1;
+
+// The structure used to pass information from the app mode loader to the
+// (browser) framework. This is versioned using major and minor version numbers,
+// written below as v<major>.<minor>. Version-number checking is done by the
+// framework, and the framework must accept all structures with the same major
+// version number. It may refuse to load if the major version of the structure
+// is different from the one it accepts.
+struct ChromeAppModeInfo {
+ // Major and minor version number of this structure.
+ unsigned major_version; // Required: all versions
+ unsigned minor_version; // Required: all versions
+
+ // Original |argc| and |argv|.
+ int argc; // Required: v0.1
+ char** argv; // Required: v0.1
+
+ // Versioned path to the browser which is being loaded.
+ char* chrome_versioned_path; // Required: v0.1
+
+ // Information about the App Mode shortcut:
+
+ // Path to the App Mode Loader application bundle originally run.
+ char* app_mode_bundle_path; // Optional: v0.1
+
+ // Short ID string, preferably derived from |app_mode_short_name|. Should be
+ // safe for the file system.
+ char* app_mode_id; // Required: v0.1
+
+ // Short (e.g., one-word) UTF8-encoded name for the shortcut.
+ char* app_mode_short_name; // Optional: v0.1
+
+ // Unrestricted (e.g., several-word) UTF8-encoded name for the shortcut.
+ char* app_mode_name; // Optional: v0.1
+
+ // URL for the shortcut. Must be a valid URL.
+ char* app_mode_url; // Required: v0.1
+};
+
} // namespace app_mode
#endif // CHROME_COMMON_APP_MODE_COMMON_MAC_H_
diff --git a/chrome/common/app_mode_common_mac.mm b/chrome/common/app_mode_common_mac.mm
index aa35af7..3705c34 100644
--- a/chrome/common/app_mode_common_mac.mm
+++ b/chrome/common/app_mode_common_mac.mm
@@ -7,9 +7,9 @@
namespace app_mode {
#if defined(GOOGLE_CHROME_BUILD)
-extern const CFStringRef kAppPrefsID = CFSTR("com.google.Chrome");
+const CFStringRef kAppPrefsID = CFSTR("com.google.Chrome");
#else
-extern const CFStringRef kAppPrefsID = CFSTR("org.chromium.Chromium");
+const CFStringRef kAppPrefsID = CFSTR("org.chromium.Chromium");
#endif
const CFStringRef kLastRunAppBundlePathPrefsKey = CFSTR("LastRunAppBundlePath");
diff --git a/chrome/common/chrome_paths_internal.h b/chrome/common/chrome_paths_internal.h
index 5254f51..7d731e1 100644
--- a/chrome/common/chrome_paths_internal.h
+++ b/chrome/common/chrome_paths_internal.h
@@ -40,6 +40,13 @@ bool GetUserDesktop(FilePath* result);
// in the .app at Contents/Versions/w.x.y.z.
FilePath GetVersionedDirectory();
+// This overrides the directory returned by |GetVersionedDirectory()|, to be
+// used when |GetVersionedDirectory()| can't automatically determine the proper
+// location. This is the case when the browser didn't load itself but by, e.g.,
+// the app mode loader. This should be called before |ChromeMain()|. This takes
+// ownership of the object |path| and the caller must not delete it.
+void SetOverrideVersionedDirectory(const FilePath* path);
+
// Most of the application is further contained within the framework. The
// framework bundle is located within the versioned directory at a specific
// path. The only components in the versioned directory not included in the
diff --git a/chrome/common/chrome_paths_mac.mm b/chrome/common/chrome_paths_mac.mm
index 21868cd..c7db483 100644
--- a/chrome/common/chrome_paths_mac.mm
+++ b/chrome/common/chrome_paths_mac.mm
@@ -12,6 +12,10 @@
#include "base/path_service.h"
#include "chrome/common/chrome_constants.h"
+namespace {
+const FilePath* g_override_versioned_directory = NULL;
+} // namespace
+
namespace chrome {
bool GetDefaultUserDataDirectory(FilePath* result) {
@@ -53,6 +57,9 @@ bool GetUserDesktop(FilePath* result) {
}
FilePath GetVersionedDirectory() {
+ if (g_override_versioned_directory)
+ return *g_override_versioned_directory;
+
// Start out with the path to the running executable.
FilePath path;
PathService::Get(base::FILE_EXE, &path);
@@ -75,6 +82,13 @@ FilePath GetVersionedDirectory() {
return path;
}
+void SetOverrideVersionedDirectory(const FilePath* path) {
+ if (path != g_override_versioned_directory) {
+ delete g_override_versioned_directory;
+ g_override_versioned_directory = path;
+ }
+}
+
FilePath GetFrameworkBundlePath() {
// It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really
// slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating