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