• 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 <vector>
8 
9 #include "ash/accessibility_delegate.h"
10 #include "ash/root_window_controller.h"
11 #include "ash/shelf/shelf_layout_manager.h"
12 #include "ash/shelf/shelf_widget.h"
13 #include "ash/shell.h"
14 #include "ash/system/status_area_widget.h"
15 #include "ash/system/tray/system_tray_item.h"
16 #include "ash/system/tray/tray_constants.h"
17 #include "ash/test/ash_test_base.h"
18 #include "ash/wm/window_util.h"
19 #include "base/run_loop.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "ui/aura/window.h"
22 #include "ui/base/ui_base_types.h"
23 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
24 #include "ui/events/test/event_generator.h"
25 #include "ui/gfx/geometry/rect.h"
26 #include "ui/views/controls/label.h"
27 #include "ui/views/layout/fill_layout.h"
28 #include "ui/views/view.h"
29 #include "ui/views/widget/widget.h"
30 #include "ui/views/widget/widget_delegate.h"
31 
32 #if defined(OS_WIN)
33 #include "base/win/windows_version.h"
34 #endif
35 
36 namespace ash {
37 namespace test {
38 
39 namespace {
40 
GetSystemTray()41 SystemTray* GetSystemTray() {
42   return Shell::GetPrimaryRootWindowController()->shelf()->
43       status_area_widget()->system_tray();
44 }
45 
46 // Trivial item implementation that tracks its views for testing.
47 class TestItem : public SystemTrayItem {
48  public:
TestItem()49   TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {}
50 
CreateTrayView(user::LoginStatus status)51   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
52     tray_view_ = new views::View;
53     // Add a label so it has non-zero width.
54     tray_view_->SetLayoutManager(new views::FillLayout);
55     tray_view_->AddChildView(new views::Label(base::UTF8ToUTF16("Tray")));
56     return tray_view_;
57   }
58 
CreateDefaultView(user::LoginStatus status)59   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
60     default_view_ = new views::View;
61     default_view_->SetLayoutManager(new views::FillLayout);
62     default_view_->AddChildView(new views::Label(base::UTF8ToUTF16("Default")));
63     return default_view_;
64   }
65 
CreateDetailedView(user::LoginStatus status)66   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
67     detailed_view_ = new views::View;
68     detailed_view_->SetLayoutManager(new views::FillLayout);
69     detailed_view_->AddChildView(
70         new views::Label(base::UTF8ToUTF16("Detailed")));
71     return detailed_view_;
72   }
73 
CreateNotificationView(user::LoginStatus status)74   virtual views::View* CreateNotificationView(
75       user::LoginStatus status) OVERRIDE {
76     notification_view_ = new views::View;
77     return notification_view_;
78   }
79 
DestroyTrayView()80   virtual void DestroyTrayView() OVERRIDE {
81     tray_view_ = NULL;
82   }
83 
DestroyDefaultView()84   virtual void DestroyDefaultView() OVERRIDE {
85     default_view_ = NULL;
86   }
87 
DestroyDetailedView()88   virtual void DestroyDetailedView() OVERRIDE {
89     detailed_view_ = NULL;
90   }
91 
DestroyNotificationView()92   virtual void DestroyNotificationView() OVERRIDE {
93     notification_view_ = NULL;
94   }
95 
UpdateAfterLoginStatusChange(user::LoginStatus status)96   virtual void UpdateAfterLoginStatusChange(
97       user::LoginStatus status) OVERRIDE {
98   }
99 
tray_view() const100   views::View* tray_view() const { return tray_view_; }
default_view() const101   views::View* default_view() const { return default_view_; }
detailed_view() const102   views::View* detailed_view() const { return detailed_view_; }
notification_view() const103   views::View* notification_view() const { return notification_view_; }
104 
105  private:
106   views::View* tray_view_;
107   views::View* default_view_;
108   views::View* detailed_view_;
109   views::View* notification_view_;
110 };
111 
112 // Trivial item implementation that returns NULL from tray/default/detailed
113 // view creation methods.
114 class TestNoViewItem : public SystemTrayItem {
115  public:
TestNoViewItem()116   TestNoViewItem() : SystemTrayItem(GetSystemTray()) {}
117 
CreateTrayView(user::LoginStatus status)118   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
119     return NULL;
120   }
121 
CreateDefaultView(user::LoginStatus status)122   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
123     return NULL;
124   }
125 
CreateDetailedView(user::LoginStatus status)126   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
127     return NULL;
128   }
129 
CreateNotificationView(user::LoginStatus status)130   virtual views::View* CreateNotificationView(
131       user::LoginStatus status) OVERRIDE {
132     return NULL;
133   }
134 
DestroyTrayView()135   virtual void DestroyTrayView() OVERRIDE {}
DestroyDefaultView()136   virtual void DestroyDefaultView() OVERRIDE {}
DestroyDetailedView()137   virtual void DestroyDetailedView() OVERRIDE {}
DestroyNotificationView()138   virtual void DestroyNotificationView() OVERRIDE {}
UpdateAfterLoginStatusChange(user::LoginStatus status)139   virtual void UpdateAfterLoginStatusChange(
140       user::LoginStatus status) OVERRIDE {
141   }
142 };
143 
144 class ModalWidgetDelegate : public views::WidgetDelegateView {
145  public:
ModalWidgetDelegate()146   ModalWidgetDelegate() {}
~ModalWidgetDelegate()147   virtual ~ModalWidgetDelegate() {}
148 
GetContentsView()149   virtual views::View* GetContentsView() OVERRIDE { return this; }
GetModalType() const150   virtual ui::ModalType GetModalType() const OVERRIDE {
151     return ui::MODAL_TYPE_SYSTEM;
152   }
153 
154  private:
155   DISALLOW_COPY_AND_ASSIGN(ModalWidgetDelegate);
156 };
157 
158 }  // namespace
159 
160 typedef AshTestBase SystemTrayTest;
161 
TEST_F(SystemTrayTest,SystemTrayDefaultView)162 TEST_F(SystemTrayTest, SystemTrayDefaultView) {
163   SystemTray* tray = GetSystemTray();
164   ASSERT_TRUE(tray->GetWidget());
165 
166   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
167 
168   // Ensure that closing the bubble destroys it.
169   ASSERT_TRUE(tray->CloseSystemBubble());
170   RunAllPendingInMessageLoop();
171   ASSERT_FALSE(tray->CloseSystemBubble());
172 }
173 
174 // Opening and closing the bubble should change the coloring of the tray.
TEST_F(SystemTrayTest,SystemTrayColoring)175 TEST_F(SystemTrayTest, SystemTrayColoring) {
176   SystemTray* tray = GetSystemTray();
177   ASSERT_TRUE(tray->GetWidget());
178   // At the beginning the tray coloring is not active.
179   ASSERT_FALSE(tray->draw_background_as_active());
180 
181   // Showing the system bubble should show the background as active.
182   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
183   ASSERT_TRUE(tray->draw_background_as_active());
184 
185   // Closing the system menu should change the coloring back to normal.
186   ASSERT_TRUE(tray->CloseSystemBubble());
187   RunAllPendingInMessageLoop();
188   ASSERT_FALSE(tray->draw_background_as_active());
189 }
190 
191 // Closing the system bubble through an alignment change should change the
192 // system tray coloring back to normal.
TEST_F(SystemTrayTest,SystemTrayColoringAfterAlignmentChange)193 TEST_F(SystemTrayTest, SystemTrayColoringAfterAlignmentChange) {
194   SystemTray* tray = GetSystemTray();
195   ASSERT_TRUE(tray->GetWidget());
196   ShelfLayoutManager* manager =
197       Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
198   manager->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
199   // At the beginning the tray coloring is not active.
200   ASSERT_FALSE(tray->draw_background_as_active());
201 
202   // Showing the system bubble should show the background as active.
203   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
204   ASSERT_TRUE(tray->draw_background_as_active());
205 
206   // Changing the alignment should close the system bubble and change the
207   // background color.
208   manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
209   ASSERT_FALSE(tray->draw_background_as_active());
210   RunAllPendingInMessageLoop();
211   // The bubble should already be closed by now.
212   ASSERT_FALSE(tray->CloseSystemBubble());
213 }
214 
TEST_F(SystemTrayTest,SystemTrayTestItems)215 TEST_F(SystemTrayTest, SystemTrayTestItems) {
216   SystemTray* tray = GetSystemTray();
217   ASSERT_TRUE(tray->GetWidget());
218 
219   TestItem* test_item = new TestItem;
220   TestItem* detailed_item = new TestItem;
221   tray->AddTrayItem(test_item);
222   tray->AddTrayItem(detailed_item);
223 
224   // Check items have been added
225   const std::vector<SystemTrayItem*>& items = tray->GetTrayItems();
226   ASSERT_TRUE(
227       std::find(items.begin(), items.end(), test_item) != items.end());
228   ASSERT_TRUE(
229       std::find(items.begin(), items.end(), detailed_item) != items.end());
230 
231   // Ensure the tray views are created.
232   ASSERT_TRUE(test_item->tray_view() != NULL);
233   ASSERT_TRUE(detailed_item->tray_view() != NULL);
234 
235   // Ensure a default views are created.
236   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
237   ASSERT_TRUE(test_item->default_view() != NULL);
238   ASSERT_TRUE(detailed_item->default_view() != NULL);
239 
240   // Show the detailed view, ensure it's created and the default view destroyed.
241   tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
242   RunAllPendingInMessageLoop();
243   ASSERT_TRUE(test_item->default_view() == NULL);
244   ASSERT_TRUE(detailed_item->detailed_view() != NULL);
245 
246   // Show the default view, ensure it's created and the detailed view destroyed.
247   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
248   RunAllPendingInMessageLoop();
249   ASSERT_TRUE(test_item->default_view() != NULL);
250   ASSERT_TRUE(detailed_item->detailed_view() == NULL);
251 }
252 
TEST_F(SystemTrayTest,SystemTrayNoViewItems)253 TEST_F(SystemTrayTest, SystemTrayNoViewItems) {
254   SystemTray* tray = GetSystemTray();
255   ASSERT_TRUE(tray->GetWidget());
256 
257   // Verify that no crashes occur on items lacking some views.
258   TestNoViewItem* no_view_item = new TestNoViewItem;
259   tray->AddTrayItem(no_view_item);
260   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
261   tray->ShowDetailedView(no_view_item, 0, false, BUBBLE_USE_EXISTING);
262   RunAllPendingInMessageLoop();
263 }
264 
TEST_F(SystemTrayTest,TrayWidgetAutoResizes)265 TEST_F(SystemTrayTest, TrayWidgetAutoResizes) {
266   SystemTray* tray = GetSystemTray();
267   ASSERT_TRUE(tray->GetWidget());
268 
269   // Add an initial tray item so that the tray gets laid out correctly.
270   TestItem* initial_item = new TestItem;
271   tray->AddTrayItem(initial_item);
272 
273   gfx::Size initial_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
274 
275   TestItem* new_item = new TestItem;
276   tray->AddTrayItem(new_item);
277 
278   gfx::Size new_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
279 
280   // Adding the new item should change the size of the tray.
281   EXPECT_NE(initial_size.ToString(), new_size.ToString());
282 
283   // Hiding the tray view of the new item should also change the size of the
284   // tray.
285   new_item->tray_view()->SetVisible(false);
286   EXPECT_EQ(initial_size.ToString(),
287             tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
288 
289   new_item->tray_view()->SetVisible(true);
290   EXPECT_EQ(new_size.ToString(),
291             tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
292 }
293 
TEST_F(SystemTrayTest,SystemTrayNotifications)294 TEST_F(SystemTrayTest, SystemTrayNotifications) {
295   SystemTray* tray = GetSystemTray();
296   ASSERT_TRUE(tray->GetWidget());
297 
298   TestItem* test_item = new TestItem;
299   TestItem* detailed_item = new TestItem;
300   tray->AddTrayItem(test_item);
301   tray->AddTrayItem(detailed_item);
302 
303   // Ensure the tray views are created.
304   ASSERT_TRUE(test_item->tray_view() != NULL);
305   ASSERT_TRUE(detailed_item->tray_view() != NULL);
306 
307   // Ensure a notification view is created.
308   tray->ShowNotificationView(test_item);
309   ASSERT_TRUE(test_item->notification_view() != NULL);
310 
311   // Show the default view, notification view should remain.
312   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
313   RunAllPendingInMessageLoop();
314   ASSERT_TRUE(test_item->notification_view() != NULL);
315 
316   // Show the detailed view, ensure the notification view remains.
317   tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
318   RunAllPendingInMessageLoop();
319   ASSERT_TRUE(detailed_item->detailed_view() != NULL);
320   ASSERT_TRUE(test_item->notification_view() != NULL);
321 
322   // Hide the detailed view, ensure the notification view still exists.
323   ASSERT_TRUE(tray->CloseSystemBubble());
324   RunAllPendingInMessageLoop();
325   ASSERT_TRUE(detailed_item->detailed_view() == NULL);
326   ASSERT_TRUE(test_item->notification_view() != NULL);
327 }
328 
TEST_F(SystemTrayTest,BubbleCreationTypesTest)329 TEST_F(SystemTrayTest, BubbleCreationTypesTest) {
330   SystemTray* tray = GetSystemTray();
331   ASSERT_TRUE(tray->GetWidget());
332 
333   TestItem* test_item = new TestItem;
334   tray->AddTrayItem(test_item);
335 
336   // Ensure the tray views are created.
337   ASSERT_TRUE(test_item->tray_view() != NULL);
338 
339   // Show the default view, ensure the notification view is destroyed.
340   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
341   RunAllPendingInMessageLoop();
342 
343   views::Widget* widget = test_item->default_view()->GetWidget();
344   gfx::Rect bubble_bounds = widget->GetWindowBoundsInScreen();
345 
346   tray->ShowDetailedView(test_item, 0, true, BUBBLE_USE_EXISTING);
347   RunAllPendingInMessageLoop();
348 
349   EXPECT_FALSE(test_item->default_view());
350 
351   EXPECT_EQ(bubble_bounds.ToString(), test_item->detailed_view()->GetWidget()->
352       GetWindowBoundsInScreen().ToString());
353   EXPECT_EQ(widget, test_item->detailed_view()->GetWidget());
354 
355   tray->ShowDefaultView(BUBBLE_USE_EXISTING);
356   RunAllPendingInMessageLoop();
357 
358   EXPECT_EQ(bubble_bounds.ToString(), test_item->default_view()->GetWidget()->
359       GetWindowBoundsInScreen().ToString());
360   EXPECT_EQ(widget, test_item->default_view()->GetWidget());
361 }
362 
363 // Tests that the tray is laid out properly and is fully contained within
364 // the shelf.
TEST_F(SystemTrayTest,TrayBoundsInWidget)365 TEST_F(SystemTrayTest, TrayBoundsInWidget) {
366   ShelfLayoutManager* manager =
367       Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
368   StatusAreaWidget* widget =
369       Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
370   SystemTray* tray = widget->system_tray();
371 
372   // Test in bottom alignment.
373   manager->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
374   gfx::Rect window_bounds = widget->GetWindowBoundsInScreen();
375   gfx::Rect tray_bounds = tray->GetBoundsInScreen();
376   EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
377   EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
378   EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
379   EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
380 
381   // Test in the left alignment.
382   manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
383   window_bounds = widget->GetWindowBoundsInScreen();
384   tray_bounds = tray->GetBoundsInScreen();
385   EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
386   EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
387   EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
388   EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
389 
390   // Test in the right alignment.
391   manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
392   window_bounds = widget->GetWindowBoundsInScreen();
393   tray_bounds = tray->GetBoundsInScreen();
394   EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
395   EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
396   EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
397   EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
398 }
399 
TEST_F(SystemTrayTest,PersistentBubble)400 TEST_F(SystemTrayTest, PersistentBubble) {
401   SystemTray* tray = GetSystemTray();
402   ASSERT_TRUE(tray->GetWidget());
403 
404   TestItem* test_item = new TestItem;
405   tray->AddTrayItem(test_item);
406 
407   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
408 
409   // Tests for usual default view.
410   // Activating window.
411   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
412   ASSERT_TRUE(tray->HasSystemBubble());
413   wm::ActivateWindow(window.get());
414   base::RunLoop().RunUntilIdle();
415   ASSERT_FALSE(tray->HasSystemBubble());
416 
417   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
418   ASSERT_TRUE(tray->HasSystemBubble());
419   {
420     ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
421                                        gfx::Point(5, 5));
422     generator.ClickLeftButton();
423     ASSERT_FALSE(tray->HasSystemBubble());
424   }
425 
426   // Same tests for persistent default view.
427   tray->ShowPersistentDefaultView();
428   ASSERT_TRUE(tray->HasSystemBubble());
429   wm::ActivateWindow(window.get());
430   base::RunLoop().RunUntilIdle();
431   ASSERT_TRUE(tray->HasSystemBubble());
432 
433   {
434     ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
435                                        gfx::Point(5, 5));
436     generator.ClickLeftButton();
437     ASSERT_TRUE(tray->HasSystemBubble());
438   }
439 }
440 
441 #if defined(OS_CHROMEOS)
442 // Accessibility/Settings tray items are available only on cros.
443 #define MAYBE_WithSystemModal WithSystemModal
444 #else
445 #define MAYBE_WithSystemModal DISABLED_WithSystemModal
446 #endif
TEST_F(SystemTrayTest,MAYBE_WithSystemModal)447 TEST_F(SystemTrayTest, MAYBE_WithSystemModal) {
448   // Check if the accessibility item is created even with system modal
449   // dialog.
450   Shell::GetInstance()->accessibility_delegate()->SetVirtualKeyboardEnabled(
451       true);
452   views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds(
453       new ModalWidgetDelegate(),
454       Shell::GetPrimaryRootWindow(),
455       gfx::Rect(0, 0, 100, 100));
456   widget->Show();
457 
458   SystemTray* tray = GetSystemTray();
459   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
460 
461   ASSERT_TRUE(tray->HasSystemBubble());
462   const views::View* accessibility =
463       tray->GetSystemBubble()->bubble_view()->GetViewByID(
464           test::kAccessibilityTrayItemViewId);
465   ASSERT_TRUE(accessibility);
466   EXPECT_TRUE(accessibility->visible());
467   EXPECT_FALSE(tray->GetSystemBubble()->bubble_view()->GetViewByID(
468       test::kSettingsTrayItemViewId));
469 
470   widget->Close();
471 
472   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
473   // System modal is gone. The bubble should now contains settings
474   // as well.
475   accessibility = tray->GetSystemBubble()->bubble_view()->GetViewByID(
476       test::kAccessibilityTrayItemViewId);
477   ASSERT_TRUE(accessibility);
478   EXPECT_TRUE(accessibility->visible());
479 
480   const views::View* settings =
481       tray->GetSystemBubble()->bubble_view()->GetViewByID(
482           test::kSettingsTrayItemViewId);
483   ASSERT_TRUE(settings);
484   EXPECT_TRUE(settings->visible());
485 }
486 
487 // Tests that if SetVisible(true) is called while animating to hidden that the
488 // tray becomes visible, and stops animating to hidden.
TEST_F(SystemTrayTest,SetVisibleDuringHideAnimation)489 TEST_F(SystemTrayTest, SetVisibleDuringHideAnimation) {
490   SystemTray* tray = GetSystemTray();
491   ASSERT_TRUE(tray->visible());
492 
493   scoped_ptr<ui::ScopedAnimationDurationScaleMode> animation_duration;
494   animation_duration.reset(
495       new ui::ScopedAnimationDurationScaleMode(
496           ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
497   tray->SetVisible(false);
498   EXPECT_TRUE(tray->visible());
499   EXPECT_EQ(0.0f, tray->layer()->GetTargetOpacity());
500 
501   tray->SetVisible(true);
502   animation_duration.reset();
503   tray->layer()->GetAnimator()->StopAnimating();
504   EXPECT_TRUE(tray->visible());
505   EXPECT_EQ(1.0f, tray->layer()->GetTargetOpacity());
506 }
507 
508 }  // namespace test
509 }  // namespace ash
510