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

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: 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;
31
32 class AppShimController : public IPC::Listener {
33 public:
34 AppShimController();
35
36 private:
37 virtual bool OnMessageReceived(const IPC::Message& message);
38 virtual void OnChannelError();
39
40 void OnLaunchAppDone(bool success);
41 void OnDidActivateApplicationNotification(NSNotification* notification);
42
43 IPC::ChannelProxy* channel_;
44 };
Mark Mentovai 2013/03/08 16:50:17 DISALLOW_COPY_AND_ASSIGN?
jeremya 2013/03/11 02:47:33 Done.
45
46 AppShimController::AppShimController() {
Mark Mentovai 2013/03/08 16:50:17 There’s enough stuff in this constructor that coul
jeremya 2013/03/11 02:47:33 All the stuff that could fail here fails on a sepa
47 base::FilePath user_data_dir;
48 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
49 base::FilePath socket_path =
50 user_data_dir.Append(app_mode::kAppShimSocketName);
51 IPC::ChannelHandle handle(socket_path.value());
52 channel_ = new IPC::ChannelProxy(handle, IPC::Channel::MODE_NAMED_CLIENT,
53 this, g_io_thread->message_loop_proxy());
54
55 channel_->Send(new AppShimHostMsg_LaunchApp(
56 g_info->profile_dir.value(), g_info->app_mode_id));
57 }
58
59 bool AppShimController::OnMessageReceived(const IPC::Message& message) {
60 bool handled = true;
61 IPC_BEGIN_MESSAGE_MAP(AppShimController, message)
62 IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
63
64 IPC_MESSAGE_UNHANDLED(handled = false)
65 IPC_END_MESSAGE_MAP()
66
67 return handled;
68 }
69
70 void AppShimController::OnChannelError() {
71 [NSApp terminate:nil];
72 }
73
74 void AppShimController::OnLaunchAppDone(bool success) {
75 if (!success)
76 [NSApp terminate:nil];
77 }
78
79 //-----------------------------------------------------------------------------
80
81 @interface ReplyEventHandler : NSObject {
Mark Mentovai 2013/03/08 16:50:17 Just because you’re defining this class in an .mm
82 base::Closure onReply_;
83 AEDesc replyEvent_;
84 }
85 + (void)pingProcess:(const ProcessSerialNumber&)psn
Mark Mentovai 2013/03/08 16:50:17 For example, the memory management of ReplyEventHa
jeremya 2013/03/11 02:47:33 Done.
86 andCall:(base::Closure)replyFn;
87 @end
88
89 @interface ReplyEventHandler (PrivateMethods)
90 - (id)initWithCallback:(base::Closure)replyFn;
91 - (void)pingProcess:(const ProcessSerialNumber&)psn;
92 - (void)message:(NSAppleEventDescriptor*)event
93 withReply:(NSAppleEventDescriptor*)reply;
94 @end
95
96 @implementation ReplyEventHandler
97 + (void)pingProcess:(const ProcessSerialNumber&)psn
98 andCall:(base::Closure)replyFn {
99 ReplyEventHandler* handler = [[ReplyEventHandler alloc]
Mark Mentovai 2013/03/08 16:50:17 This would be easier to read if you broke the line
jeremya 2013/03/11 02:47:33 Done.
100 initWithCallback:replyFn];
101 [handler pingProcess:psn];
102 }
103 @end
104
105 @implementation ReplyEventHandler (PrivateMethods)
106 - (id)initWithCallback:(base::Closure)replyFn {
107 if (self = [super init]) {
Mark Mentovai 2013/03/08 16:50:17 We always write if ((self = [super init])) to av
jeremya 2013/03/11 02:47:33 Done, I'm surprised too.
108 onReply_ = replyFn;
109 }
110 return self;
111 }
112 - (void)pingProcess:(const ProcessSerialNumber&)psn {
Mark Mentovai 2013/03/08 16:50:17 Blank line between methods implementations, please
jeremya 2013/03/11 02:47:33 Done.
113 // Register the reply listener.
114 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
115 [em setEventHandler:self
116 andSelector:@selector(message:withReply:)
117 forEventClass:'aevt'
118 andEventID:'ansr'];
119 // Craft the Apple Event to send.
120 NSAppleEventDescriptor* target = [NSAppleEventDescriptor
121 descriptorWithDescriptorType:typeProcessSerialNumber
122 bytes:&psn
123 length:sizeof(ProcessSerialNumber)];
Mark Mentovai 2013/03/08 16:50:17 Use sizeof(variable) and not sizeof(type) whenever
jeremya 2013/03/11 02:47:33 Done.
124 NSAppleEventDescriptor* initial_event =
125 [NSAppleEventDescriptor
126 appleEventWithEventClass:app_mode::kAEChromeAppClass
127 eventID:app_mode::kAEChromeAppPing
128 targetDescriptor:target
129 returnID:kAutoGenerateReturnID
130 transactionID:kAnyTransactionID];
131 // And away we go.
132 // TODO(jeremya): if we don't care about the contents of the reply, can we
133 // pass NULL for the reply event parameter?
134 AESendMessage(
Mark Mentovai 2013/03/08 16:50:17 Considering how much logging you did in the previo
jeremya 2013/03/11 02:47:33 Done, plus the thing dies if AESendMessage fails.
135 [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout);
136 }
137 - (void)message:(NSAppleEventDescriptor*)event
138 withReply:(NSAppleEventDescriptor*)reply {
139 onReply_.Run();
140 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
141 [em removeEventHandlerForEventClass:'aevt' andEventID:'ansr'];
142 [self release];
143 }
144 @end
145
146 //-----------------------------------------------------------------------------
147
148 void Begin() {
Mark Mentovai 2013/03/08 16:50:17 This is unnamespaced and non-static. Putting somet
jeremya 2013/03/11 02:47:33 Done.
149 new AppShimController;
150 }
24 151
25 extern "C" { 152 extern "C" {
26 153
27 // |ChromeAppModeStart()| is the point of entry into the framework from the app 154 // |ChromeAppModeStart()| is the point of entry into the framework from the app
28 // mode loader. 155 // mode loader.
29 __attribute__((visibility("default"))) 156 __attribute__((visibility("default")))
30 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info); 157 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info);
31 158
32 } // extern "C" 159 } // extern "C"
33 160
34 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) { 161 int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
35 base::mac::ScopedNSAutoreleasePool scoped_pool; 162 base::mac::ScopedNSAutoreleasePool scoped_pool;
163 base::AtExitManager exit_manager;
164 chrome::RegisterPathProvider();
36 165
37 if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) { 166 if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) {
38 RAW_LOG(ERROR, "App Mode Loader too old."); 167 RAW_LOG(ERROR, "App Mode Loader too old.");
39 return 1; 168 return 1;
40 } 169 }
41 if (info->major_version > app_mode::kCurrentChromeAppModeInfoMajorVersion) { 170 if (info->major_version > app_mode::kCurrentChromeAppModeInfoMajorVersion) {
42 RAW_LOG(ERROR, "Browser Framework too old to load App Shortcut."); 171 RAW_LOG(ERROR, "Browser Framework too old to load App Shortcut.");
43 return 1; 172 return 1;
44 } 173 }
45 174
175 g_info = info;
176
177 // Launch the IO thread.
178 base::Thread::Options io_thread_options;
179 io_thread_options.message_loop_type = MessageLoop::TYPE_IO;
180 base::Thread *io_thread = new base::Thread("CrAppShimIO");
181 io_thread->StartWithOptions(io_thread_options);
182 g_io_thread = io_thread;
183
184 // Launch Chrome if it isn't already running.
46 FSRef app_fsref; 185 FSRef app_fsref;
47 if (!base::mac::FSRefFromPath(info->chrome_outer_bundle_path.value(), 186 if (!base::mac::FSRefFromPath(info->chrome_outer_bundle_path.value(),
48 &app_fsref)) { 187 &app_fsref)) {
49 PLOG(ERROR) << "base::mac::FSRefFromPath failed for " 188 PLOG(ERROR) << "base::mac::FSRefFromPath failed for "
Mark Mentovai 2013/03/08 16:50:17 PLOG is wrong here, because base::mac::FSRefFromPa
jeremya 2013/03/11 02:47:33 -> LOG
50 << info->chrome_outer_bundle_path.value(); 189 << info->chrome_outer_bundle_path.value();
51 return 1; 190 return 1;
52 } 191 }
53 std::string silent = std::string("--") + switches::kSilentLaunch; 192 std::string silent = std::string("--") + switches::kSilentLaunch;
54 CFArrayRef launch_args = 193 CFArrayRef launch_args =
55 base::mac::NSToCFCast(@[base::SysUTF8ToNSString(silent)]); 194 base::mac::NSToCFCast(@[base::SysUTF8ToNSString(silent)]);
56 195
57 LSApplicationParameters ls_parameters = { 196 LSApplicationParameters ls_parameters = {
58 0, // version 197 0, // version
59 kLSLaunchDefaults, 198 kLSLaunchDefaults,
60 &app_fsref, 199 &app_fsref,
61 NULL, // asyncLaunchRefCon 200 NULL, // asyncLaunchRefCon
62 NULL, // environment 201 NULL, // environment
63 launch_args, 202 launch_args,
64 NULL // initialEvent 203 NULL // initialEvent
65 }; 204 };
66 NSAppleEventDescriptor* initial_event = 205 ProcessSerialNumber psn;
67 [NSAppleEventDescriptor 206 // TODO(jeremya): this opens a new browser window if Chrome is already
68 appleEventWithEventClass:app_mode::kAEChromeAppClass 207 // running without any windows open.
69 eventID:app_mode::kAEChromeAppLaunch 208 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) { 209 if (status != noErr) {
85 OSSTATUS_LOG(ERROR, status) << "LSOpenApplication"; 210 OSSTATUS_LOG(ERROR, status) << "LSOpenApplication";
86 return 1; 211 return 1;
87 } 212 }
213
214 // This code abuses the fact that Apple Events sent before the process is
215 // fully initialized don't receive a reply until its run loop starts. Once
216 // the reply is received, Chrome will have opened its IPC port, guaranteed.
217 [ReplyEventHandler pingProcess:psn andCall:base::Bind(&Begin)];
218
219 MessageLoopForUI main_message_loop;
220 main_message_loop.set_thread_name("MainThread");
221 base::PlatformThread::SetName("CrAppShimMain");
Mark Mentovai 2013/03/08 16:50:17 I like that you’ve stuck with the appropriate nami
jeremya 2013/03/11 02:47:33 I did? Awesome!
222 main_message_loop.Run();
88 return 0; 223 return 0;
89 } 224 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/app_controller_mac.mm » ('j') | chrome/browser/web_applications/app_shim_host_controller.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698