| Index: apps/app_shim/chrome_main_app_mode_mac.mm
|
| diff --git a/apps/app_shim/chrome_main_app_mode_mac.mm b/apps/app_shim/chrome_main_app_mode_mac.mm
|
| deleted file mode 100644
|
| index 21e3fe21835e1efbebd21f8c04a52a290f62d15e..0000000000000000000000000000000000000000
|
| --- a/apps/app_shim/chrome_main_app_mode_mac.mm
|
| +++ /dev/null
|
| @@ -1,693 +0,0 @@
|
| -// Copyright 2013 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.
|
| -
|
| -#import <Cocoa/Cocoa.h>
|
| -#include <vector>
|
| -
|
| -#include "apps/app_shim/app_shim_messages.h"
|
| -#include "base/at_exit.h"
|
| -#include "base/command_line.h"
|
| -#include "base/files/file_path.h"
|
| -#include "base/files/file_util.h"
|
| -#include "base/logging.h"
|
| -#include "base/mac/bundle_locations.h"
|
| -#include "base/mac/foundation_util.h"
|
| -#include "base/mac/launch_services_util.h"
|
| -#include "base/mac/mac_logging.h"
|
| -#include "base/mac/mac_util.h"
|
| -#include "base/mac/scoped_nsautorelease_pool.h"
|
| -#include "base/mac/scoped_nsobject.h"
|
| -#include "base/mac/sdk_forward_declarations.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/path_service.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/sys_string_conversions.h"
|
| -#include "base/threading/thread.h"
|
| -#include "chrome/common/chrome_constants.h"
|
| -#include "chrome/common/chrome_paths.h"
|
| -#include "chrome/common/chrome_switches.h"
|
| -#include "chrome/common/mac/app_mode_common.h"
|
| -#include "grit/generated_resources.h"
|
| -#include "ipc/ipc_channel_proxy.h"
|
| -#include "ipc/ipc_listener.h"
|
| -#include "ipc/ipc_message.h"
|
| -#include "ui/base/l10n/l10n_util.h"
|
| -#include "ui/base/resource/resource_bundle.h"
|
| -
|
| -namespace {
|
| -
|
| -// Timeout in seconds to wait for a reply for the initial Apple Event. Note that
|
| -// kAEDefaultTimeout on Mac is "about one minute" according to Apple's
|
| -// documentation, but is no longer supported for asynchronous Apple Events.
|
| -const int kPingChromeTimeoutSeconds = 60;
|
| -
|
| -const app_mode::ChromeAppModeInfo* g_info;
|
| -base::Thread* g_io_thread = NULL;
|
| -
|
| -} // namespace
|
| -
|
| -class AppShimController;
|
| -
|
| -// An application delegate to catch user interactions and send the appropriate
|
| -// IPC messages to Chrome.
|
| -@interface AppShimDelegate : NSObject<NSApplicationDelegate> {
|
| - @private
|
| - AppShimController* appShimController_; // Weak, initially NULL.
|
| - BOOL terminateNow_;
|
| - BOOL terminateRequested_;
|
| - std::vector<base::FilePath> filesToOpenAtStartup_;
|
| -}
|
| -
|
| -// The controller is initially NULL. Setting it indicates to the delegate that
|
| -// the controller has finished initialization.
|
| -- (void)setController:(AppShimController*)controller;
|
| -
|
| -// Gets files that were queued because the controller was not ready.
|
| -// Returns whether any FilePaths were added to |out|.
|
| -- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out;
|
| -
|
| -// If the controller is ready, this sends a FocusApp with the files to open.
|
| -// Otherwise, this adds the files to |filesToOpenAtStartup_|.
|
| -// Takes an array of NSString*.
|
| -- (void)openFiles:(NSArray*)filename;
|
| -
|
| -// Terminate immediately. This is necessary as we override terminate: to send
|
| -// a QuitApp message.
|
| -- (void)terminateNow;
|
| -
|
| -@end
|
| -
|
| -// The AppShimController is responsible for communication with the main Chrome
|
| -// process, and generally controls the lifetime of the app shim process.
|
| -class AppShimController : public IPC::Listener {
|
| - public:
|
| - AppShimController();
|
| - virtual ~AppShimController();
|
| -
|
| - // Called when the main Chrome process responds to the Apple Event ping that
|
| - // was sent, or when the ping fails (if |success| is false).
|
| - void OnPingChromeReply(bool success);
|
| -
|
| - // Called |kPingChromeTimeoutSeconds| after startup, to allow a timeout on the
|
| - // ping event to be detected.
|
| - void OnPingChromeTimeout();
|
| -
|
| - // Connects to Chrome and sends a LaunchApp message.
|
| - void Init();
|
| -
|
| - // Create a channel from |socket_path| and send a LaunchApp message.
|
| - void CreateChannelAndSendLaunchApp(const base::FilePath& socket_path);
|
| -
|
| - // Builds main menu bar items.
|
| - void SetUpMenu();
|
| -
|
| - void SendSetAppHidden(bool hidden);
|
| -
|
| - void SendQuitApp();
|
| -
|
| - // Called when the app is activated, e.g. by clicking on it in the dock, by
|
| - // dropping a file on the dock icon, or by Cmd+Tabbing to it.
|
| - // Returns whether the message was sent.
|
| - bool SendFocusApp(apps::AppShimFocusType focus_type,
|
| - const std::vector<base::FilePath>& files);
|
| -
|
| - private:
|
| - // IPC::Listener implemetation.
|
| - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
| - virtual void OnChannelError() OVERRIDE;
|
| -
|
| - // If Chrome failed to launch the app, |success| will be false and the app
|
| - // shim process should die.
|
| - void OnLaunchAppDone(apps::AppShimLaunchResult result);
|
| -
|
| - // Hide this app.
|
| - void OnHide();
|
| -
|
| - // Requests user attention.
|
| - void OnRequestUserAttention();
|
| - void OnSetUserAttention(apps::AppShimAttentionType attention_type);
|
| -
|
| - // Terminates the app shim process.
|
| - void Close();
|
| -
|
| - base::FilePath user_data_dir_;
|
| - scoped_ptr<IPC::ChannelProxy> channel_;
|
| - base::scoped_nsobject<AppShimDelegate> delegate_;
|
| - bool launch_app_done_;
|
| - bool ping_chrome_reply_received_;
|
| - NSInteger attention_request_id_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(AppShimController);
|
| -};
|
| -
|
| -AppShimController::AppShimController()
|
| - : delegate_([[AppShimDelegate alloc] init]),
|
| - launch_app_done_(false),
|
| - ping_chrome_reply_received_(false),
|
| - attention_request_id_(0) {
|
| - // Since AppShimController is created before the main message loop starts,
|
| - // NSApp will not be set, so use sharedApplication.
|
| - [[NSApplication sharedApplication] setDelegate:delegate_];
|
| -}
|
| -
|
| -AppShimController::~AppShimController() {
|
| - // Un-set the delegate since NSApplication does not retain it.
|
| - [[NSApplication sharedApplication] setDelegate:nil];
|
| -}
|
| -
|
| -void AppShimController::OnPingChromeReply(bool success) {
|
| - ping_chrome_reply_received_ = true;
|
| - if (!success) {
|
| - [NSApp terminate:nil];
|
| - return;
|
| - }
|
| -
|
| - Init();
|
| -}
|
| -
|
| -void AppShimController::OnPingChromeTimeout() {
|
| - if (!ping_chrome_reply_received_)
|
| - [NSApp terminate:nil];
|
| -}
|
| -
|
| -void AppShimController::Init() {
|
| - DCHECK(g_io_thread);
|
| -
|
| - SetUpMenu();
|
| -
|
| - // Chrome will relaunch shims when relaunching apps.
|
| - if (base::mac::IsOSLionOrLater())
|
| - [NSApp disableRelaunchOnLogin];
|
| -
|
| - // The user_data_dir for shims actually contains the app_data_path.
|
| - // I.e. <user_data_dir>/<profile_dir>/Web Applications/_crx_extensionid/
|
| - user_data_dir_ = g_info->user_data_dir.DirName().DirName().DirName();
|
| - CHECK(!user_data_dir_.empty());
|
| -
|
| - base::FilePath symlink_path =
|
| - user_data_dir_.Append(app_mode::kAppShimSocketSymlinkName);
|
| -
|
| - base::FilePath socket_path;
|
| - if (!base::ReadSymbolicLink(symlink_path, &socket_path)) {
|
| - // The path in the user data dir is not a symlink, try connecting directly.
|
| - CreateChannelAndSendLaunchApp(symlink_path);
|
| - return;
|
| - }
|
| -
|
| - app_mode::VerifySocketPermissions(socket_path);
|
| -
|
| - CreateChannelAndSendLaunchApp(socket_path);
|
| -}
|
| -
|
| -void AppShimController::CreateChannelAndSendLaunchApp(
|
| - const base::FilePath& socket_path) {
|
| - IPC::ChannelHandle handle(socket_path.value());
|
| - channel_ = IPC::ChannelProxy::Create(handle,
|
| - IPC::Channel::MODE_NAMED_CLIENT,
|
| - this,
|
| - g_io_thread->message_loop_proxy().get());
|
| -
|
| - bool launched_by_chrome =
|
| - CommandLine::ForCurrentProcess()->HasSwitch(
|
| - app_mode::kLaunchedByChromeProcessId);
|
| - apps::AppShimLaunchType launch_type = launched_by_chrome ?
|
| - apps::APP_SHIM_LAUNCH_REGISTER_ONLY : apps::APP_SHIM_LAUNCH_NORMAL;
|
| -
|
| - [delegate_ setController:this];
|
| -
|
| - std::vector<base::FilePath> files;
|
| - [delegate_ getFilesToOpenAtStartup:&files];
|
| -
|
| - channel_->Send(new AppShimHostMsg_LaunchApp(
|
| - g_info->profile_dir, g_info->app_mode_id, launch_type, files));
|
| -}
|
| -
|
| -void AppShimController::SetUpMenu() {
|
| - NSString* title = base::SysUTF16ToNSString(g_info->app_mode_name);
|
| -
|
| - // Create a main menu since [NSApp mainMenu] is nil.
|
| - base::scoped_nsobject<NSMenu> main_menu([[NSMenu alloc] initWithTitle:title]);
|
| -
|
| - // The title of the first item is replaced by OSX with the name of the app and
|
| - // bold styling. Create a dummy item for this and make it hidden.
|
| - NSMenuItem* dummy_item = [main_menu addItemWithTitle:title
|
| - action:nil
|
| - keyEquivalent:@""];
|
| - base::scoped_nsobject<NSMenu> dummy_submenu(
|
| - [[NSMenu alloc] initWithTitle:title]);
|
| - [dummy_item setSubmenu:dummy_submenu];
|
| - [dummy_item setHidden:YES];
|
| -
|
| - // Construct an unbolded app menu, to match how it appears in the Chrome menu
|
| - // bar when the app is focused.
|
| - NSMenuItem* item = [main_menu addItemWithTitle:title
|
| - action:nil
|
| - keyEquivalent:@""];
|
| - base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:title]);
|
| - [item setSubmenu:submenu];
|
| -
|
| - // Add a quit entry.
|
| - NSString* quit_localized_string =
|
| - l10n_util::GetNSStringF(IDS_EXIT_MAC, g_info->app_mode_name);
|
| - [submenu addItemWithTitle:quit_localized_string
|
| - action:@selector(terminate:)
|
| - keyEquivalent:@"q"];
|
| -
|
| - // Add File, Edit, and Window menus. These are just here to make the
|
| - // transition smoother, i.e. from another application to the shim then to
|
| - // Chrome.
|
| - [main_menu addItemWithTitle:l10n_util::GetNSString(IDS_FILE_MENU_MAC)
|
| - action:nil
|
| - keyEquivalent:@""];
|
| - [main_menu addItemWithTitle:l10n_util::GetNSString(IDS_EDIT_MENU_MAC)
|
| - action:nil
|
| - keyEquivalent:@""];
|
| - [main_menu addItemWithTitle:l10n_util::GetNSString(IDS_WINDOW_MENU_MAC)
|
| - action:nil
|
| - keyEquivalent:@""];
|
| -
|
| - [NSApp setMainMenu:main_menu];
|
| -}
|
| -
|
| -void AppShimController::SendQuitApp() {
|
| - channel_->Send(new AppShimHostMsg_QuitApp);
|
| -}
|
| -
|
| -bool AppShimController::OnMessageReceived(const IPC::Message& message) {
|
| - bool handled = true;
|
| - IPC_BEGIN_MESSAGE_MAP(AppShimController, message)
|
| - IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
|
| - IPC_MESSAGE_HANDLER(AppShimMsg_Hide, OnHide)
|
| - IPC_MESSAGE_HANDLER(AppShimMsg_RequestUserAttention, OnRequestUserAttention)
|
| - IPC_MESSAGE_HANDLER(AppShimMsg_SetUserAttention, OnSetUserAttention)
|
| - IPC_MESSAGE_UNHANDLED(handled = false)
|
| - IPC_END_MESSAGE_MAP()
|
| -
|
| - return handled;
|
| -}
|
| -
|
| -void AppShimController::OnChannelError() {
|
| - Close();
|
| -}
|
| -
|
| -void AppShimController::OnLaunchAppDone(apps::AppShimLaunchResult result) {
|
| - if (result != apps::APP_SHIM_LAUNCH_SUCCESS) {
|
| - Close();
|
| - return;
|
| - }
|
| -
|
| - std::vector<base::FilePath> files;
|
| - if ([delegate_ getFilesToOpenAtStartup:&files])
|
| - SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES, files);
|
| -
|
| - launch_app_done_ = true;
|
| -}
|
| -
|
| -void AppShimController::OnHide() {
|
| - [NSApp hide:nil];
|
| -}
|
| -
|
| -void AppShimController::OnRequestUserAttention() {
|
| - OnSetUserAttention(apps::APP_SHIM_ATTENTION_INFORMATIONAL);
|
| -}
|
| -
|
| -void AppShimController::OnSetUserAttention(
|
| - apps::AppShimAttentionType attention_type) {
|
| - switch (attention_type) {
|
| - case apps::APP_SHIM_ATTENTION_CANCEL:
|
| - [NSApp cancelUserAttentionRequest:attention_request_id_];
|
| - attention_request_id_ = 0;
|
| - break;
|
| - case apps::APP_SHIM_ATTENTION_CRITICAL:
|
| - attention_request_id_ = [NSApp requestUserAttention:NSCriticalRequest];
|
| - break;
|
| - case apps::APP_SHIM_ATTENTION_INFORMATIONAL:
|
| - attention_request_id_ =
|
| - [NSApp requestUserAttention:NSInformationalRequest];
|
| - break;
|
| - case apps::APP_SHIM_ATTENTION_NUM_TYPES:
|
| - NOTREACHED();
|
| - }
|
| -}
|
| -
|
| -void AppShimController::Close() {
|
| - [delegate_ terminateNow];
|
| -}
|
| -
|
| -bool AppShimController::SendFocusApp(apps::AppShimFocusType focus_type,
|
| - const std::vector<base::FilePath>& files) {
|
| - if (launch_app_done_) {
|
| - channel_->Send(new AppShimHostMsg_FocusApp(focus_type, files));
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -void AppShimController::SendSetAppHidden(bool hidden) {
|
| - channel_->Send(new AppShimHostMsg_SetAppHidden(hidden));
|
| -}
|
| -
|
| -@implementation AppShimDelegate
|
| -
|
| -- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out {
|
| - if (filesToOpenAtStartup_.empty())
|
| - return NO;
|
| -
|
| - out->insert(out->end(),
|
| - filesToOpenAtStartup_.begin(),
|
| - filesToOpenAtStartup_.end());
|
| - filesToOpenAtStartup_.clear();
|
| - return YES;
|
| -}
|
| -
|
| -- (void)setController:(AppShimController*)controller {
|
| - appShimController_ = controller;
|
| -}
|
| -
|
| -- (void)openFiles:(NSArray*)filenames {
|
| - std::vector<base::FilePath> filePaths;
|
| - for (NSString* filename in filenames)
|
| - filePaths.push_back(base::mac::NSStringToFilePath(filename));
|
| -
|
| - // If the AppShimController is ready, try to send a FocusApp. If that fails,
|
| - // (e.g. if launching has not finished), enqueue the files.
|
| - if (appShimController_ &&
|
| - appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES,
|
| - filePaths)) {
|
| - return;
|
| - }
|
| -
|
| - filesToOpenAtStartup_.insert(filesToOpenAtStartup_.end(),
|
| - filePaths.begin(),
|
| - filePaths.end());
|
| -}
|
| -
|
| -- (BOOL)application:(NSApplication*)app
|
| - openFile:(NSString*)filename {
|
| - [self openFiles:@[filename]];
|
| - return YES;
|
| -}
|
| -
|
| -- (void)application:(NSApplication*)app
|
| - openFiles:(NSArray*)filenames {
|
| - [self openFiles:filenames];
|
| - [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
|
| -}
|
| -
|
| -- (BOOL)applicationOpenUntitledFile:(NSApplication*)app {
|
| - if (appShimController_) {
|
| - return appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_REOPEN,
|
| - std::vector<base::FilePath>());
|
| - }
|
| -
|
| - return NO;
|
| -}
|
| -
|
| -- (void)applicationWillBecomeActive:(NSNotification*)notification {
|
| - if (appShimController_) {
|
| - appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_NORMAL,
|
| - std::vector<base::FilePath>());
|
| - }
|
| -}
|
| -
|
| -- (NSApplicationTerminateReply)
|
| - applicationShouldTerminate:(NSApplication*)sender {
|
| - if (terminateNow_ || !appShimController_)
|
| - return NSTerminateNow;
|
| -
|
| - appShimController_->SendQuitApp();
|
| - // Wait for the channel to close before terminating.
|
| - terminateRequested_ = YES;
|
| - return NSTerminateLater;
|
| -}
|
| -
|
| -- (void)applicationWillHide:(NSNotification*)notification {
|
| - if (appShimController_)
|
| - appShimController_->SendSetAppHidden(true);
|
| -}
|
| -
|
| -- (void)applicationWillUnhide:(NSNotification*)notification {
|
| - if (appShimController_)
|
| - appShimController_->SendSetAppHidden(false);
|
| -}
|
| -
|
| -- (void)terminateNow {
|
| - if (terminateRequested_) {
|
| - [NSApp replyToApplicationShouldTerminate:NSTerminateNow];
|
| - return;
|
| - }
|
| -
|
| - terminateNow_ = YES;
|
| - [NSApp terminate:nil];
|
| -}
|
| -
|
| -@end
|
| -
|
| -//-----------------------------------------------------------------------------
|
| -
|
| -// A ReplyEventHandler is a helper class to send an Apple Event to a process
|
| -// and call a callback when the reply returns.
|
| -//
|
| -// This is used to 'ping' the main Chrome process -- once Chrome has sent back
|
| -// an Apple Event reply, it's guaranteed that it has opened the IPC channel
|
| -// that the app shim will connect to.
|
| -@interface ReplyEventHandler : NSObject {
|
| - base::Callback<void(bool)> onReply_;
|
| - AEDesc replyEvent_;
|
| -}
|
| -// Sends an Apple Event to the process identified by |psn|, and calls |replyFn|
|
| -// when the reply is received. Internally this creates a ReplyEventHandler,
|
| -// which will delete itself once the reply event has been received.
|
| -+ (void)pingProcess:(const ProcessSerialNumber&)psn
|
| - andCall:(base::Callback<void(bool)>)replyFn;
|
| -@end
|
| -
|
| -@interface ReplyEventHandler (PrivateMethods)
|
| -// Initialise the reply event handler. Doesn't register any handlers until
|
| -// |-pingProcess:| is called. |replyFn| is the function to be called when the
|
| -// Apple Event reply arrives.
|
| -- (id)initWithCallback:(base::Callback<void(bool)>)replyFn;
|
| -
|
| -// Sends an Apple Event ping to the process identified by |psn| and registers
|
| -// to listen for a reply.
|
| -- (void)pingProcess:(const ProcessSerialNumber&)psn;
|
| -
|
| -// Called when a response is received from the target process for the ping sent
|
| -// by |-pingProcess:|.
|
| -- (void)message:(NSAppleEventDescriptor*)event
|
| - withReply:(NSAppleEventDescriptor*)reply;
|
| -
|
| -// Calls |onReply_|, passing it |success| to specify whether the ping was
|
| -// successful.
|
| -- (void)closeWithSuccess:(bool)success;
|
| -@end
|
| -
|
| -@implementation ReplyEventHandler
|
| -+ (void)pingProcess:(const ProcessSerialNumber&)psn
|
| - andCall:(base::Callback<void(bool)>)replyFn {
|
| - // The object will release itself when the reply arrives, or possibly earlier
|
| - // if an unrecoverable error occurs.
|
| - ReplyEventHandler* handler =
|
| - [[ReplyEventHandler alloc] initWithCallback:replyFn];
|
| - [handler pingProcess:psn];
|
| -}
|
| -@end
|
| -
|
| -@implementation ReplyEventHandler (PrivateMethods)
|
| -- (id)initWithCallback:(base::Callback<void(bool)>)replyFn {
|
| - if ((self = [super init])) {
|
| - onReply_ = replyFn;
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)pingProcess:(const ProcessSerialNumber&)psn {
|
| - // Register the reply listener.
|
| - NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
|
| - [em setEventHandler:self
|
| - andSelector:@selector(message:withReply:)
|
| - forEventClass:'aevt'
|
| - andEventID:'ansr'];
|
| - // Craft the Apple Event to send.
|
| - NSAppleEventDescriptor* target = [NSAppleEventDescriptor
|
| - descriptorWithDescriptorType:typeProcessSerialNumber
|
| - bytes:&psn
|
| - length:sizeof(psn)];
|
| - NSAppleEventDescriptor* initial_event =
|
| - [NSAppleEventDescriptor
|
| - appleEventWithEventClass:app_mode::kAEChromeAppClass
|
| - eventID:app_mode::kAEChromeAppPing
|
| - targetDescriptor:target
|
| - returnID:kAutoGenerateReturnID
|
| - transactionID:kAnyTransactionID];
|
| -
|
| - // Note that AESendMessage effectively ignores kAEDefaultTimeout, because this
|
| - // call does not pass kAEWantReceipt (which is deprecated and unsupported on
|
| - // Mac). Instead, rely on OnPingChromeTimeout().
|
| - OSStatus status = AESendMessage(
|
| - [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout);
|
| - if (status != noErr) {
|
| - OSSTATUS_LOG(ERROR, status) << "AESendMessage";
|
| - [self closeWithSuccess:false];
|
| - }
|
| -}
|
| -
|
| -- (void)message:(NSAppleEventDescriptor*)event
|
| - withReply:(NSAppleEventDescriptor*)reply {
|
| - [self closeWithSuccess:true];
|
| -}
|
| -
|
| -- (void)closeWithSuccess:(bool)success {
|
| - onReply_.Run(success);
|
| - NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
|
| - [em removeEventHandlerForEventClass:'aevt' andEventID:'ansr'];
|
| - [self release];
|
| -}
|
| -@end
|
| -
|
| -//-----------------------------------------------------------------------------
|
| -
|
| -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);
|
| -
|
| -} // extern "C"
|
| -
|
| -int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
|
| - CommandLine::Init(info->argc, info->argv);
|
| -
|
| - base::mac::ScopedNSAutoreleasePool scoped_pool;
|
| - base::AtExitManager exit_manager;
|
| - chrome::RegisterPathProvider();
|
| -
|
| - 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;
|
| - }
|
| -
|
| - g_info = info;
|
| -
|
| - // Set bundle paths. This loads the bundles.
|
| - base::mac::SetOverrideOuterBundlePath(g_info->chrome_outer_bundle_path);
|
| - base::mac::SetOverrideFrameworkBundlePath(
|
| - g_info->chrome_versioned_path.Append(chrome::kFrameworkName));
|
| -
|
| - // Calculate the preferred locale used by Chrome.
|
| - // We can't use l10n_util::OverrideLocaleWithCocoaLocale() because it calls
|
| - // [base::mac::OuterBundle() preferredLocalizations] which gets localizations
|
| - // from the bundle of the running app (i.e. it is equivalent to
|
| - // [[NSBundle mainBundle] preferredLocalizations]) instead of the target
|
| - // bundle.
|
| - NSArray* preferred_languages = [NSLocale preferredLanguages];
|
| - NSArray* supported_languages = [base::mac::OuterBundle() localizations];
|
| - std::string preferred_localization;
|
| - for (NSString* language in preferred_languages) {
|
| - if ([supported_languages containsObject:language]) {
|
| - preferred_localization = base::SysNSStringToUTF8(language);
|
| - break;
|
| - }
|
| - }
|
| - std::string locale = l10n_util::NormalizeLocale(
|
| - l10n_util::GetApplicationLocale(preferred_localization));
|
| -
|
| - // Load localized strings.
|
| - ui::ResourceBundle::InitSharedInstanceWithLocale(
|
| - locale, NULL, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
|
| -
|
| - // Launch the IO thread.
|
| - base::Thread::Options io_thread_options;
|
| - io_thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
|
| - base::Thread *io_thread = new base::Thread("CrAppShimIO");
|
| - io_thread->StartWithOptions(io_thread_options);
|
| - g_io_thread = io_thread;
|
| -
|
| - // Find already running instances of Chrome.
|
| - pid_t pid = -1;
|
| - std::string chrome_process_id = CommandLine::ForCurrentProcess()->
|
| - GetSwitchValueASCII(app_mode::kLaunchedByChromeProcessId);
|
| - if (!chrome_process_id.empty()) {
|
| - if (!base::StringToInt(chrome_process_id, &pid))
|
| - LOG(FATAL) << "Invalid PID: " << chrome_process_id;
|
| - } else {
|
| - NSString* chrome_bundle_id = [base::mac::OuterBundle() bundleIdentifier];
|
| - NSArray* existing_chrome = [NSRunningApplication
|
| - runningApplicationsWithBundleIdentifier:chrome_bundle_id];
|
| - if ([existing_chrome count] > 0)
|
| - pid = [[existing_chrome objectAtIndex:0] processIdentifier];
|
| - }
|
| -
|
| - AppShimController controller;
|
| - base::MessageLoopForUI main_message_loop;
|
| - main_message_loop.set_thread_name("MainThread");
|
| - base::PlatformThread::SetName("CrAppShimMain");
|
| -
|
| - // In tests, launching Chrome does nothing, and we won't get a ping response,
|
| - // so just assume the socket exists.
|
| - if (pid == -1 &&
|
| - !CommandLine::ForCurrentProcess()->HasSwitch(
|
| - app_mode::kLaunchedForTest)) {
|
| - // Launch Chrome if it isn't already running.
|
| - ProcessSerialNumber psn;
|
| - CommandLine command_line(CommandLine::NO_PROGRAM);
|
| - command_line.AppendSwitch(switches::kSilentLaunch);
|
| -
|
| - // If the shim is the app launcher, pass --show-app-list when starting a new
|
| - // Chrome process to inform startup codepaths and load the correct profile.
|
| - if (info->app_mode_id == app_mode::kAppListModeId) {
|
| - command_line.AppendSwitch(switches::kShowAppList);
|
| - } else {
|
| - command_line.AppendSwitchPath(switches::kProfileDirectory,
|
| - info->profile_dir);
|
| - }
|
| -
|
| - bool success =
|
| - base::mac::OpenApplicationWithPath(base::mac::OuterBundlePath(),
|
| - command_line,
|
| - kLSLaunchDefaults,
|
| - &psn);
|
| - if (!success)
|
| - return 1;
|
| -
|
| - base::Callback<void(bool)> on_ping_chrome_reply =
|
| - base::Bind(&AppShimController::OnPingChromeReply,
|
| - base::Unretained(&controller));
|
| -
|
| - // This code abuses the fact that Apple Events sent before the process is
|
| - // fully initialized don't receive a reply until its run loop starts. Once
|
| - // the reply is received, Chrome will have opened its IPC port, guaranteed.
|
| - [ReplyEventHandler pingProcess:psn
|
| - andCall:on_ping_chrome_reply];
|
| -
|
| - main_message_loop.PostDelayedTask(
|
| - FROM_HERE,
|
| - base::Bind(&AppShimController::OnPingChromeTimeout,
|
| - base::Unretained(&controller)),
|
| - base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds));
|
| - } else {
|
| - // Chrome already running. Proceed to init. This could still fail if Chrome
|
| - // is still starting up or shutting down, but the process will exit quickly,
|
| - // which is preferable to waiting for the Apple Event to timeout after one
|
| - // minute.
|
| - main_message_loop.PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&AppShimController::Init,
|
| - base::Unretained(&controller)));
|
| - }
|
| -
|
| - main_message_loop.Run();
|
| - return 0;
|
| -}
|
|
|