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