Commit f17047c2 authored by jackhou@chromium.org's avatar jackhou@chromium.org

Close all windows when app shim quits.

This adds a new IPC message QuitApp which is sent by the shim when the user
quits the shim (right click -> Quit). The AppShimHost responds by closing all
windows associated with the app. The shim actually quits when the extension
eventually closes and AppShimHost closes the channel to the shim.

BUG=168080
TEST=Start an app using its shim.
Right click the app in the dock and select quit.
The shell windows of that app should close.

Review URL: https://chromiumcodereview.appspot.com/14579005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203098 0039d316-1c4b-4281-b951-d872f2087c98
parent 9fa2b496
...@@ -11,6 +11,7 @@ include_rules = [ ...@@ -11,6 +11,7 @@ include_rules = [
"+chrome/browser/profiles", "+chrome/browser/profiles",
"+chrome/browser/shell_integration.h", "+chrome/browser/shell_integration.h",
"+chrome/browser/ui/extensions/application_launch.h", "+chrome/browser/ui/extensions/application_launch.h",
"+chrome/browser/ui/extensions/native_app_window.h",
"+chrome/browser/ui/extensions/shell_window.h", "+chrome/browser/ui/extensions/shell_window.h",
"+chrome/browser/ui/host_desktop.h", "+chrome/browser/ui/host_desktop.h",
"+chrome/browser/ui/web_applications/web_app_ui.h", "+chrome/browser/ui/web_applications/web_app_ui.h",
......
...@@ -55,6 +55,9 @@ class AppShimHandler { ...@@ -55,6 +55,9 @@ class AppShimHandler {
// Invoked by the shim host when the shim process receives a focus event. // Invoked by the shim host when the shim process receives a focus event.
virtual void OnShimFocus(Host* host) = 0; virtual void OnShimFocus(Host* host) = 0;
// Invoked by the shim host when the shim process receives a quit event.
virtual void OnShimQuit(Host* host) = 0;
protected: protected:
AppShimHandler() {} AppShimHandler() {}
virtual ~AppShimHandler() {} virtual ~AppShimHandler() {}
......
...@@ -47,6 +47,7 @@ bool AppShimHost::OnMessageReceived(const IPC::Message& message) { ...@@ -47,6 +47,7 @@ bool AppShimHost::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(AppShimHost, message) IPC_BEGIN_MESSAGE_MAP(AppShimHost, message)
IPC_MESSAGE_HANDLER(AppShimHostMsg_LaunchApp, OnLaunchApp) IPC_MESSAGE_HANDLER(AppShimHostMsg_LaunchApp, OnLaunchApp)
IPC_MESSAGE_HANDLER(AppShimHostMsg_FocusApp, OnFocus) IPC_MESSAGE_HANDLER(AppShimHostMsg_FocusApp, OnFocus)
IPC_MESSAGE_HANDLER(AppShimHostMsg_QuitApp, OnQuit)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() IPC_END_MESSAGE_MAP()
...@@ -85,6 +86,13 @@ void AppShimHost::OnFocus() { ...@@ -85,6 +86,13 @@ void AppShimHost::OnFocus() {
handler->OnShimFocus(this); handler->OnShimFocus(this);
} }
void AppShimHost::OnQuit() {
DCHECK(CalledOnValidThread());
apps::AppShimHandler* handler = apps::AppShimHandler::GetForAppMode(app_id_);
if (handler)
handler->OnShimQuit(this);
}
Profile* AppShimHost::FetchProfileForDirectory(const std::string& profile_dir) { Profile* AppShimHost::FetchProfileForDirectory(const std::string& profile_dir) {
ProfileManager* profileManager = g_browser_process->profile_manager(); ProfileManager* profileManager = g_browser_process->profile_manager();
// Even though the name of this function is "unsafe", there's no security // Even though the name of this function is "unsafe", there's no security
......
...@@ -62,6 +62,9 @@ class AppShimHost : public IPC::Listener, ...@@ -62,6 +62,9 @@ class AppShimHost : public IPC::Listener,
// Cmd+Tabbed to it.) // Cmd+Tabbed to it.)
void OnFocus(); void OnFocus();
// Called when the app shim process notifies that the app should quit.
void OnQuit();
// apps::AppShimHandler::Host overrides: // apps::AppShimHandler::Host overrides:
virtual void OnAppClosed() OVERRIDE; virtual void OnAppClosed() OVERRIDE;
virtual Profile* GetProfile() const OVERRIDE; virtual Profile* GetProfile() const OVERRIDE;
......
...@@ -72,7 +72,10 @@ Profile* TestingAppShimHost::FetchProfileForDirectory( ...@@ -72,7 +72,10 @@ Profile* TestingAppShimHost::FetchProfileForDirectory(
class AppShimHostTest : public testing::Test, class AppShimHostTest : public testing::Test,
public apps::AppShimHandler { public apps::AppShimHandler {
public: public:
AppShimHostTest() : launch_count_(0), close_count_(0), focus_count_(0) {} AppShimHostTest() : launch_count_(0),
close_count_(0),
focus_count_(0),
quit_count_(0) {}
TestingAppShimHost* host() { return host_.get(); } TestingAppShimHost* host() { return host_.get(); }
TestingProfile* profile() { return profile_.get(); } TestingProfile* profile() { return profile_.get(); }
...@@ -98,10 +101,12 @@ class AppShimHostTest : public testing::Test, ...@@ -98,10 +101,12 @@ class AppShimHostTest : public testing::Test,
virtual void OnShimClose(Host* host) OVERRIDE { ++close_count_; } virtual void OnShimClose(Host* host) OVERRIDE { ++close_count_; }
virtual void OnShimFocus(Host* host) OVERRIDE { ++focus_count_; } virtual void OnShimFocus(Host* host) OVERRIDE { ++focus_count_; }
virtual void OnShimQuit(Host* host) OVERRIDE { ++quit_count_; }
int launch_count_; int launch_count_;
int close_count_; int close_count_;
int focus_count_; int focus_count_;
int quit_count_;
private: private:
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
...@@ -135,6 +140,9 @@ TEST_F(AppShimHostTest, TestLaunchAppWithHandler) { ...@@ -135,6 +140,9 @@ TEST_F(AppShimHostTest, TestLaunchAppWithHandler) {
EXPECT_TRUE(host()->ReceiveMessage(new AppShimHostMsg_FocusApp())); EXPECT_TRUE(host()->ReceiveMessage(new AppShimHostMsg_FocusApp()));
EXPECT_EQ(1, focus_count_); EXPECT_EQ(1, focus_count_);
EXPECT_TRUE(host()->ReceiveMessage(new AppShimHostMsg_QuitApp()));
EXPECT_EQ(1, quit_count_);
SimulateDisconnect(); SimulateDisconnect();
EXPECT_EQ(1, close_count_); EXPECT_EQ(1, close_count_);
apps::AppShimHandler::RemoveHandler(kTestAppId); apps::AppShimHandler::RemoveHandler(kTestAppId);
......
...@@ -25,3 +25,9 @@ IPC_MESSAGE_CONTROL2(AppShimHostMsg_LaunchApp, ...@@ -25,3 +25,9 @@ IPC_MESSAGE_CONTROL2(AppShimHostMsg_LaunchApp,
// clicking on the app's icon in the dock or by selecting it with Cmd+Tab. In // clicking on the app's icon in the dock or by selecting it with Cmd+Tab. In
// response, Chrome brings the app's windows to the foreground. // response, Chrome brings the app's windows to the foreground.
IPC_MESSAGE_CONTROL0(AppShimHostMsg_FocusApp) IPC_MESSAGE_CONTROL0(AppShimHostMsg_FocusApp)
// Sent when the shim process receives a request to terminate. Once all of the
// app's windows have closed, and the extension is unloaded, the AppShimHost
// closes the channel. The shim process then completes the terminate request
// and exits.
IPC_MESSAGE_CONTROL0(AppShimHostMsg_QuitApp)
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/shell_window_registry.h" #include "chrome/browser/extensions/shell_window_registry.h"
#include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/extensions/native_app_window.h"
#include "chrome/browser/ui/extensions/shell_window.h" #include "chrome/browser/ui/extensions/shell_window.h"
#include "ui/base/cocoa/focus_window_set.h" #include "ui/base/cocoa/focus_window_set.h"
...@@ -73,13 +74,26 @@ void ExtensionAppShimHandler::OnShimFocus(Host* host) { ...@@ -73,13 +74,26 @@ void ExtensionAppShimHandler::OnShimFocus(Host* host) {
const extensions::ShellWindowRegistry::ShellWindowList windows = const extensions::ShellWindowRegistry::ShellWindowList windows =
registry->GetShellWindowsForApp(host->GetAppId()); registry->GetShellWindowsForApp(host->GetAppId());
std::set<gfx::NativeWindow> native_windows; std::set<gfx::NativeWindow> native_windows;
for (extensions::ShellWindowRegistry::const_iterator i = windows.begin(); for (extensions::ShellWindowRegistry::const_iterator it = windows.begin();
i != windows.end(); ++i) { it != windows.end(); ++it) {
native_windows.insert((*i)->GetNativeWindow()); native_windows.insert((*it)->GetNativeWindow());
} }
ui::FocusWindowSet(native_windows); ui::FocusWindowSet(native_windows);
} }
void ExtensionAppShimHandler::OnShimQuit(Host* host) {
if (!host->GetProfile())
return;
extensions::ShellWindowRegistry::ShellWindowList windows =
extensions::ShellWindowRegistry::Get(host->GetProfile())->
GetShellWindowsForApp(host->GetAppId());
for (extensions::ShellWindowRegistry::const_iterator it = windows.begin();
it != windows.end(); ++it) {
(*it)->GetBaseWindow()->Close();
}
}
bool ExtensionAppShimHandler::LaunchApp(Profile* profile, bool ExtensionAppShimHandler::LaunchApp(Profile* profile,
const std::string& app_id) { const std::string& app_id) {
extensions::ExtensionSystem* extension_system = extensions::ExtensionSystem* extension_system =
......
...@@ -28,6 +28,7 @@ class ExtensionAppShimHandler : public AppShimHandler, ...@@ -28,6 +28,7 @@ class ExtensionAppShimHandler : public AppShimHandler,
virtual bool OnShimLaunch(Host* host) OVERRIDE; virtual bool OnShimLaunch(Host* host) OVERRIDE;
virtual void OnShimClose(Host* host) OVERRIDE; virtual void OnShimClose(Host* host) OVERRIDE;
virtual void OnShimFocus(Host* host) OVERRIDE; virtual void OnShimFocus(Host* host) OVERRIDE;
virtual void OnShimQuit(Host* host) OVERRIDE;
protected: protected:
typedef std::map<std::pair<Profile*, std::string>, AppShimHandler::Host*> typedef std::map<std::pair<Profile*, std::string>, AppShimHandler::Host*>
......
...@@ -36,8 +36,8 @@ ...@@ -36,8 +36,8 @@
'app_shim/app_shim_host_mac.h', 'app_shim/app_shim_host_mac.h',
'app_shim/app_shim_host_manager_mac.h', 'app_shim/app_shim_host_manager_mac.h',
'app_shim/app_shim_host_manager_mac.mm', 'app_shim/app_shim_host_manager_mac.mm',
'app_shim/extension_app_shim_handler_mac.h',
'app_shim/extension_app_shim_handler_mac.cc', 'app_shim/extension_app_shim_handler_mac.cc',
'app_shim/extension_app_shim_handler_mac.h',
'field_trial_names.cc', 'field_trial_names.cc',
'field_trial_names.h', 'field_trial_names.h',
'pref_names.cc', 'pref_names.cc',
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/mac/mac_logging.h" #include "base/mac/mac_logging.h"
#include "base/mac/mac_util.h" #include "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h" #include "base/mac/scoped_nsautorelease_pool.h"
#include "base/memory/scoped_nsobject.h"
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
...@@ -36,6 +37,21 @@ base::Thread* g_io_thread = NULL; ...@@ -36,6 +37,21 @@ base::Thread* g_io_thread = NULL;
} // namespace } // namespace
class AppShimController;
@interface AppShimDelegate : NSObject<NSApplicationDelegate> {
@private
AppShimController* appShimController_; // Weak. Owns us.
BOOL terminateNow_;
BOOL terminateRequested_;
}
- (id)initWithController:(AppShimController*)controller;
- (void)terminateNow;
@end
// The AppShimController is responsible for communication with the main Chrome // The AppShimController is responsible for communication with the main Chrome
// process, and generally controls the lifetime of the app shim process. // process, and generally controls the lifetime of the app shim process.
class AppShimController : public IPC::Listener { class AppShimController : public IPC::Listener {
...@@ -45,6 +61,9 @@ class AppShimController : public IPC::Listener { ...@@ -45,6 +61,9 @@ class AppShimController : public IPC::Listener {
// Connects to Chrome and sends a LaunchApp message. // Connects to Chrome and sends a LaunchApp message.
void Init(); void Init();
// Sends a QuitApp message to Chrome.
void QuitApp();
private: private:
// IPC::Listener implemetation. // IPC::Listener implemetation.
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
...@@ -58,10 +77,11 @@ class AppShimController : public IPC::Listener { ...@@ -58,10 +77,11 @@ class AppShimController : public IPC::Listener {
// dock or by Cmd+Tabbing to it. // dock or by Cmd+Tabbing to it.
void OnDidActivateApplication(); void OnDidActivateApplication();
// Quits the app shim process. // Terminates the app shim process.
void Quit(); void Close();
IPC::ChannelProxy* channel_; IPC::ChannelProxy* channel_;
scoped_nsobject<AppShimDelegate> nsapp_delegate_;
DISALLOW_COPY_AND_ASSIGN(AppShimController); DISALLOW_COPY_AND_ASSIGN(AppShimController);
}; };
...@@ -77,7 +97,7 @@ void AppShimController::Init() { ...@@ -77,7 +97,7 @@ void AppShimController::Init() {
base::FilePath user_data_dir; base::FilePath user_data_dir;
if (!chrome::GetUserDataDirectoryForBrowserBundle(chrome_bundle, if (!chrome::GetUserDataDirectoryForBrowserBundle(chrome_bundle,
&user_data_dir)) { &user_data_dir)) {
Quit(); Close();
return; return;
} }
...@@ -89,6 +109,14 @@ void AppShimController::Init() { ...@@ -89,6 +109,14 @@ void AppShimController::Init() {
channel_->Send(new AppShimHostMsg_LaunchApp( channel_->Send(new AppShimHostMsg_LaunchApp(
g_info->profile_dir.value(), g_info->app_mode_id)); g_info->profile_dir.value(), g_info->app_mode_id));
nsapp_delegate_.reset([[AppShimDelegate alloc] initWithController:this]);
DCHECK(![NSApp delegate]);
[NSApp setDelegate:nsapp_delegate_];
}
void AppShimController::QuitApp() {
channel_->Send(new AppShimHostMsg_QuitApp);
} }
bool AppShimController::OnMessageReceived(const IPC::Message& message) { bool AppShimController::OnMessageReceived(const IPC::Message& message) {
...@@ -103,12 +131,12 @@ bool AppShimController::OnMessageReceived(const IPC::Message& message) { ...@@ -103,12 +131,12 @@ bool AppShimController::OnMessageReceived(const IPC::Message& message) {
void AppShimController::OnChannelError() { void AppShimController::OnChannelError() {
LOG(ERROR) << "App shim channel error."; LOG(ERROR) << "App shim channel error.";
Quit(); Close();
} }
void AppShimController::OnLaunchAppDone(bool success) { void AppShimController::OnLaunchAppDone(bool success) {
if (!success) { if (!success) {
Quit(); Close();
return; return;
} }
[[[NSWorkspace sharedWorkspace] notificationCenter] [[[NSWorkspace sharedWorkspace] notificationCenter]
...@@ -123,14 +151,46 @@ void AppShimController::OnLaunchAppDone(bool success) { ...@@ -123,14 +151,46 @@ void AppShimController::OnLaunchAppDone(bool success) {
}]; }];
} }
void AppShimController::Quit() { void AppShimController::Close() {
[NSApp terminate:nil]; [nsapp_delegate_ terminateNow];
} }
void AppShimController::OnDidActivateApplication() { void AppShimController::OnDidActivateApplication() {
channel_->Send(new AppShimHostMsg_FocusApp); channel_->Send(new AppShimHostMsg_FocusApp);
} }
@implementation AppShimDelegate
- (id)initWithController:(AppShimController*)controller {
if ((self = [super init])) {
appShimController_ = controller;
}
return self;
}
- (NSApplicationTerminateReply)
applicationShouldTerminate:(NSApplication*)sender {
if (terminateNow_)
return NSTerminateNow;
appShimController_->QuitApp();
// Wait for the channel to close before terminating.
terminateRequested_ = YES;
return NSTerminateLater;
}
- (void)terminateNow {
if (terminateRequested_) {
[NSApp replyToApplicationShouldTerminate:NSTerminateNow];
return;
}
terminateNow_ = YES;
[NSApp terminate:nil];
}
@end
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// A ReplyEventHandler is a helper class to send an Apple Event to a process // A ReplyEventHandler is a helper class to send an Apple Event to a process
......
...@@ -64,6 +64,7 @@ class AppListServiceMac : public AppListServiceImpl, ...@@ -64,6 +64,7 @@ class AppListServiceMac : public AppListServiceImpl,
virtual bool OnShimLaunch(apps::AppShimHandler::Host* host) OVERRIDE; virtual bool OnShimLaunch(apps::AppShimHandler::Host* host) OVERRIDE;
virtual void OnShimClose(apps::AppShimHandler::Host* host) OVERRIDE; virtual void OnShimClose(apps::AppShimHandler::Host* host) OVERRIDE;
virtual void OnShimFocus(apps::AppShimHandler::Host* host) OVERRIDE; virtual void OnShimFocus(apps::AppShimHandler::Host* host) OVERRIDE;
virtual void OnShimQuit(apps::AppShimHandler::Host* host) OVERRIDE;
private: private:
friend struct DefaultSingletonTraits<AppListServiceMac>; friend struct DefaultSingletonTraits<AppListServiceMac>;
...@@ -285,6 +286,10 @@ void AppListServiceMac::OnShimFocus(apps::AppShimHandler::Host* host) { ...@@ -285,6 +286,10 @@ void AppListServiceMac::OnShimFocus(apps::AppShimHandler::Host* host) {
DismissAppList(); DismissAppList();
} }
void AppListServiceMac::OnShimQuit(apps::AppShimHandler::Host* host) {
DismissAppList();
}
enum DockLocation { enum DockLocation {
DockLocationOtherDisplay, DockLocationOtherDisplay,
DockLocationBottom, DockLocationBottom,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment