[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / ash / system / web_notification / web_notification_tray.cc
blob5e7822fbb8d3c063863e94884f5916ff799a8a78
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
3 // found in the LICENSE file.
5 #include "ash/system/web_notification/web_notification_tray.h"
7 #include "ash/shell.h"
8 #include "ash/shell_window_ids.h"
9 #include "ash/system/status_area_widget.h"
10 #include "ash/system/tray/tray_bubble_wrapper.h"
11 #include "ash/system/tray/tray_constants.h"
12 #include "ash/system/tray/tray_views.h"
13 #include "ash/wm/shelf_layout_manager.h"
14 #include "base/message_loop.h"
15 #include "base/stringprintf.h"
16 #include "grit/ash_resources.h"
17 #include "grit/ash_strings.h"
18 #include "ui/aura/window.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/screen.h"
22 #include "ui/message_center/message_center_bubble.h"
23 #include "ui/message_center/message_popup_bubble.h"
24 #include "ui/message_center/quiet_mode_bubble.h"
25 #include "ui/views/bubble/tray_bubble_view.h"
26 #include "ui/views/widget/widget_observer.h"
28 namespace {
30 // Tray constants
31 const int kTrayContainerVerticalPaddingBottomAlignment = 3;
32 const int kTrayContainerHorizontalPaddingBottomAlignment = 1;
33 const int kTrayContainerVerticalPaddingVerticalAlignment = 1;
34 const int kTrayContainerHorizontalPaddingVerticalAlignment = 0;
35 const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment = 8;
36 const int kPaddingFromTopEdgeOfSystemTrayVerticalAlignment = 10;
38 } // namespace
40 namespace ash {
42 namespace internal {
44 // Class to initialize and manage the WebNotificationBubble and
45 // TrayBubbleWrapper instances for a bubble.
47 class WebNotificationBubbleWrapper {
48 public:
49 // Takes ownership of |bubble| and creates |bubble_wrapper_|.
50 WebNotificationBubbleWrapper(WebNotificationTray* tray,
51 message_center::MessageBubbleBase* bubble) {
52 bubble_.reset(bubble);
53 views::TrayBubbleView::AnchorAlignment anchor_alignment =
54 tray->GetAnchorAlignment();
55 views::TrayBubbleView::InitParams init_params =
56 bubble->GetInitParams(anchor_alignment);
57 views::View* anchor = tray->tray_container();
58 if (anchor_alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
59 gfx::Point bounds(anchor->width() / 2, 0);
60 views::View::ConvertPointToWidget(anchor, &bounds);
61 init_params.arrow_offset = bounds.x();
63 views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create(
64 tray->GetBubbleWindowContainer(), anchor, tray, &init_params);
65 bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view));
66 bubble->InitializeContents(bubble_view);
69 message_center::MessageBubbleBase* bubble() const { return bubble_.get(); }
71 // Convenience accessors.
72 views::TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
74 private:
75 scoped_ptr<message_center::MessageBubbleBase> bubble_;
76 scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_;
79 } // namespace internal
81 WebNotificationTray::WebNotificationTray(
82 internal::StatusAreaWidget* status_area_widget)
83 : internal::TrayBackgroundView(status_area_widget),
84 button_(NULL),
85 show_message_center_on_unlock_(false) {
86 message_center_.reset(new message_center::MessageCenter(this));
87 button_ = new views::ImageButton(this);
88 button_->set_triggerable_event_flags(
89 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON);
90 tray_container()->AddChildView(button_);
91 UpdateTray();
94 WebNotificationTray::~WebNotificationTray() {
95 // message_center_ has a weak pointer to this; destroy it early.
96 message_center_.reset();
97 // Release any child views that might have back pointers before ~View().
98 message_center_bubble_.reset();
99 popup_bubble_.reset();
100 if (quiet_mode_bubble() && quiet_mode_bubble_->GetBubbleWidget())
101 quiet_mode_bubble_->GetBubbleWidget()->RemoveObserver(this);
102 quiet_mode_bubble_.reset();
105 void WebNotificationTray::ShowMessageCenterBubble() {
106 if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED)
107 return;
108 if (quiet_mode_bubble())
109 quiet_mode_bubble_.reset();
110 if (message_center_bubble()) {
111 UpdateTray();
112 return;
114 // Indicate that the message center is visible. Clears the unread count.
115 message_center_->SetMessageCenterVisible(true);
116 UpdateTray();
117 HidePopupBubble();
118 message_center::MessageCenterBubble* bubble =
119 new message_center::MessageCenterBubble(message_center_.get());
120 message_center_bubble_.reset(
121 new internal::WebNotificationBubbleWrapper(this, bubble));
123 status_area_widget()->SetHideSystemNotifications(true);
124 GetShelfLayoutManager()->UpdateAutoHideState();
127 void WebNotificationTray::HideMessageCenterBubble() {
128 if (!message_center_bubble())
129 return;
130 message_center_bubble_.reset();
131 show_message_center_on_unlock_ = false;
132 message_center_->SetMessageCenterVisible(false);
133 UpdateTray();
134 status_area_widget()->SetHideSystemNotifications(false);
135 GetShelfLayoutManager()->UpdateAutoHideState();
138 void WebNotificationTray::SetHidePopupBubble(bool hide) {
139 if (hide)
140 HidePopupBubble();
141 else
142 ShowPopupBubble();
145 void WebNotificationTray::ShowPopupBubble() {
146 if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED)
147 return;
148 if (message_center_bubble())
149 return;
150 if (!status_area_widget()->ShouldShowWebNotifications())
151 return;
152 UpdateTray();
153 if (popup_bubble()) {
154 popup_bubble()->bubble()->ScheduleUpdate();
155 } else if (message_center_->HasPopupNotifications()) {
156 popup_bubble_.reset(
157 new internal::WebNotificationBubbleWrapper(
158 this, new message_center::MessagePopupBubble(
159 message_center_.get())));
163 void WebNotificationTray::HidePopupBubble() {
164 popup_bubble_.reset();
167 bool WebNotificationTray::ShouldShowQuietModeBubble(const ui::Event& event) {
168 // TODO(mukai): Add keyboard event handler.
169 if (!event.IsMouseEvent())
170 return false;
172 const ui::MouseEvent* mouse_event =
173 static_cast<const ui::MouseEvent*>(&event);
175 return mouse_event->IsRightMouseButton();
178 void WebNotificationTray::ShowQuietModeBubble() {
179 aura::Window* parent = Shell::GetContainer(
180 Shell::GetPrimaryRootWindow(),
181 internal::kShellWindowId_SettingBubbleContainer);
182 quiet_mode_bubble_.reset(new message_center::QuietModeBubble(
183 button_, parent, message_center_->notification_list()));
184 quiet_mode_bubble_->GetBubbleWidget()->StackAtTop();
185 quiet_mode_bubble_->GetBubbleWidget()->AddObserver(this);
188 void WebNotificationTray::UpdateAfterLoginStatusChange(
189 user::LoginStatus login_status) {
190 if (login_status == user::LOGGED_IN_LOCKED) {
191 if (message_center_bubble()) {
192 message_center_bubble_.reset();
193 show_message_center_on_unlock_ = true;
195 HidePopupBubble();
196 } else {
197 if (show_message_center_on_unlock_)
198 ShowMessageCenterBubble();
199 show_message_center_on_unlock_ = false;
201 UpdateTray();
204 bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
205 return (message_center_bubble() &&
206 message_center_bubble_->bubble()->IsVisible());
209 bool WebNotificationTray::IsMouseInNotificationBubble() const {
210 if (!popup_bubble())
211 return false;
212 return popup_bubble_->bubble_view()->GetBoundsInScreen().Contains(
213 Shell::GetScreen()->GetCursorScreenPoint());
216 void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
217 if (alignment == shelf_alignment())
218 return;
219 internal::TrayBackgroundView::SetShelfAlignment(alignment);
220 // Destroy any existing bubble so that it will be rebuilt correctly.
221 HideMessageCenterBubble();
222 HidePopupBubble();
225 void WebNotificationTray::AnchorUpdated() {
226 if (popup_bubble_.get()) {
227 popup_bubble_->bubble_view()->UpdateBubble();
228 // Ensure that the notification buble is above the launcher/status area.
229 popup_bubble_->bubble_view()->GetWidget()->StackAtTop();
230 UpdateBubbleViewArrow(popup_bubble_->bubble_view());
232 if (message_center_bubble_.get()) {
233 message_center_bubble_->bubble_view()->UpdateBubble();
234 UpdateBubbleViewArrow(message_center_bubble_->bubble_view());
236 // Quiet mode settings bubble has to be on top.
237 if (quiet_mode_bubble() && quiet_mode_bubble_->GetBubbleWidget())
238 quiet_mode_bubble_->GetBubbleWidget()->StackAtTop();
241 string16 WebNotificationTray::GetAccessibleNameForTray() {
242 return l10n_util::GetStringUTF16(
243 IDS_ASH_WEB_NOTIFICATION_TRAY_ACCESSIBLE_NAME);
246 void WebNotificationTray::HideBubbleWithView(
247 const views::TrayBubbleView* bubble_view) {
248 if (message_center_bubble() &&
249 bubble_view == message_center_bubble()->bubble_view()) {
250 HideMessageCenterBubble();
251 } else if (popup_bubble() && bubble_view == popup_bubble()->bubble_view()) {
252 HidePopupBubble();
256 bool WebNotificationTray::PerformAction(const ui::Event& event) {
257 if (!quiet_mode_bubble() && ShouldShowQuietModeBubble(event)) {
258 ShowQuietModeBubble();
259 return true;
261 quiet_mode_bubble_.reset();
262 ToggleMessageCenterBubble();
263 return true;
266 void WebNotificationTray::BubbleViewDestroyed() {
267 if (message_center_bubble())
268 message_center_bubble()->bubble()->BubbleViewDestroyed();
269 if (popup_bubble())
270 popup_bubble()->bubble()->BubbleViewDestroyed();
273 void WebNotificationTray::OnMouseEnteredView() {
274 if (popup_bubble())
275 popup_bubble()->bubble()->OnMouseEnteredView();
278 void WebNotificationTray::OnMouseExitedView() {
279 if (popup_bubble())
280 popup_bubble()->bubble()->OnMouseExitedView();
283 string16 WebNotificationTray::GetAccessibleNameForBubble() {
284 return GetAccessibleNameForTray();
287 gfx::Rect WebNotificationTray::GetAnchorRect(views::Widget* anchor_widget,
288 AnchorType anchor_type,
289 AnchorAlignment anchor_alignment) {
290 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
293 void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
294 HideBubbleWithView(bubble_view);
297 void WebNotificationTray::MessageCenterChanged(bool new_notification) {
298 if (message_center_bubble()) {
299 if (message_center_->NotificationCount() == 0)
300 HideMessageCenterBubble();
301 else
302 message_center_bubble()->bubble()->ScheduleUpdate();
304 if (popup_bubble()) {
305 if (message_center_->NotificationCount() == 0)
306 HidePopupBubble();
307 else
308 popup_bubble()->bubble()->ScheduleUpdate();
310 UpdateTray();
311 if (new_notification)
312 ShowPopupBubble();
315 void WebNotificationTray::ButtonPressed(views::Button* sender,
316 const ui::Event& event) {
317 DCHECK_EQ(button_, sender);
318 PerformAction(event);
321 void WebNotificationTray::OnWidgetClosing(views::Widget* widget) {
322 if (quiet_mode_bubble() && quiet_mode_bubble_->GetBubbleWidget() == widget) {
323 widget->RemoveObserver(this);
325 quiet_mode_bubble_.reset();
328 // Private methods
330 void WebNotificationTray::ToggleMessageCenterBubble() {
331 if (message_center_bubble())
332 HideMessageCenterBubble();
333 else
334 ShowMessageCenterBubble();
335 UpdateTray();
338 void WebNotificationTray::UpdateTray() {
339 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
340 if (message_center_->UnreadNotificationCount() > 0) {
341 button_->SetImage(views::CustomButton::STATE_NORMAL, rb.GetImageSkiaNamed(
342 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_NORMAL));
343 button_->SetImage(views::CustomButton::STATE_HOVERED, rb.GetImageSkiaNamed(
344 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_HOVER));
345 button_->SetImage(views::CustomButton::STATE_PRESSED, rb.GetImageSkiaNamed(
346 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_PRESSED));
347 } else {
348 button_->SetImage(views::CustomButton::STATE_NORMAL, rb.GetImageSkiaNamed(
349 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_NORMAL));
350 button_->SetImage(views::CustomButton::STATE_HOVERED, rb.GetImageSkiaNamed(
351 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_HOVER));
352 button_->SetImage(views::CustomButton::STATE_PRESSED, rb.GetImageSkiaNamed(
353 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_PRESSED));
355 if (message_center_bubble())
356 button_->SetState(views::CustomButton::STATE_PRESSED);
357 else
358 button_->SetState(views::CustomButton::STATE_NORMAL);
359 bool is_visible =
360 (status_area_widget()->login_status() != user::LOGGED_IN_NONE) &&
361 (status_area_widget()->login_status() != user::LOGGED_IN_LOCKED) &&
362 (message_center_->NotificationCount() > 0);
363 SetVisible(is_visible);
364 Layout();
365 SchedulePaint();
368 bool WebNotificationTray::ClickedOutsideBubble() {
369 // Only hide the message center and quiet mode bubble.
370 if (!message_center_bubble() && !quiet_mode_bubble())
371 return false;
372 quiet_mode_bubble_.reset();
373 HideMessageCenterBubble();
374 return true;
377 // Methods for testing
379 message_center::MessageCenterBubble*
380 WebNotificationTray::GetMessageCenterBubbleForTest() {
381 if (!message_center_bubble_.get())
382 return NULL;
383 return static_cast<message_center::MessageCenterBubble*>(
384 message_center_bubble_->bubble());
387 message_center::MessagePopupBubble*
388 WebNotificationTray::GetPopupBubbleForTest() {
389 if (!popup_bubble_.get())
390 return NULL;
391 return static_cast<message_center::MessagePopupBubble*>(
392 popup_bubble_->bubble());
395 } // namespace ash