• 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/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|.
SystemBubbleWrapper(internal::SystemTrayBubble * 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_|.
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(
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:
bubble() const112   SystemTrayBubble* bubble() const { return bubble_.get(); }
bubble_type() const113   SystemTrayBubble::BubbleType bubble_type() const {
114     return bubble_->bubble_type();
115   }
bubble_view() const116   TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
is_persistent() const117   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 
SystemTray(internal::StatusAreaWidget * status_area_widget)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 
~SystemTray()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 
InitializeTrayItems(SystemTrayDelegate * delegate)155 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) {
156   internal::TrayBackgroundView::Initialize();
157   CreateItems(delegate);
158 }
159 
CreateItems(SystemTrayDelegate * delegate)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 
AddTrayItem(SystemTrayItem * item)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 
RemoveTrayItem(SystemTrayItem * item)242 void SystemTray::RemoveTrayItem(SystemTrayItem* item) {
243   NOTIMPLEMENTED();
244 }
245 
GetTrayItems() const246 const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const {
247   return items_.get();
248 }
249 
GetTrayUserItems() const250 const std::vector<internal::TrayUser*>& SystemTray::GetTrayUserItems() const {
251   return user_items_;
252 }
253 
ShowDefaultView(BubbleCreationType creation_type)254 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
255   ShowDefaultViewWithOffset(
256       creation_type,
257       TrayBubbleView::InitParams::kArrowDefaultOffset,
258       false);
259 }
260 
ShowPersistentDefaultView()261 void SystemTray::ShowPersistentDefaultView() {
262   ShowItems(items_.get(),
263             false,
264             false,
265             BUBBLE_CREATE_NEW,
266             TrayBubbleView::InitParams::kArrowDefaultOffset,
267             true);
268 }
269 
ShowDetailedView(SystemTrayItem * item,int close_delay,bool activate,BubbleCreationType creation_type)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 
SetDetailedViewCloseDelay(int close_delay)281 void SystemTray::SetDetailedViewCloseDelay(int close_delay) {
282   if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED))
283     system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
284 }
285 
HideDetailedView(SystemTrayItem * item)286 void SystemTray::HideDetailedView(SystemTrayItem* item) {
287   if (item != detailed_item_)
288     return;
289   DestroySystemBubble();
290   UpdateNotificationBubble();
291 }
292 
ShowNotificationView(SystemTrayItem * item)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 
HideNotificationView(SystemTrayItem * item)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 
UpdateAfterLoginStatusChange(user::LoginStatus login_status)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 
UpdateAfterShelfAlignmentChange(ShelfAlignment alignment)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 
SetHideNotifications(bool hide_notifications)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 
ShouldShowLauncher() const345 bool SystemTray::ShouldShowLauncher() const {
346   return system_bubble_.get() && system_bubble_->bubble()->ShouldShowLauncher();
347 }
348 
HasSystemBubble() const349 bool SystemTray::HasSystemBubble() const {
350   return system_bubble_.get() != NULL;
351 }
352 
HasNotificationBubble() const353 bool SystemTray::HasNotificationBubble() const {
354   return notification_bubble_.get() != NULL;
355 }
356 
GetSystemBubble()357 internal::SystemTrayBubble* SystemTray::GetSystemBubble() {
358   if (!system_bubble_)
359     return NULL;
360   return system_bubble_->bubble();
361 }
362 
IsAnyBubbleVisible() const363 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 
IsMouseInNotificationBubble() const370 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 
CloseSystemBubble() const377 bool SystemTray::CloseSystemBubble() const {
378   if (!system_bubble_)
379     return false;
380   system_bubble_->bubble()->Close();
381   return true;
382 }
383 
GetHelpButtonView() const384 views::View* SystemTray::GetHelpButtonView() const {
385   return tray_date_->GetHelpButtonView();
386 }
387 
CloseNotificationBubbleForTest() const388 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 
HasSystemBubbleType(SystemTrayBubble::BubbleType type)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 
DestroySystemBubble()402 void SystemTray::DestroySystemBubble() {
403   CloseSystemBubbleAndDeactivateSystemTray();
404   detailed_item_ = NULL;
405   UpdateWebNotifications();
406 }
407 
DestroyNotificationBubble()408 void SystemTray::DestroyNotificationBubble() {
409   if (notification_bubble_) {
410     notification_bubble_.reset();
411     UpdateWebNotifications();
412   }
413 }
414 
GetTrayXOffset(SystemTrayItem * item) const415 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 
ShowDefaultViewWithOffset(BubbleCreationType creation_type,int arrow_offset,bool persistent)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 
ShowItems(const std::vector<SystemTrayItem * > & items,bool detailed,bool can_activate,BubbleCreationType creation_type,int arrow_offset,bool persistent)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 
UpdateNotificationBubble()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 
UpdateWebNotifications()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 
SetShelfAlignment(ShelfAlignment alignment)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 
AnchorUpdated()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 
GetAccessibleNameForTray()631 base::string16 SystemTray::GetAccessibleNameForTray() {
632   return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME);
633 }
634 
BubbleResized(const TrayBubbleView * bubble_view)635 void SystemTray::BubbleResized(const TrayBubbleView* bubble_view) {
636   UpdateWebNotifications();
637 }
638 
HideBubbleWithView(const TrayBubbleView * bubble_view)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 
ClickedOutsideBubble()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 
BubbleViewDestroyed()657 void SystemTray::BubbleViewDestroyed() {
658   if (system_bubble_) {
659     system_bubble_->bubble()->DestroyItemViews();
660     system_bubble_->bubble()->BubbleViewDestroyed();
661   }
662 }
663 
OnMouseEnteredView()664 void SystemTray::OnMouseEnteredView() {
665   if (system_bubble_)
666     system_bubble_->bubble()->StopAutoCloseTimer();
667 }
668 
OnMouseExitedView()669 void SystemTray::OnMouseExitedView() {
670   if (system_bubble_)
671     system_bubble_->bubble()->RestartAutoCloseTimer();
672 }
673 
GetAccessibleNameForBubble()674 base::string16 SystemTray::GetAccessibleNameForBubble() {
675   return GetAccessibleNameForTray();
676 }
677 
GetAnchorRect(views::Widget * anchor_widget,TrayBubbleView::AnchorType anchor_type,TrayBubbleView::AnchorAlignment anchor_alignment)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 
HideBubble(const TrayBubbleView * bubble_view)685 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) {
686   HideBubbleWithView(bubble_view);
687 }
688 
GetTrayItemViewForTest(SystemTrayItem * item)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 
AddTrayUserItemForTest(internal::TrayUser * tray_user)695 void SystemTray::AddTrayUserItemForTest(internal::TrayUser* tray_user) {
696   AddTrayItem(tray_user);
697   user_items_.push_back(tray_user);
698 }
699 
PerformAction(const ui::Event & event)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 
CloseSystemBubbleAndDeactivateSystemTray()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