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/wm/window_positioner.h"
6
7#include "ash/shell.h"
8#include "ash/test/ash_test_base.h"
9#include "ash/test/test_shell_delegate.h"
10#include "ash/wm/window_resizer.h"
11#include "base/compiler_specific.h"
12#include "base/logging.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/browser/ui/host_desktop.h"
15#include "chrome/test/base/test_browser_window.h"
16#include "chrome/test/base/testing_profile.h"
17#include "content/public/test/render_view_test.h"
18#include "testing/gtest/include/gtest/gtest.h"
19#include "ui/aura/env.h"
20#include "ui/aura/test/test_windows.h"
21#include "ui/aura/window_event_dispatcher.h"
22#include "ui/gfx/screen.h"
23
24namespace ash {
25namespace test {
26
27namespace {
28
29// A browser window proxy which is able to associate an aura native window with
30// it.
31class TestBrowserWindowAura : public TestBrowserWindow {
32 public:
33  explicit TestBrowserWindowAura(aura::Window* native_window);
34  virtual ~TestBrowserWindowAura();
35
36  virtual gfx::NativeWindow GetNativeWindow() OVERRIDE {
37    return native_window_;
38  }
39
40 private:
41  gfx::NativeWindow native_window_;
42
43  DISALLOW_COPY_AND_ASSIGN(TestBrowserWindowAura);
44};
45
46TestBrowserWindowAura::TestBrowserWindowAura(aura::Window *native_window)
47    : native_window_(native_window) {
48}
49
50TestBrowserWindowAura::~TestBrowserWindowAura() {}
51
52}  // namespace
53
54// A test class for preparing window positioner tests - it creates a testing
55// base by adding a window and a popup which can be independently
56// positioned to see where the positioner will place the window.
57class WindowPositionerTest : public AshTestBase {
58 public:
59  WindowPositionerTest();
60
61  virtual void SetUp() OVERRIDE;
62  virtual void TearDown() OVERRIDE;
63
64 protected:
65  aura::Window* window() { return window_.get(); }
66  aura::Window* popup() { return popup_.get(); }
67
68  Browser* window_browser() { return window_owning_browser_.get(); }
69  Browser* popup_browser() { return popup_owning_browser_.get(); }
70
71  WindowPositioner* window_positioner() { return window_positioner_; }
72
73  // The positioner & desktop's used grid alignment size.
74  const int grid_size_;
75
76 private:
77  WindowPositioner* window_positioner_;
78
79  // These two need to be deleted after everything else is gone.
80  TestingProfile profile_;
81
82  // These get created for each session.
83  scoped_ptr<aura::Window> window_;
84  scoped_ptr<aura::Window> popup_;
85
86  scoped_ptr<BrowserWindow> browser_window_;
87  scoped_ptr<BrowserWindow> browser_popup_;
88
89  scoped_ptr<Browser> window_owning_browser_;
90  scoped_ptr<Browser> popup_owning_browser_;
91
92  DISALLOW_COPY_AND_ASSIGN(WindowPositionerTest);
93};
94
95WindowPositionerTest::WindowPositionerTest()
96    : grid_size_(WindowPositioner::kMinimumWindowOffset),
97      window_positioner_(NULL) {
98}
99
100void WindowPositionerTest::SetUp() {
101  AshTestBase::SetUp();
102  // Create some default dummy windows.
103  window_.reset(CreateTestWindowInShellWithId(0));
104  window_->SetBounds(gfx::Rect(16, 32, 640, 320));
105  popup_.reset(CreateTestWindowInShellWithId(1));
106  popup_->SetBounds(gfx::Rect(16, 32, 128, 256));
107
108  // Create a browser for the window.
109  browser_window_.reset(new TestBrowserWindowAura(window_.get()));
110  Browser::CreateParams window_params(&profile_,
111                                      chrome::HOST_DESKTOP_TYPE_ASH);
112  window_params.window = browser_window_.get();
113  window_owning_browser_.reset(new Browser(window_params));
114
115  // Creating a browser for the popup.
116  browser_popup_.reset(new TestBrowserWindowAura(popup_.get()));
117  Browser::CreateParams popup_params(Browser::TYPE_POPUP, &profile_,
118                                     chrome::HOST_DESKTOP_TYPE_ASH);
119  popup_params.window = browser_popup_.get();
120  popup_owning_browser_.reset(new Browser(popup_params));
121
122  // We hide all windows upon start - each user is required to set it up
123  // as he needs it.
124  window()->Hide();
125  popup()->Hide();
126  window_positioner_ = new WindowPositioner();
127}
128
129void WindowPositionerTest::TearDown() {
130  // Since the AuraTestBase is needed to create our assets, we have to
131  // also delete them before we tear it down.
132  window_owning_browser_.reset(NULL);
133  popup_owning_browser_.reset(NULL);
134
135  browser_window_.reset(NULL);
136  browser_popup_.reset(NULL);
137
138  window_.reset(NULL);
139  popup_.reset(NULL);
140
141  AshTestBase::TearDown();
142  delete window_positioner_;
143  window_positioner_ = NULL;
144}
145
146int AlignToGridRoundDown(int location, int grid_size) {
147  if (grid_size <= 1 || location % grid_size == 0)
148    return location;
149  return location / grid_size * grid_size;
150}
151
152TEST_F(WindowPositionerTest, cascading) {
153  const gfx::Rect work_area =
154      Shell::GetScreen()->GetPrimaryDisplay().work_area();
155
156  // First see that the window will cascade down when there is no space.
157  window()->SetBounds(work_area);
158  window()->Show();
159
160  gfx::Rect popup_position(0, 0, 200, 200);
161  // Check that it gets cascaded.
162  gfx::Rect cascade_1 = window_positioner()->GetPopupPosition(popup_position);
163  EXPECT_EQ(gfx::Rect(work_area.x() + grid_size_, work_area.y() + grid_size_,
164                      popup_position.width(), popup_position.height()),
165                      cascade_1);
166
167  gfx::Rect cascade_2 = window_positioner()->GetPopupPosition(popup_position);
168  EXPECT_EQ(gfx::Rect(work_area.x() + 2 * grid_size_,
169                      work_area.y() + 2 * grid_size_,
170                      popup_position.width(), popup_position.height()),
171                      cascade_2);
172
173  // Check that if there is even only a pixel missing it will cascade.
174  window()->SetBounds(gfx::Rect(work_area.x() + popup_position.width() - 1,
175                                work_area.y() + popup_position.height() - 1,
176                                work_area.width() -
177                                    2 * (popup_position.width() - 1),
178                                work_area.height() -
179                                    2 * (popup_position.height() - 1)));
180
181  gfx::Rect cascade_3 = window_positioner()->GetPopupPosition(popup_position);
182  EXPECT_EQ(gfx::Rect(work_area.x() + 3 * grid_size_,
183                      work_area.y() + 3 * grid_size_,
184                      popup_position.width(), popup_position.height()),
185                      cascade_3);
186
187  // Check that we overflow into the next line when we do not fit anymore in Y.
188  gfx::Rect popup_position_4(0, 0, 200,
189                             work_area.height() -
190                                 (cascade_3.y() - work_area.y()));
191  gfx::Rect cascade_4 =
192      window_positioner()->GetPopupPosition(popup_position_4);
193  EXPECT_EQ(gfx::Rect(work_area.x() + 2 * grid_size_,
194                      work_area.y() + grid_size_,
195                      popup_position_4.width(), popup_position_4.height()),
196                      cascade_4);
197
198  // Check that we overflow back to the first possible location if we overflow
199  // to the end.
200  gfx::Rect popup_position_5(0, 0,
201                            work_area.width() + 1 -
202                                (cascade_4.x() - work_area.x()),
203                            work_area.height() -
204                                (2 * grid_size_ - work_area.y()));
205  gfx::Rect cascade_5 =
206      window_positioner()->GetPopupPosition(popup_position_5);
207  EXPECT_EQ(gfx::Rect(work_area.x() + grid_size_,
208                      work_area.y() + grid_size_,
209                      popup_position_5.width(), popup_position_5.height()),
210                      cascade_5);
211}
212
213TEST_F(WindowPositionerTest, filling) {
214  const gfx::Rect work_area =
215      Shell::GetScreen()->GetPrimaryDisplay().work_area();
216  gfx::Rect popup_position(0, 0, 256, 128);
217  // Leave space on the left and the right and see if we fill top to bottom.
218  window()->SetBounds(gfx::Rect(work_area.x() + popup_position.width(),
219                                work_area.y(),
220                                work_area.width() - 2 * popup_position.width(),
221                                work_area.height()));
222  window()->Show();
223  // Check that we are positioned in the top left corner.
224  gfx::Rect top_left = window_positioner()->GetPopupPosition(popup_position);
225  EXPECT_EQ(gfx::Rect(work_area.x(), work_area.y(),
226                      popup_position.width(), popup_position.height()),
227                      top_left);
228
229  // Now block the found location.
230  popup()->SetBounds(top_left);
231  popup()->Show();
232  gfx::Rect mid_left = window_positioner()->GetPopupPosition(popup_position);
233  EXPECT_EQ(gfx::Rect(work_area.x(),
234                      AlignToGridRoundDown(
235                          work_area.y() + top_left.height(), grid_size_),
236                          popup_position.width(), popup_position.height()),
237                      mid_left);
238
239  // Block now everything so that we can only put the popup on the bottom
240  // of the left side.
241  // Note: We need to keep one "grid spacing free" if the window does not
242  // fit into the grid (which is true for 200 height).`
243  popup()->SetBounds(gfx::Rect(work_area.x(), work_area.y(),
244                               popup_position.width(),
245                               work_area.height() - popup_position.height() -
246                                   grid_size_ + 1));
247  gfx::Rect bottom_left = window_positioner()->GetPopupPosition(
248                              popup_position);
249  EXPECT_EQ(gfx::Rect(work_area.x(),
250                      work_area.bottom() - popup_position.height(),
251                      popup_position.width(), popup_position.height()),
252                      bottom_left);
253
254  // Block now enough to force the right side.
255  popup()->SetBounds(gfx::Rect(work_area.x(), work_area.y(),
256                               popup_position.width(),
257                               work_area.height() - popup_position.height() +
258                               1));
259  gfx::Rect top_right = window_positioner()->GetPopupPosition(
260                            popup_position);
261  EXPECT_EQ(gfx::Rect(AlignToGridRoundDown(work_area.right() -
262                                           popup_position.width(), grid_size_),
263                      work_area.y(),
264                      popup_position.width(), popup_position.height()),
265                      top_right);
266}
267
268TEST_F(WindowPositionerTest, biggerThenBorder) {
269  const gfx::Rect work_area =
270      Shell::GetScreen()->GetPrimaryDisplay().work_area();
271
272  gfx::Rect pop_position(0, 0, work_area.width(), work_area.height());
273
274  // Check that the popup is placed full screen.
275  gfx::Rect full = window_positioner()->GetPopupPosition(pop_position);
276  EXPECT_EQ(gfx::Rect(work_area.x(), work_area.y(),
277                      pop_position.width(), pop_position.height()),
278                      full);
279}
280
281}  // namespace test
282}  // namespace ash
283