1// Copyright (c) 2013 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/shelf/shelf_widget.h"
6
7#include "ash/root_window_controller.h"
8#include "ash/shelf/shelf.h"
9#include "ash/shelf/shelf_button.h"
10#include "ash/shelf/shelf_layout_manager.h"
11#include "ash/shelf/shelf_model.h"
12#include "ash/shelf/shelf_view.h"
13#include "ash/shell.h"
14#include "ash/test/ash_test_base.h"
15#include "ash/test/shelf_test_api.h"
16#include "ash/test/shelf_view_test_api.h"
17#include "ash/wm/window_util.h"
18#include "ui/aura/window_event_dispatcher.h"
19#include "ui/events/event_utils.h"
20#include "ui/gfx/display.h"
21#include "ui/gfx/screen.h"
22#include "ui/views/view.h"
23#include "ui/views/widget/widget.h"
24
25namespace ash {
26
27namespace {
28
29ShelfWidget* GetShelfWidget() {
30  return Shelf::ForPrimaryDisplay()->shelf_widget();
31}
32
33ShelfLayoutManager* GetShelfLayoutManager() {
34  return GetShelfWidget()->shelf_layout_manager();
35}
36
37} // namespace
38
39typedef test::AshTestBase ShelfWidgetTest;
40
41void TestLauncherAlignment(aura::Window* root,
42                           ShelfAlignment alignment,
43                           const std::string& expected) {
44  Shell::GetInstance()->SetShelfAlignment(alignment, root);
45  gfx::Screen* screen = gfx::Screen::GetScreenFor(root);
46  EXPECT_EQ(expected,
47            screen->GetDisplayNearestWindow(root).work_area().ToString());
48}
49
50TEST_F(ShelfWidgetTest, TestAlignment) {
51  Shelf* shelf = Shelf::ForPrimaryDisplay();
52  UpdateDisplay("400x400");
53  ASSERT_TRUE(shelf);
54  {
55    SCOPED_TRACE("Single Bottom");
56    TestLauncherAlignment(Shell::GetPrimaryRootWindow(),
57                          SHELF_ALIGNMENT_BOTTOM,
58                          "0,0 400x353");
59  }
60  {
61    SCOPED_TRACE("Single Right");
62    TestLauncherAlignment(Shell::GetPrimaryRootWindow(),
63                          SHELF_ALIGNMENT_RIGHT,
64                          "0,0 353x400");
65  }
66  {
67    SCOPED_TRACE("Single Left");
68    TestLauncherAlignment(Shell::GetPrimaryRootWindow(),
69                          SHELF_ALIGNMENT_LEFT,
70                          "47,0 353x400");
71  }
72  if (!SupportsMultipleDisplays())
73    return;
74
75  UpdateDisplay("300x300,500x500");
76  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
77  {
78    SCOPED_TRACE("Primary Bottom");
79    TestLauncherAlignment(root_windows[0],
80                          SHELF_ALIGNMENT_BOTTOM,
81                          "0,0 300x253");
82  }
83  {
84    SCOPED_TRACE("Primary Right");
85    TestLauncherAlignment(root_windows[0],
86                          SHELF_ALIGNMENT_RIGHT,
87                          "0,0 253x300");
88  }
89  {
90    SCOPED_TRACE("Primary Left");
91    TestLauncherAlignment(root_windows[0],
92                          SHELF_ALIGNMENT_LEFT,
93                          "47,0 253x300");
94  }
95  {
96    SCOPED_TRACE("Secondary Bottom");
97    TestLauncherAlignment(root_windows[1],
98                          SHELF_ALIGNMENT_BOTTOM,
99                          "300,0 500x453");
100  }
101  {
102    SCOPED_TRACE("Secondary Right");
103    TestLauncherAlignment(root_windows[1],
104                          SHELF_ALIGNMENT_RIGHT,
105                          "300,0 453x500");
106  }
107  {
108    SCOPED_TRACE("Secondary Left");
109    TestLauncherAlignment(root_windows[1],
110                          SHELF_ALIGNMENT_LEFT,
111                          "347,0 453x500");
112  }
113}
114
115// Makes sure the shelf is initially sized correctly.
116TEST_F(ShelfWidgetTest, LauncherInitiallySized) {
117  ShelfWidget* shelf_widget = GetShelfWidget();
118  Shelf* shelf = shelf_widget->shelf();
119  ASSERT_TRUE(shelf);
120  ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
121  ASSERT_TRUE(shelf_layout_manager);
122  ASSERT_TRUE(shelf_widget->status_area_widget());
123  int status_width = shelf_widget->status_area_widget()->
124      GetWindowBoundsInScreen().width();
125  // Test only makes sense if the status is > 0, which it better be.
126  EXPECT_GT(status_width, 0);
127  EXPECT_EQ(status_width, shelf_widget->GetContentsView()->width() -
128            test::ShelfTestAPI(shelf).shelf_view()->width());
129}
130
131// Verifies when the shell is deleted with a full screen window we don't crash.
132TEST_F(ShelfWidgetTest, DontReferenceShelfAfterDeletion) {
133  views::Widget* widget = new views::Widget;
134  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
135  params.bounds = gfx::Rect(0, 0, 200, 200);
136  params.context = CurrentContext();
137  // Widget is now owned by the parent window.
138  widget->Init(params);
139  widget->SetFullscreen(true);
140}
141
142#if defined(OS_CHROMEOS)
143// Verifies shelf is created with correct size after user login and when its
144// container and status widget has finished sizing.
145// See http://crbug.com/252533
146TEST_F(ShelfWidgetTest, ShelfInitiallySizedAfterLogin) {
147  SetUserLoggedIn(false);
148  UpdateDisplay("300x200,400x300");
149
150  ShelfWidget* shelf_widget = NULL;
151  Shell::RootWindowControllerList controllers(
152      Shell::GetAllRootWindowControllers());
153  for (Shell::RootWindowControllerList::const_iterator i = controllers.begin();
154       i != controllers.end();
155       ++i) {
156    if (!(*i)->shelf()->shelf()) {
157      shelf_widget = (*i)->shelf();
158      break;
159    }
160  }
161  ASSERT_TRUE(shelf_widget != NULL);
162
163  SetUserLoggedIn(true);
164  Shell::GetInstance()->CreateShelf();
165
166  Shelf* shelf = shelf_widget->shelf();
167  ASSERT_TRUE(shelf != NULL);
168
169  const int status_width =
170      shelf_widget->status_area_widget()->GetWindowBoundsInScreen().width();
171  EXPECT_GT(status_width, 0);
172  EXPECT_EQ(status_width,
173            shelf_widget->GetContentsView()->width() -
174                test::ShelfTestAPI(shelf).shelf_view()->width());
175}
176#endif  // defined(OS_CHROMEOS)
177
178// Tests that the shelf lets mouse-events close to the edge fall through to the
179// window underneath.
180TEST_F(ShelfWidgetTest, ShelfEdgeOverlappingWindowHitTestMouse) {
181  ShelfWidget* shelf_widget = GetShelfWidget();
182  gfx::Rect shelf_bounds = shelf_widget->GetWindowBoundsInScreen();
183  EXPECT_TRUE(!shelf_bounds.IsEmpty());
184  ShelfLayoutManager* shelf_layout_manager =
185      shelf_widget->shelf_layout_manager();
186  ASSERT_TRUE(shelf_layout_manager);
187  EXPECT_EQ(SHELF_VISIBLE, shelf_layout_manager->visibility_state());
188
189  // Create a Widget which overlaps with the shelf in the top edge.
190  const int kOverlapSize = 15;
191  const int kWindowHeight = 200;
192  views::Widget* widget = new views::Widget;
193  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
194  params.bounds = gfx::Rect(0, shelf_bounds.y() - kWindowHeight + kOverlapSize,
195                            200, kWindowHeight);
196  params.context = CurrentContext();
197  // Widget is now owned by the parent window.
198  widget->Init(params);
199  widget->Show();
200  gfx::Rect widget_bounds = widget->GetWindowBoundsInScreen();
201  EXPECT_TRUE(widget_bounds.Intersects(shelf_bounds));
202
203
204  ui::EventTarget* root = widget->GetNativeWindow()->GetRootWindow();
205  ui::EventTargeter* targeter = root->GetEventTargeter();
206  {
207    // Create a mouse-event targetting the top of the shelf widget. The
208    // window-targeter should find |widget| as the target (instead of the
209    // shelf).
210    gfx::Point event_location(20, shelf_bounds.y() + 1);
211    ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
212                         ui::EF_NONE, ui::EF_NONE);
213    ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
214    EXPECT_EQ(widget->GetNativeWindow(), target);
215  }
216
217  // Now auto-hide (hidden) the shelf.
218  shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
219  shelf_layout_manager->LayoutShelf();
220  EXPECT_EQ(SHELF_AUTO_HIDE, shelf_layout_manager->visibility_state());
221  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_layout_manager->auto_hide_state());
222  shelf_bounds = shelf_widget->GetWindowBoundsInScreen();
223  EXPECT_TRUE(!shelf_bounds.IsEmpty());
224
225  // Move |widget| so it still overlaps the shelf.
226  widget->SetBounds(gfx::Rect(0, shelf_bounds.y() - kWindowHeight +
227                              kOverlapSize, 200, kWindowHeight));
228  widget_bounds = widget->GetWindowBoundsInScreen();
229  EXPECT_TRUE(widget_bounds.Intersects(shelf_bounds));
230  {
231    // Create a mouse-event targetting the top of the shelf widget. This time,
232    // window-target should find the shelf as the target.
233    gfx::Point event_location(20, shelf_bounds.y() + 1);
234    ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
235                         ui::EF_NONE, ui::EF_NONE);
236    ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
237    EXPECT_EQ(shelf_widget->GetNativeWindow(), target);
238  }
239}
240
241// Tests that the shelf has a slightly larger hit-region for touch-events when
242// it's in the auto-hidden state.
243TEST_F(ShelfWidgetTest, HiddenShelfHitTestTouch) {
244  ShelfWidget* shelf_widget = GetShelfWidget();
245  gfx::Rect shelf_bounds = shelf_widget->GetWindowBoundsInScreen();
246  EXPECT_TRUE(!shelf_bounds.IsEmpty());
247  ShelfLayoutManager* shelf_layout_manager =
248      shelf_widget->shelf_layout_manager();
249  ASSERT_TRUE(shelf_layout_manager);
250  EXPECT_EQ(SHELF_VISIBLE, shelf_layout_manager->visibility_state());
251
252  // Create a widget to make sure that the shelf does auto-hide.
253  views::Widget* widget = new views::Widget;
254  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
255  params.bounds = gfx::Rect(0, 0, 200, 200);
256  params.context = CurrentContext();
257  // Widget is now owned by the parent window.
258  widget->Init(params);
259  widget->Show();
260
261  ui::EventTarget* root = shelf_widget->GetNativeWindow()->GetRootWindow();
262  ui::EventTargeter* targeter = root->GetEventTargeter();
263  // Touch just over the shelf. Since the shelf is visible, the window-targeter
264  // should not find the shelf as the target.
265  {
266    gfx::Point event_location(20, shelf_bounds.y() - 1);
267    ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, event_location, 0,
268                         ui::EventTimeForNow());
269    EXPECT_NE(shelf_widget->GetNativeWindow(),
270              targeter->FindTargetForEvent(root, &touch));
271  }
272
273  // Now auto-hide (hidden) the shelf.
274  shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
275  shelf_layout_manager->LayoutShelf();
276  EXPECT_EQ(SHELF_AUTO_HIDE, shelf_layout_manager->visibility_state());
277  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_layout_manager->auto_hide_state());
278  shelf_bounds = shelf_widget->GetWindowBoundsInScreen();
279  EXPECT_TRUE(!shelf_bounds.IsEmpty());
280
281  // Touch just over the shelf again. This time, the targeter should find the
282  // shelf as the target.
283  {
284    gfx::Point event_location(20, shelf_bounds.y() - 1);
285    ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, event_location, 0,
286                         ui::EventTimeForNow());
287    EXPECT_EQ(shelf_widget->GetNativeWindow(),
288              targeter->FindTargetForEvent(root, &touch));
289  }
290}
291
292}  // namespace ash
293