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

[Mac] Bounce app shims when app windows request attention.

Currently we only bounce the app shim when the app is hidden but wants
to show a window.

This CL makes AppWindow.drawAttention() bounce the app icon continuously
until the app is focused or AppWindow.clearAttention() is called.

BUG=402722

Review URL: https://codereview.chromium.org/461303002

Cr-Commit-Position: refs/heads/master@{#290768}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290768 0039d316-1c4b-4281-b951-d872f2087c98
parent c9b52534
......@@ -26,7 +26,7 @@ class AppShimHandler {
// Invoked when the app should be hidden.
virtual void OnAppHide() = 0;
// Invoked when the app is requesting user attention.
virtual void OnAppRequestUserAttention() = 0;
virtual void OnAppRequestUserAttention(AppShimAttentionType type) = 0;
// Allows the handler to determine which app this host corresponds to.
virtual base::FilePath GetProfilePath() const = 0;
......
......@@ -120,8 +120,8 @@ void AppShimHost::OnAppHide() {
Send(new AppShimMsg_Hide);
}
void AppShimHost::OnAppRequestUserAttention() {
Send(new AppShimMsg_RequestUserAttention);
void AppShimHost::OnAppRequestUserAttention(apps::AppShimAttentionType type) {
Send(new AppShimMsg_SetUserAttention(type));
}
void AppShimHost::Close() {
......
......@@ -69,7 +69,8 @@ class AppShimHost : public IPC::Listener,
virtual void OnAppLaunchComplete(apps::AppShimLaunchResult result) OVERRIDE;
virtual void OnAppClosed() OVERRIDE;
virtual void OnAppHide() OVERRIDE;
virtual void OnAppRequestUserAttention() OVERRIDE;
virtual void OnAppRequestUserAttention(
apps::AppShimAttentionType type) OVERRIDE;
virtual base::FilePath GetProfilePath() const OVERRIDE;
virtual std::string GetAppId() const OVERRIDE;
......
......@@ -41,6 +41,17 @@ enum AppShimFocusType {
APP_SHIM_FOCUS_NUM_TYPES
};
enum AppShimAttentionType {
// Removes any active attention request.
APP_SHIM_ATTENTION_CANCEL = 0,
// Bounces the shim in the dock briefly.
APP_SHIM_ATTENTION_INFORMATIONAL,
// Bounces the shim in the dock continuously.
APP_SHIM_ATTENTION_CRITICAL,
// Counter and end marker.
APP_SHIM_ATTENTION_NUM_TYPES
};
} // namespace apps
#endif // APPS_APP_SHIM_APP_SHIM_LAUNCH_H_
......@@ -21,6 +21,12 @@ IPC_ENUM_TRAITS_MAX_VALUE(apps::AppShimLaunchResult,
apps::APP_SHIM_LAUNCH_NUM_RESULTS - 1)
IPC_ENUM_TRAITS_MAX_VALUE(apps::AppShimFocusType,
apps::APP_SHIM_FOCUS_NUM_TYPES - 1)
IPC_ENUM_TRAITS_MAX_VALUE(apps::AppShimAttentionType,
apps::APP_SHIM_ATTENTION_NUM_TYPES - 1)
// IMPORTANT: Since app shims could be running a newer framework version to the
// currently running Chrome, any changes to these IPCs must maintain the same
// order and format. I.e. Only add to the bottom, don't delete any.
// Signals that a previous LaunchApp message has been processed, and lets the
// app shim process know whether it was registered successfully.
......@@ -30,10 +36,10 @@ IPC_MESSAGE_CONTROL1(AppShimMsg_LaunchApp_Done,
// Instructs the shim to hide the app.
IPC_MESSAGE_CONTROL0(AppShimMsg_Hide)
// Instructs the shim to request user attention.
// Deprecated. Do not delete.
IPC_MESSAGE_CONTROL0(AppShimMsg_RequestUserAttention)
// Signals to the main Chrome process that a shim has started indicating the
// Signals to the main Chrome process that a shim has started. Indicates the
// profile and app_id that the shim should be associated with and whether to
// launch the app immediately.
IPC_MESSAGE_CONTROL4(AppShimHostMsg_LaunchApp,
......@@ -59,3 +65,7 @@ IPC_MESSAGE_CONTROL1(AppShimHostMsg_SetAppHidden,
// closes the channel. The shim process then completes the terminate request
// and exits.
IPC_MESSAGE_CONTROL0(AppShimHostMsg_QuitApp)
// Instructs the shim to request or cancel user attention.
IPC_MESSAGE_CONTROL1(AppShimMsg_SetUserAttention,
apps::AppShimAttentionType /* attention_type */)
......@@ -41,7 +41,7 @@ class FakeHost : public apps::AppShimHandler::Host {
handler_->OnShimClose(this);
}
virtual void OnAppHide() OVERRIDE {}
virtual void OnAppRequestUserAttention() OVERRIDE {}
virtual void OnAppRequestUserAttention(AppShimAttentionType type) OVERRIDE {}
virtual base::FilePath GetProfilePath() const OVERRIDE {
return profile_path_;
}
......
......@@ -131,6 +131,7 @@ class AppShimController : public IPC::Listener {
// Requests user attention.
void OnRequestUserAttention();
void OnSetUserAttention(apps::AppShimAttentionType attention_type);
// Terminates the app shim process.
void Close();
......@@ -140,6 +141,7 @@ class AppShimController : public IPC::Listener {
base::scoped_nsobject<AppShimDelegate> delegate_;
bool launch_app_done_;
bool ping_chrome_reply_received_;
NSInteger attention_request_id_;
DISALLOW_COPY_AND_ASSIGN(AppShimController);
};
......@@ -147,7 +149,8 @@ class AppShimController : public IPC::Listener {
AppShimController::AppShimController()
: delegate_([[AppShimDelegate alloc] init]),
launch_app_done_(false),
ping_chrome_reply_received_(false) {
ping_chrome_reply_received_(false),
attention_request_id_(0) {
// Since AppShimController is created before the main message loop starts,
// NSApp will not be set, so use sharedApplication.
[[NSApplication sharedApplication] setDelegate:delegate_];
......@@ -282,6 +285,7 @@ bool AppShimController::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
IPC_MESSAGE_HANDLER(AppShimMsg_Hide, OnHide)
IPC_MESSAGE_HANDLER(AppShimMsg_RequestUserAttention, OnRequestUserAttention)
IPC_MESSAGE_HANDLER(AppShimMsg_SetUserAttention, OnSetUserAttention)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
......@@ -310,7 +314,26 @@ void AppShimController::OnHide() {
}
void AppShimController::OnRequestUserAttention() {
[NSApp requestUserAttention:NSInformationalRequest];
OnSetUserAttention(apps::APP_SHIM_ATTENTION_INFORMATIONAL);
}
void AppShimController::OnSetUserAttention(
apps::AppShimAttentionType attention_type) {
switch (attention_type) {
case apps::APP_SHIM_ATTENTION_CANCEL:
[NSApp cancelUserAttentionRequest:attention_request_id_];
attention_request_id_ = 0;
break;
case apps::APP_SHIM_ATTENTION_CRITICAL:
attention_request_id_ = [NSApp requestUserAttention:NSCriticalRequest];
break;
case apps::APP_SHIM_ATTENTION_INFORMATIONAL:
attention_request_id_ =
[NSApp requestUserAttention:NSInformationalRequest];
break;
case apps::APP_SHIM_ATTENTION_NUM_TYPES:
NOTREACHED();
}
}
void AppShimController::Close() {
......
......@@ -267,7 +267,7 @@ void ExtensionAppShimHandler::FocusAppForWindow(AppWindow* app_window) {
}
// static
bool ExtensionAppShimHandler::RequestUserAttentionForWindow(
bool ExtensionAppShimHandler::ActivateAndRequestUserAttentionForWindow(
AppWindow* app_window) {
ExtensionAppShimHandler* handler = GetInstance();
Profile* profile = Profile::FromBrowserContext(app_window->browser_context());
......@@ -275,7 +275,7 @@ bool ExtensionAppShimHandler::RequestUserAttentionForWindow(
if (host) {
// Bring the window to the front without showing it.
AppWindowRegistry::Get(profile)->AppWindowActivated(app_window);
host->OnAppRequestUserAttention();
host->OnAppRequestUserAttention(APP_SHIM_ATTENTION_INFORMATIONAL);
return true;
} else {
// Just show the app.
......@@ -284,6 +284,17 @@ bool ExtensionAppShimHandler::RequestUserAttentionForWindow(
}
}
// static
void ExtensionAppShimHandler::RequestUserAttentionForWindow(
AppWindow* app_window,
AppShimAttentionType attention_type) {
ExtensionAppShimHandler* handler = GetInstance();
Profile* profile = Profile::FromBrowserContext(app_window->browser_context());
Host* host = handler->FindHost(profile, app_window->extension_id());
if (host)
host->OnAppRequestUserAttention(attention_type);
}
// static
void ExtensionAppShimHandler::OnChromeWillHide() {
// Send OnAppHide to all the shims so that they go into the hidden state.
......
......@@ -80,8 +80,14 @@ class ExtensionAppShimHandler : public AppShimHandler,
static void FocusAppForWindow(AppWindow* app_window);
// Brings the window to the front without showing it and instructs the shim to
// request user attention. Returns false if there is no shim for this window.
static bool RequestUserAttentionForWindow(AppWindow* app_window);
// request user attention. If there is no shim, show the app and return false.
static bool ActivateAndRequestUserAttentionForWindow(AppWindow* app_window);
// Instructs the shim to request user attention. Returns false if there is no
// shim for this window.
static void RequestUserAttentionForWindow(
AppWindow* app_window,
AppShimAttentionType attention_type);
// Called by AppControllerMac when Chrome hides.
static void OnChromeWillHide();
......
......@@ -121,7 +121,7 @@ class FakeHost : public apps::AppShimHandler::Host {
++close_count_;
}
virtual void OnAppHide() OVERRIDE {}
virtual void OnAppRequestUserAttention() OVERRIDE {}
virtual void OnAppRequestUserAttention(AppShimAttentionType type) OVERRIDE {}
virtual base::FilePath GetProfilePath() const OVERRIDE {
return profile_path_;
}
......
......@@ -439,3 +439,7 @@ IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, MAYBE_TestCreate) {
IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, MAYBE_TestShow) {
ASSERT_TRUE(RunAppWindowInteractiveTest("testShow")) << message_;
}
IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, TestDrawAttention) {
ASSERT_TRUE(RunAppWindowInteractiveTest("testDrawAttention")) << message_;
}
......@@ -43,7 +43,8 @@ class AppListServiceMacInteractiveTest : public InProcessBrowserTest,
NOTREACHED();
}
virtual void OnAppHide() OVERRIDE {}
virtual void OnAppRequestUserAttention() OVERRIDE {}
virtual void OnAppRequestUserAttention(
apps::AppShimAttentionType type) OVERRIDE {}
virtual base::FilePath GetProfilePath() const OVERRIDE {
NOTREACHED(); // Currently unused in this test.
return base::FilePath();
......
......@@ -209,7 +209,6 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow,
SkColor inactive_frame_color_;
base::scoped_nsobject<NativeAppWindowController> window_controller_;
NSInteger attention_request_id_; // identifier from requestUserAttention
// For system drag, the whole window is draggable and the non-draggable areas
// have to been explicitly excluded.
......
......@@ -337,8 +337,7 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
shows_fullscreen_controls_(true),
has_frame_color_(params.has_frame_color),
active_frame_color_(params.active_frame_color),
inactive_frame_color_(params.inactive_frame_color),
attention_request_id_(0) {
inactive_frame_color_(params.inactive_frame_color) {
Observe(WebContents());
base::scoped_nsobject<NSWindow> window;
......@@ -565,7 +564,7 @@ void NativeAppWindowCocoa::Show() {
if (is_hidden_with_app_) {
// If there is a shim to gently request attention, return here. Otherwise
// show the window as usual.
if (apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
if (apps::ExtensionAppShimHandler::ActivateAndRequestUserAttentionForWindow(
app_window_)) {
return;
}
......@@ -724,12 +723,10 @@ void NativeAppWindowCocoa::UpdateDraggableRegionViews() {
}
void NativeAppWindowCocoa::FlashFrame(bool flash) {
if (flash) {
attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
} else {
[NSApp cancelUserAttentionRequest:attention_request_id_];
attention_request_id_ = 0;
}
apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
app_window_,
flash ? apps::APP_SHIM_ATTENTION_CRITICAL
: apps::APP_SHIM_ATTENTION_CANCEL);
}
bool NativeAppWindowCocoa::IsAlwaysOnTop() const {
......
......@@ -221,6 +221,17 @@ function testShow() {
]);
}
function testDrawAttention() {
chrome.test.runTests([
function drawThenClearAttention() {
chrome.app.window.create('test.html', {}, callbackPass(function(win) {
win.drawAttention();
win.clearAttention();
}));
}
]);
}
chrome.app.runtime.onLaunched.addListener(function() {
chrome.test.sendMessage('Launched', function(reply) {
window[reply]();
......
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