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_widget.h"
11 #include "ash/shell.h"
12 #include "ash/system/status_area_widget.h"
13 #include "ash/system/tray/system_tray_item.h"
14 #include "ash/test/ash_test_base.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "ui/views/controls/label.h"
17 #include "ui/views/layout/fill_layout.h"
18 #include "ui/views/view.h"
19 #include "ui/views/widget/widget.h"
22 #include "base/win/windows_version.h"
30 const int kStatusTrayOffsetFromScreenEdgeForTest
= 4;
32 SystemTray
* GetSystemTray() {
33 return Shell::GetPrimaryRootWindowController()->shelf()->
34 status_area_widget()->system_tray();
37 // Trivial item implementation that tracks its views for testing.
38 class TestItem
: public SystemTrayItem
{
40 TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL
) {}
42 virtual views::View
* CreateTrayView(user::LoginStatus status
) OVERRIDE
{
43 tray_view_
= new views::View
;
44 // Add a label so it has non-zero width.
45 tray_view_
->SetLayoutManager(new views::FillLayout
);
46 tray_view_
->AddChildView(new views::Label(UTF8ToUTF16("Tray")));
50 virtual views::View
* CreateDefaultView(user::LoginStatus status
) OVERRIDE
{
51 default_view_
= new views::View
;
52 default_view_
->SetLayoutManager(new views::FillLayout
);
53 default_view_
->AddChildView(new views::Label(UTF8ToUTF16("Default")));
57 virtual views::View
* CreateDetailedView(user::LoginStatus status
) OVERRIDE
{
58 detailed_view_
= new views::View
;
59 detailed_view_
->SetLayoutManager(new views::FillLayout
);
60 detailed_view_
->AddChildView(new views::Label(UTF8ToUTF16("Detailed")));
61 return detailed_view_
;
64 virtual views::View
* CreateNotificationView(
65 user::LoginStatus status
) OVERRIDE
{
66 notification_view_
= new views::View
;
67 return notification_view_
;
70 virtual void DestroyTrayView() OVERRIDE
{
74 virtual void DestroyDefaultView() OVERRIDE
{
78 virtual void DestroyDetailedView() OVERRIDE
{
79 detailed_view_
= NULL
;
82 virtual void DestroyNotificationView() OVERRIDE
{
83 notification_view_
= NULL
;
86 virtual void UpdateAfterLoginStatusChange(
87 user::LoginStatus status
) OVERRIDE
{
90 views::View
* tray_view() const { return tray_view_
; }
91 views::View
* default_view() const { return default_view_
; }
92 views::View
* detailed_view() const { return detailed_view_
; }
93 views::View
* notification_view() const { return notification_view_
; }
96 views::View
* tray_view_
;
97 views::View
* default_view_
;
98 views::View
* detailed_view_
;
99 views::View
* notification_view_
;
102 // Trivial item implementation that returns NULL from tray/default/detailed
103 // view creation methods.
104 class TestNoViewItem
: public SystemTrayItem
{
106 TestNoViewItem() : SystemTrayItem(GetSystemTray()) {}
108 virtual views::View
* CreateTrayView(user::LoginStatus status
) OVERRIDE
{
112 virtual views::View
* CreateDefaultView(user::LoginStatus status
) OVERRIDE
{
116 virtual views::View
* CreateDetailedView(user::LoginStatus status
) OVERRIDE
{
120 virtual views::View
* CreateNotificationView(
121 user::LoginStatus status
) OVERRIDE
{
125 virtual void DestroyTrayView() OVERRIDE
{}
126 virtual void DestroyDefaultView() OVERRIDE
{}
127 virtual void DestroyDetailedView() OVERRIDE
{}
128 virtual void DestroyNotificationView() OVERRIDE
{}
129 virtual void UpdateAfterLoginStatusChange(
130 user::LoginStatus status
) OVERRIDE
{
136 typedef AshTestBase SystemTrayTest
;
138 TEST_F(SystemTrayTest
, SystemTrayDefaultView
) {
139 SystemTray
* tray
= GetSystemTray();
140 ASSERT_TRUE(tray
->GetWidget());
142 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
144 // Ensure that closing the bubble destroys it.
145 ASSERT_TRUE(tray
->CloseSystemBubble());
146 RunAllPendingInMessageLoop();
147 ASSERT_FALSE(tray
->CloseSystemBubble());
150 TEST_F(SystemTrayTest
, SystemTrayTestItems
) {
151 SystemTray
* tray
= GetSystemTray();
152 ASSERT_TRUE(tray
->GetWidget());
154 TestItem
* test_item
= new TestItem
;
155 TestItem
* detailed_item
= new TestItem
;
156 tray
->AddTrayItem(test_item
);
157 tray
->AddTrayItem(detailed_item
);
159 // Check items have been added
160 const std::vector
<SystemTrayItem
*>& items
= tray
->GetTrayItems();
162 std::find(items
.begin(), items
.end(), test_item
) != items
.end());
164 std::find(items
.begin(), items
.end(), detailed_item
) != items
.end());
166 // Ensure the tray views are created.
167 ASSERT_TRUE(test_item
->tray_view() != NULL
);
168 ASSERT_TRUE(detailed_item
->tray_view() != NULL
);
170 // Ensure a default views are created.
171 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
172 ASSERT_TRUE(test_item
->default_view() != NULL
);
173 ASSERT_TRUE(detailed_item
->default_view() != NULL
);
175 // Show the detailed view, ensure it's created and the default view destroyed.
176 tray
->ShowDetailedView(detailed_item
, 0, false, BUBBLE_CREATE_NEW
);
177 RunAllPendingInMessageLoop();
178 ASSERT_TRUE(test_item
->default_view() == NULL
);
179 ASSERT_TRUE(detailed_item
->detailed_view() != NULL
);
181 // Show the default view, ensure it's created and the detailed view destroyed.
182 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
183 RunAllPendingInMessageLoop();
184 ASSERT_TRUE(test_item
->default_view() != NULL
);
185 ASSERT_TRUE(detailed_item
->detailed_view() == NULL
);
188 TEST_F(SystemTrayTest
, SystemTrayNoViewItems
) {
189 SystemTray
* tray
= GetSystemTray();
190 ASSERT_TRUE(tray
->GetWidget());
192 // Verify that no crashes occur on items lacking some views.
193 TestNoViewItem
* no_view_item
= new TestNoViewItem
;
194 tray
->AddTrayItem(no_view_item
);
195 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
196 tray
->ShowDetailedView(no_view_item
, 0, false, BUBBLE_USE_EXISTING
);
197 RunAllPendingInMessageLoop();
200 TEST_F(SystemTrayTest
, TrayWidgetAutoResizes
) {
201 SystemTray
* tray
= GetSystemTray();
202 ASSERT_TRUE(tray
->GetWidget());
204 // Add an initial tray item so that the tray gets laid out correctly.
205 TestItem
* initial_item
= new TestItem
;
206 tray
->AddTrayItem(initial_item
);
208 gfx::Size initial_size
= tray
->GetWidget()->GetWindowBoundsInScreen().size();
210 TestItem
* new_item
= new TestItem
;
211 tray
->AddTrayItem(new_item
);
213 gfx::Size new_size
= tray
->GetWidget()->GetWindowBoundsInScreen().size();
215 // Adding the new item should change the size of the tray.
216 EXPECT_NE(initial_size
.ToString(), new_size
.ToString());
218 // Hiding the tray view of the new item should also change the size of the
220 new_item
->tray_view()->SetVisible(false);
221 EXPECT_EQ(initial_size
.ToString(),
222 tray
->GetWidget()->GetWindowBoundsInScreen().size().ToString());
224 new_item
->tray_view()->SetVisible(true);
225 EXPECT_EQ(new_size
.ToString(),
226 tray
->GetWidget()->GetWindowBoundsInScreen().size().ToString());
229 TEST_F(SystemTrayTest
, SystemTrayNotifications
) {
230 SystemTray
* tray
= GetSystemTray();
231 ASSERT_TRUE(tray
->GetWidget());
233 TestItem
* test_item
= new TestItem
;
234 TestItem
* detailed_item
= new TestItem
;
235 tray
->AddTrayItem(test_item
);
236 tray
->AddTrayItem(detailed_item
);
238 // Ensure the tray views are created.
239 ASSERT_TRUE(test_item
->tray_view() != NULL
);
240 ASSERT_TRUE(detailed_item
->tray_view() != NULL
);
242 // Ensure a notification view is created.
243 tray
->ShowNotificationView(test_item
);
244 ASSERT_TRUE(test_item
->notification_view() != NULL
);
246 // Show the default view, notification view should remain.
247 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
248 RunAllPendingInMessageLoop();
249 ASSERT_TRUE(test_item
->notification_view() != NULL
);
251 // Show the detailed view, ensure the notificaiton view remains.
252 tray
->ShowDetailedView(detailed_item
, 0, false, BUBBLE_CREATE_NEW
);
253 RunAllPendingInMessageLoop();
254 ASSERT_TRUE(detailed_item
->detailed_view() != NULL
);
255 ASSERT_TRUE(test_item
->notification_view() != NULL
);
257 // Hide the detailed view, ensure the notification view still exists.
258 ASSERT_TRUE(tray
->CloseSystemBubble());
259 RunAllPendingInMessageLoop();
260 ASSERT_TRUE(detailed_item
->detailed_view() == NULL
);
261 ASSERT_TRUE(test_item
->notification_view() != NULL
);
264 TEST_F(SystemTrayTest
, BubbleCreationTypesTest
) {
265 SystemTray
* tray
= GetSystemTray();
266 ASSERT_TRUE(tray
->GetWidget());
268 TestItem
* test_item
= new TestItem
;
269 tray
->AddTrayItem(test_item
);
271 // Ensure the tray views are created.
272 ASSERT_TRUE(test_item
->tray_view() != NULL
);
274 // Show the default view, ensure the notification view is destroyed.
275 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
276 RunAllPendingInMessageLoop();
278 views::Widget
* widget
= test_item
->default_view()->GetWidget();
279 gfx::Rect bubble_bounds
= widget
->GetWindowBoundsInScreen();
281 tray
->ShowDetailedView(test_item
, 0, true, BUBBLE_USE_EXISTING
);
282 RunAllPendingInMessageLoop();
284 EXPECT_FALSE(test_item
->default_view());
286 EXPECT_EQ(bubble_bounds
.ToString(), test_item
->detailed_view()->GetWidget()->
287 GetWindowBoundsInScreen().ToString());
288 EXPECT_EQ(widget
, test_item
->detailed_view()->GetWidget());
290 tray
->ShowDefaultView(BUBBLE_USE_EXISTING
);
291 RunAllPendingInMessageLoop();
293 EXPECT_EQ(bubble_bounds
.ToString(), test_item
->default_view()->GetWidget()->
294 GetWindowBoundsInScreen().ToString());
295 EXPECT_EQ(widget
, test_item
->default_view()->GetWidget());
298 // Tests that the tray is laid out properly in the widget to make sure that the
299 // tray extends to the correct edge of the screen.
300 TEST_F(SystemTrayTest
, TrayBoundsInWidget
) {
301 internal::StatusAreaWidget
* widget
=
302 Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
303 SystemTray
* tray
= widget
->system_tray();
305 // Test in bottom alignment. Bottom and right edges of the view should be
306 // aligned with the widget.
307 widget
->SetShelfAlignment(SHELF_ALIGNMENT_BOTTOM
);
308 gfx::Rect window_bounds
= widget
->GetWindowBoundsInScreen();
309 gfx::Rect tray_bounds
= tray
->GetBoundsInScreen();
310 EXPECT_EQ(window_bounds
.bottom(),
311 tray_bounds
.bottom() + kStatusTrayOffsetFromScreenEdgeForTest
);
312 EXPECT_EQ(window_bounds
.right(), tray_bounds
.right());
314 // Test in the top alignment. Top and right edges should match.
315 widget
->SetShelfAlignment(SHELF_ALIGNMENT_TOP
);
316 window_bounds
= widget
->GetWindowBoundsInScreen();
317 tray_bounds
= tray
->GetBoundsInScreen();
318 EXPECT_EQ(window_bounds
.y(),
319 tray_bounds
.y() - kStatusTrayOffsetFromScreenEdgeForTest
);
320 EXPECT_EQ(window_bounds
.right(), tray_bounds
.right());
322 // Test in the left alignment. Left and bottom edges should match.
323 widget
->SetShelfAlignment(SHELF_ALIGNMENT_LEFT
);
324 window_bounds
= widget
->GetWindowBoundsInScreen();
325 tray_bounds
= tray
->GetBoundsInScreen();
326 EXPECT_EQ(window_bounds
.bottom(), tray_bounds
.bottom());
327 EXPECT_EQ(window_bounds
.x(),
328 tray_bounds
.x() - kStatusTrayOffsetFromScreenEdgeForTest
);
330 // Test in the right alignment. Right and bottom edges should match.
331 widget
->SetShelfAlignment(SHELF_ALIGNMENT_LEFT
);
332 window_bounds
= widget
->GetWindowBoundsInScreen();
333 tray_bounds
= tray
->GetBoundsInScreen();
334 EXPECT_EQ(window_bounds
.bottom(), tray_bounds
.bottom());
335 EXPECT_EQ(window_bounds
.right(), tray_bounds
.right());