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 24 namespace ash { 25 namespace test { 26 27 namespace { 28 29 // A browser window proxy which is able to associate an aura native window with 30 // it. 31 class 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 46 TestBrowserWindowAura::TestBrowserWindowAura(aura::Window *native_window) 47 : native_window_(native_window) { 48 } 49 50 TestBrowserWindowAura::~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. 57 class 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 95 WindowPositionerTest::WindowPositionerTest() 96 : grid_size_(WindowPositioner::kMinimumWindowOffset), 97 window_positioner_(NULL) { 98 } 99 100 void 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 129 void 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 146 int 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 152 TEST_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 213 TEST_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 268 TEST_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