17dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On Mac, one can't make shortcuts with command-line arguments. Instead, we
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// produce small app bundles which locate the Chromium framework and load it,
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// passing the appropriate data. This is the entry point into the framework for
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// those app bundles.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import <Cocoa/Cocoa.h>
1158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <vector>
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/at_exit.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/command_line.h"
1558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/files/file_path.h"
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
18fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch#include "base/mac/bundle_locations.h"
1958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/mac/foundation_util.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/mac/launch_services_util.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/mac/mac_logging.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/mac/mac_util.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/mac/scoped_nsautorelease_pool.h"
24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/mac/scoped_nsobject.h"
2546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/mac/sdk_forward_declarations.h"
267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "base/message_loop/message_loop.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/path_service.h"
28424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/threading/thread.h"
31fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch#include "chrome/common/chrome_constants.h"
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/chrome_paths.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/mac/app_mode_common.h"
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/common/mac/app_shim_messages.h"
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/grit/generated_resources.h"
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ipc/ipc_channel_proxy.h"
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ipc/ipc_listener.h"
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ipc/ipc_message.h"
40fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch#include "ui/base/l10n/l10n_util.h"
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/base/resource/resource_bundle.h"
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Timeout in seconds to wait for a reply for the initial Apple Event. Note that
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// kAEDefaultTimeout on Mac is "about one minute" according to Apple's
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// documentation, but is no longer supported for asynchronous Apple Events.
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kPingChromeTimeoutSeconds = 60;
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const app_mode::ChromeAppModeInfo* g_info;
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::Thread* g_io_thread = NULL;
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
55868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class AppShimController;
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// An application delegate to catch user interactions and send the appropriate
5858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// IPC messages to Chrome.
59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)@interface AppShimDelegate : NSObject<NSApplicationDelegate> {
60868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) @private
6158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  AppShimController* appShimController_;  // Weak, initially NULL.
62868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  BOOL terminateNow_;
63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  BOOL terminateRequested_;
6458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::vector<base::FilePath> filesToOpenAtStartup_;
65868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
66868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
6758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// The controller is initially NULL. Setting it indicates to the delegate that
6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// the controller has finished initialization.
6958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (void)setController:(AppShimController*)controller;
7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Gets files that were queued because the controller was not ready.
7258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Returns whether any FilePaths were added to |out|.
7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out;
7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// If the controller is ready, this sends a FocusApp with the files to open.
7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Otherwise, this adds the files to |filesToOpenAtStartup_|.
7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Takes an array of NSString*.
7858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (void)openFiles:(NSArray*)filename;
7958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Terminate immediately. This is necessary as we override terminate: to send
8158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// a QuitApp message.
82868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)- (void)terminateNow;
83868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
84868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)@end
85868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The AppShimController is responsible for communication with the main Chrome
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// process, and generally controls the lifetime of the app shim process.
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class AppShimController : public IPC::Listener {
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AppShimController();
9158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  virtual ~AppShimController();
9258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
9358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Called when the main Chrome process responds to the Apple Event ping that
9458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // was sent, or when the ping fails (if |success| is false).
9558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  void OnPingChromeReply(bool success);
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Called |kPingChromeTimeoutSeconds| after startup, to allow a timeout on the
98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // ping event to be detected.
99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  void OnPingChromeTimeout();
100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Connects to Chrome and sends a LaunchApp message.
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void Init();
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Create a channel from |socket_path| and send a LaunchApp message.
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void CreateChannelAndSendLaunchApp(const base::FilePath& socket_path);
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
107fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Builds main menu bar items.
108fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  void SetUpMenu();
109fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SendSetAppHidden(bool hidden);
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SendQuitApp();
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
11458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Called when the app is activated, e.g. by clicking on it in the dock, by
11558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // dropping a file on the dock icon, or by Cmd+Tabbing to it.
11658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Returns whether the message was sent.
11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  bool SendFocusApp(apps::AppShimFocusType focus_type,
11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                    const std::vector<base::FilePath>& files);
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // IPC::Listener implemetation.
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnChannelError() OVERRIDE;
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If Chrome failed to launch the app, |success| will be false and the app
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // shim process should die.
1277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  void OnLaunchAppDone(apps::AppShimLaunchResult result);
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Hide this app.
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  void OnHide();
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
132424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Requests user attention.
133424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void OnRequestUserAttention();
13403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  void OnSetUserAttention(apps::AppShimAttentionType attention_type);
135424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Terminates the app shim process.
137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  void Close();
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::FilePath user_data_dir_;
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<IPC::ChannelProxy> channel_;
14158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  base::scoped_nsobject<AppShimDelegate> delegate_;
142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  bool launch_app_done_;
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bool ping_chrome_reply_received_;
14403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  NSInteger attention_request_id_;
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(AppShimController);
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)AppShimController::AppShimController()
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : delegate_([[AppShimDelegate alloc] init]),
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      launch_app_done_(false),
15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ping_chrome_reply_received_(false),
15303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      attention_request_id_(0) {
15458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Since AppShimController is created before the main message loop starts,
15558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // NSApp will not be set, so use sharedApplication.
15658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [[NSApplication sharedApplication] setDelegate:delegate_];
15758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
15858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
15958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)AppShimController::~AppShimController() {
16058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Un-set the delegate since NSApplication does not retain it.
161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  [[NSApplication sharedApplication] setDelegate:nil];
16258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
16358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
16458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void AppShimController::OnPingChromeReply(bool success) {
165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ping_chrome_reply_received_ = true;
16658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!success) {
16758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    [NSApp terminate:nil];
16858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
16958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
17058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
17158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  Init();
17258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void AppShimController::OnPingChromeTimeout() {
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!ping_chrome_reply_received_)
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    [NSApp terminate:nil];
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShimController::Init() {
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(g_io_thread);
181fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
182fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  SetUpMenu();
183fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
184424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Chrome will relaunch shims when relaunching apps.
185424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (base::mac::IsOSLionOrLater())
186424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    [NSApp disableRelaunchOnLogin];
187424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
188a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // The user_data_dir for shims actually contains the app_data_path.
189a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // I.e. <user_data_dir>/<profile_dir>/Web Applications/_crx_extensionid/
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  user_data_dir_ = g_info->user_data_dir.DirName().DirName().DirName();
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK(!user_data_dir_.empty());
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::FilePath symlink_path =
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      user_data_dir_.Append(app_mode::kAppShimSocketSymlinkName);
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::FilePath socket_path;
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!base::ReadSymbolicLink(symlink_path, &socket_path)) {
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // The path in the user data dir is not a symlink, try connecting directly.
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CreateChannelAndSendLaunchApp(symlink_path);
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  app_mode::VerifySocketPermissions(socket_path);
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CreateChannelAndSendLaunchApp(socket_path);
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppShimController::CreateChannelAndSendLaunchApp(
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::FilePath& socket_path) {
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IPC::ChannelHandle handle(socket_path.value());
21146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  channel_ = IPC::ChannelProxy::Create(handle,
21246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                       IPC::Channel::MODE_NAMED_CLIENT,
21346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                       this,
21446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                       g_io_thread->message_loop_proxy().get());
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
216424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  bool launched_by_chrome =
217424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      CommandLine::ForCurrentProcess()->HasSwitch(
218424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          app_mode::kLaunchedByChromeProcessId);
21958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  apps::AppShimLaunchType launch_type = launched_by_chrome ?
22058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          apps::APP_SHIM_LAUNCH_REGISTER_ONLY : apps::APP_SHIM_LAUNCH_NORMAL;
22158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
22258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [delegate_ setController:this];
223868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
22458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::vector<base::FilePath> files;
22558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [delegate_ getFilesToOpenAtStartup:&files];
22658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  channel_->Send(new AppShimHostMsg_LaunchApp(
22858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      g_info->profile_dir, g_info->app_mode_id, launch_type, files));
229868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
230868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
231fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdochvoid AppShimController::SetUpMenu() {
232fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  NSString* title = base::SysUTF16ToNSString(g_info->app_mode_name);
233fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
234fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Create a main menu since [NSApp mainMenu] is nil.
235fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  base::scoped_nsobject<NSMenu> main_menu([[NSMenu alloc] initWithTitle:title]);
236fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
237fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // The title of the first item is replaced by OSX with the name of the app and
238fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // bold styling. Create a dummy item for this and make it hidden.
239fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  NSMenuItem* dummy_item = [main_menu addItemWithTitle:title
240fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch                                                action:nil
241fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch                                         keyEquivalent:@""];
242fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  base::scoped_nsobject<NSMenu> dummy_submenu(
243fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch      [[NSMenu alloc] initWithTitle:title]);
244fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  [dummy_item setSubmenu:dummy_submenu];
245fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  [dummy_item setHidden:YES];
246fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
247fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Construct an unbolded app menu, to match how it appears in the Chrome menu
248fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // bar when the app is focused.
249fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  NSMenuItem* item = [main_menu addItemWithTitle:title
250fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch                                          action:nil
251fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch                                   keyEquivalent:@""];
252fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:title]);
253fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  [item setSubmenu:submenu];
254fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
255fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Add a quit entry.
256fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  NSString* quit_localized_string =
257fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch      l10n_util::GetNSStringF(IDS_EXIT_MAC, g_info->app_mode_name);
258fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  [submenu addItemWithTitle:quit_localized_string
259fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch                     action:@selector(terminate:)
260fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch              keyEquivalent:@"q"];
261fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
26268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // Add File, Edit, and Window menus. These are just here to make the
26368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // transition smoother, i.e. from another application to the shim then to
26468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // Chrome.
26568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  [main_menu addItemWithTitle:l10n_util::GetNSString(IDS_FILE_MENU_MAC)
26668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                       action:nil
26768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                keyEquivalent:@""];
26868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  [main_menu addItemWithTitle:l10n_util::GetNSString(IDS_EDIT_MENU_MAC)
26968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                       action:nil
27068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                keyEquivalent:@""];
27168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  [main_menu addItemWithTitle:l10n_util::GetNSString(IDS_WINDOW_MENU_MAC)
27268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                       action:nil
27368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                keyEquivalent:@""];
27468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
275fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  [NSApp setMainMenu:main_menu];
276fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch}
277fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
278eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid AppShimController::SendQuitApp() {
279868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  channel_->Send(new AppShimHostMsg_QuitApp);
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShimController::OnMessageReceived(const IPC::Message& message) {
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool handled = true;
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IPC_BEGIN_MESSAGE_MAP(AppShimController, message)
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
28658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    IPC_MESSAGE_HANDLER(AppShimMsg_Hide, OnHide)
287424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    IPC_MESSAGE_HANDLER(AppShimMsg_RequestUserAttention, OnRequestUserAttention)
28803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    IPC_MESSAGE_HANDLER(AppShimMsg_SetUserAttention, OnSetUserAttention)
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_UNHANDLED(handled = false)
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IPC_END_MESSAGE_MAP()
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return handled;
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShimController::OnChannelError() {
296868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  Close();
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid AppShimController::OnLaunchAppDone(apps::AppShimLaunchResult result) {
3007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (result != apps::APP_SHIM_LAUNCH_SUCCESS) {
301868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    Close();
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
304868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
30558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::vector<base::FilePath> files;
30658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if ([delegate_ getFilesToOpenAtStartup:&files])
30758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES, files);
30858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
309eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  launch_app_done_ = true;
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
31258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void AppShimController::OnHide() {
31358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [NSApp hide:nil];
31458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
31558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
316424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void AppShimController::OnRequestUserAttention() {
31703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  OnSetUserAttention(apps::APP_SHIM_ATTENTION_INFORMATIONAL);
31803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
31903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
32003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void AppShimController::OnSetUserAttention(
32103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    apps::AppShimAttentionType attention_type) {
32203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  switch (attention_type) {
32303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    case apps::APP_SHIM_ATTENTION_CANCEL:
32403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      [NSApp cancelUserAttentionRequest:attention_request_id_];
32503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      attention_request_id_ = 0;
32603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      break;
32703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    case apps::APP_SHIM_ATTENTION_CRITICAL:
32803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      attention_request_id_ = [NSApp requestUserAttention:NSCriticalRequest];
32903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      break;
33003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    case apps::APP_SHIM_ATTENTION_INFORMATIONAL:
33103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      attention_request_id_ =
33203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          [NSApp requestUserAttention:NSInformationalRequest];
33303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      break;
33403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    case apps::APP_SHIM_ATTENTION_NUM_TYPES:
33503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      NOTREACHED();
33603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
337424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
338424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
339868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void AppShimController::Close() {
34058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [delegate_ terminateNow];
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
34358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool AppShimController::SendFocusApp(apps::AppShimFocusType focus_type,
34458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                     const std::vector<base::FilePath>& files) {
345eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (launch_app_done_) {
34658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    channel_->Send(new AppShimHostMsg_FocusApp(focus_type, files));
34758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return true;
348eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
34958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
35058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return false;
351eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
352eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
353eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid AppShimController::SendSetAppHidden(bool hidden) {
354eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  channel_->Send(new AppShimHostMsg_SetAppHidden(hidden));
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
357868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)@implementation AppShimDelegate
358868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
35958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out {
36058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (filesToOpenAtStartup_.empty())
36158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return NO;
36258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
36358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  out->insert(out->end(),
36458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              filesToOpenAtStartup_.begin(),
36558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              filesToOpenAtStartup_.end());
36658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  filesToOpenAtStartup_.clear();
36758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return YES;
36858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
36958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
37058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (void)setController:(AppShimController*)controller {
37158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  appShimController_ = controller;
37258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
37358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
37458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (void)openFiles:(NSArray*)filenames {
37558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::vector<base::FilePath> filePaths;
37658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  for (NSString* filename in filenames)
37758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    filePaths.push_back(base::mac::NSStringToFilePath(filename));
37858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
37958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // If the AppShimController is ready, try to send a FocusApp. If that fails,
38058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // (e.g. if launching has not finished), enqueue the files.
38158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (appShimController_ &&
38258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES,
38358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                       filePaths)) {
38458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
385868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
38658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
38758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  filesToOpenAtStartup_.insert(filesToOpenAtStartup_.end(),
38858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                               filePaths.begin(),
38958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                               filePaths.end());
390868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
391868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
39258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (BOOL)application:(NSApplication*)app
39358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)           openFile:(NSString*)filename {
39458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [self openFiles:@[filename]];
395eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return YES;
396eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
397eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
39858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (void)application:(NSApplication*)app
39958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          openFiles:(NSArray*)filenames {
40058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [self openFiles:filenames];
40158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
40258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
40358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
40458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)- (BOOL)applicationOpenUntitledFile:(NSApplication*)app {
40558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (appShimController_) {
40658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_REOPEN,
40758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                            std::vector<base::FilePath>());
40858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
40958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
41058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return NO;
41158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
41258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
413eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch- (void)applicationWillBecomeActive:(NSNotification*)notification {
41458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (appShimController_) {
41558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_NORMAL,
41658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                     std::vector<base::FilePath>());
41758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
418eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
419eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
420868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)- (NSApplicationTerminateReply)
421868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    applicationShouldTerminate:(NSApplication*)sender {
42258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (terminateNow_ || !appShimController_)
423868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return NSTerminateNow;
424868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
425eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  appShimController_->SendQuitApp();
426868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Wait for the channel to close before terminating.
427868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  terminateRequested_ = YES;
428868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return NSTerminateLater;
429868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
430868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
431eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch- (void)applicationWillHide:(NSNotification*)notification {
43258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (appShimController_)
43358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    appShimController_->SendSetAppHidden(true);
434eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
435eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
436eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch- (void)applicationWillUnhide:(NSNotification*)notification {
43758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (appShimController_)
43858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    appShimController_->SendSetAppHidden(false);
439eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
440eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
441868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)- (void)terminateNow {
442868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (terminateRequested_) {
443868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    [NSApp replyToApplicationShouldTerminate:NSTerminateNow];
444868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
445868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
446868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
447868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  terminateNow_ = YES;
448868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  [NSApp terminate:nil];
449868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
450868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
451868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)@end
452868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//-----------------------------------------------------------------------------
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A ReplyEventHandler is a helper class to send an Apple Event to a process
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// and call a callback when the reply returns.
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This is used to 'ping' the main Chrome process -- once Chrome has sent back
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// an Apple Event reply, it's guaranteed that it has opened the IPC channel
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// that the app shim will connect to.
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@interface ReplyEventHandler : NSObject {
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Callback<void(bool)> onReply_;
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AEDesc replyEvent_;
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Sends an Apple Event to the process identified by |psn|, and calls |replyFn|
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// when the reply is received. Internally this creates a ReplyEventHandler,
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// which will delete itself once the reply event has been received.
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)+ (void)pingProcess:(const ProcessSerialNumber&)psn
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            andCall:(base::Callback<void(bool)>)replyFn;
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@end
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@interface ReplyEventHandler (PrivateMethods)
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Initialise the reply event handler. Doesn't register any handlers until
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |-pingProcess:| is called. |replyFn| is the function to be called when the
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Apple Event reply arrives.
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (id)initWithCallback:(base::Callback<void(bool)>)replyFn;
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Sends an Apple Event ping to the process identified by |psn| and registers
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// to listen for a reply.
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)pingProcess:(const ProcessSerialNumber&)psn;
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Called when a response is received from the target process for the ping sent
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// by |-pingProcess:|.
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)message:(NSAppleEventDescriptor*)event
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      withReply:(NSAppleEventDescriptor*)reply;
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Calls |onReply_|, passing it |success| to specify whether the ping was
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// successful.
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)closeWithSuccess:(bool)success;
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@end
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@implementation ReplyEventHandler
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)+ (void)pingProcess:(const ProcessSerialNumber&)psn
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            andCall:(base::Callback<void(bool)>)replyFn {
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The object will release itself when the reply arrives, or possibly earlier
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // if an unrecoverable error occurs.
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ReplyEventHandler* handler =
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      [[ReplyEventHandler alloc] initWithCallback:replyFn];
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  [handler pingProcess:psn];
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@end
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@implementation ReplyEventHandler (PrivateMethods)
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (id)initWithCallback:(base::Callback<void(bool)>)replyFn {
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if ((self = [super init])) {
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    onReply_ = replyFn;
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return self;
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)pingProcess:(const ProcessSerialNumber&)psn {
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register the reply listener.
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  [em setEventHandler:self
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          andSelector:@selector(message:withReply:)
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        forEventClass:'aevt'
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           andEventID:'ansr'];
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Craft the Apple Event to send.
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NSAppleEventDescriptor* target = [NSAppleEventDescriptor
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      descriptorWithDescriptorType:typeProcessSerialNumber
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             bytes:&psn
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            length:sizeof(psn)];
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NSAppleEventDescriptor* initial_event =
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      [NSAppleEventDescriptor
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          appleEventWithEventClass:app_mode::kAEChromeAppClass
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           eventID:app_mode::kAEChromeAppPing
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  targetDescriptor:target
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          returnID:kAutoGenerateReturnID
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     transactionID:kAnyTransactionID];
530f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
531f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Note that AESendMessage effectively ignores kAEDefaultTimeout, because this
532f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // call does not pass kAEWantReceipt (which is deprecated and unsupported on
533f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Mac). Instead, rely on OnPingChromeTimeout().
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OSStatus status = AESendMessage(
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout);
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (status != noErr) {
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    OSSTATUS_LOG(ERROR, status) << "AESendMessage";
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [self closeWithSuccess:false];
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)message:(NSAppleEventDescriptor*)event
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      withReply:(NSAppleEventDescriptor*)reply {
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  [self closeWithSuccess:true];
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)closeWithSuccess:(bool)success {
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  onReply_.Run(success);
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  [em removeEventHandlerForEventClass:'aevt' andEventID:'ansr'];
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  [self release];
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@end
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//-----------------------------------------------------------------------------
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extern "C" {
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |ChromeAppModeStart()| is the point of entry into the framework from the app
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// mode loader.
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__attribute__((visibility("default")))
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info);
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // extern "C"
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
567868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  CommandLine::Init(info->argc, info->argv);
568868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::mac::ScopedNSAutoreleasePool scoped_pool;
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::AtExitManager exit_manager;
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::RegisterPathProvider();
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) {
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RAW_LOG(ERROR, "App Mode Loader too old.");
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (info->major_version > app_mode::kCurrentChromeAppModeInfoMajorVersion) {
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RAW_LOG(ERROR, "Browser Framework too old to load App Shortcut.");
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  g_info = info;
5832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
584fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Set bundle paths. This loads the bundles.
585fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  base::mac::SetOverrideOuterBundlePath(g_info->chrome_outer_bundle_path);
586fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  base::mac::SetOverrideFrameworkBundlePath(
587fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch      g_info->chrome_versioned_path.Append(chrome::kFrameworkName));
588fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
589fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Calculate the preferred locale used by Chrome.
590fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // We can't use l10n_util::OverrideLocaleWithCocoaLocale() because it calls
591fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // [base::mac::OuterBundle() preferredLocalizations] which gets localizations
592fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // from the bundle of the running app (i.e. it is equivalent to
593fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // [[NSBundle mainBundle] preferredLocalizations]) instead of the target
594fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // bundle.
595fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  NSArray* preferred_languages = [NSLocale preferredLanguages];
596fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  NSArray* supported_languages = [base::mac::OuterBundle() localizations];
597fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  std::string preferred_localization;
598fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  for (NSString* language in preferred_languages) {
599fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch    if ([supported_languages containsObject:language]) {
600fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch      preferred_localization = base::SysNSStringToUTF8(language);
601fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch      break;
602fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch    }
603fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  }
604fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  std::string locale = l10n_util::NormalizeLocale(
605fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch      l10n_util::GetApplicationLocale(preferred_localization));
606fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
607fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch  // Load localized strings.
6086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  ui::ResourceBundle::InitSharedInstanceWithLocale(
6096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      locale, NULL, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
610fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Launch the IO thread.
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Thread::Options io_thread_options;
613a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  io_thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Thread *io_thread = new base::Thread("CrAppShimIO");
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  io_thread->StartWithOptions(io_thread_options);
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  g_io_thread = io_thread;
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
618868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Find already running instances of Chrome.
619424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  pid_t pid = -1;
620424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  std::string chrome_process_id = CommandLine::ForCurrentProcess()->
621424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      GetSwitchValueASCII(app_mode::kLaunchedByChromeProcessId);
622424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (!chrome_process_id.empty()) {
623424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if (!base::StringToInt(chrome_process_id, &pid))
624424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      LOG(FATAL) << "Invalid PID: " << chrome_process_id;
625424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  } else {
626424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    NSString* chrome_bundle_id = [base::mac::OuterBundle() bundleIdentifier];
627424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    NSArray* existing_chrome = [NSRunningApplication
628424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        runningApplicationsWithBundleIdentifier:chrome_bundle_id];
629424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if ([existing_chrome count] > 0)
630424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      pid = [[existing_chrome objectAtIndex:0] processIdentifier];
631424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
632868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
633f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AppShimController controller;
634f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::MessageLoopForUI main_message_loop;
635f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  main_message_loop.set_thread_name("MainThread");
636f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::PlatformThread::SetName("CrAppShimMain");
637868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
638f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // In tests, launching Chrome does nothing, and we won't get a ping response,
639f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // so just assume the socket exists.
640f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (pid == -1 &&
641f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      !CommandLine::ForCurrentProcess()->HasSwitch(
642f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          app_mode::kLaunchedForTest)) {
643f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // Launch Chrome if it isn't already running.
644f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    ProcessSerialNumber psn;
645868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    CommandLine command_line(CommandLine::NO_PROGRAM);
646868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    command_line.AppendSwitch(switches::kSilentLaunch);
6475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If the shim is the app launcher, pass --show-app-list when starting a new
6495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Chrome process to inform startup codepaths and load the correct profile.
6505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (info->app_mode_id == app_mode::kAppListModeId) {
6515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      command_line.AppendSwitch(switches::kShowAppList);
6525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
6535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      command_line.AppendSwitchPath(switches::kProfileDirectory,
6545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    info->profile_dir);
6555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
6565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
657868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    bool success =
658fb250657ef40d7500f20882d5c9909c1013367d3Ben Murdoch        base::mac::OpenApplicationWithPath(base::mac::OuterBundlePath(),
659868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                           command_line,
660bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch                                           kLSLaunchDefaults,
661868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                           &psn);
662868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (!success)
663868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return 1;
66458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
665f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    base::Callback<void(bool)> on_ping_chrome_reply =
666f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        base::Bind(&AppShimController::OnPingChromeReply,
667f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   base::Unretained(&controller));
668f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
669f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // This code abuses the fact that Apple Events sent before the process is
670f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // fully initialized don't receive a reply until its run loop starts. Once
671f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // the reply is received, Chrome will have opened its IPC port, guaranteed.
672f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    [ReplyEventHandler pingProcess:psn
673f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                           andCall:on_ping_chrome_reply];
674f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
675f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    main_message_loop.PostDelayedTask(
676f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        FROM_HERE,
677f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        base::Bind(&AppShimController::OnPingChromeTimeout,
678f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   base::Unretained(&controller)),
679f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds));
680f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  } else {
681f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // Chrome already running. Proceed to init. This could still fail if Chrome
682f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // is still starting up or shutting down, but the process will exit quickly,
683f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // which is preferable to waiting for the Apple Event to timeout after one
684f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // minute.
685f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    main_message_loop.PostTask(
686f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        FROM_HERE,
687f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        base::Bind(&AppShimController::Init,
688f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   base::Unretained(&controller)));
689f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
6902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  main_message_loop.Run();
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return 0;
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
694