diff options
Diffstat (limited to 'apps/app_shim/chrome_main_app_mode_mac.mm')
-rw-r--r-- | apps/app_shim/chrome_main_app_mode_mac.mm | 188 |
1 files changed, 139 insertions, 49 deletions
diff --git a/apps/app_shim/chrome_main_app_mode_mac.mm b/apps/app_shim/chrome_main_app_mode_mac.mm index 09d7ab7..6dca6b7 100644 --- a/apps/app_shim/chrome_main_app_mode_mac.mm +++ b/apps/app_shim/chrome_main_app_mode_mac.mm @@ -8,12 +8,15 @@ // those app bundles. #import <Cocoa/Cocoa.h> +#include <vector> #include "apps/app_shim/app_shim_messages.h" #include "base/at_exit.h" #include "base/command_line.h" +#include "base/files/file_path.h" #include "base/logging.h" #include "base/mac/bundle_locations.h" +#include "base/mac/foundation_util.h" #include "base/mac/launch_services_util.h" #include "base/mac/mac_logging.h" #include "base/mac/mac_util.h" @@ -54,18 +57,31 @@ base::Thread* g_io_thread = NULL; class AppShimController; +// An application delegate to catch user interactions and send the appropriate +// IPC messages to Chrome. @interface AppShimDelegate : NSObject<NSApplicationDelegate> { @private - AppShimController* appShimController_; // Weak. Owns us. + AppShimController* appShimController_; // Weak, initially NULL. BOOL terminateNow_; BOOL terminateRequested_; + std::vector<base::FilePath> filesToOpenAtStartup_; } -- (id)initWithController:(AppShimController*)controller; -- (BOOL)applicationOpenUntitledFile:(NSApplication *)app; -- (void)applicationWillBecomeActive:(NSNotification*)notification; -- (void)applicationWillHide:(NSNotification*)notification; -- (void)applicationWillUnhide:(NSNotification*)notification; +// The controller is initially NULL. Setting it indicates to the delegate that +// the controller has finished initialization. +- (void)setController:(AppShimController*)controller; + +// Gets files that were queued because the controller was not ready. +// Returns whether any FilePaths were added to |out|. +- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out; + +// If the controller is ready, this sends a FocusApp with the files to open. +// Otherwise, this adds the files to |filesToOpenAtStartup_|. +// Takes an array of NSString*. +- (void)openFiles:(NSArray*)filename; + +// Terminate immediately. This is necessary as we override terminate: to send +// a QuitApp message. - (void)terminateNow; @end @@ -75,6 +91,11 @@ class AppShimController; class AppShimController : public IPC::Listener { public: AppShimController(); + virtual ~AppShimController(); + + // Called when the main Chrome process responds to the Apple Event ping that + // was sent, or when the ping fails (if |success| is false). + void OnPingChromeReply(bool success); // Connects to Chrome and sends a LaunchApp message. void Init(); @@ -86,9 +107,11 @@ class AppShimController : public IPC::Listener { void SendQuitApp(); - // Called when the app is activated, either by the user clicking on it in the - // dock or by Cmd+Tabbing to it. - void ActivateApp(bool is_reopen); + // Called when the app is activated, e.g. by clicking on it in the dock, by + // dropping a file on the dock icon, or by Cmd+Tabbing to it. + // Returns whether the message was sent. + bool SendFocusApp(apps::AppShimFocusType focus_type, + const std::vector<base::FilePath>& files); private: // IPC::Listener implemetation. @@ -109,14 +132,34 @@ class AppShimController : public IPC::Listener { void Close(); IPC::ChannelProxy* channel_; - base::scoped_nsobject<AppShimDelegate> nsapp_delegate_; + base::scoped_nsobject<AppShimDelegate> delegate_; bool launch_app_done_; DISALLOW_COPY_AND_ASSIGN(AppShimController); }; -AppShimController::AppShimController() : channel_(NULL), - launch_app_done_(false) {} +AppShimController::AppShimController() + : channel_(NULL), + delegate_([[AppShimDelegate alloc] init]), + launch_app_done_(false) { + // Since AppShimController is created before the main message loop starts, + // NSApp will not be set, so use sharedApplication. + [[NSApplication sharedApplication] setDelegate:delegate_]; +} + +AppShimController::~AppShimController() { + // Un-set the delegate since NSApplication does not retain it. + [NSApp setDelegate:nil]; +} + +void AppShimController::OnPingChromeReply(bool success) { + if (!success) { + [NSApp terminate:nil]; + return; + } + + Init(); +} void AppShimController::Init() { DCHECK(g_io_thread); @@ -142,14 +185,16 @@ void AppShimController::Init() { bool launched_by_chrome = CommandLine::ForCurrentProcess()->HasSwitch( app_mode::kLaunchedByChromeProcessId); - channel_->Send(new AppShimHostMsg_LaunchApp( - g_info->profile_dir, g_info->app_mode_id, - launched_by_chrome ? - apps::APP_SHIM_LAUNCH_REGISTER_ONLY : apps::APP_SHIM_LAUNCH_NORMAL)); + apps::AppShimLaunchType launch_type = launched_by_chrome ? + apps::APP_SHIM_LAUNCH_REGISTER_ONLY : apps::APP_SHIM_LAUNCH_NORMAL; + + [delegate_ setController:this]; - nsapp_delegate_.reset([[AppShimDelegate alloc] initWithController:this]); - DCHECK(![NSApp delegate]); - [NSApp setDelegate:nsapp_delegate_]; + std::vector<base::FilePath> files; + [delegate_ getFilesToOpenAtStartup:&files]; + + channel_->Send(new AppShimHostMsg_LaunchApp( + g_info->profile_dir, g_info->app_mode_id, launch_type, files)); } void AppShimController::SetUpMenu() { @@ -212,6 +257,10 @@ void AppShimController::OnLaunchAppDone(apps::AppShimLaunchResult result) { return; } + std::vector<base::FilePath> files; + if ([delegate_ getFilesToOpenAtStartup:&files]) + SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES, files); + launch_app_done_ = true; } @@ -224,14 +273,17 @@ void AppShimController::OnRequestUserAttention() { } void AppShimController::Close() { - [nsapp_delegate_ terminateNow]; + [delegate_ terminateNow]; } -void AppShimController::ActivateApp(bool is_reopen) { +bool AppShimController::SendFocusApp(apps::AppShimFocusType focus_type, + const std::vector<base::FilePath>& files) { if (launch_app_done_) { - channel_->Send(new AppShimHostMsg_FocusApp( - is_reopen ? apps::APP_SHIM_FOCUS_REOPEN : apps::APP_SHIM_FOCUS_NORMAL)); + channel_->Send(new AppShimHostMsg_FocusApp(focus_type, files)); + return true; } + + return false; } void AppShimController::SendSetAppHidden(bool hidden) { @@ -240,25 +292,70 @@ void AppShimController::SendSetAppHidden(bool hidden) { @implementation AppShimDelegate -- (id)initWithController:(AppShimController*)controller { - if ((self = [super init])) { - appShimController_ = controller; +- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out { + if (filesToOpenAtStartup_.empty()) + return NO; + + out->insert(out->end(), + filesToOpenAtStartup_.begin(), + filesToOpenAtStartup_.end()); + filesToOpenAtStartup_.clear(); + return YES; +} + +- (void)setController:(AppShimController*)controller { + appShimController_ = controller; +} + +- (void)openFiles:(NSArray*)filenames { + std::vector<base::FilePath> filePaths; + for (NSString* filename in filenames) + filePaths.push_back(base::mac::NSStringToFilePath(filename)); + + // If the AppShimController is ready, try to send a FocusApp. If that fails, + // (e.g. if launching has not finished), enqueue the files. + if (appShimController_ && + appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES, + filePaths)) { + return; } - return self; + + filesToOpenAtStartup_.insert(filesToOpenAtStartup_.end(), + filePaths.begin(), + filePaths.end()); } -- (BOOL)applicationOpenUntitledFile:(NSApplication *)app { - appShimController_->ActivateApp(true); +- (BOOL)application:(NSApplication*)app + openFile:(NSString*)filename { + [self openFiles:@[filename]]; return YES; } +- (void)application:(NSApplication*)app + openFiles:(NSArray*)filenames { + [self openFiles:filenames]; + [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; +} + +- (BOOL)applicationOpenUntitledFile:(NSApplication*)app { + if (appShimController_) { + return appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_REOPEN, + std::vector<base::FilePath>()); + } + + return NO; +} + - (void)applicationWillBecomeActive:(NSNotification*)notification { - appShimController_->ActivateApp(false); + if (appShimController_) { + appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_NORMAL, + std::vector<base::FilePath>()); + } } - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender { - if (terminateNow_) + if (terminateNow_ || !appShimController_) return NSTerminateNow; appShimController_->SendQuitApp(); @@ -268,11 +365,13 @@ void AppShimController::SendSetAppHidden(bool hidden) { } - (void)applicationWillHide:(NSNotification*)notification { - appShimController_->SendSetAppHidden(true); + if (appShimController_) + appShimController_->SendSetAppHidden(true); } - (void)applicationWillUnhide:(NSNotification*)notification { - appShimController_->SendSetAppHidden(false); + if (appShimController_) + appShimController_->SendSetAppHidden(false); } - (void)terminateNow { @@ -390,21 +489,6 @@ void AppShimController::SendSetAppHidden(bool hidden) { //----------------------------------------------------------------------------- -namespace { - -// Called when the main Chrome process responds to the Apple Event ping that -// was sent, or when the ping fails (if |success| is false). -void OnPingChromeReply(bool success) { - if (!success) { - [NSApp terminate:nil]; - return; - } - AppShimController* controller = new AppShimController; - controller->Init(); -} - -} // namespace - extern "C" { // |ChromeAppModeStart()| is the point of entry into the framework from the app @@ -501,10 +585,16 @@ int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) { return 1; } + AppShimController controller; + base::Callback<void(bool)> on_ping_chrome_reply = + base::Bind(&AppShimController::OnPingChromeReply, + base::Unretained(&controller)); + // This code abuses the fact that Apple Events sent before the process is // fully initialized don't receive a reply until its run loop starts. Once // the reply is received, Chrome will have opened its IPC port, guaranteed. - [ReplyEventHandler pingProcess:psn andCall:base::Bind(&OnPingChromeReply)]; + [ReplyEventHandler pingProcess:psn + andCall:on_ping_chrome_reply]; base::MessageLoopForUI main_message_loop; main_message_loop.set_thread_name("MainThread"); |