• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ash/system/web_notification/web_notification_tray.h"
6 
7 #include "ash/ash_switches.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shelf/shelf_layout_manager_observer.h"
11 #include "ash/shelf/shelf_widget.h"
12 #include "ash/shell.h"
13 #include "ash/shell_window_ids.h"
14 #include "ash/system/status_area_widget.h"
15 #include "ash/system/tray/system_tray.h"
16 #include "ash/system/tray/tray_background_view.h"
17 #include "ash/system/tray/tray_bubble_wrapper.h"
18 #include "ash/system/tray/tray_constants.h"
19 #include "ash/system/tray/tray_utils.h"
20 #include "base/auto_reset.h"
21 #include "base/i18n/number_formatting.h"
22 #include "base/i18n/rtl.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "grit/ash_strings.h"
25 #include "grit/ui_strings.h"
26 #include "ui/aura/root_window.h"
27 #include "ui/aura/window.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/gfx/screen.h"
30 #include "ui/message_center/message_center_style.h"
31 #include "ui/message_center/message_center_tray_delegate.h"
32 #include "ui/message_center/message_center_util.h"
33 #include "ui/message_center/views/message_bubble_base.h"
34 #include "ui/message_center/views/message_center_bubble.h"
35 #include "ui/message_center/views/message_popup_collection.h"
36 #include "ui/views/bubble/tray_bubble_view.h"
37 #include "ui/views/controls/button/custom_button.h"
38 #include "ui/views/controls/image_view.h"
39 #include "ui/views/controls/label.h"
40 #include "ui/views/controls/menu/menu_runner.h"
41 #include "ui/views/layout/fill_layout.h"
42 
43 #if defined(OS_CHROMEOS)
44 
45 namespace message_center {
46 
CreateMessageCenterTray()47 MessageCenterTrayDelegate* CreateMessageCenterTray() {
48   // On Windows+Ash the Tray will not be hosted in ash::Shell.
49   NOTREACHED();
50   return NULL;
51 }
52 
53 }  // namespace message_center
54 
55 #endif  // defined(OS_CHROMEOS)
56 
57 namespace ash {
58 namespace {
59 
60 // Menu commands
61 const int kToggleQuietMode = 0;
62 const int kEnableQuietModeDay = 2;
63 
64 }
65 
66 namespace internal {
67 namespace {
68 
69 const SkColor kWebNotificationColorNoUnread = SkColorSetA(SK_ColorWHITE, 128);
70 const SkColor kWebNotificationColorWithUnread = SK_ColorWHITE;
71 
72 }
73 
74 // Observes the change of work area (including temporary change by auto-hide)
75 // and notifies MessagePopupCollection.
76 class WorkAreaObserver : public ShelfLayoutManagerObserver,
77                          public ShellObserver {
78  public:
79   WorkAreaObserver();
80   virtual ~WorkAreaObserver();
81 
82   void SetSystemTrayHeight(int height);
83 
84   // Starts observing |shelf| and shell and sends the change to |collection|.
85   void StartObserving(message_center::MessagePopupCollection* collection,
86                       aura::Window* root_window);
87 
88   // Stops the observing session.
89   void StopObserving();
90 
91   // Overridden from ShellObserver:
92   virtual void OnDisplayWorkAreaInsetsChanged() OVERRIDE;
93 
94   // Overridden from ShelfLayoutManagerObserver:
95   virtual void OnAutoHideStateChanged(ShelfAutoHideState new_state) OVERRIDE;
96 
97  private:
98   // Updates |shelf_| from |root_window_|.
99   void UpdateShelf();
100 
101   message_center::MessagePopupCollection* collection_;
102   aura::Window* root_window_;
103   ShelfLayoutManager* shelf_;
104   int system_tray_height_;
105 
106   DISALLOW_COPY_AND_ASSIGN(WorkAreaObserver);
107 };
108 
WorkAreaObserver()109 WorkAreaObserver::WorkAreaObserver()
110     : collection_(NULL),
111       root_window_(NULL),
112       shelf_(NULL),
113       system_tray_height_(0) {
114 }
115 
~WorkAreaObserver()116 WorkAreaObserver::~WorkAreaObserver() {
117   StopObserving();
118 }
119 
SetSystemTrayHeight(int height)120 void WorkAreaObserver::SetSystemTrayHeight(int height) {
121   system_tray_height_ = height;
122 
123   // If the shelf is shown during auto-hide state, the distance from the edge
124   // should be reduced by the height of shelf's shown height.
125   if (shelf_ && shelf_->visibility_state() == SHELF_AUTO_HIDE &&
126       shelf_->auto_hide_state() == SHELF_AUTO_HIDE_SHOWN) {
127     system_tray_height_ -= ShelfLayoutManager::GetPreferredShelfSize() -
128         ShelfLayoutManager::kAutoHideSize;
129   }
130 
131   if (system_tray_height_ > 0 && ash::switches::UseAlternateShelfLayout())
132     system_tray_height_ += message_center::kMarginBetweenItems;
133 
134   if (!shelf_)
135     return;
136 
137   OnAutoHideStateChanged(shelf_->auto_hide_state());
138 }
139 
StartObserving(message_center::MessagePopupCollection * collection,aura::Window * root_window)140 void WorkAreaObserver::StartObserving(
141     message_center::MessagePopupCollection* collection,
142     aura::Window* root_window) {
143   DCHECK(collection);
144   collection_ = collection;
145   root_window_ = root_window;
146   UpdateShelf();
147   Shell::GetInstance()->AddShellObserver(this);
148   if (system_tray_height_ > 0)
149     OnAutoHideStateChanged(shelf_->auto_hide_state());
150 }
151 
StopObserving()152 void WorkAreaObserver::StopObserving() {
153   Shell::GetInstance()->RemoveShellObserver(this);
154   if (shelf_)
155     shelf_->RemoveObserver(this);
156   collection_ = NULL;
157   shelf_ = NULL;
158 }
159 
OnDisplayWorkAreaInsetsChanged()160 void WorkAreaObserver::OnDisplayWorkAreaInsetsChanged() {
161   UpdateShelf();
162 
163   collection_->OnDisplayBoundsChanged(
164       Shell::GetScreen()->GetDisplayNearestWindow(
165           shelf_->shelf_widget()->GetNativeView()));
166 }
167 
OnAutoHideStateChanged(ShelfAutoHideState new_state)168 void WorkAreaObserver::OnAutoHideStateChanged(ShelfAutoHideState new_state) {
169   gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
170       shelf_->shelf_widget()->GetNativeView());
171   gfx::Rect work_area = display.work_area();
172   int width = 0;
173   if ((shelf_->visibility_state() == SHELF_AUTO_HIDE) &&
174       new_state == SHELF_AUTO_HIDE_SHOWN) {
175     // Since the work_area is already reduced by kAutoHideSize, the inset width
176     // should be just the difference.
177     width = ShelfLayoutManager::GetPreferredShelfSize() -
178         ShelfLayoutManager::kAutoHideSize;
179   }
180   work_area.Inset(shelf_->SelectValueForShelfAlignment(
181       gfx::Insets(0, 0, width, 0),
182       gfx::Insets(0, width, 0, 0),
183       gfx::Insets(0, 0, 0, width),
184       gfx::Insets(width, 0, 0, 0)));
185   if (system_tray_height_ > 0) {
186     work_area.set_height(
187         std::max(0, work_area.height() - system_tray_height_));
188     if (shelf_->GetAlignment() == SHELF_ALIGNMENT_TOP)
189       work_area.set_y(work_area.y() + system_tray_height_);
190   }
191   collection_->SetDisplayInfo(work_area, display.bounds());
192 }
193 
UpdateShelf()194 void WorkAreaObserver::UpdateShelf() {
195   if (shelf_)
196     return;
197 
198   shelf_ = ShelfLayoutManager::ForLauncher(root_window_);
199   if (shelf_)
200     shelf_->AddObserver(this);
201 }
202 
203 // Class to initialize and manage the WebNotificationBubble and
204 // TrayBubbleWrapper instances for a bubble.
205 class WebNotificationBubbleWrapper {
206  public:
207   // Takes ownership of |bubble| and creates |bubble_wrapper_|.
WebNotificationBubbleWrapper(WebNotificationTray * tray,message_center::MessageBubbleBase * bubble)208   WebNotificationBubbleWrapper(WebNotificationTray* tray,
209                                message_center::MessageBubbleBase* bubble) {
210     bubble_.reset(bubble);
211     views::TrayBubbleView::AnchorAlignment anchor_alignment =
212         tray->GetAnchorAlignment();
213     views::TrayBubbleView::InitParams init_params =
214         bubble->GetInitParams(anchor_alignment);
215     views::View* anchor = tray->tray_container();
216     if (anchor_alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
217       gfx::Point bounds(anchor->width() / 2, 0);
218       views::View::ConvertPointToWidget(anchor, &bounds);
219       init_params.arrow_offset = bounds.x();
220     }
221     views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create(
222         tray->GetBubbleWindowContainer(), anchor, tray, &init_params);
223     if (ash::switches::UseAlternateShelfLayout())
224       bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
225     bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view));
226     bubble->InitializeContents(bubble_view);
227   }
228 
bubble() const229   message_center::MessageBubbleBase* bubble() const { return bubble_.get(); }
230 
231   // Convenience accessors.
bubble_view() const232   views::TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
233 
234  private:
235   scoped_ptr<message_center::MessageBubbleBase> bubble_;
236   scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_;
237 
238   DISALLOW_COPY_AND_ASSIGN(WebNotificationBubbleWrapper);
239 };
240 
241 class WebNotificationButton : public views::CustomButton {
242  public:
WebNotificationButton(views::ButtonListener * listener)243   WebNotificationButton(views::ButtonListener* listener)
244       : views::CustomButton(listener),
245         is_bubble_visible_(false),
246         unread_count_(0) {
247     SetLayoutManager(new views::FillLayout);
248     unread_label_ = new views::Label();
249     SetupLabelForTray(unread_label_);
250     AddChildView(unread_label_);
251   }
252 
SetBubbleVisible(bool visible)253   void SetBubbleVisible(bool visible) {
254     if (visible == is_bubble_visible_)
255       return;
256 
257     is_bubble_visible_ = visible;
258     UpdateIconVisibility();
259   }
260 
SetUnreadCount(int unread_count)261   void SetUnreadCount(int unread_count) {
262     // base::FormatNumber doesn't convert to arabic numeric characters.
263     // TODO(mukai): use ICU to support conversion for such locales.
264     unread_count_ = unread_count;
265     // TODO(mukai): move NINE_PLUS message to ui_strings, it doesn't need to be
266     // in ash_strings.
267     unread_label_->SetText((unread_count > 9) ?
268         l10n_util::GetStringUTF16(IDS_ASH_NOTIFICATION_UNREAD_COUNT_NINE_PLUS) :
269         base::FormatNumber(unread_count));
270     UpdateIconVisibility();
271   }
272 
273  protected:
274   // Overridden from views::ImageButton:
GetPreferredSize()275   virtual gfx::Size GetPreferredSize() OVERRIDE {
276     const int notification_item_size = GetShelfItemHeight();
277     return gfx::Size(notification_item_size, notification_item_size);
278   }
279 
GetHeightForWidth(int width)280   virtual int GetHeightForWidth(int width) OVERRIDE {
281     return GetPreferredSize().height();
282   }
283 
284  private:
UpdateIconVisibility()285   void UpdateIconVisibility() {
286     unread_label_->SetEnabledColor(
287         (!is_bubble_visible_ && unread_count_ > 0) ?
288         kWebNotificationColorWithUnread : kWebNotificationColorNoUnread);
289     SchedulePaint();
290   }
291 
292   bool is_bubble_visible_;
293   int unread_count_;
294 
295   views::Label* unread_label_;
296 
297   DISALLOW_COPY_AND_ASSIGN(WebNotificationButton);
298 };
299 
300 }  // namespace internal
301 
WebNotificationTray(internal::StatusAreaWidget * status_area_widget)302 WebNotificationTray::WebNotificationTray(
303     internal::StatusAreaWidget* status_area_widget)
304     : TrayBackgroundView(status_area_widget),
305       button_(NULL),
306       show_message_center_on_unlock_(false),
307       should_update_tray_content_(false),
308       should_block_shelf_auto_hide_(false) {
309   button_ = new internal::WebNotificationButton(this);
310   button_->set_triggerable_event_flags(
311       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON);
312   tray_container()->AddChildView(button_);
313   SetContentsBackground();
314   tray_container()->set_border(NULL);
315   SetVisible(false);
316   message_center_tray_.reset(new message_center::MessageCenterTray(
317       this,
318       message_center::MessageCenter::Get()));
319   work_area_observer_.reset(new internal::WorkAreaObserver());
320   OnMessageCenterTrayChanged();
321 }
322 
~WebNotificationTray()323 WebNotificationTray::~WebNotificationTray() {
324   // Release any child views that might have back pointers before ~View().
325   message_center_bubble_.reset();
326   popup_collection_.reset();
327   work_area_observer_.reset();
328 }
329 
330 // Public methods.
331 
ShowMessageCenterInternal(bool show_settings)332 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
333   if (!ShouldShowMessageCenter())
334     return false;
335 
336   should_block_shelf_auto_hide_ = true;
337   message_center::MessageCenterBubble* message_center_bubble =
338       new message_center::MessageCenterBubble(
339           message_center(),
340           message_center_tray_.get(),
341           ash::switches::UseAlternateShelfLayout());
342 
343   int max_height = 0;
344   aura::Window* status_area_window = status_area_widget()->GetNativeView();
345   switch (GetShelfLayoutManager()->GetAlignment()) {
346     case SHELF_ALIGNMENT_BOTTOM: {
347       gfx::Rect shelf_bounds = GetShelfLayoutManager()->GetIdealBounds();
348       max_height = shelf_bounds.y();
349       break;
350     }
351     case SHELF_ALIGNMENT_TOP: {
352       aura::Window* root = status_area_window->GetRootWindow();
353       max_height =
354           root->bounds().height() - status_area_window->bounds().height();
355       break;
356     }
357     case SHELF_ALIGNMENT_LEFT:
358     case SHELF_ALIGNMENT_RIGHT: {
359       // Assume that the bottom line of the status area widget and the bubble
360       // are aligned.
361       max_height = status_area_window->GetBoundsInRootWindow().bottom();
362       break;
363     }
364     default:
365       NOTREACHED();
366   }
367 
368   message_center_bubble->SetMaxHeight(std::max(0,
369                                                max_height - GetTraySpacing()));
370   if (show_settings)
371     message_center_bubble->SetSettingsVisible();
372   message_center_bubble_.reset(
373       new internal::WebNotificationBubbleWrapper(this, message_center_bubble));
374 
375   status_area_widget()->SetHideSystemNotifications(true);
376   GetShelfLayoutManager()->UpdateAutoHideState();
377   button_->SetBubbleVisible(true);
378   SetDrawBackgroundAsActive(true);
379   return true;
380 }
381 
ShowMessageCenter()382 bool WebNotificationTray::ShowMessageCenter() {
383   return ShowMessageCenterInternal(false /* show_settings */);
384 }
385 
HideMessageCenter()386 void WebNotificationTray::HideMessageCenter() {
387   if (!message_center_bubble())
388     return;
389   SetDrawBackgroundAsActive(false);
390   message_center_bubble_.reset();
391   should_block_shelf_auto_hide_ = false;
392   show_message_center_on_unlock_ = false;
393   status_area_widget()->SetHideSystemNotifications(false);
394   GetShelfLayoutManager()->UpdateAutoHideState();
395   button_->SetBubbleVisible(false);
396 }
397 
SetSystemTrayHeight(int height)398 void WebNotificationTray::SetSystemTrayHeight(int height) {
399   work_area_observer_->SetSystemTrayHeight(height);
400 }
401 
ShowPopups()402 bool WebNotificationTray::ShowPopups() {
403   if (message_center_bubble())
404     return false;
405 
406   popup_collection_.reset(new message_center::MessagePopupCollection(
407       ash::Shell::GetContainer(
408           GetWidget()->GetNativeView()->GetRootWindow(),
409           internal::kShellWindowId_StatusContainer),
410       message_center(),
411       message_center_tray_.get(),
412       ash::switches::UseAlternateShelfLayout()));
413   work_area_observer_->StartObserving(
414       popup_collection_.get(), GetWidget()->GetNativeView()->GetRootWindow());
415   return true;
416 }
417 
HidePopups()418 void WebNotificationTray::HidePopups() {
419   DCHECK(popup_collection_.get());
420 
421   popup_collection_->MarkAllPopupsShown();
422   popup_collection_.reset();
423   work_area_observer_->StopObserving();
424 }
425 
426 // Private methods.
427 
ShouldShowMessageCenter()428 bool WebNotificationTray::ShouldShowMessageCenter() {
429   return status_area_widget()->login_status() != user::LOGGED_IN_LOCKED &&
430       !(status_area_widget()->system_tray() &&
431         status_area_widget()->system_tray()->HasNotificationBubble());
432 }
433 
ShouldBlockLauncherAutoHide() const434 bool WebNotificationTray::ShouldBlockLauncherAutoHide() const {
435   return should_block_shelf_auto_hide_;
436 }
437 
IsMessageCenterBubbleVisible() const438 bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
439   return (message_center_bubble() &&
440           message_center_bubble()->bubble()->IsVisible());
441 }
442 
IsMouseInNotificationBubble() const443 bool WebNotificationTray::IsMouseInNotificationBubble() const {
444   return false;
445 }
446 
ShowMessageCenterBubble()447 void WebNotificationTray::ShowMessageCenterBubble() {
448   if (!IsMessageCenterBubbleVisible())
449     message_center_tray_->ShowMessageCenterBubble();
450 }
451 
UpdateAfterLoginStatusChange(user::LoginStatus login_status)452 void WebNotificationTray::UpdateAfterLoginStatusChange(
453     user::LoginStatus login_status) {
454   OnMessageCenterTrayChanged();
455 }
456 
SetShelfAlignment(ShelfAlignment alignment)457 void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
458   if (alignment == shelf_alignment())
459     return;
460   internal::TrayBackgroundView::SetShelfAlignment(alignment);
461   tray_container()->set_border(NULL);
462   // Destroy any existing bubble so that it will be rebuilt correctly.
463   message_center_tray_->HideMessageCenterBubble();
464   message_center_tray_->HidePopupBubble();
465 }
466 
AnchorUpdated()467 void WebNotificationTray::AnchorUpdated() {
468   if (message_center_bubble()) {
469     message_center_bubble()->bubble_view()->UpdateBubble();
470     UpdateBubbleViewArrow(message_center_bubble()->bubble_view());
471   }
472 }
473 
GetAccessibleNameForTray()474 base::string16 WebNotificationTray::GetAccessibleNameForTray() {
475   return l10n_util::GetStringUTF16(
476       IDS_MESSAGE_CENTER_ACCESSIBLE_NAME);
477 }
478 
HideBubbleWithView(const views::TrayBubbleView * bubble_view)479 void WebNotificationTray::HideBubbleWithView(
480     const views::TrayBubbleView* bubble_view) {
481   if (message_center_bubble() &&
482       bubble_view == message_center_bubble()->bubble_view()) {
483     message_center_tray_->HideMessageCenterBubble();
484   } else if (popup_collection_.get()) {
485     message_center_tray_->HidePopupBubble();
486   }
487 }
488 
PerformAction(const ui::Event & event)489 bool WebNotificationTray::PerformAction(const ui::Event& event) {
490   if (message_center_bubble())
491     message_center_tray_->HideMessageCenterBubble();
492   else
493     message_center_tray_->ShowMessageCenterBubble();
494   return true;
495 }
496 
BubbleViewDestroyed()497 void WebNotificationTray::BubbleViewDestroyed() {
498   if (message_center_bubble())
499     message_center_bubble()->bubble()->BubbleViewDestroyed();
500 }
501 
OnMouseEnteredView()502 void WebNotificationTray::OnMouseEnteredView() {}
503 
OnMouseExitedView()504 void WebNotificationTray::OnMouseExitedView() {}
505 
GetAccessibleNameForBubble()506 base::string16 WebNotificationTray::GetAccessibleNameForBubble() {
507   return GetAccessibleNameForTray();
508 }
509 
GetAnchorRect(views::Widget * anchor_widget,views::TrayBubbleView::AnchorType anchor_type,views::TrayBubbleView::AnchorAlignment anchor_alignment)510 gfx::Rect WebNotificationTray::GetAnchorRect(
511     views::Widget* anchor_widget,
512     views::TrayBubbleView::AnchorType anchor_type,
513     views::TrayBubbleView::AnchorAlignment anchor_alignment) {
514   return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
515 }
516 
HideBubble(const views::TrayBubbleView * bubble_view)517 void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
518   HideBubbleWithView(bubble_view);
519 }
520 
ShowNotifierSettings()521 bool WebNotificationTray::ShowNotifierSettings() {
522   if (message_center_bubble()) {
523     static_cast<message_center::MessageCenterBubble*>(
524         message_center_bubble()->bubble())->SetSettingsVisible();
525     return true;
526   }
527   return ShowMessageCenterInternal(true /* show_settings */);
528 }
529 
GetMessageCenterTray()530 message_center::MessageCenterTray* WebNotificationTray::GetMessageCenterTray() {
531   return message_center_tray_.get();
532 }
533 
IsCommandIdChecked(int command_id) const534 bool WebNotificationTray::IsCommandIdChecked(int command_id) const {
535   if (command_id != kToggleQuietMode)
536     return false;
537   return message_center()->IsQuietMode();
538 }
539 
IsCommandIdEnabled(int command_id) const540 bool WebNotificationTray::IsCommandIdEnabled(int command_id) const {
541   return true;
542 }
543 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)544 bool WebNotificationTray::GetAcceleratorForCommandId(
545     int command_id,
546     ui::Accelerator* accelerator) {
547   return false;
548 }
549 
ExecuteCommand(int command_id,int event_flags)550 void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) {
551   if (command_id == kToggleQuietMode) {
552     bool in_quiet_mode = message_center()->IsQuietMode();
553     message_center()->SetQuietMode(!in_quiet_mode);
554     return;
555   }
556   base::TimeDelta expires_in = command_id == kEnableQuietModeDay ?
557       base::TimeDelta::FromDays(1):
558       base::TimeDelta::FromHours(1);
559   message_center()->EnterQuietModeWithExpire(expires_in);
560 }
561 
ButtonPressed(views::Button * sender,const ui::Event & event)562 void WebNotificationTray::ButtonPressed(views::Button* sender,
563                                         const ui::Event& event) {
564   DCHECK_EQ(button_, sender);
565   PerformAction(event);
566 }
567 
OnMessageCenterTrayChanged()568 void WebNotificationTray::OnMessageCenterTrayChanged() {
569   // Do not update the tray contents directly. Multiple change events can happen
570   // consecutively, and calling Update in the middle of those events will show
571   // intermediate unread counts for a moment.
572   should_update_tray_content_ = true;
573   base::MessageLoop::current()->PostTask(
574       FROM_HERE,
575       base::Bind(&WebNotificationTray::UpdateTrayContent, AsWeakPtr()));
576 }
577 
UpdateTrayContent()578 void WebNotificationTray::UpdateTrayContent() {
579   if (!should_update_tray_content_)
580     return;
581   should_update_tray_content_ = false;
582 
583   message_center::MessageCenter* message_center =
584       message_center_tray_->message_center();
585   button_->SetUnreadCount(message_center->UnreadNotificationCount());
586   if (IsMessageCenterBubbleVisible())
587     button_->SetState(views::CustomButton::STATE_PRESSED);
588   else
589     button_->SetState(views::CustomButton::STATE_NORMAL);
590   SetVisible((status_area_widget()->login_status() != user::LOGGED_IN_NONE) &&
591              (status_area_widget()->login_status() != user::LOGGED_IN_LOCKED) &&
592              (message_center->NotificationCount() > 0));
593   Layout();
594   SchedulePaint();
595 }
596 
ClickedOutsideBubble()597 bool WebNotificationTray::ClickedOutsideBubble() {
598   // Only hide the message center
599   if (!message_center_bubble())
600     return false;
601 
602   message_center_tray_->HideMessageCenterBubble();
603   return true;
604 }
605 
message_center() const606 message_center::MessageCenter* WebNotificationTray::message_center() const {
607   return message_center_tray_->message_center();
608 }
609 
610 // Methods for testing
611 
IsPopupVisible() const612 bool WebNotificationTray::IsPopupVisible() const {
613   return message_center_tray_->popups_visible();
614 }
615 
616 message_center::MessageCenterBubble*
GetMessageCenterBubbleForTest()617 WebNotificationTray::GetMessageCenterBubbleForTest() {
618   if (!message_center_bubble())
619     return NULL;
620   return static_cast<message_center::MessageCenterBubble*>(
621       message_center_bubble()->bubble());
622 }
623 
624 }  // namespace ash
625