Home | History | Annotate | Download | only in web_notification
      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 
     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 internal {
     59 namespace {
     60 
     61 const int kWebNotificationIconSize = 31;
     62 // Height of the art assets used in alternate shelf layout,
     63 // see ash/ash_switches.h:UseAlternateShelfLayout.
     64 const int kWebNotificationAlternateSize = 38;
     65 const SkColor kWebNotificationColorNoUnread = SkColorSetA(SK_ColorWHITE, 128);
     66 const SkColor kWebNotificationColorWithUnread = SK_ColorWHITE;
     67 
     68 }
     69 
     70 // Observes the change of work area (including temporary change by auto-hide)
     71 // and notifies MessagePopupCollection.
     72 class WorkAreaObserver : public ShelfLayoutManagerObserver,
     73                          public ShellObserver {
     74  public:
     75   WorkAreaObserver(message_center::MessagePopupCollection* collection,
     76                    ShelfLayoutManager* shelf);
     77   virtual ~WorkAreaObserver();
     78 
     79   void SetSystemTrayHeight(int height);
     80 
     81   // Overridden from ShellObserver:
     82   virtual void OnDisplayWorkAreaInsetsChanged() OVERRIDE;
     83 
     84   // Overridden from ShelfLayoutManagerObserver:
     85   virtual void OnAutoHideStateChanged(ShelfAutoHideState new_state) OVERRIDE;
     86 
     87  private:
     88   message_center::MessagePopupCollection* collection_;
     89   ShelfLayoutManager* shelf_;
     90   int system_tray_height_;
     91 
     92   DISALLOW_COPY_AND_ASSIGN(WorkAreaObserver);
     93 };
     94 
     95 WorkAreaObserver::WorkAreaObserver(
     96     message_center::MessagePopupCollection* collection,
     97     ShelfLayoutManager* shelf)
     98     : collection_(collection),
     99       shelf_(shelf),
    100       system_tray_height_(0) {
    101   DCHECK(collection_);
    102   shelf_->AddObserver(this);
    103   Shell::GetInstance()->AddShellObserver(this);
    104 }
    105 
    106 WorkAreaObserver::~WorkAreaObserver() {
    107   Shell::GetInstance()->RemoveShellObserver(this);
    108   shelf_->RemoveObserver(this);
    109 }
    110 
    111 void WorkAreaObserver::SetSystemTrayHeight(int height) {
    112   system_tray_height_ = height;
    113   if (system_tray_height_ > 0 && ash::switches::UseAlternateShelfLayout())
    114     system_tray_height_ += message_center::kMarginBetweenItems;
    115 
    116   OnAutoHideStateChanged(shelf_->auto_hide_state());
    117 }
    118 
    119 void WorkAreaObserver::OnDisplayWorkAreaInsetsChanged() {
    120   collection_->OnDisplayBoundsChanged(
    121       Shell::GetScreen()->GetDisplayNearestWindow(
    122           shelf_->shelf_widget()->GetNativeView()));
    123 }
    124 
    125 void WorkAreaObserver::OnAutoHideStateChanged(ShelfAutoHideState new_state) {
    126   gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
    127       shelf_->shelf_widget()->GetNativeView());
    128   gfx::Rect work_area = display.work_area();
    129   int width = 0;
    130   if (shelf_->auto_hide_behavior() != SHELF_AUTO_HIDE_BEHAVIOR_NEVER) {
    131     width = (new_state == SHELF_AUTO_HIDE_HIDDEN) ?
    132         ShelfLayoutManager::kAutoHideSize :
    133         ShelfLayoutManager::GetPreferredShelfSize();
    134   }
    135   switch (shelf_->GetAlignment()) {
    136     case SHELF_ALIGNMENT_BOTTOM:
    137       work_area.Inset(0, 0, 0, width);
    138       if (system_tray_height_ > 0) {
    139         work_area.set_height(
    140             std::max(0, work_area.height() - system_tray_height_));
    141       }
    142       break;
    143     case SHELF_ALIGNMENT_LEFT:
    144       work_area.Inset(width, 0, 0, 0);
    145       // Popups appear on the left bottom only when UI is RTL.
    146       if (base::i18n::IsRTL() && system_tray_height_ > 0) {
    147         work_area.set_height(
    148             std::max(0, work_area.height() - system_tray_height_));
    149       }
    150       break;
    151     case SHELF_ALIGNMENT_RIGHT:
    152       work_area.Inset(0, 0, width, 0);
    153       // Popups appear on the right bottom only when UI isn't RTL.
    154       if (!base::i18n::IsRTL() && system_tray_height_ > 0) {
    155         work_area.set_height(
    156             std::max(0, work_area.height() - system_tray_height_));
    157       }
    158       break;
    159     case SHELF_ALIGNMENT_TOP:
    160       work_area.Inset(0, width, 0, 0);
    161       if (system_tray_height_ > 0) {
    162         work_area.set_y(work_area.y() + system_tray_height_);
    163         work_area.set_height(
    164             std::max(0, work_area.height() - system_tray_height_));
    165       }
    166       break;
    167   }
    168   collection_->SetDisplayInfo(work_area, display.bounds());
    169 }
    170 
    171 // Class to initialize and manage the WebNotificationBubble and
    172 // TrayBubbleWrapper instances for a bubble.
    173 class WebNotificationBubbleWrapper {
    174  public:
    175   // Takes ownership of |bubble| and creates |bubble_wrapper_|.
    176   WebNotificationBubbleWrapper(WebNotificationTray* tray,
    177                                message_center::MessageBubbleBase* bubble) {
    178     bubble_.reset(bubble);
    179     views::TrayBubbleView::AnchorAlignment anchor_alignment =
    180         tray->GetAnchorAlignment();
    181     views::TrayBubbleView::InitParams init_params =
    182         bubble->GetInitParams(anchor_alignment);
    183     views::View* anchor = tray->tray_container();
    184     if (anchor_alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
    185       gfx::Point bounds(anchor->width() / 2, 0);
    186       views::View::ConvertPointToWidget(anchor, &bounds);
    187       init_params.arrow_offset = bounds.x();
    188     }
    189     views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create(
    190         tray->GetBubbleWindowContainer(), anchor, tray, &init_params);
    191     if (ash::switches::UseAlternateShelfLayout())
    192       bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
    193     bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view));
    194     bubble->InitializeContents(bubble_view);
    195   }
    196 
    197   message_center::MessageBubbleBase* bubble() const { return bubble_.get(); }
    198 
    199   // Convenience accessors.
    200   views::TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
    201 
    202  private:
    203   scoped_ptr<message_center::MessageBubbleBase> bubble_;
    204   scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_;
    205 
    206   DISALLOW_COPY_AND_ASSIGN(WebNotificationBubbleWrapper);
    207 };
    208 
    209 class WebNotificationButton : public views::CustomButton {
    210  public:
    211   WebNotificationButton(views::ButtonListener* listener)
    212       : views::CustomButton(listener),
    213         is_bubble_visible_(false),
    214         unread_count_(0) {
    215     SetLayoutManager(new views::FillLayout);
    216     unread_label_ = new views::Label();
    217     SetupLabelForTray(unread_label_);
    218     AddChildView(unread_label_);
    219   }
    220 
    221   void SetBubbleVisible(bool visible) {
    222     if (visible == is_bubble_visible_)
    223       return;
    224 
    225     is_bubble_visible_ = visible;
    226     UpdateIconVisibility();
    227   }
    228 
    229   void SetUnreadCount(int unread_count) {
    230     // base::FormatNumber doesn't convert to arabic numeric characters.
    231     // TODO(mukai): use ICU to support conversion for such locales.
    232     unread_count_ = unread_count;
    233     // TODO(mukai): move NINE_PLUS message to ui_strings, it doesn't need to be
    234     // in ash_strings.
    235     unread_label_->SetText((unread_count > 9) ?
    236         l10n_util::GetStringUTF16(IDS_ASH_NOTIFICATION_UNREAD_COUNT_NINE_PLUS) :
    237         base::FormatNumber(unread_count));
    238     UpdateIconVisibility();
    239   }
    240 
    241  protected:
    242   // Overridden from views::ImageButton:
    243   virtual gfx::Size GetPreferredSize() OVERRIDE {
    244     if (ash::switches::UseAlternateShelfLayout())
    245       return gfx::Size(kWebNotificationAlternateSize,
    246                        kWebNotificationAlternateSize);
    247     return gfx::Size(kWebNotificationIconSize, kWebNotificationIconSize);
    248   }
    249 
    250  private:
    251   void UpdateIconVisibility() {
    252     unread_label_->SetEnabledColor(
    253         (!is_bubble_visible_ && unread_count_ > 0) ?
    254         kWebNotificationColorWithUnread : kWebNotificationColorNoUnread);
    255     SchedulePaint();
    256   }
    257 
    258   bool is_bubble_visible_;
    259   int unread_count_;
    260 
    261   views::Label* unread_label_;
    262 
    263   DISALLOW_COPY_AND_ASSIGN(WebNotificationButton);
    264 };
    265 
    266 }  // namespace internal
    267 
    268 WebNotificationTray::WebNotificationTray(
    269     internal::StatusAreaWidget* status_area_widget)
    270     : TrayBackgroundView(status_area_widget),
    271       button_(NULL),
    272       show_message_center_on_unlock_(false),
    273       should_update_tray_content_(false),
    274       should_block_shelf_auto_hide_(false) {
    275   button_ = new internal::WebNotificationButton(this);
    276   button_->set_triggerable_event_flags(
    277       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON);
    278   tray_container()->AddChildView(button_);
    279   SetContentsBackground();
    280   tray_container()->set_border(NULL);
    281   SetVisible(false);
    282   message_center_tray_.reset(new message_center::MessageCenterTray(
    283       this,
    284       message_center::MessageCenter::Get()));
    285   OnMessageCenterTrayChanged();
    286 }
    287 
    288 WebNotificationTray::~WebNotificationTray() {
    289   // Release any child views that might have back pointers before ~View().
    290   message_center_bubble_.reset();
    291   popup_collection_.reset();
    292   work_area_observer_.reset();
    293 }
    294 
    295 // Public methods.
    296 
    297 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
    298   if (!ShouldShowMessageCenter())
    299     return false;
    300 
    301   should_block_shelf_auto_hide_ = true;
    302   message_center::MessageCenterBubble* message_center_bubble =
    303       new message_center::MessageCenterBubble(
    304           message_center(),
    305           message_center_tray_.get(),
    306           ash::switches::UseAlternateShelfLayout());
    307 
    308   int max_height = 0;
    309   aura::Window* status_area_window = status_area_widget()->GetNativeView();
    310   switch (GetShelfLayoutManager()->GetAlignment()) {
    311     case SHELF_ALIGNMENT_BOTTOM: {
    312       gfx::Rect shelf_bounds = GetShelfLayoutManager()->GetIdealBounds();
    313       max_height = shelf_bounds.y();
    314       break;
    315     }
    316     case SHELF_ALIGNMENT_TOP: {
    317       aura::RootWindow* root = status_area_window->GetRootWindow();
    318       max_height =
    319           root->bounds().height() - status_area_window->bounds().height();
    320       break;
    321     }
    322     case SHELF_ALIGNMENT_LEFT:
    323     case SHELF_ALIGNMENT_RIGHT: {
    324       // Assume that the bottom line of the status area widget and the bubble
    325       // are aligned.
    326       max_height = status_area_window->GetBoundsInRootWindow().bottom();
    327       break;
    328     }
    329     default:
    330       NOTREACHED();
    331   }
    332 
    333   message_center_bubble->SetMaxHeight(std::max(0,
    334                                                max_height - GetTraySpacing()));
    335   if (show_settings)
    336     message_center_bubble->SetSettingsVisible();
    337   message_center_bubble_.reset(
    338       new internal::WebNotificationBubbleWrapper(this, message_center_bubble));
    339 
    340   status_area_widget()->SetHideSystemNotifications(true);
    341   GetShelfLayoutManager()->UpdateAutoHideState();
    342   button_->SetBubbleVisible(true);
    343   return true;
    344 }
    345 
    346 bool WebNotificationTray::ShowMessageCenter() {
    347   return ShowMessageCenterInternal(false /* show_settings */);
    348 }
    349 
    350 void WebNotificationTray::HideMessageCenter() {
    351   if (!message_center_bubble())
    352     return;
    353   message_center_bubble_.reset();
    354   should_block_shelf_auto_hide_ = false;
    355   show_message_center_on_unlock_ = false;
    356   status_area_widget()->SetHideSystemNotifications(false);
    357   GetShelfLayoutManager()->UpdateAutoHideState();
    358   button_->SetBubbleVisible(false);
    359 }
    360 
    361 void WebNotificationTray::SetSystemTrayHeight(int height) {
    362   if (!work_area_observer_)
    363     return;
    364   work_area_observer_->SetSystemTrayHeight(height);
    365 }
    366 
    367 bool WebNotificationTray::ShowPopups() {
    368   if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED ||
    369       message_center_bubble() ||
    370       !status_area_widget()->ShouldShowWebNotifications()) {
    371     return false;
    372   }
    373 
    374   popup_collection_.reset(new message_center::MessagePopupCollection(
    375       ash::Shell::GetContainer(
    376           GetWidget()->GetNativeView()->GetRootWindow(),
    377           internal::kShellWindowId_StatusContainer),
    378       message_center(),
    379       message_center_tray_.get(),
    380       ash::switches::UseAlternateShelfLayout()));
    381   work_area_observer_.reset(new internal::WorkAreaObserver(
    382       popup_collection_.get(), GetShelfLayoutManager()));
    383   return true;
    384 }
    385 
    386 void WebNotificationTray::HidePopups() {
    387   popup_collection_.reset();
    388   work_area_observer_.reset();
    389 }
    390 
    391 // Private methods.
    392 
    393 bool WebNotificationTray::ShouldShowMessageCenter() {
    394   return status_area_widget()->login_status() != user::LOGGED_IN_LOCKED &&
    395       !(status_area_widget()->system_tray() &&
    396         status_area_widget()->system_tray()->HasNotificationBubble());
    397 }
    398 
    399 void WebNotificationTray::ShowQuietModeMenu(const ui::Event& event) {
    400   base::AutoReset<bool> reset(&should_block_shelf_auto_hide_, true);
    401   scoped_ptr<ui::MenuModel> menu_model(
    402       message_center_tray_->CreateQuietModeMenu());
    403   quiet_mode_menu_runner_.reset(new views::MenuRunner(menu_model.get()));
    404   gfx::Point point;
    405   views::View::ConvertPointToScreen(this, &point);
    406   if (quiet_mode_menu_runner_->RunMenuAt(
    407       GetWidget(),
    408       NULL,
    409       gfx::Rect(point, bounds().size()),
    410       views::MenuItemView::BUBBLE_ABOVE,
    411       ui::GetMenuSourceTypeForEvent(event),
    412       views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
    413     return;
    414 
    415   quiet_mode_menu_runner_.reset();
    416   GetShelfLayoutManager()->UpdateAutoHideState();
    417 }
    418 
    419 bool WebNotificationTray::ShouldShowQuietModeMenu(const ui::Event& event) {
    420   // TODO(mukai): Add keyboard event handler.
    421   if (!event.IsMouseEvent())
    422     return false;
    423 
    424   const ui::MouseEvent* mouse_event =
    425       static_cast<const ui::MouseEvent*>(&event);
    426 
    427   return mouse_event->IsRightMouseButton();
    428 }
    429 
    430 void WebNotificationTray::UpdateAfterLoginStatusChange(
    431     user::LoginStatus login_status) {
    432   if (login_status == user::LOGGED_IN_LOCKED) {
    433     show_message_center_on_unlock_ =
    434         message_center_tray_->HideMessageCenterBubble();
    435     message_center_tray_->HidePopupBubble();
    436   } else {
    437     // Only try once to show the message center bubble on login status change,
    438     // so always set |show_message_center_on_unlock_| to false.
    439     if (show_message_center_on_unlock_)
    440       message_center_tray_->ShowMessageCenterBubble();
    441     show_message_center_on_unlock_ = false;
    442   }
    443   OnMessageCenterTrayChanged();
    444 }
    445 
    446 bool WebNotificationTray::ShouldBlockLauncherAutoHide() const {
    447   return should_block_shelf_auto_hide_;
    448 }
    449 
    450 bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
    451   return (message_center_bubble() &&
    452           message_center_bubble()->bubble()->IsVisible());
    453 }
    454 
    455 bool WebNotificationTray::IsMouseInNotificationBubble() const {
    456   return false;
    457 }
    458 
    459 void WebNotificationTray::ShowMessageCenterBubble() {
    460   if (!IsMessageCenterBubbleVisible())
    461     message_center_tray_->ShowMessageCenterBubble();
    462 }
    463 
    464 void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
    465   if (alignment == shelf_alignment())
    466     return;
    467   internal::TrayBackgroundView::SetShelfAlignment(alignment);
    468   tray_container()->set_border(NULL);
    469   // Destroy any existing bubble so that it will be rebuilt correctly.
    470   message_center_tray_->HideMessageCenterBubble();
    471   message_center_tray_->HidePopupBubble();
    472 }
    473 
    474 void WebNotificationTray::AnchorUpdated() {
    475   if (message_center_bubble()) {
    476     message_center_bubble()->bubble_view()->UpdateBubble();
    477     UpdateBubbleViewArrow(message_center_bubble()->bubble_view());
    478   }
    479 }
    480 
    481 base::string16 WebNotificationTray::GetAccessibleNameForTray() {
    482   return l10n_util::GetStringUTF16(
    483       IDS_MESSAGE_CENTER_ACCESSIBLE_NAME);
    484 }
    485 
    486 void WebNotificationTray::HideBubbleWithView(
    487     const views::TrayBubbleView* bubble_view) {
    488   if (message_center_bubble() &&
    489       bubble_view == message_center_bubble()->bubble_view()) {
    490     message_center_tray_->HideMessageCenterBubble();
    491   } else if (popup_collection_.get()) {
    492     message_center_tray_->HidePopupBubble();
    493   }
    494 }
    495 
    496 bool WebNotificationTray::PerformAction(const ui::Event& event) {
    497   if (ShouldShowQuietModeMenu(event)) {
    498     ShowQuietModeMenu(event);
    499     return true;
    500   }
    501 
    502   if (message_center_bubble())
    503     message_center_tray_->HideMessageCenterBubble();
    504   else
    505     message_center_tray_->ShowMessageCenterBubble();
    506   return true;
    507 }
    508 
    509 void WebNotificationTray::BubbleViewDestroyed() {
    510   if (message_center_bubble())
    511     message_center_bubble()->bubble()->BubbleViewDestroyed();
    512 }
    513 
    514 void WebNotificationTray::OnMouseEnteredView() {}
    515 
    516 void WebNotificationTray::OnMouseExitedView() {}
    517 
    518 base::string16 WebNotificationTray::GetAccessibleNameForBubble() {
    519   return GetAccessibleNameForTray();
    520 }
    521 
    522 gfx::Rect WebNotificationTray::GetAnchorRect(
    523     views::Widget* anchor_widget,
    524     views::TrayBubbleView::AnchorType anchor_type,
    525     views::TrayBubbleView::AnchorAlignment anchor_alignment) {
    526   return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
    527 }
    528 
    529 void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
    530   HideBubbleWithView(bubble_view);
    531 }
    532 
    533 bool WebNotificationTray::ShowNotifierSettings() {
    534   if (message_center_bubble()) {
    535     static_cast<message_center::MessageCenterBubble*>(
    536         message_center_bubble()->bubble())->SetSettingsVisible();
    537     return true;
    538   }
    539   return ShowMessageCenterInternal(true /* show_settings */);
    540 }
    541 
    542 message_center::MessageCenterTray* WebNotificationTray::GetMessageCenterTray() {
    543   return message_center_tray_.get();
    544 }
    545 
    546 bool WebNotificationTray::IsPressed() {
    547   return IsMessageCenterBubbleVisible();
    548 }
    549 
    550 void WebNotificationTray::ButtonPressed(views::Button* sender,
    551                                         const ui::Event& event) {
    552   DCHECK_EQ(button_, sender);
    553   PerformAction(event);
    554 }
    555 
    556 void WebNotificationTray::OnMessageCenterTrayChanged() {
    557   // Do not update the tray contents directly. Multiple change events can happen
    558   // consecutively, and calling Update in the middle of those events will show
    559   // intermediate unread counts for a moment.
    560   should_update_tray_content_ = true;
    561   base::MessageLoop::current()->PostTask(
    562       FROM_HERE,
    563       base::Bind(&WebNotificationTray::UpdateTrayContent, AsWeakPtr()));
    564 }
    565 
    566 void WebNotificationTray::UpdateTrayContent() {
    567   if (!should_update_tray_content_)
    568     return;
    569   should_update_tray_content_ = false;
    570 
    571   message_center::MessageCenter* message_center =
    572       message_center_tray_->message_center();
    573   button_->SetUnreadCount(message_center->UnreadNotificationCount());
    574   if (IsMessageCenterBubbleVisible())
    575     button_->SetState(views::CustomButton::STATE_PRESSED);
    576   else
    577     button_->SetState(views::CustomButton::STATE_NORMAL);
    578   SetVisible((status_area_widget()->login_status() != user::LOGGED_IN_NONE) &&
    579              (status_area_widget()->login_status() != user::LOGGED_IN_LOCKED) &&
    580              (message_center->NotificationCount() > 0));
    581   Layout();
    582   SchedulePaint();
    583 }
    584 
    585 bool WebNotificationTray::ClickedOutsideBubble() {
    586   // Only hide the message center
    587   if (!message_center_bubble())
    588     return false;
    589 
    590   message_center_tray_->HideMessageCenterBubble();
    591   return true;
    592 }
    593 
    594 message_center::MessageCenter* WebNotificationTray::message_center() {
    595   return message_center_tray_->message_center();
    596 }
    597 
    598 // Methods for testing
    599 
    600 bool WebNotificationTray::IsPopupVisible() const {
    601   return message_center_tray_->popups_visible();
    602 }
    603 
    604 message_center::MessageCenterBubble*
    605 WebNotificationTray::GetMessageCenterBubbleForTest() {
    606   if (!message_center_bubble())
    607     return NULL;
    608   return static_cast<message_center::MessageCenterBubble*>(
    609       message_center_bubble()->bubble());
    610 }
    611 
    612 }  // namespace ash
    613