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