Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(606)

Side by Side Diff: chrome/app/chrome_main_app_mode_mac.mm

Issue 12623005: [mac] App shims (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // On Mac, one can't make shortcuts with command-line arguments. Instead, we 5 // On Mac, one can't make shortcuts with command-line arguments. Instead, we
6 // produce small app bundles which locate the Chromium framework and load it, 6 // produce small app bundles which locate the Chromium framework and load it,
7 // passing the appropriate data. This is the entry point into the framework for 7 // passing the appropriate data. This is the entry point into the framework for
8 // those app bundles. 8 // those app bundles.
9 9
10 #include "base/basictypes.h" 10 #import <Cocoa/Cocoa.h>
11 #include "base/files/file_path.h" 11
12 #include "base/at_exit.h"
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/mac/bundle_locations.h" 14 #include "base/threading/thread.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/mac_logging.h" 15 #include "base/mac/mac_logging.h"
16 #include "base/mac/mac_util.h" 16 #include "base/mac/mac_util.h"
17 #include "base/mac/scoped_nsautorelease_pool.h" 17 #include "base/mac/scoped_nsautorelease_pool.h"
18 #include "base/message_loop.h"
19 #include "base/path_service.h"
18 #include "base/sys_string_conversions.h" 20 #include "base/sys_string_conversions.h"
19 #include "chrome/browser/shell_integration.h" 21 #include "chrome/common/app_shim_messages.h"
20 #include "chrome/common/chrome_constants.h" 22 #include "chrome/common/chrome_paths.h"
21 #include "chrome/common/chrome_paths_internal.h"
22 #include "chrome/common/chrome_switches.h" 23 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/mac/app_mode_common.h" 24 #include "chrome/common/mac/app_mode_common.h"
25 #include "ipc/ipc_channel_proxy.h"
26 #include "ipc/ipc_listener.h"
27 #include "ipc/ipc_message.h"
28
29 const app_mode::ChromeAppModeInfo* g_info;
30 base::Thread* g_io_thread = NULL;
jeremy 2013/03/11 06:49:59 Wrap these in an anon namespace?
jeremya 2013/03/11 08:42:26 Done.
31
32 class AppShimController : public IPC::Listener {
jeremy 2013/03/11 06:49:59 Class level comment + method comments.
jeremya 2013/03/11 08:42:26 Done.
33 public:
34 AppShimController();
35
36 void Init();
37
38 private:
39 virtual bool OnMessageReceived(const IPC::Message& message);
jeremy 2013/03/11 06:49:59 Add an // IPC::Listener implementation. comment
jeremya 2013/03/11 08:42:26 Done.
40 virtual void OnChannelError();
41
42 void OnLaunchAppDone(bool success);
43 void OnDidActivateApplicationNotification(NSNotification* notification);
jeremy 2013/03/11 06:49:59 Where is this implemented?
jeremya 2013/03/11 08:42:26 Ghost of a future patch. Removed.
44
45 IPC::ChannelProxy* channel_;
46
47 DISALLOW_COPY_AND_ASSIGN(AppShimController);
48 };
49
50 AppShimController::AppShimController() {
jeremy 2013/03/11 06:49:59 channel_(NULL)
jeremya 2013/03/11 08:42:26 Done.
51 }
52
53 void AppShimController::Init() {
54 base::FilePath user_data_dir;
55 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
jeremy 2013/03/11 06:49:59 What do you do if this fails?
jeremya 2013/03/11 08:42:26 Let's just die. Didn't realise it could fail.
56 base::FilePath socket_path =
57 user_data_dir.Append(app_mode::kAppShimSocketName);
58 IPC::ChannelHandle handle(socket_path.value());
59 channel_ = new IPC::ChannelProxy(handle, IPC::Channel::MODE_NAMED_CLIENT,
60 this, g_io_thread->message_loop_proxy());
jeremy 2013/03/11 06:49:59 DCHECK(g_io_thread) somewhere ?
jeremya 2013/03/11 08:42:26 Done.
61
62 channel_->Send(new AppShimHostMsg_LaunchApp(
63 g_info->profile_dir.value(), g_info->app_mode_id));
64 }
65
66 bool AppShimController::OnMessageReceived(const IPC::Message& message) {
67 bool handled = true;
68 IPC_BEGIN_MESSAGE_MAP(AppShimController, message)
69 IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
70
jeremy 2013/03/11 06:49:59 remove NL
jeremya 2013/03/11 08:42:26 Done.
71 IPC_MESSAGE_UNHANDLED(handled = false)
72 IPC_END_MESSAGE_MAP()
73
74 return handled;
75 }
76
77 void AppShimController::OnChannelError() {
78 [NSApp terminate:nil];
jeremy 2013/03/11 06:49:59 LOG(ERROR) ?
jeremya 2013/03/11 08:42:26 Done.
79 }
80
81 void AppShimController::OnLaunchAppDone(bool success) {
82 if (!success)
83 [NSApp terminate:nil];
84 }
85
86 //-----------------------------------------------------------------------------
87
88 @interface ReplyEventHandler : NSObject {
jeremy 2013/03/11 06:49:59 Can you add a header comment explaining why we nee
jeremya 2013/03/11 08:42:26 Done.
89 base::Callback<void(bool)> onReply_;
90 AEDesc replyEvent_;
91 }
92 // Sends an Apple Event to the process identified by |psn|, and calls |replyFn|
93 // when the reply is received. Internally this creates a ReplyEventHandler,
94 // which will be destroyed once the reply event has been received.
95 + (void)pingProcess:(const ProcessSerialNumber&)psn
96 andCall:(base::Callback<void(bool)>)replyFn;
97 @end
98
99 @interface ReplyEventHandler (PrivateMethods)
100 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn;
jeremy 2013/03/11 06:49:59 Add high level comments for methods.
jeremya 2013/03/11 08:42:26 Done.
101 - (void)pingProcess:(const ProcessSerialNumber&)psn;
102 - (void)message:(NSAppleEventDescriptor*)event
103 withReply:(NSAppleEventDescriptor*)reply;
104 - (void)closeWithSuccess:(bool)success;
105 @end
106
107 @implementation ReplyEventHandler
108 + (void)pingProcess:(const ProcessSerialNumber&)psn
109 andCall:(base::Callback<void(bool)>)replyFn {
110 ReplyEventHandler* handler =
111 [[ReplyEventHandler alloc] initWithCallback:replyFn];
jeremy 2013/03/11 06:49:59 Add a comment on where this is released.
jeremya 2013/03/11 08:42:26 Done.
112 [handler pingProcess:psn];
113 }
114 @end
115
116 @implementation ReplyEventHandler (PrivateMethods)
117 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn {
118 if ((self = [super init])) {
119 onReply_ = replyFn;
120 }
121 return self;
122 }
123
124 - (void)pingProcess:(const ProcessSerialNumber&)psn {
125 // Register the reply listener.
126 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
127 [em setEventHandler:self
128 andSelector:@selector(message:withReply:)
129 forEventClass:'aevt'
130 andEventID:'ansr'];
131 // Craft the Apple Event to send.
132 NSAppleEventDescriptor* target = [NSAppleEventDescriptor
133 descriptorWithDescriptorType:typeProcessSerialNumber
134 bytes:&psn
135 length:sizeof(psn)];
136 NSAppleEventDescriptor* initial_event =
137 [NSAppleEventDescriptor
138 appleEventWithEventClass:app_mode::kAEChromeAppClass
139 eventID:app_mode::kAEChromeAppPing
140 targetDescriptor:target
141 returnID:kAutoGenerateReturnID
142 transactionID:kAnyTransactionID];
143 // And away we go.
144 // TODO(jeremya): if we don't care about the contents of the reply, can we
145 // pass NULL for the reply event parameter?
146 OSStatus status = AESendMessage(
147 [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout);
148 if (status != noErr) {
149 OSSTATUS_LOG(ERROR, status) << "AESendMessage";
150 [self closeWithSuccess:false];
151 }
152 }
153
154 - (void)message:(NSAppleEventDescriptor*)event
155 withReply:(NSAppleEventDescriptor*)reply {
156 [self closeWithSuccess:true];
157 }
158
159 - (void)closeWithSuccess:(bool)success {
160 onReply_.Run(success);
161 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
162 [em removeEventHandlerForEventClass:'aevt' andEventID:'ansr'];
163 [self release];
164 }
165 @end
166
167 //-----------------------------------------------------------------------------
168
169 namespace {
170
171 void OnPingChromeReply(bool success) {
jeremy 2013/03/11 06:49:59 Function comment.
jeremya 2013/03/11 08:42:26 Done.
172 if (!success) {
173 [NSApp terminate:nil];
174 return;
175 }
176 AppShimController* controller = new AppShimController;
177 controller->Init();
178 }
179
180 } // namespace
24 181
25 extern "C" { 182 extern "C" {
26 183
27 // |ChromeAppModeStart()| is the point of entry into the framework from the app 184 // |ChromeAppModeStart()| is the point of entry into the framework from the app
28 // mode loader. 185 // mode loader.
29 __attribute__((visibility("default"))) 186 __attribute__((visibility("default")))
30 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info); 187 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info);
31 188
32 } // extern "C" 189 } // extern "C"
33 190
34 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) { 191 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
35 base::mac::ScopedNSAutoreleasePool scoped_pool; 192 base::mac::ScopedNSAutoreleasePool scoped_pool;
193 base::AtExitManager exit_manager;
194 chrome::RegisterPathProvider();
36 195
37 if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) { 196 if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) {
38 RAW_LOG(ERROR, "App Mode Loader too old."); 197 RAW_LOG(ERROR, "App Mode Loader too old.");
39 return 1; 198 return 1;
40 } 199 }
41 if (info->major_version > app_mode::kCurrentChromeAppModeInfoMajorVersion) { 200 if (info->major_version > app_mode::kCurrentChromeAppModeInfoMajorVersion) {
42 RAW_LOG(ERROR, "Browser Framework too old to load App Shortcut."); 201 RAW_LOG(ERROR, "Browser Framework too old to load App Shortcut.");
43 return 1; 202 return 1;
44 } 203 }
45 204
205 g_info = info;
206
207 // Launch the IO thread.
208 base::Thread::Options io_thread_options;
209 io_thread_options.message_loop_type = MessageLoop::TYPE_IO;
210 base::Thread *io_thread = new base::Thread("CrAppShimIO");
211 io_thread->StartWithOptions(io_thread_options);
212 g_io_thread = io_thread;
213
214 // Launch Chrome if it isn't already running.
46 FSRef app_fsref; 215 FSRef app_fsref;
47 if (!base::mac::FSRefFromPath(info->chrome_outer_bundle_path.value(), 216 if (!base::mac::FSRefFromPath(info->chrome_outer_bundle_path.value(),
48 &app_fsref)) { 217 &app_fsref)) {
49 PLOG(ERROR) << "base::mac::FSRefFromPath failed for " 218 LOG(ERROR) << "base::mac::FSRefFromPath failed for "
50 << info->chrome_outer_bundle_path.value(); 219 << info->chrome_outer_bundle_path.value();
51 return 1; 220 return 1;
52 } 221 }
53 std::string silent = std::string("--") + switches::kSilentLaunch; 222 std::string silent = std::string("--") + switches::kSilentLaunch;
54 CFArrayRef launch_args = 223 CFArrayRef launch_args =
55 base::mac::NSToCFCast(@[base::SysUTF8ToNSString(silent)]); 224 base::mac::NSToCFCast(@[base::SysUTF8ToNSString(silent)]);
56 225
57 LSApplicationParameters ls_parameters = { 226 LSApplicationParameters ls_parameters = {
58 0, // version 227 0, // version
59 kLSLaunchDefaults, 228 kLSLaunchDefaults,
60 &app_fsref, 229 &app_fsref,
61 NULL, // asyncLaunchRefCon 230 NULL, // asyncLaunchRefCon
62 NULL, // environment 231 NULL, // environment
63 launch_args, 232 launch_args,
64 NULL // initialEvent 233 NULL // initialEvent
65 }; 234 };
66 NSAppleEventDescriptor* initial_event = 235 ProcessSerialNumber psn;
67 [NSAppleEventDescriptor 236 // TODO(jeremya): this opens a new browser window if Chrome is already
68 appleEventWithEventClass:app_mode::kAEChromeAppClass 237 // running without any windows open.
69 eventID:app_mode::kAEChromeAppLaunch 238 OSStatus status = LSOpenApplication(&ls_parameters, &psn);
70 targetDescriptor:nil
71 returnID:kAutoGenerateReturnID
72 transactionID:kAnyTransactionID];
73 NSAppleEventDescriptor* appid_descriptor = [NSAppleEventDescriptor
74 descriptorWithString:base::SysUTF8ToNSString(info->app_mode_id)];
75 [initial_event setParamDescriptor:appid_descriptor
76 forKeyword:keyDirectObject];
77 NSAppleEventDescriptor* profile_dir_descriptor = [NSAppleEventDescriptor
78 descriptorWithString:base::SysUTF8ToNSString(info->profile_dir.value())];
79 [initial_event setParamDescriptor:profile_dir_descriptor
80 forKeyword:app_mode::kAEProfileDirKey];
81 ls_parameters.initialEvent = const_cast<AEDesc*>([initial_event aeDesc]);
82 // Send the Apple Event using launch services, launching Chrome if necessary.
83 OSStatus status = LSOpenApplication(&ls_parameters, NULL);
84 if (status != noErr) { 239 if (status != noErr) {
85 OSSTATUS_LOG(ERROR, status) << "LSOpenApplication"; 240 OSSTATUS_LOG(ERROR, status) << "LSOpenApplication";
86 return 1; 241 return 1;
87 } 242 }
243
244 // This code abuses the fact that Apple Events sent before the process is
245 // fully initialized don't receive a reply until its run loop starts. Once
246 // the reply is received, Chrome will have opened its IPC port, guaranteed.
247 [ReplyEventHandler pingProcess:psn andCall:base::Bind(&OnPingChromeReply)];
248
249 MessageLoopForUI main_message_loop;
250 main_message_loop.set_thread_name("MainThread");
251 base::PlatformThread::SetName("CrAppShimMain");
252 main_message_loop.Run();
88 return 0; 253 return 0;
89 } 254 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/app_controller_mac.mm » ('j') | chrome/browser/browser_process_impl.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698