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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | chrome/browser/app_controller_mac.mm » ('j') | chrome/browser/browser_process_impl.h » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/app/chrome_main_app_mode_mac.mm
diff --git a/chrome/app/chrome_main_app_mode_mac.mm b/chrome/app/chrome_main_app_mode_mac.mm
index d102fc8f591e667bc0b2e00a5d67391d30f41729..1c0d2afdd40249d33858fd675c24f3000dfee2ee 100644
--- a/chrome/app/chrome_main_app_mode_mac.mm
+++ b/chrome/app/chrome_main_app_mode_mac.mm
@@ -7,20 +7,177 @@
// passing the appropriate data. This is the entry point into the framework for
// those app bundles.
-#include "base/basictypes.h"
-#include "base/files/file_path.h"
+#import <Cocoa/Cocoa.h>
+
+#include "base/at_exit.h"
#include "base/logging.h"
-#include "base/mac/bundle_locations.h"
-#include "base/mac/foundation_util.h"
+#include "base/threading/thread.h"
#include "base/mac/mac_logging.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
#include "base/sys_string_conversions.h"
-#include "chrome/browser/shell_integration.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/chrome_paths_internal.h"
+#include "chrome/common/app_shim_messages.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/mac/app_mode_common.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
+
+const app_mode::ChromeAppModeInfo* g_info;
+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.
+
+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.
+ public:
+ AppShimController();
+
+ void Init();
+
+ private:
+ 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.
+ virtual void OnChannelError();
+
+ void OnLaunchAppDone(bool success);
+ 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.
+
+ IPC::ChannelProxy* channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppShimController);
+};
+
+AppShimController::AppShimController() {
jeremy 2013/03/11 06:49:59 channel_(NULL)
jeremya 2013/03/11 08:42:26 Done.
+}
+
+void AppShimController::Init() {
+ base::FilePath user_data_dir;
+ 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.
+ base::FilePath socket_path =
+ user_data_dir.Append(app_mode::kAppShimSocketName);
+ IPC::ChannelHandle handle(socket_path.value());
+ channel_ = new IPC::ChannelProxy(handle, IPC::Channel::MODE_NAMED_CLIENT,
+ 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.
+
+ channel_->Send(new AppShimHostMsg_LaunchApp(
+ g_info->profile_dir.value(), g_info->app_mode_id));
+}
+
+bool AppShimController::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AppShimController, message)
+ IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
+
jeremy 2013/03/11 06:49:59 remove NL
jeremya 2013/03/11 08:42:26 Done.
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void AppShimController::OnChannelError() {
+ [NSApp terminate:nil];
jeremy 2013/03/11 06:49:59 LOG(ERROR) ?
jeremya 2013/03/11 08:42:26 Done.
+}
+
+void AppShimController::OnLaunchAppDone(bool success) {
+ if (!success)
+ [NSApp terminate:nil];
+}
+
+//-----------------------------------------------------------------------------
+
+@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.
+ base::Callback<void(bool)> onReply_;
+ AEDesc replyEvent_;
+}
+// Sends an Apple Event to the process identified by |psn|, and calls |replyFn|
+// when the reply is received. Internally this creates a ReplyEventHandler,
+// which will be destroyed once the reply event has been received.
++ (void)pingProcess:(const ProcessSerialNumber&)psn
+ andCall:(base::Callback<void(bool)>)replyFn;
+@end
+
+@interface ReplyEventHandler (PrivateMethods)
+- (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.
+- (void)pingProcess:(const ProcessSerialNumber&)psn;
+- (void)message:(NSAppleEventDescriptor*)event
+ withReply:(NSAppleEventDescriptor*)reply;
+- (void)closeWithSuccess:(bool)success;
+@end
+
+@implementation ReplyEventHandler
++ (void)pingProcess:(const ProcessSerialNumber&)psn
+ andCall:(base::Callback<void(bool)>)replyFn {
+ ReplyEventHandler* handler =
+ [[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.
+ [handler pingProcess:psn];
+}
+@end
+
+@implementation ReplyEventHandler (PrivateMethods)
+- (id)initWithCallback:(base::Callback<void(bool)>)replyFn {
+ if ((self = [super init])) {
+ onReply_ = replyFn;
+ }
+ return self;
+}
+
+- (void)pingProcess:(const ProcessSerialNumber&)psn {
+ // Register the reply listener.
+ NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
+ [em setEventHandler:self
+ andSelector:@selector(message:withReply:)
+ forEventClass:'aevt'
+ andEventID:'ansr'];
+ // Craft the Apple Event to send.
+ NSAppleEventDescriptor* target = [NSAppleEventDescriptor
+ descriptorWithDescriptorType:typeProcessSerialNumber
+ bytes:&psn
+ length:sizeof(psn)];
+ NSAppleEventDescriptor* initial_event =
+ [NSAppleEventDescriptor
+ appleEventWithEventClass:app_mode::kAEChromeAppClass
+ eventID:app_mode::kAEChromeAppPing
+ targetDescriptor:target
+ returnID:kAutoGenerateReturnID
+ transactionID:kAnyTransactionID];
+ // And away we go.
+ // TODO(jeremya): if we don't care about the contents of the reply, can we
+ // pass NULL for the reply event parameter?
+ OSStatus status = AESendMessage(
+ [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout);
+ if (status != noErr) {
+ OSSTATUS_LOG(ERROR, status) << "AESendMessage";
+ [self closeWithSuccess:false];
+ }
+}
+
+- (void)message:(NSAppleEventDescriptor*)event
+ withReply:(NSAppleEventDescriptor*)reply {
+ [self closeWithSuccess:true];
+}
+
+- (void)closeWithSuccess:(bool)success {
+ onReply_.Run(success);
+ NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
+ [em removeEventHandlerForEventClass:'aevt' andEventID:'ansr'];
+ [self release];
+}
+@end
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+void OnPingChromeReply(bool success) {
jeremy 2013/03/11 06:49:59 Function comment.
jeremya 2013/03/11 08:42:26 Done.
+ if (!success) {
+ [NSApp terminate:nil];
+ return;
+ }
+ AppShimController* controller = new AppShimController;
+ controller->Init();
+}
+
+} // namespace
extern "C" {
@@ -33,6 +190,8 @@ int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info);
int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
base::mac::ScopedNSAutoreleasePool scoped_pool;
+ base::AtExitManager exit_manager;
+ chrome::RegisterPathProvider();
if (info->major_version < app_mode::kCurrentChromeAppModeInfoMajorVersion) {
RAW_LOG(ERROR, "App Mode Loader too old.");
@@ -43,11 +202,21 @@ int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
return 1;
}
+ g_info = info;
+
+ // Launch the IO thread.
+ base::Thread::Options io_thread_options;
+ io_thread_options.message_loop_type = MessageLoop::TYPE_IO;
+ base::Thread *io_thread = new base::Thread("CrAppShimIO");
+ io_thread->StartWithOptions(io_thread_options);
+ g_io_thread = io_thread;
+
+ // Launch Chrome if it isn't already running.
FSRef app_fsref;
if (!base::mac::FSRefFromPath(info->chrome_outer_bundle_path.value(),
&app_fsref)) {
- PLOG(ERROR) << "base::mac::FSRefFromPath failed for "
- << info->chrome_outer_bundle_path.value();
+ LOG(ERROR) << "base::mac::FSRefFromPath failed for "
+ << info->chrome_outer_bundle_path.value();
return 1;
}
std::string silent = std::string("--") + switches::kSilentLaunch;
@@ -63,27 +232,23 @@ int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) {
launch_args,
NULL // initialEvent
};
- NSAppleEventDescriptor* initial_event =
- [NSAppleEventDescriptor
- appleEventWithEventClass:app_mode::kAEChromeAppClass
- eventID:app_mode::kAEChromeAppLaunch
- targetDescriptor:nil
- returnID:kAutoGenerateReturnID
- transactionID:kAnyTransactionID];
- NSAppleEventDescriptor* appid_descriptor = [NSAppleEventDescriptor
- descriptorWithString:base::SysUTF8ToNSString(info->app_mode_id)];
- [initial_event setParamDescriptor:appid_descriptor
- forKeyword:keyDirectObject];
- NSAppleEventDescriptor* profile_dir_descriptor = [NSAppleEventDescriptor
- descriptorWithString:base::SysUTF8ToNSString(info->profile_dir.value())];
- [initial_event setParamDescriptor:profile_dir_descriptor
- forKeyword:app_mode::kAEProfileDirKey];
- ls_parameters.initialEvent = const_cast<AEDesc*>([initial_event aeDesc]);
- // Send the Apple Event using launch services, launching Chrome if necessary.
- OSStatus status = LSOpenApplication(&ls_parameters, NULL);
+ ProcessSerialNumber psn;
+ // TODO(jeremya): this opens a new browser window if Chrome is already
+ // running without any windows open.
+ OSStatus status = LSOpenApplication(&ls_parameters, &psn);
if (status != noErr) {
OSSTATUS_LOG(ERROR, status) << "LSOpenApplication";
return 1;
}
+
+ // This code abuses the fact that Apple Events sent before the process is
+ // fully initialized don't receive a reply until its run loop starts. Once
+ // the reply is received, Chrome will have opened its IPC port, guaranteed.
+ [ReplyEventHandler pingProcess:psn andCall:base::Bind(&OnPingChromeReply)];
+
+ MessageLoopForUI main_message_loop;
+ main_message_loop.set_thread_name("MainThread");
+ base::PlatformThread::SetName("CrAppShimMain");
+ main_message_loop.Run();
return 0;
}
« 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