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.
5 #include "ash/system/tray/system_tray.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shelf/shelf_layout_manager.h"
11 #include "ash/shelf/shelf_widget.h"
12 #include "ash/shell.h"
13 #include "ash/system/status_area_widget.h"
14 #include "ash/system/tray/system_tray_item.h"
15 #include "ash/test/ash_test_base.h"
16 #include "ash/wm/window_util.h"
17 #include "base/run_loop.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "ui/aura/test/event_generator.h"
20 #include "ui/aura/window.h"
21 #include "ui/views/controls/label.h"
22 #include "ui/views/layout/fill_layout.h"
23 #include "ui/views/view.h"
24 #include "ui/views/widget/widget.h"
27 #include "base/win/windows_version.h"
35 SystemTray* GetSystemTray() {
36 return Shell::GetPrimaryRootWindowController()->shelf()->
37 status_area_widget()->system_tray();
40 // Trivial item implementation that tracks its views for testing.
41 class TestItem : public SystemTrayItem {
43 TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {}
45 virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
46 tray_view_ = new views::View;
47 // Add a label so it has non-zero width.
48 tray_view_->SetLayoutManager(new views::FillLayout);
49 tray_view_->AddChildView(new views::Label(UTF8ToUTF16("Tray")));
53 virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
54 default_view_ = new views::View;
55 default_view_->SetLayoutManager(new views::FillLayout);
56 default_view_->AddChildView(new views::Label(UTF8ToUTF16("Default")));
60 virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
61 detailed_view_ = new views::View;
62 detailed_view_->SetLayoutManager(new views::FillLayout);
63 detailed_view_->AddChildView(new views::Label(UTF8ToUTF16("Detailed")));
64 return detailed_view_;
67 virtual views::View* CreateNotificationView(
68 user::LoginStatus status) OVERRIDE {
69 notification_view_ = new views::View;
70 return notification_view_;
73 virtual void DestroyTrayView() OVERRIDE {
77 virtual void DestroyDefaultView() OVERRIDE {
81 virtual void DestroyDetailedView() OVERRIDE {
82 detailed_view_ = NULL;
85 virtual void DestroyNotificationView() OVERRIDE {
86 notification_view_ = NULL;
89 virtual void UpdateAfterLoginStatusChange(
90 user::LoginStatus status) OVERRIDE {
93 views::View* tray_view() const { return tray_view_; }
94 views::View* default_view() const { return default_view_; }
95 views::View* detailed_view() const { return detailed_view_; }
96 views::View* notification_view() const { return notification_view_; }
99 views::View* tray_view_;
100 views::View* default_view_;
101 views::View* detailed_view_;
102 views::View* notification_view_;
105 // Trivial item implementation that returns NULL from tray/default/detailed
106 // view creation methods.
107 class TestNoViewItem : public SystemTrayItem {
109 TestNoViewItem() : SystemTrayItem(GetSystemTray()) {}
111 virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
115 virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
119 virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
123 virtual views::View* CreateNotificationView(
124 user::LoginStatus status) OVERRIDE {
128 virtual void DestroyTrayView() OVERRIDE {}
129 virtual void DestroyDefaultView() OVERRIDE {}
130 virtual void DestroyDetailedView() OVERRIDE {}
131 virtual void DestroyNotificationView() OVERRIDE {}
132 virtual void UpdateAfterLoginStatusChange(
133 user::LoginStatus status) OVERRIDE {
139 typedef AshTestBase SystemTrayTest;
141 TEST_F(SystemTrayTest, SystemTrayDefaultView) {
142 SystemTray* tray = GetSystemTray();
143 ASSERT_TRUE(tray->GetWidget());
145 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
147 // Ensure that closing the bubble destroys it.
148 ASSERT_TRUE(tray->CloseSystemBubble());
149 RunAllPendingInMessageLoop();
150 ASSERT_FALSE(tray->CloseSystemBubble());
153 // Opening and closing the bubble should change the coloring of the tray.
154 TEST_F(SystemTrayTest, SystemTrayColoring) {
155 SystemTray* tray = GetSystemTray();
156 ASSERT_TRUE(tray->GetWidget());
157 // At the beginning the tray coloring is not active.
158 ASSERT_FALSE(tray->draw_background_as_active());
160 // Showing the system bubble should show the background as active.
161 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
162 ASSERT_TRUE(tray->draw_background_as_active());
164 // Closing the system menu should change the coloring back to normal.
165 ASSERT_TRUE(tray->CloseSystemBubble());
166 RunAllPendingInMessageLoop();
167 ASSERT_FALSE(tray->draw_background_as_active());
170 // Closing the system bubble through an alignment change should change the
171 // system tray coloring back to normal.
172 TEST_F(SystemTrayTest, SystemTrayColoringAfterAlignmentChange) {
173 SystemTray* tray = GetSystemTray();
174 ASSERT_TRUE(tray->GetWidget());
175 internal::ShelfLayoutManager* manager =
176 Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
177 manager->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
178 // At the beginning the tray coloring is not active.
179 ASSERT_FALSE(tray->draw_background_as_active());
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());
185 // Changing the alignment should close the system bubble and change the
187 manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
188 ASSERT_FALSE(tray->draw_background_as_active());
189 RunAllPendingInMessageLoop();
190 // The bubble should already be closed by now.
191 ASSERT_FALSE(tray->CloseSystemBubble());
194 TEST_F(SystemTrayTest, SystemTrayTestItems) {
195 SystemTray* tray = GetSystemTray();
196 ASSERT_TRUE(tray->GetWidget());
198 TestItem* test_item = new TestItem;
199 TestItem* detailed_item = new TestItem;
200 tray->AddTrayItem(test_item);
201 tray->AddTrayItem(detailed_item);
203 // Check items have been added
204 const std::vector<SystemTrayItem*>& items = tray->GetTrayItems();
206 std::find(items.begin(), items.end(), test_item) != items.end());
208 std::find(items.begin(), items.end(), detailed_item) != items.end());
210 // Ensure the tray views are created.
211 ASSERT_TRUE(test_item->tray_view() != NULL);
212 ASSERT_TRUE(detailed_item->tray_view() != NULL);
214 // Ensure a default views are created.
215 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
216 ASSERT_TRUE(test_item->default_view() != NULL);
217 ASSERT_TRUE(detailed_item->default_view() != NULL);
219 // Show the detailed view, ensure it's created and the default view destroyed.
220 tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
221 RunAllPendingInMessageLoop();
222 ASSERT_TRUE(test_item->default_view() == NULL);
223 ASSERT_TRUE(detailed_item->detailed_view() != NULL);
225 // Show the default view, ensure it's created and the detailed view destroyed.
226 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
227 RunAllPendingInMessageLoop();
228 ASSERT_TRUE(test_item->default_view() != NULL);
229 ASSERT_TRUE(detailed_item->detailed_view() == NULL);
232 TEST_F(SystemTrayTest, SystemTrayNoViewItems) {
233 SystemTray* tray = GetSystemTray();
234 ASSERT_TRUE(tray->GetWidget());
236 // Verify that no crashes occur on items lacking some views.
237 TestNoViewItem* no_view_item = new TestNoViewItem;
238 tray->AddTrayItem(no_view_item);
239 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
240 tray->ShowDetailedView(no_view_item, 0, false, BUBBLE_USE_EXISTING);
241 RunAllPendingInMessageLoop();
244 TEST_F(SystemTrayTest, TrayWidgetAutoResizes) {
245 SystemTray* tray = GetSystemTray();
246 ASSERT_TRUE(tray->GetWidget());
248 // Add an initial tray item so that the tray gets laid out correctly.
249 TestItem* initial_item = new TestItem;
250 tray->AddTrayItem(initial_item);
252 gfx::Size initial_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
254 TestItem* new_item = new TestItem;
255 tray->AddTrayItem(new_item);
257 gfx::Size new_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
259 // Adding the new item should change the size of the tray.
260 EXPECT_NE(initial_size.ToString(), new_size.ToString());
262 // Hiding the tray view of the new item should also change the size of the
264 new_item->tray_view()->SetVisible(false);
265 EXPECT_EQ(initial_size.ToString(),
266 tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
268 new_item->tray_view()->SetVisible(true);
269 EXPECT_EQ(new_size.ToString(),
270 tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
273 TEST_F(SystemTrayTest, SystemTrayNotifications) {
274 SystemTray* tray = GetSystemTray();
275 ASSERT_TRUE(tray->GetWidget());
277 TestItem* test_item = new TestItem;
278 TestItem* detailed_item = new TestItem;
279 tray->AddTrayItem(test_item);
280 tray->AddTrayItem(detailed_item);
282 // Ensure the tray views are created.
283 ASSERT_TRUE(test_item->tray_view() != NULL);
284 ASSERT_TRUE(detailed_item->tray_view() != NULL);
286 // Ensure a notification view is created.
287 tray->ShowNotificationView(test_item);
288 ASSERT_TRUE(test_item->notification_view() != NULL);
290 // Show the default view, notification view should remain.
291 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
292 RunAllPendingInMessageLoop();
293 ASSERT_TRUE(test_item->notification_view() != NULL);
295 // Show the detailed view, ensure the notificaiton view remains.
296 tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
297 RunAllPendingInMessageLoop();
298 ASSERT_TRUE(detailed_item->detailed_view() != NULL);
299 ASSERT_TRUE(test_item->notification_view() != NULL);
301 // Hide the detailed view, ensure the notification view still exists.
302 ASSERT_TRUE(tray->CloseSystemBubble());
303 RunAllPendingInMessageLoop();
304 ASSERT_TRUE(detailed_item->detailed_view() == NULL);
305 ASSERT_TRUE(test_item->notification_view() != NULL);
308 TEST_F(SystemTrayTest, BubbleCreationTypesTest) {
309 SystemTray* tray = GetSystemTray();
310 ASSERT_TRUE(tray->GetWidget());
312 TestItem* test_item = new TestItem;
313 tray->AddTrayItem(test_item);
315 // Ensure the tray views are created.
316 ASSERT_TRUE(test_item->tray_view() != NULL);
318 // Show the default view, ensure the notification view is destroyed.
319 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
320 RunAllPendingInMessageLoop();
322 views::Widget* widget = test_item->default_view()->GetWidget();
323 gfx::Rect bubble_bounds = widget->GetWindowBoundsInScreen();
325 tray->ShowDetailedView(test_item, 0, true, BUBBLE_USE_EXISTING);
326 RunAllPendingInMessageLoop();
328 EXPECT_FALSE(test_item->default_view());
330 EXPECT_EQ(bubble_bounds.ToString(), test_item->detailed_view()->GetWidget()->
331 GetWindowBoundsInScreen().ToString());
332 EXPECT_EQ(widget, test_item->detailed_view()->GetWidget());
334 tray->ShowDefaultView(BUBBLE_USE_EXISTING);
335 RunAllPendingInMessageLoop();
337 EXPECT_EQ(bubble_bounds.ToString(), test_item->default_view()->GetWidget()->
338 GetWindowBoundsInScreen().ToString());
339 EXPECT_EQ(widget, test_item->default_view()->GetWidget());
342 // Tests that the tray is laid out properly and is fully contained within
344 TEST_F(SystemTrayTest, TrayBoundsInWidget) {
345 internal::ShelfLayoutManager* manager =
346 Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
347 internal::StatusAreaWidget* widget =
348 Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
349 SystemTray* tray = widget->system_tray();
351 // Test in bottom alignment.
352 manager->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
353 gfx::Rect window_bounds = widget->GetWindowBoundsInScreen();
354 gfx::Rect tray_bounds = tray->GetBoundsInScreen();
355 EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
356 EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
357 EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
358 EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
360 // Test in the left alignment.
361 manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
362 window_bounds = widget->GetWindowBoundsInScreen();
363 tray_bounds = tray->GetBoundsInScreen();
364 EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
365 EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
366 EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
367 EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
369 // Test in the right alignment.
370 manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
371 window_bounds = widget->GetWindowBoundsInScreen();
372 tray_bounds = tray->GetBoundsInScreen();
373 EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
374 EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
375 EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
376 EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
379 TEST_F(SystemTrayTest, PersistentBubble) {
380 SystemTray* tray = GetSystemTray();
381 ASSERT_TRUE(tray->GetWidget());
383 TestItem* test_item = new TestItem;
384 tray->AddTrayItem(test_item);
386 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
388 // Tests for usual default view.
389 // Activating window.
390 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
391 ASSERT_TRUE(tray->HasSystemBubble());
392 wm::ActivateWindow(window.get());
393 base::RunLoop().RunUntilIdle();
394 ASSERT_FALSE(tray->HasSystemBubble());
396 tray->ShowDefaultView(BUBBLE_CREATE_NEW);
397 ASSERT_TRUE(tray->HasSystemBubble());
399 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
401 generator.ClickLeftButton();
402 ASSERT_FALSE(tray->HasSystemBubble());
405 // Same tests for persistent default view.
406 tray->ShowPersistentDefaultView();
407 ASSERT_TRUE(tray->HasSystemBubble());
408 wm::ActivateWindow(window.get());
409 base::RunLoop().RunUntilIdle();
410 ASSERT_TRUE(tray->HasSystemBubble());
413 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
415 generator.ClickLeftButton();
416 ASSERT_TRUE(tray->HasSystemBubble());