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