Home | History | Annotate | Download | only in tray
      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/tray/system_tray.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/shelf/shelf_layout_manager.h"
      9 #include "ash/shell.h"
     10 #include "ash/shell/panel_window.h"
     11 #include "ash/shell_window_ids.h"
     12 #include "ash/system/bluetooth/tray_bluetooth.h"
     13 #include "ash/system/brightness/tray_brightness.h"
     14 #include "ash/system/chromeos/tray_tracing.h"
     15 #include "ash/system/date/tray_date.h"
     16 #include "ash/system/drive/tray_drive.h"
     17 #include "ash/system/ime/tray_ime.h"
     18 #include "ash/system/logout_button/tray_logout_button.h"
     19 #include "ash/system/monitor/tray_monitor.h"
     20 #include "ash/system/session_length_limit/tray_session_length_limit.h"
     21 #include "ash/system/status_area_widget.h"
     22 #include "ash/system/tray/system_tray_delegate.h"
     23 #include "ash/system/tray/system_tray_item.h"
     24 #include "ash/system/tray/tray_bubble_wrapper.h"
     25 #include "ash/system/tray/tray_constants.h"
     26 #include "ash/system/tray_accessibility.h"
     27 #include "ash/system/tray_caps_lock.h"
     28 #include "ash/system/tray_update.h"
     29 #include "ash/system/user/login_status.h"
     30 #include "ash/system/user/tray_user.h"
     31 #include "ash/system/web_notification/web_notification_tray.h"
     32 #include "base/command_line.h"
     33 #include "base/logging.h"
     34 #include "base/strings/utf_string_conversions.h"
     35 #include "base/timer/timer.h"
     36 #include "grit/ash_strings.h"
     37 #include "ui/aura/root_window.h"
     38 #include "ui/base/events/event_constants.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "ui/compositor/layer.h"
     41 #include "ui/gfx/canvas.h"
     42 #include "ui/gfx/screen.h"
     43 #include "ui/gfx/skia_util.h"
     44 #include "ui/views/border.h"
     45 #include "ui/views/controls/label.h"
     46 #include "ui/views/layout/box_layout.h"
     47 #include "ui/views/layout/fill_layout.h"
     48 #include "ui/views/view.h"
     49 
     50 #if defined(OS_CHROMEOS)
     51 #include "ash/system/chromeos/audio/tray_audio.h"
     52 #include "ash/system/chromeos/enterprise/tray_enterprise.h"
     53 #include "ash/system/chromeos/managed/tray_locally_managed_user.h"
     54 #include "ash/system/chromeos/network/tray_network.h"
     55 #include "ash/system/chromeos/network/tray_sms.h"
     56 #include "ash/system/chromeos/network/tray_vpn.h"
     57 #include "ash/system/chromeos/power/tray_power.h"
     58 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
     59 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
     60 #include "ash/system/chromeos/settings/tray_settings.h"
     61 #include "ash/system/chromeos/tray_display.h"
     62 #include "ui/message_center/message_center.h"
     63 #endif
     64 
     65 using views::TrayBubbleView;
     66 
     67 namespace ash {
     68 
     69 // The minimum width of the system tray menu width.
     70 const int kMinimumSystemTrayMenuWidth = 300;
     71 
     72 namespace internal {
     73 
     74 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper
     75 // instances for a bubble.
     76 
     77 class SystemBubbleWrapper {
     78  public:
     79   // Takes ownership of |bubble|.
     80   explicit SystemBubbleWrapper(internal::SystemTrayBubble* bubble)
     81       : bubble_(bubble) {
     82   }
     83 
     84   // Initializes the bubble view and creates |bubble_wrapper_|.
     85   void InitView(TrayBackgroundView* tray,
     86                 views::View* anchor,
     87                 TrayBubbleView::InitParams* init_params) {
     88     DCHECK(anchor);
     89     user::LoginStatus login_status =
     90         Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
     91     bubble_->InitView(anchor, login_status, init_params);
     92     bubble_wrapper_.reset(
     93         new internal::TrayBubbleWrapper(tray, bubble_->bubble_view()));
     94     if (ash::switches::UseAlternateShelfLayout()) {
     95       // The system bubble should not have an arrow.
     96       bubble_->bubble_view()->SetArrowPaintType(
     97           views::BubbleBorder::PAINT_NONE);
     98     }
     99   }
    100 
    101   // Convenience accessors:
    102   SystemTrayBubble* bubble() const { return bubble_.get(); }
    103   SystemTrayBubble::BubbleType bubble_type() const {
    104     return bubble_->bubble_type();
    105   }
    106   TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
    107 
    108  private:
    109   scoped_ptr<internal::SystemTrayBubble> bubble_;
    110   scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_;
    111 
    112   DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper);
    113 };
    114 
    115 }  // namespace internal
    116 
    117 // SystemTray
    118 
    119 using internal::SystemTrayBubble;
    120 
    121 SystemTray::SystemTray(internal::StatusAreaWidget* status_area_widget)
    122     : internal::TrayBackgroundView(status_area_widget),
    123       items_(),
    124       default_bubble_height_(0),
    125       hide_notifications_(false),
    126       full_system_tray_menu_(false),
    127       tray_accessibility_(NULL) {
    128   SetContentsBackground();
    129 }
    130 
    131 SystemTray::~SystemTray() {
    132   // Destroy any child views that might have back pointers before ~View().
    133   system_bubble_.reset();
    134   notification_bubble_.reset();
    135   for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
    136        it != items_.end();
    137        ++it) {
    138     (*it)->DestroyTrayView();
    139   }
    140 }
    141 
    142 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) {
    143   internal::TrayBackgroundView::Initialize();
    144   CreateItems(delegate);
    145 }
    146 
    147 void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
    148 #if !defined(OS_WIN)
    149   AddTrayItem(new internal::TraySessionLengthLimit(this));
    150   AddTrayItem(new internal::TrayLogoutButton(this));
    151   // In multi-profile user mode we can have multiple user tiles.
    152   ash::Shell* shell = ash::Shell::GetInstance();
    153   int maximum_user_profiles =
    154       shell->delegate()->IsMultiProfilesEnabled() ?
    155           shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers() :
    156           0;
    157   // Note: We purposely use one more item then logged in users to account for
    158   // the additional separator.
    159   for (int i = 0; i <= maximum_user_profiles; i++)
    160     AddTrayItem(new internal::TrayUser(this, i));
    161 
    162 #endif
    163 #if defined(OS_CHROMEOS)
    164   AddTrayItem(new internal::TrayEnterprise(this));
    165   AddTrayItem(new internal::TrayLocallyManagedUser(this));
    166 #endif
    167   AddTrayItem(new internal::TrayIME(this));
    168   tray_accessibility_ = new internal::TrayAccessibility(this);
    169   AddTrayItem(tray_accessibility_);
    170 #if defined(OS_CHROMEOS)
    171   AddTrayItem(new internal::TrayTracing(this));
    172   AddTrayItem(
    173       new internal::TrayPower(this, message_center::MessageCenter::Get()));
    174 #endif
    175 #if defined(OS_CHROMEOS)
    176   AddTrayItem(new internal::TrayNetwork(this));
    177   AddTrayItem(new internal::TrayVPN(this));
    178   AddTrayItem(new internal::TraySms(this));
    179 #endif
    180 #if !defined(OS_WIN)
    181   AddTrayItem(new internal::TrayBluetooth(this));
    182 #endif
    183   AddTrayItem(new internal::TrayDrive(this));
    184 #if defined(OS_CHROMEOS)
    185   AddTrayItem(new internal::TrayDisplay(this));
    186   AddTrayItem(new internal::ScreenCaptureTrayItem(this));
    187   AddTrayItem(new internal::ScreenShareTrayItem(this));
    188   AddTrayItem(new internal::TrayAudio(this));
    189 #endif
    190 #if !defined(OS_WIN)
    191   AddTrayItem(new internal::TrayBrightness(this));
    192   AddTrayItem(new internal::TrayCapsLock(this));
    193 #endif
    194 #if defined(OS_CHROMEOS)
    195   AddTrayItem(new internal::TraySettings(this));
    196 #endif
    197   AddTrayItem(new internal::TrayUpdate(this));
    198   AddTrayItem(new internal::TrayDate(this));
    199 
    200 #if defined(OS_LINUX)
    201   // Add memory monitor if enabled.
    202   CommandLine* cmd = CommandLine::ForCurrentProcess();
    203   if (cmd->HasSwitch(ash::switches::kAshEnableMemoryMonitor))
    204     AddTrayItem(new internal::TrayMonitor(this));
    205 #endif
    206 
    207   SetVisible(ash::Shell::GetInstance()->system_tray_delegate()->
    208       GetTrayVisibilityOnStartup());
    209 }
    210 
    211 void SystemTray::AddTrayItem(SystemTrayItem* item) {
    212   items_.push_back(item);
    213 
    214   SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
    215   views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus());
    216   item->UpdateAfterShelfAlignmentChange(shelf_alignment());
    217 
    218   if (tray_item) {
    219     tray_container()->AddChildViewAt(tray_item, 0);
    220     PreferredSizeChanged();
    221     tray_item_map_[item] = tray_item;
    222   }
    223 }
    224 
    225 void SystemTray::RemoveTrayItem(SystemTrayItem* item) {
    226   NOTIMPLEMENTED();
    227 }
    228 
    229 const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const {
    230   return items_.get();
    231 }
    232 
    233 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
    234   ShowDefaultViewWithOffset(creation_type,
    235                             TrayBubbleView::InitParams::kArrowDefaultOffset);
    236 }
    237 
    238 void SystemTray::ShowDetailedView(SystemTrayItem* item,
    239                                   int close_delay,
    240                                   bool activate,
    241                                   BubbleCreationType creation_type) {
    242   std::vector<SystemTrayItem*> items;
    243   items.push_back(item);
    244   ShowItems(items, true, activate, creation_type, GetTrayXOffset(item));
    245   if (system_bubble_)
    246     system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
    247 }
    248 
    249 void SystemTray::SetDetailedViewCloseDelay(int close_delay) {
    250   if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED))
    251     system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
    252 }
    253 
    254 void SystemTray::HideDetailedView(SystemTrayItem* item) {
    255   if (item != detailed_item_)
    256     return;
    257   DestroySystemBubble();
    258   UpdateNotificationBubble();
    259 }
    260 
    261 void SystemTray::ShowNotificationView(SystemTrayItem* item) {
    262   if (std::find(notification_items_.begin(), notification_items_.end(), item)
    263       != notification_items_.end())
    264     return;
    265   notification_items_.push_back(item);
    266   UpdateNotificationBubble();
    267 }
    268 
    269 void SystemTray::HideNotificationView(SystemTrayItem* item) {
    270   std::vector<SystemTrayItem*>::iterator found_iter =
    271       std::find(notification_items_.begin(), notification_items_.end(), item);
    272   if (found_iter == notification_items_.end())
    273     return;
    274   notification_items_.erase(found_iter);
    275   // Only update the notification bubble if visible (i.e. don't create one).
    276   if (notification_bubble_)
    277     UpdateNotificationBubble();
    278 }
    279 
    280 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) {
    281   DestroySystemBubble();
    282   UpdateNotificationBubble();
    283 
    284   for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
    285       it != items_.end();
    286       ++it) {
    287     (*it)->UpdateAfterLoginStatusChange(login_status);
    288   }
    289 
    290   // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial
    291   // position of the shelf differs.
    292   if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM)
    293     UpdateAfterShelfAlignmentChange(shelf_alignment());
    294 
    295   SetVisible(true);
    296   PreferredSizeChanged();
    297 }
    298 
    299 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
    300   for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
    301       it != items_.end();
    302       ++it) {
    303     (*it)->UpdateAfterShelfAlignmentChange(alignment);
    304   }
    305 }
    306 
    307 void SystemTray::SetHideNotifications(bool hide_notifications) {
    308   if (notification_bubble_)
    309     notification_bubble_->bubble()->SetVisible(!hide_notifications);
    310   hide_notifications_ = hide_notifications;
    311 }
    312 
    313 bool SystemTray::ShouldShowLauncher() const {
    314   return system_bubble_.get() && system_bubble_->bubble()->ShouldShowLauncher();
    315 }
    316 
    317 bool SystemTray::HasSystemBubble() const {
    318   return system_bubble_.get() != NULL;
    319 }
    320 
    321 bool SystemTray::HasNotificationBubble() const {
    322   return notification_bubble_.get() != NULL;
    323 }
    324 
    325 bool SystemTray::IsPressed() {
    326   // Only when a full system tray bubble gets shown true will be returned.
    327   // Small bubbles (like audio modifications via keyboard) should return false.
    328   // Since showing the e.g. network portion of the system tray menu will convert
    329   // the |system_bubble_| from type |BUBBLE_TYPE_DEFAULT| into
    330   // |BUBBLE_TYPE_DETAILED| the full tray cannot reliably be checked trhough the
    331   // type. As such |full_system_tray_menu_| gets checked here.
    332   return HasSystemBubble() && full_system_tray_menu_;
    333 }
    334 
    335 internal::SystemTrayBubble* SystemTray::GetSystemBubble() {
    336   if (!system_bubble_)
    337     return NULL;
    338   return system_bubble_->bubble();
    339 }
    340 
    341 bool SystemTray::IsAnyBubbleVisible() const {
    342   return ((system_bubble_.get() &&
    343            system_bubble_->bubble()->IsVisible()) ||
    344           (notification_bubble_.get() &&
    345            notification_bubble_->bubble()->IsVisible()));
    346 }
    347 
    348 bool SystemTray::IsMouseInNotificationBubble() const {
    349   if (!notification_bubble_)
    350     return false;
    351   return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains(
    352       Shell::GetScreen()->GetCursorScreenPoint());
    353 }
    354 
    355 bool SystemTray::CloseSystemBubble() const {
    356   if (!system_bubble_)
    357     return false;
    358   system_bubble_->bubble()->Close();
    359   return true;
    360 }
    361 
    362 bool SystemTray::CloseNotificationBubbleForTest() const {
    363   if (!notification_bubble_)
    364     return false;
    365   notification_bubble_->bubble()->Close();
    366   return true;
    367 }
    368 
    369 // Private methods.
    370 
    371 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) {
    372   DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
    373   return system_bubble_.get() && system_bubble_->bubble_type() == type;
    374 }
    375 
    376 void SystemTray::DestroySystemBubble() {
    377   system_bubble_.reset();
    378   detailed_item_ = NULL;
    379   UpdateWebNotifications();
    380 }
    381 
    382 void SystemTray::DestroyNotificationBubble() {
    383   if (notification_bubble_) {
    384     notification_bubble_.reset();
    385     UpdateWebNotifications();
    386   }
    387 }
    388 
    389 int SystemTray::GetTrayXOffset(SystemTrayItem* item) const {
    390   // Don't attempt to align the arrow if the shelf is on the left or right.
    391   if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM &&
    392       shelf_alignment() != SHELF_ALIGNMENT_TOP)
    393     return TrayBubbleView::InitParams::kArrowDefaultOffset;
    394 
    395   std::map<SystemTrayItem*, views::View*>::const_iterator it =
    396       tray_item_map_.find(item);
    397   if (it == tray_item_map_.end())
    398     return TrayBubbleView::InitParams::kArrowDefaultOffset;
    399 
    400   const views::View* item_view = it->second;
    401   if (item_view->bounds().IsEmpty()) {
    402     // The bounds of item could be still empty if it does not have a visible
    403     // tray view. In that case, use the default (minimum) offset.
    404     return TrayBubbleView::InitParams::kArrowDefaultOffset;
    405   }
    406 
    407   gfx::Point point(item_view->width() / 2, 0);
    408   ConvertPointToWidget(item_view, &point);
    409   return point.x();
    410 }
    411 
    412 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type,
    413                                            int arrow_offset) {
    414   ShowItems(items_.get(), false, true, creation_type, arrow_offset);
    415 }
    416 
    417 void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items,
    418                            bool detailed,
    419                            bool can_activate,
    420                            BubbleCreationType creation_type,
    421                            int arrow_offset) {
    422   // No system tray bubbles in kiosk mode.
    423   if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() ==
    424       ash::user::LOGGED_IN_KIOSK_APP) {
    425     return;
    426   }
    427 
    428   // Destroy any existing bubble and create a new one.
    429   SystemTrayBubble::BubbleType bubble_type = detailed ?
    430       SystemTrayBubble::BUBBLE_TYPE_DETAILED :
    431       SystemTrayBubble::BUBBLE_TYPE_DEFAULT;
    432 
    433   // Destroy the notification bubble here so that it doesn't get rebuilt
    434   // while we add items to the main bubble_ (e.g. in HideNotificationView).
    435   notification_bubble_.reset();
    436   if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
    437     system_bubble_->bubble()->UpdateView(items, bubble_type);
    438   } else {
    439     // Remember if the menu is a single property (like e.g. volume) or the
    440     // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
    441     // above, |full_system_tray_menu_| does not get changed since the fact that
    442     // the menu is full (or not) doesn't change even if a "single property"
    443     // (like network) replaces most of the menu.
    444     full_system_tray_menu_ = items.size() > 1;
    445     // The menu width is fixed, and it is a per language setting.
    446     int menu_width = std::max(kMinimumSystemTrayMenuWidth,
    447         Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth());
    448 
    449     TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY,
    450                                            GetAnchorAlignment(),
    451                                            menu_width,
    452                                            kTrayPopupMaxWidth);
    453     init_params.can_activate = can_activate;
    454     init_params.first_item_has_no_margin =
    455         ash::switches::UseAlternateShelfLayout();
    456     if (detailed) {
    457       // This is the case where a volume control or brightness control bubble
    458       // is created.
    459       init_params.max_height = default_bubble_height_;
    460       init_params.arrow_color = kBackgroundColor;
    461     } else {
    462       init_params.arrow_color = kHeaderBackgroundColor;
    463     }
    464     init_params.arrow_offset = arrow_offset;
    465     // For Volume and Brightness we don't want to show an arrow when
    466     // they are shown in a bubble by themselves.
    467     init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL;
    468     if (items.size() == 1 && items[0]->ShouldHideArrow())
    469       init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT;
    470     SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type);
    471     system_bubble_.reset(new internal::SystemBubbleWrapper(bubble));
    472     system_bubble_->InitView(this, tray_container(), &init_params);
    473   }
    474   // Save height of default view for creating detailed views directly.
    475   if (!detailed)
    476     default_bubble_height_ = system_bubble_->bubble_view()->height();
    477 
    478   if (detailed && items.size() > 0)
    479     detailed_item_ = items[0];
    480   else
    481     detailed_item_ = NULL;
    482 
    483   UpdateNotificationBubble();  // State changed, re-create notifications.
    484   if (!notification_bubble_)
    485     UpdateWebNotifications();
    486   GetShelfLayoutManager()->UpdateAutoHideState();
    487 }
    488 
    489 void SystemTray::UpdateNotificationBubble() {
    490   // Only show the notification bubble if we have notifications.
    491   if (notification_items_.empty()) {
    492     DestroyNotificationBubble();
    493     return;
    494   }
    495   // Destroy the existing bubble before constructing a new one.
    496   notification_bubble_.reset();
    497   SystemTrayBubble* notification_bubble;
    498   notification_bubble = new SystemTrayBubble(
    499       this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
    500   views::View* anchor;
    501   TrayBubbleView::AnchorType anchor_type;
    502   // Tray items might want to show notifications while we are creating and
    503   // initializing the |system_bubble_| - but it might not be fully initialized
    504   // when coming here - this would produce a crashed like crbug.com/247416.
    505   // As such we check the existence of the widget here.
    506   if (system_bubble_.get() &&
    507       system_bubble_->bubble_view() &&
    508       system_bubble_->bubble_view()->GetWidget()) {
    509     anchor = system_bubble_->bubble_view();
    510     anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE;
    511   } else {
    512     anchor = tray_container();
    513     anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY;
    514   }
    515   TrayBubbleView::InitParams init_params(anchor_type,
    516                                          GetAnchorAlignment(),
    517                                          kTrayPopupMinWidth,
    518                                          kTrayPopupMaxWidth);
    519   init_params.first_item_has_no_margin =
    520       ash::switches::UseAlternateShelfLayout();
    521   init_params.arrow_color = kBackgroundColor;
    522   init_params.arrow_offset = GetTrayXOffset(notification_items_[0]);
    523   notification_bubble_.reset(
    524       new internal::SystemBubbleWrapper(notification_bubble));
    525   notification_bubble_->InitView(this, anchor, &init_params);
    526 
    527   if (notification_bubble->bubble_view()->child_count() == 0) {
    528     // It is possible that none of the items generated actual notifications.
    529     DestroyNotificationBubble();
    530     return;
    531   }
    532   if (hide_notifications_)
    533     notification_bubble->SetVisible(false);
    534   else
    535     UpdateWebNotifications();
    536 }
    537 
    538 void SystemTray::UpdateWebNotifications() {
    539   TrayBubbleView* bubble_view = NULL;
    540   if (notification_bubble_)
    541     bubble_view = notification_bubble_->bubble_view();
    542   else if (system_bubble_)
    543     bubble_view = system_bubble_->bubble_view();
    544 
    545   int height = 0;
    546   if (bubble_view) {
    547     gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
    548         bubble_view->GetWidget()->GetNativeView()).work_area();
    549     if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP) {
    550       height = std::max(
    551           0, work_area.height() - bubble_view->GetBoundsInScreen().y());
    552     } else {
    553       height = std::max(
    554           0, bubble_view->GetBoundsInScreen().bottom() - work_area.y());
    555     }
    556   }
    557   status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height);
    558 }
    559 
    560 void SystemTray::SetShelfAlignment(ShelfAlignment alignment) {
    561   if (alignment == shelf_alignment())
    562     return;
    563   internal::TrayBackgroundView::SetShelfAlignment(alignment);
    564   UpdateAfterShelfAlignmentChange(alignment);
    565   // Destroy any existing bubble so that it is rebuilt correctly.
    566   system_bubble_.reset();
    567   // Rebuild any notification bubble.
    568   if (notification_bubble_) {
    569     notification_bubble_.reset();
    570     UpdateNotificationBubble();
    571   }
    572 }
    573 
    574 void SystemTray::AnchorUpdated() {
    575   if (notification_bubble_) {
    576     notification_bubble_->bubble_view()->UpdateBubble();
    577     // Ensure that the notification buble is above the launcher/status area.
    578     notification_bubble_->bubble_view()->GetWidget()->StackAtTop();
    579     UpdateBubbleViewArrow(notification_bubble_->bubble_view());
    580   }
    581   if (system_bubble_) {
    582     system_bubble_->bubble_view()->UpdateBubble();
    583     UpdateBubbleViewArrow(system_bubble_->bubble_view());
    584   }
    585 }
    586 
    587 base::string16 SystemTray::GetAccessibleNameForTray() {
    588   return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME);
    589 }
    590 
    591 void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
    592   if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) {
    593     DestroySystemBubble();
    594     UpdateNotificationBubble();  // State changed, re-create notifications.
    595     GetShelfLayoutManager()->UpdateAutoHideState();
    596   } else if (notification_bubble_.get() &&
    597              bubble_view == notification_bubble_->bubble_view()) {
    598     DestroyNotificationBubble();
    599   }
    600 }
    601 
    602 bool SystemTray::ClickedOutsideBubble() {
    603   if (!system_bubble_)
    604     return false;
    605   HideBubbleWithView(system_bubble_->bubble_view());
    606   return true;
    607 }
    608 
    609 void SystemTray::BubbleViewDestroyed() {
    610   if (system_bubble_) {
    611     system_bubble_->bubble()->DestroyItemViews();
    612     system_bubble_->bubble()->BubbleViewDestroyed();
    613   }
    614 }
    615 
    616 void SystemTray::OnMouseEnteredView() {
    617   if (system_bubble_)
    618     system_bubble_->bubble()->StopAutoCloseTimer();
    619 }
    620 
    621 void SystemTray::OnMouseExitedView() {
    622   if (system_bubble_)
    623     system_bubble_->bubble()->RestartAutoCloseTimer();
    624 }
    625 
    626 base::string16 SystemTray::GetAccessibleNameForBubble() {
    627   return GetAccessibleNameForTray();
    628 }
    629 
    630 gfx::Rect SystemTray::GetAnchorRect(
    631     views::Widget* anchor_widget,
    632     TrayBubbleView::AnchorType anchor_type,
    633     TrayBubbleView::AnchorAlignment anchor_alignment) {
    634   return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
    635 }
    636 
    637 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) {
    638   HideBubbleWithView(bubble_view);
    639 }
    640 
    641 bool SystemTray::PerformAction(const ui::Event& event) {
    642   // If we're already showing the default view, hide it; otherwise, show it
    643   // (and hide any popup that's currently shown).
    644   if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) {
    645     system_bubble_->bubble()->Close();
    646   } else {
    647     int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset;
    648     if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) {
    649       const ui::LocatedEvent& located_event =
    650           static_cast<const ui::LocatedEvent&>(event);
    651       if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM ||
    652           shelf_alignment() == SHELF_ALIGNMENT_TOP) {
    653         gfx::Point point(located_event.x(), 0);
    654         ConvertPointToWidget(this, &point);
    655         arrow_offset = point.x();
    656       }
    657     }
    658     ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset);
    659   }
    660   return true;
    661 }
    662 
    663 }  // namespace ash
    664