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