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/workspace/workspace_layout_manager.h" 6 7 #include <string> 8 9 #include "ash/display/display_layout.h" 10 #include "ash/display/display_manager.h" 11 #include "ash/root_window_controller.h" 12 #include "ash/screen_util.h" 13 #include "ash/session/session_state_delegate.h" 14 #include "ash/shelf/shelf_layout_manager.h" 15 #include "ash/shell.h" 16 #include "ash/shell_observer.h" 17 #include "ash/shell_window_ids.h" 18 #include "ash/test/ash_test_base.h" 19 #include "ash/wm/maximize_mode/workspace_backdrop_delegate.h" 20 #include "ash/wm/window_state.h" 21 #include "ash/wm/window_util.h" 22 #include "ash/wm/wm_event.h" 23 #include "ash/wm/workspace/workspace_window_resizer.h" 24 #include "base/basictypes.h" 25 #include "base/compiler_specific.h" 26 #include "ui/aura/client/aura_constants.h" 27 #include "ui/aura/test/test_windows.h" 28 #include "ui/aura/window.h" 29 #include "ui/aura/window_event_dispatcher.h" 30 #include "ui/base/ime/dummy_text_input_client.h" 31 #include "ui/base/ime/input_method.h" 32 #include "ui/base/ime/text_input_focus_manager.h" 33 #include "ui/base/ui_base_switches_util.h" 34 #include "ui/base/ui_base_types.h" 35 #include "ui/gfx/insets.h" 36 #include "ui/gfx/screen.h" 37 #include "ui/views/widget/widget.h" 38 #include "ui/views/widget/widget_delegate.h" 39 #include "ui/wm/core/window_util.h" 40 41 namespace ash { 42 namespace { 43 44 class MaximizeDelegateView : public views::WidgetDelegateView { 45 public: 46 explicit MaximizeDelegateView(const gfx::Rect& initial_bounds) 47 : initial_bounds_(initial_bounds) { 48 } 49 virtual ~MaximizeDelegateView() {} 50 51 virtual bool GetSavedWindowPlacement( 52 const views::Widget* widget, 53 gfx::Rect* bounds, 54 ui::WindowShowState* show_state) const OVERRIDE { 55 *bounds = initial_bounds_; 56 *show_state = ui::SHOW_STATE_MAXIMIZED; 57 return true; 58 } 59 60 private: 61 const gfx::Rect initial_bounds_; 62 63 DISALLOW_COPY_AND_ASSIGN(MaximizeDelegateView); 64 }; 65 66 class TestShellObserver : public ShellObserver { 67 public: 68 TestShellObserver() : call_count_(0), 69 is_fullscreen_(false) { 70 Shell::GetInstance()->AddShellObserver(this); 71 } 72 73 virtual ~TestShellObserver() { 74 Shell::GetInstance()->RemoveShellObserver(this); 75 } 76 77 virtual void OnFullscreenStateChanged(bool is_fullscreen, 78 aura::Window* root_window) OVERRIDE { 79 call_count_++; 80 is_fullscreen_ = is_fullscreen; 81 } 82 83 int call_count() const { 84 return call_count_; 85 } 86 87 bool is_fullscreen() const { 88 return is_fullscreen_; 89 } 90 91 private: 92 int call_count_; 93 bool is_fullscreen_; 94 95 DISALLOW_COPY_AND_ASSIGN(TestShellObserver); 96 }; 97 98 } // namespace 99 100 typedef test::AshTestBase WorkspaceLayoutManagerTest; 101 102 // Verifies that a window containing a restore coordinate will be restored to 103 // to the size prior to minimize, keeping the restore rectangle in tact (if 104 // there is one). 105 TEST_F(WorkspaceLayoutManagerTest, RestoreFromMinimizeKeepsRestore) { 106 scoped_ptr<aura::Window> window( 107 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4))); 108 gfx::Rect bounds(10, 15, 25, 35); 109 window->SetBounds(bounds); 110 111 wm::WindowState* window_state = wm::GetWindowState(window.get()); 112 113 // This will not be used for un-minimizing window. 114 window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100)); 115 window_state->Minimize(); 116 window_state->Restore(); 117 EXPECT_EQ("0,0 100x100", window_state->GetRestoreBoundsInScreen().ToString()); 118 EXPECT_EQ("10,15 25x35", window.get()->bounds().ToString()); 119 120 if (!SupportsMultipleDisplays()) 121 return; 122 123 UpdateDisplay("400x300,500x400"); 124 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100), 125 ScreenUtil::GetSecondaryDisplay()); 126 EXPECT_EQ(Shell::GetAllRootWindows()[1], window->GetRootWindow()); 127 window_state->Minimize(); 128 // This will not be used for un-minimizing window. 129 window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100)); 130 window_state->Restore(); 131 EXPECT_EQ("600,0 100x100", window->GetBoundsInScreen().ToString()); 132 133 // Make sure the unminimized window moves inside the display when 134 // 2nd display is disconnected. 135 window_state->Minimize(); 136 UpdateDisplay("400x300"); 137 window_state->Restore(); 138 EXPECT_EQ(Shell::GetPrimaryRootWindow(), window->GetRootWindow()); 139 EXPECT_TRUE( 140 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds())); 141 } 142 143 TEST_F(WorkspaceLayoutManagerTest, KeepMinimumVisibilityInDisplays) { 144 if (!SupportsMultipleDisplays()) 145 return; 146 147 UpdateDisplay("300x400,400x500"); 148 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 149 150 DisplayLayout layout(DisplayLayout::TOP, 0); 151 Shell::GetInstance()->display_manager()-> 152 SetLayoutForCurrentDisplays(layout); 153 EXPECT_EQ("0,-500 400x500", root_windows[1]->GetBoundsInScreen().ToString()); 154 155 scoped_ptr<aura::Window> window1( 156 CreateTestWindowInShellWithBounds(gfx::Rect(10, -400, 200, 200))); 157 EXPECT_EQ("10,-400 200x200", window1->GetBoundsInScreen().ToString()); 158 159 // Make sure the caption is visible. 160 scoped_ptr<aura::Window> window2( 161 CreateTestWindowInShellWithBounds(gfx::Rect(10, -600, 200, 200))); 162 EXPECT_EQ("10,-500 200x200", window2->GetBoundsInScreen().ToString()); 163 } 164 165 TEST_F(WorkspaceLayoutManagerTest, NoMinimumVisibilityForPopupWindows) { 166 UpdateDisplay("300x400"); 167 168 // Create a popup window out of display boundaries and make sure it is not 169 // moved to have minimum visibility. 170 scoped_ptr<aura::Window> window( 171 CreateTestWindowInShellWithDelegateAndType(NULL, 172 ui::wm::WINDOW_TYPE_POPUP, 173 0, 174 gfx::Rect(400, 100, 50, 50))); 175 EXPECT_EQ("400,100 50x50", window->GetBoundsInScreen().ToString()); 176 } 177 178 TEST_F(WorkspaceLayoutManagerTest, KeepRestoredWindowInDisplay) { 179 if (!SupportsHostWindowResize()) 180 return; 181 scoped_ptr<aura::Window> window( 182 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); 183 wm::WindowState* window_state = wm::GetWindowState(window.get()); 184 185 // Maximized -> Normal transition. 186 window_state->Maximize(); 187 window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40)); 188 window_state->Restore(); 189 EXPECT_TRUE( 190 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds())); 191 // Y bounds should not be negative. 192 EXPECT_EQ("-20,0 30x40", window->bounds().ToString()); 193 194 // Minimized -> Normal transition. 195 window->SetBounds(gfx::Rect(-100, -100, 30, 40)); 196 window_state->Minimize(); 197 EXPECT_FALSE( 198 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds())); 199 EXPECT_EQ("-100,-100 30x40", window->bounds().ToString()); 200 window->Show(); 201 EXPECT_TRUE( 202 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds())); 203 // Y bounds should not be negative. 204 EXPECT_EQ("-20,0 30x40", window->bounds().ToString()); 205 206 // Fullscreen -> Normal transition. 207 window->SetBounds(gfx::Rect(0, 0, 30, 40)); // reset bounds. 208 ASSERT_EQ("0,0 30x40", window->bounds().ToString()); 209 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 210 EXPECT_EQ(window->bounds(), window->GetRootWindow()->bounds()); 211 window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40)); 212 window_state->Restore(); 213 EXPECT_TRUE( 214 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds())); 215 // Y bounds should not be negative. 216 EXPECT_EQ("-20,0 30x40", window->bounds().ToString()); 217 } 218 219 TEST_F(WorkspaceLayoutManagerTest, MaximizeInDisplayToBeRestored) { 220 if (!SupportsMultipleDisplays()) 221 return; 222 UpdateDisplay("300x400,400x500"); 223 224 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 225 226 scoped_ptr<aura::Window> window( 227 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); 228 EXPECT_EQ(root_windows[0], window->GetRootWindow()); 229 230 wm::WindowState* window_state = wm::GetWindowState(window.get()); 231 window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40)); 232 // Maximize the window in 2nd display as the restore bounds 233 // is inside 2nd display. 234 window_state->Maximize(); 235 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 236 EXPECT_EQ("300,0 400x453", window->GetBoundsInScreen().ToString()); 237 238 window_state->Restore(); 239 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 240 EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString()); 241 242 // If the restore bounds intersects with the current display, 243 // don't move. 244 window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40)); 245 window_state->Maximize(); 246 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 247 EXPECT_EQ("300,0 400x453", window->GetBoundsInScreen().ToString()); 248 249 window_state->Restore(); 250 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 251 EXPECT_EQ("280,0 30x40", window->GetBoundsInScreen().ToString()); 252 253 // Restoring widget state. 254 scoped_ptr<views::Widget> w1(new views::Widget); 255 views::Widget::InitParams params; 256 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 257 params.delegate = new MaximizeDelegateView(gfx::Rect(400, 0, 30, 40)); 258 params.context = root_windows[0]; 259 w1->Init(params); 260 w1->Show(); 261 EXPECT_TRUE(w1->IsMaximized()); 262 EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow()); 263 EXPECT_EQ("300,0 400x453", w1->GetWindowBoundsInScreen().ToString()); 264 w1->Restore(); 265 EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow()); 266 EXPECT_EQ("400,0 30x40", w1->GetWindowBoundsInScreen().ToString()); 267 } 268 269 TEST_F(WorkspaceLayoutManagerTest, FullscreenInDisplayToBeRestored) { 270 if (!SupportsMultipleDisplays()) 271 return; 272 UpdateDisplay("300x400,400x500"); 273 274 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 275 276 scoped_ptr<aura::Window> window( 277 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); 278 EXPECT_EQ(root_windows[0], window->GetRootWindow()); 279 280 wm::WindowState* window_state = wm::GetWindowState(window.get()); 281 window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40)); 282 // Maximize the window in 2nd display as the restore bounds 283 // is inside 2nd display. 284 window->SetProperty(aura::client::kShowStateKey, 285 ui::SHOW_STATE_FULLSCREEN); 286 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 287 EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString()); 288 289 window_state->Restore(); 290 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 291 EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString()); 292 293 // If the restore bounds intersects with the current display, 294 // don't move. 295 window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40)); 296 window->SetProperty(aura::client::kShowStateKey, 297 ui::SHOW_STATE_FULLSCREEN); 298 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 299 EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString()); 300 301 window_state->Restore(); 302 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 303 EXPECT_EQ("280,0 30x40", window->GetBoundsInScreen().ToString()); 304 } 305 306 // WindowObserver implementation used by DontClobberRestoreBoundsWindowObserver. 307 // This code mirrors what BrowserFrameAsh does. In particular when this code 308 // sees the window was maximized it changes the bounds of a secondary 309 // window. The secondary window mirrors the status window. 310 class DontClobberRestoreBoundsWindowObserver : public aura::WindowObserver { 311 public: 312 DontClobberRestoreBoundsWindowObserver() : window_(NULL) {} 313 314 void set_window(aura::Window* window) { window_ = window; } 315 316 virtual void OnWindowPropertyChanged(aura::Window* window, 317 const void* key, 318 intptr_t old) OVERRIDE { 319 if (!window_) 320 return; 321 322 if (wm::GetWindowState(window)->IsMaximized()) { 323 aura::Window* w = window_; 324 window_ = NULL; 325 326 gfx::Rect shelf_bounds(Shell::GetPrimaryRootWindowController()-> 327 GetShelfLayoutManager()->GetIdealBounds()); 328 const gfx::Rect& window_bounds(w->bounds()); 329 w->SetBounds(gfx::Rect(window_bounds.x(), shelf_bounds.y() - 1, 330 window_bounds.width(), window_bounds.height())); 331 } 332 } 333 334 private: 335 aura::Window* window_; 336 337 DISALLOW_COPY_AND_ASSIGN(DontClobberRestoreBoundsWindowObserver); 338 }; 339 340 // Creates a window, maximized the window and from within the maximized 341 // notification sets the bounds of a window to overlap the shelf. Verifies this 342 // doesn't effect the restore bounds. 343 TEST_F(WorkspaceLayoutManagerTest, DontClobberRestoreBounds) { 344 DontClobberRestoreBoundsWindowObserver window_observer; 345 scoped_ptr<aura::Window> window(new aura::Window(NULL)); 346 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); 347 window->Init(aura::WINDOW_LAYER_TEXTURED); 348 window->SetBounds(gfx::Rect(10, 20, 30, 40)); 349 // NOTE: for this test to exercise the failure the observer needs to be added 350 // before the parent set. This mimics what BrowserFrameAsh does. 351 window->AddObserver(&window_observer); 352 ParentWindowInPrimaryRootWindow(window.get()); 353 window->Show(); 354 355 wm::WindowState* window_state = wm::GetWindowState(window.get()); 356 window_state->Activate(); 357 358 scoped_ptr<aura::Window> window2( 359 CreateTestWindowInShellWithBounds(gfx::Rect(12, 20, 30, 40))); 360 ::wm::AddTransientChild(window.get(), window2.get()); 361 window2->Show(); 362 363 window_observer.set_window(window2.get()); 364 window_state->Maximize(); 365 EXPECT_EQ("10,20 30x40", 366 window_state->GetRestoreBoundsInScreen().ToString()); 367 window->RemoveObserver(&window_observer); 368 } 369 370 // Verifies when a window is maximized all descendant windows have a size. 371 TEST_F(WorkspaceLayoutManagerTest, ChildBoundsResetOnMaximize) { 372 scoped_ptr<aura::Window> window( 373 CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 30, 40))); 374 window->Show(); 375 wm::WindowState* window_state = wm::GetWindowState(window.get()); 376 window_state->Activate(); 377 scoped_ptr<aura::Window> child_window( 378 aura::test::CreateTestWindowWithBounds(gfx::Rect(5, 6, 7, 8), 379 window.get())); 380 child_window->Show(); 381 window_state->Maximize(); 382 EXPECT_EQ("5,6 7x8", child_window->bounds().ToString()); 383 } 384 385 // Verifies a window created with maximized state has the maximized 386 // bounds. 387 TEST_F(WorkspaceLayoutManagerTest, MaximizeWithEmptySize) { 388 scoped_ptr<aura::Window> window( 389 aura::test::CreateTestWindowWithBounds(gfx::Rect(0, 0, 0, 0), 390 NULL)); 391 wm::GetWindowState(window.get())->Maximize(); 392 aura::Window* default_container = Shell::GetContainer( 393 Shell::GetPrimaryRootWindow(), kShellWindowId_DefaultContainer); 394 default_container->AddChild(window.get()); 395 window->Show(); 396 gfx::Rect work_area( 397 Shell::GetScreen()->GetPrimaryDisplay().work_area()); 398 EXPECT_EQ(work_area.ToString(), window->GetBoundsInScreen().ToString()); 399 } 400 401 TEST_F(WorkspaceLayoutManagerTest, WindowShouldBeOnScreenWhenAdded) { 402 // Normal window bounds shouldn't be changed. 403 gfx::Rect window_bounds(100, 100, 200, 200); 404 scoped_ptr<aura::Window> window( 405 CreateTestWindowInShellWithBounds(window_bounds)); 406 EXPECT_EQ(window_bounds, window->bounds()); 407 408 // If the window is out of the workspace, it would be moved on screen. 409 gfx::Rect root_window_bounds = 410 Shell::GetInstance()->GetPrimaryRootWindow()->bounds(); 411 window_bounds.Offset(root_window_bounds.width(), root_window_bounds.height()); 412 ASSERT_FALSE(window_bounds.Intersects(root_window_bounds)); 413 scoped_ptr<aura::Window> out_window( 414 CreateTestWindowInShellWithBounds(window_bounds)); 415 EXPECT_EQ(window_bounds.size(), out_window->bounds().size()); 416 gfx::Rect bounds = out_window->bounds(); 417 bounds.Intersect(root_window_bounds); 418 419 // 30% of the window edge must be visible. 420 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29); 421 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29); 422 423 aura::Window* parent = out_window->parent(); 424 parent->RemoveChild(out_window.get()); 425 out_window->SetBounds(gfx::Rect(-200, -200, 200, 200)); 426 // UserHasChangedWindowPositionOrSize flag shouldn't turn off this behavior. 427 wm::GetWindowState(window.get())->set_bounds_changed_by_user(true); 428 parent->AddChild(out_window.get()); 429 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29); 430 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29); 431 432 // Make sure we always make more than 1/3 of the window edge visible even 433 // if the initial bounds intersects with display. 434 window_bounds.SetRect(-150, -150, 200, 200); 435 bounds = window_bounds; 436 bounds.Intersect(root_window_bounds); 437 438 // Make sure that the initial bounds' visible area is less than 26% 439 // so that the auto adjustment logic kicks in. 440 ASSERT_LT(bounds.width(), out_window->bounds().width() * 0.26); 441 ASSERT_LT(bounds.height(), out_window->bounds().height() * 0.26); 442 ASSERT_TRUE(window_bounds.Intersects(root_window_bounds)); 443 444 scoped_ptr<aura::Window> partially_out_window( 445 CreateTestWindowInShellWithBounds(window_bounds)); 446 EXPECT_EQ(window_bounds.size(), partially_out_window->bounds().size()); 447 bounds = partially_out_window->bounds(); 448 bounds.Intersect(root_window_bounds); 449 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29); 450 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29); 451 452 // Make sure the window whose 30% width/height is bigger than display 453 // will be placed correctly. 454 window_bounds.SetRect(-1900, -1900, 3000, 3000); 455 scoped_ptr<aura::Window> window_bigger_than_display( 456 CreateTestWindowInShellWithBounds(window_bounds)); 457 EXPECT_GE(root_window_bounds.width(), 458 window_bigger_than_display->bounds().width()); 459 EXPECT_GE(root_window_bounds.height(), 460 window_bigger_than_display->bounds().height()); 461 462 bounds = window_bigger_than_display->bounds(); 463 bounds.Intersect(root_window_bounds); 464 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29); 465 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29); 466 } 467 468 // Verifies the size of a window is enforced to be smaller than the work area. 469 TEST_F(WorkspaceLayoutManagerTest, SizeToWorkArea) { 470 // Normal window bounds shouldn't be changed. 471 gfx::Size work_area( 472 Shell::GetScreen()->GetPrimaryDisplay().work_area().size()); 473 const gfx::Rect window_bounds( 474 100, 101, work_area.width() + 1, work_area.height() + 2); 475 scoped_ptr<aura::Window> window( 476 CreateTestWindowInShellWithBounds(window_bounds)); 477 EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(), 478 window->bounds().ToString()); 479 480 // Directly setting the bounds triggers a slightly different code path. Verify 481 // that too. 482 window->SetBounds(window_bounds); 483 EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(), 484 window->bounds().ToString()); 485 } 486 487 TEST_F(WorkspaceLayoutManagerTest, NotifyFullscreenChanges) { 488 TestShellObserver observer; 489 scoped_ptr<aura::Window> window1( 490 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); 491 scoped_ptr<aura::Window> window2( 492 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); 493 wm::WindowState* window_state1 = wm::GetWindowState(window1.get()); 494 wm::WindowState* window_state2 = wm::GetWindowState(window2.get()); 495 window_state2->Activate(); 496 497 const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN); 498 window_state2->OnWMEvent(&toggle_fullscreen_event); 499 EXPECT_EQ(1, observer.call_count()); 500 EXPECT_TRUE(observer.is_fullscreen()); 501 502 // When window1 moves to the front the fullscreen state should change. 503 window_state1->Activate(); 504 EXPECT_EQ(2, observer.call_count()); 505 EXPECT_FALSE(observer.is_fullscreen()); 506 507 // It should change back if window2 becomes active again. 508 window_state2->Activate(); 509 EXPECT_EQ(3, observer.call_count()); 510 EXPECT_TRUE(observer.is_fullscreen()); 511 512 window_state2->OnWMEvent(&toggle_fullscreen_event); 513 EXPECT_EQ(4, observer.call_count()); 514 EXPECT_FALSE(observer.is_fullscreen()); 515 516 window_state2->OnWMEvent(&toggle_fullscreen_event); 517 EXPECT_EQ(5, observer.call_count()); 518 EXPECT_TRUE(observer.is_fullscreen()); 519 520 // Closing the window should change the fullscreen state. 521 window2.reset(); 522 EXPECT_EQ(6, observer.call_count()); 523 EXPECT_FALSE(observer.is_fullscreen()); 524 } 525 526 // Following "Solo" tests were originally written for BaseLayoutManager. 527 namespace { 528 529 class WorkspaceLayoutManagerSoloTest : public test::AshTestBase { 530 public: 531 WorkspaceLayoutManagerSoloTest() {} 532 virtual ~WorkspaceLayoutManagerSoloTest() {} 533 534 aura::Window* CreateTestWindow(const gfx::Rect& bounds) { 535 return CreateTestWindowInShellWithBounds(bounds); 536 } 537 538 private: 539 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerSoloTest); 540 }; 541 542 } // namespace 543 544 // Tests normal->maximize->normal. 545 TEST_F(WorkspaceLayoutManagerSoloTest, Maximize) { 546 gfx::Rect bounds(100, 100, 200, 200); 547 scoped_ptr<aura::Window> window(CreateTestWindow(bounds)); 548 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 549 // Maximized window fills the work area, not the whole display. 550 EXPECT_EQ( 551 ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(), 552 window->bounds().ToString()); 553 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 554 EXPECT_EQ(bounds.ToString(), window->bounds().ToString()); 555 } 556 557 // Tests normal->minimize->normal. 558 TEST_F(WorkspaceLayoutManagerSoloTest, Minimize) { 559 gfx::Rect bounds(100, 100, 200, 200); 560 scoped_ptr<aura::Window> window(CreateTestWindow(bounds)); 561 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 562 // Note: Currently minimize doesn't do anything except set the state. 563 // See crbug.com/104571. 564 EXPECT_EQ(bounds.ToString(), window->bounds().ToString()); 565 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 566 EXPECT_EQ(bounds.ToString(), window->bounds().ToString()); 567 } 568 569 // A WindowDelegate which sets the focus when the window 570 // becomes visible. 571 class FocusDelegate : public aura::test::TestWindowDelegate { 572 public: 573 FocusDelegate() 574 : window_(NULL), 575 show_state_(ui::SHOW_STATE_END) { 576 } 577 virtual ~FocusDelegate() {} 578 579 void set_window(aura::Window* window) { window_ = window; } 580 581 // aura::test::TestWindowDelegate overrides: 582 virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE { 583 if (window_) { 584 if (visible) 585 window_->Focus(); 586 show_state_ = window_->GetProperty(aura::client::kShowStateKey); 587 } 588 } 589 590 ui::WindowShowState GetShowStateAndReset() { 591 ui::WindowShowState ret = show_state_; 592 show_state_ = ui::SHOW_STATE_END; 593 return ret; 594 } 595 596 private: 597 aura::Window* window_; 598 ui::WindowShowState show_state_; 599 600 DISALLOW_COPY_AND_ASSIGN(FocusDelegate); 601 }; 602 603 // Make sure that the window's show state is correct in 604 // |WindowDelegate::OnWindowTargetVisibilityChanged|, and setting 605 // focus in this callback doesn't cause DCHECK error. See 606 // crbug.com/168383. 607 TEST_F(WorkspaceLayoutManagerSoloTest, FocusDuringUnminimize) { 608 FocusDelegate delegate; 609 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( 610 &delegate, 0, gfx::Rect(100, 100, 100, 100))); 611 delegate.set_window(window.get()); 612 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 613 EXPECT_FALSE(window->IsVisible()); 614 EXPECT_EQ(ui::SHOW_STATE_MINIMIZED, delegate.GetShowStateAndReset()); 615 window->Show(); 616 EXPECT_TRUE(window->IsVisible()); 617 EXPECT_EQ(ui::SHOW_STATE_NORMAL, delegate.GetShowStateAndReset()); 618 } 619 620 // Tests maximized window size during root window resize. 621 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeRootWindowResize) { 622 gfx::Rect bounds(100, 100, 200, 200); 623 scoped_ptr<aura::Window> window(CreateTestWindow(bounds)); 624 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 625 gfx::Rect initial_work_area_bounds = 626 ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()); 627 EXPECT_EQ(initial_work_area_bounds.ToString(), window->bounds().ToString()); 628 // Enlarge the root window. We should still match the work area size. 629 UpdateDisplay("900x700"); 630 EXPECT_EQ( 631 ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(), 632 window->bounds().ToString()); 633 EXPECT_NE( 634 initial_work_area_bounds.ToString(), 635 ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString()); 636 } 637 638 // Tests normal->fullscreen->normal. 639 TEST_F(WorkspaceLayoutManagerSoloTest, Fullscreen) { 640 gfx::Rect bounds(100, 100, 200, 200); 641 scoped_ptr<aura::Window> window(CreateTestWindow(bounds)); 642 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 643 // Fullscreen window fills the whole display. 644 EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow( 645 window.get()).bounds().ToString(), 646 window->bounds().ToString()); 647 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 648 EXPECT_EQ(bounds.ToString(), window->bounds().ToString()); 649 } 650 651 // Tests fullscreen window size during root window resize. 652 TEST_F(WorkspaceLayoutManagerSoloTest, FullscreenRootWindowResize) { 653 gfx::Rect bounds(100, 100, 200, 200); 654 scoped_ptr<aura::Window> window(CreateTestWindow(bounds)); 655 // Fullscreen window fills the whole display. 656 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 657 EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow( 658 window.get()).bounds().ToString(), 659 window->bounds().ToString()); 660 // Enlarge the root window. We should still match the display size. 661 UpdateDisplay("800x600"); 662 EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow( 663 window.get()).bounds().ToString(), 664 window->bounds().ToString()); 665 } 666 667 // Tests that when the screen gets smaller the windows aren't bigger than 668 // the screen. 669 TEST_F(WorkspaceLayoutManagerSoloTest, RootWindowResizeShrinksWindows) { 670 scoped_ptr<aura::Window> window( 671 CreateTestWindow(gfx::Rect(10, 20, 500, 400))); 672 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( 673 window.get()).work_area(); 674 // Invariant: Window is smaller than work area. 675 EXPECT_LE(window->bounds().width(), work_area.width()); 676 EXPECT_LE(window->bounds().height(), work_area.height()); 677 678 // Make the root window narrower than our window. 679 UpdateDisplay("300x400"); 680 work_area = Shell::GetScreen()->GetDisplayNearestWindow( 681 window.get()).work_area(); 682 EXPECT_LE(window->bounds().width(), work_area.width()); 683 EXPECT_LE(window->bounds().height(), work_area.height()); 684 685 // Make the root window shorter than our window. 686 UpdateDisplay("300x200"); 687 work_area = Shell::GetScreen()->GetDisplayNearestWindow( 688 window.get()).work_area(); 689 EXPECT_LE(window->bounds().width(), work_area.width()); 690 EXPECT_LE(window->bounds().height(), work_area.height()); 691 692 // Enlarging the root window does not change the window bounds. 693 gfx::Rect old_bounds = window->bounds(); 694 UpdateDisplay("800x600"); 695 EXPECT_EQ(old_bounds.width(), window->bounds().width()); 696 EXPECT_EQ(old_bounds.height(), window->bounds().height()); 697 } 698 699 // Verifies maximizing sets the restore bounds, and restoring 700 // restores the bounds. 701 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeSetsRestoreBounds) { 702 scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(10, 20, 30, 40))); 703 wm::WindowState* window_state = wm::GetWindowState(window.get()); 704 705 // Maximize it, which will keep the previous restore bounds. 706 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 707 EXPECT_EQ("10,20 30x40", window_state->GetRestoreBoundsInParent().ToString()); 708 709 // Restore it, which should restore bounds and reset restore bounds. 710 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 711 EXPECT_EQ("10,20 30x40", window->bounds().ToString()); 712 EXPECT_FALSE(window_state->HasRestoreBounds()); 713 } 714 715 // Verifies maximizing keeps the restore bounds if set. 716 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeResetsRestoreBounds) { 717 scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4))); 718 719 wm::WindowState* window_state = wm::GetWindowState(window.get()); 720 window_state->SetRestoreBoundsInParent(gfx::Rect(10, 11, 12, 13)); 721 722 // Maximize it, which will keep the previous restore bounds. 723 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 724 EXPECT_EQ("10,11 12x13", window_state->GetRestoreBoundsInParent().ToString()); 725 } 726 727 // Verifies that the restore bounds do not get reset when restoring to a 728 // maximzied state from a minimized state. 729 TEST_F(WorkspaceLayoutManagerSoloTest, 730 BoundsAfterRestoringToMaximizeFromMinimize) { 731 scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4))); 732 gfx::Rect bounds(10, 15, 25, 35); 733 window->SetBounds(bounds); 734 735 wm::WindowState* window_state = wm::GetWindowState(window.get()); 736 // Maximize it, which should reset restore bounds. 737 window_state->Maximize(); 738 EXPECT_EQ(bounds.ToString(), 739 window_state->GetRestoreBoundsInParent().ToString()); 740 // Minimize the window. The restore bounds should not change. 741 window_state->Minimize(); 742 EXPECT_EQ(bounds.ToString(), 743 window_state->GetRestoreBoundsInParent().ToString()); 744 745 // Show the window again. The window should be maximized, and the restore 746 // bounds should not change. 747 window->Show(); 748 EXPECT_EQ(bounds.ToString(), 749 window_state->GetRestoreBoundsInParent().ToString()); 750 EXPECT_TRUE(window_state->IsMaximized()); 751 752 window_state->Restore(); 753 EXPECT_EQ(bounds.ToString(), window->bounds().ToString()); 754 } 755 756 // Verify if the window is not resized during screen lock. See: crbug.com/173127 757 TEST_F(WorkspaceLayoutManagerSoloTest, NotResizeWhenScreenIsLocked) { 758 SetCanLockScreen(true); 759 scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4))); 760 // window with AlwaysOnTop will be managed by BaseLayoutManager. 761 window->SetProperty(aura::client::kAlwaysOnTopKey, true); 762 window->Show(); 763 764 ShelfLayoutManager* shelf = ShelfLayoutManager::ForShelf(window.get()); 765 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 766 767 window->SetBounds(ScreenUtil::GetMaximizedWindowBoundsInParent(window.get())); 768 gfx::Rect window_bounds = window->bounds(); 769 EXPECT_EQ( 770 ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(), 771 window_bounds.ToString()); 772 773 Shell::GetInstance()->session_state_delegate()->LockScreen(); 774 shelf->UpdateVisibilityState(); 775 EXPECT_NE( 776 ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(), 777 window_bounds.ToString()); 778 779 Shell::GetInstance()->session_state_delegate()->UnlockScreen(); 780 shelf->UpdateVisibilityState(); 781 EXPECT_EQ(window_bounds.ToString(), window->bounds().ToString()); 782 } 783 784 // Following tests are written to test the backdrop functionality. 785 786 namespace { 787 788 class WorkspaceLayoutManagerBackdropTest : public test::AshTestBase { 789 public: 790 WorkspaceLayoutManagerBackdropTest() {} 791 virtual ~WorkspaceLayoutManagerBackdropTest() {} 792 793 virtual void SetUp() OVERRIDE { 794 test::AshTestBase::SetUp(); 795 UpdateDisplay("800x600"); 796 default_container_ = Shell::GetContainer(Shell::GetPrimaryRootWindow(), 797 kShellWindowId_DefaultContainer); 798 } 799 800 aura::Window* CreateTestWindow(const gfx::Rect& bounds) { 801 aura::Window* window = CreateTestWindowInShellWithBounds(bounds); 802 return window; 803 } 804 805 // Turn the top window back drop on / off. 806 void ShowTopWindowBackdrop(bool show) { 807 scoped_ptr<ash::WorkspaceLayoutManagerDelegate> backdrop; 808 if (show) { 809 backdrop.reset(new ash::WorkspaceBackdropDelegate(default_container_)); 810 } 811 (static_cast<WorkspaceLayoutManager*>(default_container_->layout_manager())) 812 ->SetMaximizeBackdropDelegate(backdrop.Pass()); 813 // Closing and / or opening can be a delayed operation. 814 base::MessageLoop::current()->RunUntilIdle(); 815 } 816 817 // Return the default container. 818 aura::Window* default_container() { return default_container_; } 819 820 // Return the order of windows (top most first) as they are in the default 821 // container. If the window is visible it will be a big letter, otherwise a 822 // small one. The backdrop will be an X and unknown windows will be shown as 823 // '!'. 824 std::string GetWindowOrderAsString(aura::Window* backdrop, 825 aura::Window* wa, 826 aura::Window* wb, 827 aura::Window* wc) { 828 std::string result; 829 for (int i = static_cast<int>(default_container()->children().size()) - 1; 830 i >= 0; 831 --i) { 832 if (!result.empty()) 833 result += ","; 834 if (default_container()->children()[i] == wa) 835 result += default_container()->children()[i]->IsVisible() ? "A" : "a"; 836 else if (default_container()->children()[i] == wb) 837 result += default_container()->children()[i]->IsVisible() ? "B" : "b"; 838 else if (default_container()->children()[i] == wc) 839 result += default_container()->children()[i]->IsVisible() ? "C" : "c"; 840 else if (default_container()->children()[i] == backdrop) 841 result += default_container()->children()[i]->IsVisible() ? "X" : "x"; 842 else 843 result += "!"; 844 } 845 return result; 846 } 847 848 private: 849 // The default container. 850 aura::Window* default_container_; 851 852 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerBackdropTest); 853 }; 854 855 } // namespace 856 857 // Check that creating the BackDrop without destroying it does not lead into 858 // a crash. 859 TEST_F(WorkspaceLayoutManagerBackdropTest, BackdropCrashTest) { 860 ShowTopWindowBackdrop(true); 861 } 862 863 // Verify basic assumptions about the backdrop. 864 TEST_F(WorkspaceLayoutManagerBackdropTest, BasicBackdropTests) { 865 // Create a backdrop and see that there is one window (the backdrop) and 866 // that the size is the same as the default container as well as that it is 867 // not visible. 868 ShowTopWindowBackdrop(true); 869 ASSERT_EQ(1U, default_container()->children().size()); 870 EXPECT_FALSE(default_container()->children()[0]->IsVisible()); 871 872 { 873 // Add a window and make sure that the backdrop is the second child. 874 scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4))); 875 window->Show(); 876 ASSERT_EQ(2U, default_container()->children().size()); 877 EXPECT_TRUE(default_container()->children()[0]->IsVisible()); 878 EXPECT_TRUE(default_container()->children()[1]->IsVisible()); 879 EXPECT_EQ(window.get(), default_container()->children()[1]); 880 EXPECT_EQ(default_container()->bounds().ToString(), 881 default_container()->children()[0]->bounds().ToString()); 882 } 883 884 // With the window gone the backdrop should be invisible again. 885 ASSERT_EQ(1U, default_container()->children().size()); 886 EXPECT_FALSE(default_container()->children()[0]->IsVisible()); 887 888 // Destroying the Backdrop should empty the container. 889 ShowTopWindowBackdrop(false); 890 ASSERT_EQ(0U, default_container()->children().size()); 891 } 892 893 // Verify that the backdrop gets properly created and placed. 894 TEST_F(WorkspaceLayoutManagerBackdropTest, VerifyBackdropAndItsStacking) { 895 scoped_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(1, 2, 3, 4))); 896 window1->Show(); 897 898 // Get the default container and check that only a single window is in there. 899 ASSERT_EQ(1U, default_container()->children().size()); 900 EXPECT_EQ(window1.get(), default_container()->children()[0]); 901 EXPECT_EQ("A", GetWindowOrderAsString(NULL, window1.get(), NULL, NULL)); 902 903 // Create 2 more windows and check that they are also in the container. 904 scoped_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(10, 2, 3, 4))); 905 scoped_ptr<aura::Window> window3(CreateTestWindow(gfx::Rect(20, 2, 3, 4))); 906 window2->Show(); 907 window3->Show(); 908 909 aura::Window* backdrop = NULL; 910 EXPECT_EQ("C,B,A", 911 GetWindowOrderAsString(backdrop, window1.get(), window2.get(), 912 window3.get())); 913 914 // Turn on the backdrop mode and check that the window shows up where it 915 // should be (second highest number). 916 ShowTopWindowBackdrop(true); 917 backdrop = default_container()->children()[2]; 918 EXPECT_EQ("C,X,B,A", 919 GetWindowOrderAsString(backdrop, window1.get(), window2.get(), 920 window3.get())); 921 922 // Switch the order of windows and check that it still remains in that 923 // location. 924 default_container()->StackChildAtTop(window2.get()); 925 EXPECT_EQ("B,X,C,A", 926 GetWindowOrderAsString(backdrop, window1.get(), window2.get(), 927 window3.get())); 928 929 // Make the top window invisible and check. 930 window2.get()->Hide(); 931 EXPECT_EQ("b,C,X,A", 932 GetWindowOrderAsString(backdrop, window1.get(), window2.get(), 933 window3.get())); 934 // Then delete window after window and see that everything is in order. 935 window1.reset(); 936 EXPECT_EQ("b,C,X", 937 GetWindowOrderAsString(backdrop, window1.get(), window2.get(), 938 window3.get())); 939 window3.reset(); 940 EXPECT_EQ("b,x", 941 GetWindowOrderAsString(backdrop, window1.get(), window2.get(), 942 window3.get())); 943 ShowTopWindowBackdrop(false); 944 EXPECT_EQ("b", 945 GetWindowOrderAsString(NULL, window1.get(), window2.get(), 946 window3.get())); 947 } 948 949 // Tests that when hidding the shelf, that the backdrop resizes to fill the 950 // entire workspace area. 951 TEST_F(WorkspaceLayoutManagerBackdropTest, ShelfVisibilityChangesBounds) { 952 ShelfLayoutManager* shelf_layout_manager = 953 Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); 954 ShowTopWindowBackdrop(true); 955 RunAllPendingInMessageLoop(); 956 957 ASSERT_EQ(SHELF_VISIBLE, shelf_layout_manager->visibility_state()); 958 gfx::Rect initial_bounds = default_container()->children()[0]->bounds(); 959 shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_ALWAYS_HIDDEN); 960 shelf_layout_manager->UpdateVisibilityState(); 961 962 // When the shelf is re-shown WorkspaceLayoutManager shrinks all children 963 // including the backdrop. 964 shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 965 shelf_layout_manager->UpdateVisibilityState(); 966 gfx::Rect reduced_bounds = default_container()->children()[0]->bounds(); 967 EXPECT_LT(reduced_bounds.height(), initial_bounds.height()); 968 969 shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_ALWAYS_HIDDEN); 970 shelf_layout_manager->UpdateVisibilityState(); 971 972 EXPECT_GT(default_container()->children()[0]->bounds().height(), 973 reduced_bounds.height()); 974 } 975 976 class WorkspaceLayoutManagerKeyboardTest : public test::AshTestBase { 977 public: 978 WorkspaceLayoutManagerKeyboardTest() {} 979 virtual ~WorkspaceLayoutManagerKeyboardTest() {} 980 981 virtual void SetUp() OVERRIDE { 982 test::AshTestBase::SetUp(); 983 UpdateDisplay("800x600"); 984 aura::Window* default_container = Shell::GetContainer( 985 Shell::GetPrimaryRootWindow(), kShellWindowId_DefaultContainer); 986 layout_manager_ = static_cast<WorkspaceLayoutManager*>( 987 default_container->layout_manager()); 988 } 989 990 aura::Window* CreateTestWindow(const gfx::Rect& bounds) { 991 return CreateTestWindowInShellWithBounds(bounds); 992 } 993 994 void ShowKeyboard() { 995 layout_manager_->OnKeyboardBoundsChanging(keyboard_bounds_); 996 restore_work_area_insets_ = Shell::GetScreen()->GetPrimaryDisplay(). 997 GetWorkAreaInsets(); 998 Shell::GetInstance()->SetDisplayWorkAreaInsets( 999 Shell::GetPrimaryRootWindow(), 1000 gfx::Insets(0, 0, keyboard_bounds_.height(), 0)); 1001 } 1002 1003 void HideKeyboard() { 1004 Shell::GetInstance()->SetDisplayWorkAreaInsets( 1005 Shell::GetPrimaryRootWindow(), 1006 restore_work_area_insets_); 1007 layout_manager_->OnKeyboardBoundsChanging(gfx::Rect()); 1008 } 1009 1010 void SetKeyboardBounds(const gfx::Rect& bounds) { 1011 keyboard_bounds_ = bounds; 1012 } 1013 1014 void Focus(ui::TextInputClient* text_input_client) { 1015 if (switches::IsTextInputFocusManagerEnabled()) { 1016 ui::TextInputFocusManager::GetInstance()->FocusTextInputClient( 1017 text_input_client); 1018 } else { 1019 aura::Window* root_window = 1020 ash::Shell::GetInstance()->GetPrimaryRootWindow(); 1021 ui::InputMethod* input_method = 1022 root_window->GetProperty(aura::client::kRootWindowInputMethodKey); 1023 input_method->SetFocusedTextInputClient(text_input_client); 1024 } 1025 } 1026 1027 void Blur(ui::TextInputClient* text_input_client) { 1028 if (switches::IsTextInputFocusManagerEnabled()) { 1029 ui::TextInputFocusManager::GetInstance()->BlurTextInputClient( 1030 text_input_client); 1031 } else { 1032 aura::Window* root_window = 1033 ash::Shell::GetInstance()->GetPrimaryRootWindow(); 1034 ui::InputMethod* input_method = 1035 root_window->GetProperty(aura::client::kRootWindowInputMethodKey); 1036 input_method->SetFocusedTextInputClient(NULL); 1037 } 1038 } 1039 1040 private: 1041 gfx::Insets restore_work_area_insets_; 1042 gfx::Rect keyboard_bounds_; 1043 WorkspaceLayoutManager* layout_manager_; 1044 1045 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerKeyboardTest); 1046 }; 1047 1048 class FakeTextInputClient : public ui::DummyTextInputClient { 1049 public: 1050 explicit FakeTextInputClient(gfx::NativeWindow window) : window_(window) {} 1051 virtual ~FakeTextInputClient() {} 1052 1053 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE { 1054 return window_; 1055 } 1056 1057 private: 1058 gfx::NativeWindow window_; 1059 1060 DISALLOW_COPY_AND_ASSIGN(FakeTextInputClient); 1061 }; 1062 1063 // Tests that when a child window gains focus the top level window containing it 1064 // is resized to fit the remaining workspace area. 1065 TEST_F(WorkspaceLayoutManagerKeyboardTest, ChildWindowFocused) { 1066 gfx::Rect work_area( 1067 Shell::GetScreen()->GetPrimaryDisplay().work_area()); 1068 gfx::Rect keyboard_bounds(work_area.x(), 1069 work_area.y() + work_area.height() / 2, 1070 work_area.width(), 1071 work_area.height() / 2); 1072 1073 SetKeyboardBounds(keyboard_bounds); 1074 1075 aura::test::TestWindowDelegate delegate1; 1076 scoped_ptr<aura::Window> parent_window(CreateTestWindowInShellWithDelegate( 1077 &delegate1, -1, work_area)); 1078 aura::test::TestWindowDelegate delegate2; 1079 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( 1080 &delegate2, -1, work_area)); 1081 parent_window->AddChild(window.get()); 1082 1083 FakeTextInputClient text_input_client(window.get()); 1084 Focus(&text_input_client); 1085 1086 int available_height = 1087 Shell::GetScreen()->GetPrimaryDisplay().bounds().height() - 1088 keyboard_bounds.height(); 1089 1090 gfx::Rect initial_window_bounds(50, 50, 100, 500); 1091 parent_window->SetBounds(initial_window_bounds); 1092 EXPECT_EQ(initial_window_bounds.ToString(), 1093 parent_window->bounds().ToString()); 1094 ShowKeyboard(); 1095 EXPECT_EQ(gfx::Rect(50, 0, 100, available_height).ToString(), 1096 parent_window->bounds().ToString()); 1097 HideKeyboard(); 1098 EXPECT_EQ(initial_window_bounds.ToString(), 1099 parent_window->bounds().ToString()); 1100 1101 Blur(&text_input_client); 1102 } 1103 1104 TEST_F(WorkspaceLayoutManagerKeyboardTest, AdjustWindowForA11yKeyboard) { 1105 gfx::Rect work_area( 1106 Shell::GetScreen()->GetPrimaryDisplay().work_area()); 1107 gfx::Rect keyboard_bounds(work_area.x(), 1108 work_area.y() + work_area.height() / 2, 1109 work_area.width(), 1110 work_area.height() / 2); 1111 1112 SetKeyboardBounds(keyboard_bounds); 1113 1114 aura::test::TestWindowDelegate delegate; 1115 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( 1116 &delegate, -1, work_area)); 1117 1118 FakeTextInputClient text_input_client(window.get()); 1119 Focus(&text_input_client); 1120 1121 int available_height = 1122 Shell::GetScreen()->GetPrimaryDisplay().bounds().height() - 1123 keyboard_bounds.height(); 1124 1125 EXPECT_EQ(gfx::Rect(work_area).ToString(), window->bounds().ToString()); 1126 ShowKeyboard(); 1127 EXPECT_EQ(gfx::Rect(work_area.origin(), 1128 gfx::Size(work_area.width(), available_height)).ToString(), 1129 window->bounds().ToString()); 1130 HideKeyboard(); 1131 EXPECT_EQ(gfx::Rect(work_area).ToString(), window->bounds().ToString()); 1132 1133 gfx::Rect small_window_bound(50, 50, 100, 500); 1134 window->SetBounds(small_window_bound); 1135 EXPECT_EQ(small_window_bound.ToString(), window->bounds().ToString()); 1136 ShowKeyboard(); 1137 EXPECT_EQ(gfx::Rect(50, 0, 100, available_height).ToString(), 1138 window->bounds().ToString()); 1139 HideKeyboard(); 1140 EXPECT_EQ(small_window_bound.ToString(), window->bounds().ToString()); 1141 1142 gfx::Rect occluded_window_bounds(50, 1143 keyboard_bounds.y() + keyboard_bounds.height()/2, 50, 1144 keyboard_bounds.height()/2); 1145 window->SetBounds(occluded_window_bounds); 1146 EXPECT_EQ(occluded_window_bounds.ToString(), 1147 occluded_window_bounds.ToString()); 1148 ShowKeyboard(); 1149 EXPECT_EQ(gfx::Rect(50, 1150 keyboard_bounds.y() - keyboard_bounds.height()/2, 1151 occluded_window_bounds.width(), 1152 occluded_window_bounds.height()).ToString(), 1153 window->bounds().ToString()); 1154 HideKeyboard(); 1155 EXPECT_EQ(occluded_window_bounds.ToString(), window->bounds().ToString()); 1156 1157 Blur(&text_input_client); 1158 } 1159 1160 } // namespace ash 1161