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/display/display_controller.h" 6 #include "ash/display/display_manager.h" 7 #include "ash/root_window_controller.h" 8 #include "ash/screen_ash.h" 9 #include "ash/shell.h" 10 #include "ash/shell_window_ids.h" 11 #include "ash/system/tray/system_tray.h" 12 #include "ash/test/ash_test_base.h" 13 #include "ash/wm/coordinate_conversion.h" 14 #include "ash/wm/property_util.h" 15 #include "ash/wm/window_cycle_controller.h" 16 #include "ash/wm/window_properties.h" 17 #include "ash/wm/window_util.h" 18 #include "base/strings/string_util.h" 19 #include "ui/aura/client/activation_client.h" 20 #include "ui/aura/client/capture_client.h" 21 #include "ui/aura/client/focus_client.h" 22 #include "ui/aura/root_window.h" 23 #include "ui/aura/test/event_generator.h" 24 #include "ui/aura/test/test_windows.h" 25 #include "ui/aura/test/window_test_api.h" 26 #include "ui/aura/window.h" 27 #include "ui/base/cursor/cursor.h" 28 #include "ui/base/events/event_handler.h" 29 #include "ui/gfx/display.h" 30 #include "ui/gfx/screen.h" 31 #include "ui/views/controls/textfield/textfield.h" 32 #include "ui/views/widget/widget.h" 33 #include "ui/views/widget/widget_delegate.h" 34 35 namespace ash { 36 namespace { 37 38 void SetSecondaryDisplayLayout(DisplayLayout::Position position) { 39 DisplayController* display_controller = 40 Shell::GetInstance()->display_controller(); 41 DisplayLayout layout = display_controller->GetCurrentDisplayLayout(); 42 layout.position = position; 43 display_controller->SetLayoutForCurrentDisplays(layout); 44 } 45 46 internal::DisplayManager* GetDisplayManager() { 47 return Shell::GetInstance()->display_manager(); 48 } 49 50 class ModalWidgetDelegate : public views::WidgetDelegateView { 51 public: 52 ModalWidgetDelegate() {} 53 virtual ~ModalWidgetDelegate() {} 54 55 // Overridden from views::WidgetDelegate: 56 virtual views::View* GetContentsView() OVERRIDE { 57 return this; 58 } 59 virtual ui::ModalType GetModalType() const OVERRIDE { 60 return ui::MODAL_TYPE_SYSTEM; 61 } 62 63 private: 64 DISALLOW_COPY_AND_ASSIGN(ModalWidgetDelegate); 65 }; 66 67 // An event handler which moves the target window to the secondary root window 68 // at pre-handle phase of a mouse release event. 69 class MoveWindowByClickEventHandler : public ui::EventHandler { 70 public: 71 explicit MoveWindowByClickEventHandler(aura::Window* target) 72 : target_(target) {} 73 virtual ~MoveWindowByClickEventHandler() {} 74 75 private: 76 // ui::EventHandler overrides: 77 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 78 if (event->type() == ui::ET_MOUSE_RELEASED) { 79 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 80 DCHECK_LT(1u, root_windows.size()); 81 root_windows[1]->AddChild(target_); 82 } 83 } 84 85 aura::Window* target_; 86 DISALLOW_COPY_AND_ASSIGN(MoveWindowByClickEventHandler); 87 }; 88 89 // An event handler which records the event's locations. 90 class EventLocationRecordingEventHandler : public ui::EventHandler { 91 public: 92 explicit EventLocationRecordingEventHandler() { 93 reset(); 94 } 95 virtual ~EventLocationRecordingEventHandler() {} 96 97 std::string GetLocationsAndReset() { 98 std::string result = 99 location_.ToString() + " " + root_location_.ToString(); 100 reset(); 101 return result; 102 } 103 104 private: 105 // ui::EventHandler overrides: 106 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 107 if (event->type() == ui::ET_MOUSE_MOVED || 108 event->type() == ui::ET_MOUSE_DRAGGED) { 109 location_ = event->location(); 110 root_location_ = event->root_location(); 111 } 112 } 113 114 void reset() { 115 location_.SetPoint(-999, -999); 116 root_location_.SetPoint(-999, -999); 117 } 118 119 gfx::Point root_location_; 120 gfx::Point location_; 121 122 DISALLOW_COPY_AND_ASSIGN(EventLocationRecordingEventHandler); 123 }; 124 125 } // namespace 126 127 class ExtendedDesktopTest : public test::AshTestBase { 128 public: 129 views::Widget* CreateTestWidget(const gfx::Rect& bounds) { 130 return CreateTestWidgetWithParentAndContext( 131 NULL, CurrentContext(), bounds, false); 132 } 133 134 views::Widget* CreateTestWidgetWithParent(views::Widget* parent, 135 const gfx::Rect& bounds, 136 bool child) { 137 CHECK(parent); 138 return CreateTestWidgetWithParentAndContext(parent, NULL, bounds, child); 139 } 140 141 views::Widget* CreateTestWidgetWithParentAndContext(views::Widget* parent, 142 gfx::NativeView context, 143 const gfx::Rect& bounds, 144 bool child) { 145 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); 146 if (parent) 147 params.parent = parent->GetNativeView(); 148 params.context = context; 149 params.bounds = bounds; 150 params.child = child; 151 views::Widget* widget = new views::Widget; 152 widget->Init(params); 153 widget->Show(); 154 return widget; 155 } 156 }; 157 158 // Test conditions that root windows in extended desktop mode 159 // must satisfy. 160 TEST_F(ExtendedDesktopTest, Basic) { 161 if (!SupportsMultipleDisplays()) 162 return; 163 164 UpdateDisplay("1000x600,600x400"); 165 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 166 167 // All root windows must have the root window controller. 168 ASSERT_EQ(2U, root_windows.size()); 169 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); 170 iter != root_windows.end(); ++iter) { 171 EXPECT_TRUE(GetRootWindowController(*iter) != NULL); 172 } 173 // Make sure root windows share the same controllers. 174 EXPECT_EQ(aura::client::GetFocusClient(root_windows[0]), 175 aura::client::GetFocusClient(root_windows[1])); 176 EXPECT_EQ(aura::client::GetActivationClient(root_windows[0]), 177 aura::client::GetActivationClient(root_windows[1])); 178 EXPECT_EQ(aura::client::GetCaptureClient(root_windows[0]), 179 aura::client::GetCaptureClient(root_windows[1])); 180 } 181 182 TEST_F(ExtendedDesktopTest, Activation) { 183 if (!SupportsMultipleDisplays()) 184 return; 185 186 UpdateDisplay("1000x600,600x400"); 187 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 188 189 views::Widget* widget_on_1st = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 190 views::Widget* widget_on_2nd = 191 CreateTestWidget(gfx::Rect(1200, 10, 100, 100)); 192 EXPECT_EQ(root_windows[0], widget_on_1st->GetNativeView()->GetRootWindow()); 193 EXPECT_EQ(root_windows[1], widget_on_2nd->GetNativeView()->GetRootWindow()); 194 195 EXPECT_EQ(widget_on_2nd->GetNativeView(), 196 aura::client::GetFocusClient(root_windows[0])->GetFocusedWindow()); 197 EXPECT_TRUE(wm::IsActiveWindow(widget_on_2nd->GetNativeView())); 198 199 aura::test::EventGenerator& event_generator(GetEventGenerator()); 200 // Clicking a window changes the active window and active root window. 201 event_generator.MoveMouseToCenterOf(widget_on_1st->GetNativeView()); 202 event_generator.ClickLeftButton(); 203 204 EXPECT_EQ(widget_on_1st->GetNativeView(), 205 aura::client::GetFocusClient(root_windows[0])->GetFocusedWindow()); 206 EXPECT_TRUE(wm::IsActiveWindow(widget_on_1st->GetNativeView())); 207 208 event_generator.MoveMouseToCenterOf(widget_on_2nd->GetNativeView()); 209 event_generator.ClickLeftButton(); 210 211 EXPECT_EQ(widget_on_2nd->GetNativeView(), 212 aura::client::GetFocusClient(root_windows[0])->GetFocusedWindow()); 213 EXPECT_TRUE(wm::IsActiveWindow(widget_on_2nd->GetNativeView())); 214 } 215 216 TEST_F(ExtendedDesktopTest, SystemModal) { 217 if (!SupportsMultipleDisplays()) 218 return; 219 220 UpdateDisplay("1000x600,600x400"); 221 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 222 223 views::Widget* widget_on_1st = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 224 EXPECT_TRUE(wm::IsActiveWindow(widget_on_1st->GetNativeView())); 225 EXPECT_EQ(root_windows[0], widget_on_1st->GetNativeView()->GetRootWindow()); 226 EXPECT_EQ(root_windows[0], Shell::GetActiveRootWindow()); 227 228 // Open system modal. Make sure it's on 2nd root window and active. 229 views::Widget* modal_widget = views::Widget::CreateWindowWithContextAndBounds( 230 new ModalWidgetDelegate(), 231 CurrentContext(), 232 gfx::Rect(1200, 100, 100, 100)); 233 modal_widget->Show(); 234 EXPECT_TRUE(wm::IsActiveWindow(modal_widget->GetNativeView())); 235 EXPECT_EQ(root_windows[1], modal_widget->GetNativeView()->GetRootWindow()); 236 EXPECT_EQ(root_windows[1], Shell::GetActiveRootWindow()); 237 238 aura::test::EventGenerator& event_generator(GetEventGenerator()); 239 240 // Clicking a widget on widget_on_1st display should not change activation. 241 event_generator.MoveMouseToCenterOf(widget_on_1st->GetNativeView()); 242 event_generator.ClickLeftButton(); 243 EXPECT_TRUE(wm::IsActiveWindow(modal_widget->GetNativeView())); 244 EXPECT_EQ(root_windows[1], Shell::GetActiveRootWindow()); 245 246 // Close system modal and so clicking a widget should work now. 247 modal_widget->Close(); 248 event_generator.MoveMouseToCenterOf(widget_on_1st->GetNativeView()); 249 event_generator.ClickLeftButton(); 250 EXPECT_TRUE(wm::IsActiveWindow(widget_on_1st->GetNativeView())); 251 EXPECT_EQ(root_windows[0], Shell::GetActiveRootWindow()); 252 } 253 254 TEST_F(ExtendedDesktopTest, TestCursor) { 255 if (!SupportsMultipleDisplays()) 256 return; 257 258 UpdateDisplay("1000x600,600x400"); 259 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 260 EXPECT_EQ(ui::kCursorPointer, root_windows[0]->last_cursor().native_type()); 261 EXPECT_EQ(ui::kCursorPointer, root_windows[1]->last_cursor().native_type()); 262 Shell::GetInstance()->cursor_manager()->SetCursor(ui::kCursorCopy); 263 EXPECT_EQ(ui::kCursorCopy, root_windows[0]->last_cursor().native_type()); 264 EXPECT_EQ(ui::kCursorCopy, root_windows[1]->last_cursor().native_type()); 265 } 266 267 TEST_F(ExtendedDesktopTest, TestCursorLocation) { 268 if (!SupportsMultipleDisplays()) 269 return; 270 271 UpdateDisplay("1000x600,600x400"); 272 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 273 aura::test::WindowTestApi root_window0_test_api(root_windows[0]); 274 aura::test::WindowTestApi root_window1_test_api(root_windows[1]); 275 276 root_windows[0]->MoveCursorTo(gfx::Point(10, 10)); 277 EXPECT_EQ("10,10", Shell::GetScreen()->GetCursorScreenPoint().ToString()); 278 EXPECT_TRUE(root_window0_test_api.ContainsMouse()); 279 EXPECT_FALSE(root_window1_test_api.ContainsMouse()); 280 root_windows[1]->MoveCursorTo(gfx::Point(10, 20)); 281 EXPECT_EQ("1010,20", Shell::GetScreen()->GetCursorScreenPoint().ToString()); 282 EXPECT_FALSE(root_window0_test_api.ContainsMouse()); 283 EXPECT_TRUE(root_window1_test_api.ContainsMouse()); 284 root_windows[0]->MoveCursorTo(gfx::Point(20, 10)); 285 EXPECT_EQ("20,10", Shell::GetScreen()->GetCursorScreenPoint().ToString()); 286 EXPECT_TRUE(root_window0_test_api.ContainsMouse()); 287 EXPECT_FALSE(root_window1_test_api.ContainsMouse()); 288 } 289 290 TEST_F(ExtendedDesktopTest, CycleWindows) { 291 if (!SupportsMultipleDisplays()) 292 return; 293 294 UpdateDisplay("700x500,500x500"); 295 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 296 297 WindowCycleController* controller = 298 Shell::GetInstance()->window_cycle_controller(); 299 300 views::Widget* d1_w1 = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 301 EXPECT_EQ(root_windows[0], d1_w1->GetNativeView()->GetRootWindow()); 302 views::Widget* d2_w1 = CreateTestWidget(gfx::Rect(800, 10, 100, 100)); 303 EXPECT_EQ(root_windows[1], d2_w1->GetNativeView()->GetRootWindow()); 304 EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView())); 305 306 controller->HandleCycleWindow(WindowCycleController::FORWARD, false); 307 EXPECT_TRUE(wm::IsActiveWindow(d1_w1->GetNativeView())); 308 controller->HandleCycleWindow(WindowCycleController::FORWARD, false); 309 EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView())); 310 controller->HandleCycleWindow(WindowCycleController::BACKWARD, false); 311 EXPECT_TRUE(wm::IsActiveWindow(d1_w1->GetNativeView())); 312 controller->HandleCycleWindow(WindowCycleController::BACKWARD, false); 313 EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView())); 314 315 // Cycle through all windows across root windows. 316 views::Widget* d1_w2 = CreateTestWidget(gfx::Rect(10, 200, 100, 100)); 317 EXPECT_EQ(root_windows[0], d1_w2->GetNativeView()->GetRootWindow()); 318 views::Widget* d2_w2 = CreateTestWidget(gfx::Rect(800, 200, 100, 100)); 319 EXPECT_EQ(root_windows[1], d2_w2->GetNativeView()->GetRootWindow()); 320 321 controller->HandleCycleWindow(WindowCycleController::FORWARD, true); 322 EXPECT_TRUE(wm::IsActiveWindow(d1_w2->GetNativeView())); 323 controller->HandleCycleWindow(WindowCycleController::FORWARD, true); 324 EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView())); 325 controller->HandleCycleWindow(WindowCycleController::FORWARD, true); 326 EXPECT_TRUE(wm::IsActiveWindow(d1_w1->GetNativeView())); 327 controller->HandleCycleWindow(WindowCycleController::FORWARD, true); 328 EXPECT_TRUE(wm::IsActiveWindow(d2_w2->GetNativeView())); 329 330 // Backwards 331 controller->HandleCycleWindow(WindowCycleController::BACKWARD, true); 332 EXPECT_TRUE(wm::IsActiveWindow(d1_w1->GetNativeView())); 333 controller->HandleCycleWindow(WindowCycleController::BACKWARD, true); 334 EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView())); 335 controller->HandleCycleWindow(WindowCycleController::BACKWARD, true); 336 EXPECT_TRUE(wm::IsActiveWindow(d1_w2->GetNativeView())); 337 controller->HandleCycleWindow(WindowCycleController::BACKWARD, true); 338 EXPECT_TRUE(wm::IsActiveWindow(d2_w2->GetNativeView())); 339 } 340 341 TEST_F(ExtendedDesktopTest, GetRootWindowAt) { 342 if (!SupportsMultipleDisplays()) 343 return; 344 345 UpdateDisplay("700x500,500x500"); 346 SetSecondaryDisplayLayout(DisplayLayout::LEFT); 347 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 348 349 EXPECT_EQ(root_windows[1], wm::GetRootWindowAt(gfx::Point(-400, 100))); 350 EXPECT_EQ(root_windows[1], wm::GetRootWindowAt(gfx::Point(-1, 100))); 351 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(0, 300))); 352 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(700,300))); 353 354 // Zero origin. 355 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(0, 0))); 356 357 // Out of range point should return the primary root window 358 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(-600, 0))); 359 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(701, 100))); 360 } 361 362 TEST_F(ExtendedDesktopTest, GetRootWindowMatching) { 363 if (!SupportsMultipleDisplays()) 364 return; 365 366 UpdateDisplay("700x500,500x500"); 367 SetSecondaryDisplayLayout(DisplayLayout::LEFT); 368 369 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 370 371 // Containing rect. 372 EXPECT_EQ(root_windows[1], 373 wm::GetRootWindowMatching(gfx::Rect(-300, 10, 50, 50))); 374 EXPECT_EQ(root_windows[0], 375 wm::GetRootWindowMatching(gfx::Rect(100, 10, 50, 50))); 376 377 // Intersecting rect. 378 EXPECT_EQ(root_windows[1], 379 wm::GetRootWindowMatching(gfx::Rect(-200, 0, 300, 300))); 380 EXPECT_EQ(root_windows[0], 381 wm::GetRootWindowMatching(gfx::Rect(-100, 0, 300, 300))); 382 383 // Zero origin. 384 EXPECT_EQ(root_windows[0], 385 wm::GetRootWindowMatching(gfx::Rect(0, 0, 0, 0))); 386 EXPECT_EQ(root_windows[0], 387 wm::GetRootWindowMatching(gfx::Rect(0, 0, 1, 1))); 388 389 // Empty rect. 390 EXPECT_EQ(root_windows[1], 391 wm::GetRootWindowMatching(gfx::Rect(-400, 100, 0, 0))); 392 EXPECT_EQ(root_windows[0], 393 wm::GetRootWindowMatching(gfx::Rect(100, 100, 0, 0))); 394 395 // Out of range rect should return the primary root window. 396 EXPECT_EQ(root_windows[0], 397 wm::GetRootWindowMatching(gfx::Rect(-600, -300, 50, 50))); 398 EXPECT_EQ(root_windows[0], 399 wm::GetRootWindowMatching(gfx::Rect(0, 1000, 50, 50))); 400 } 401 402 TEST_F(ExtendedDesktopTest, Capture) { 403 if (!SupportsMultipleDisplays()) 404 return; 405 406 UpdateDisplay("1000x600,600x400"); 407 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 408 409 aura::test::EventCountDelegate r1_d1; 410 aura::test::EventCountDelegate r1_d2; 411 aura::test::EventCountDelegate r2_d1; 412 413 scoped_ptr<aura::Window> r1_w1(aura::test::CreateTestWindowWithDelegate( 414 &r1_d1, 0, gfx::Rect(10, 10, 100, 100), root_windows[0])); 415 scoped_ptr<aura::Window> r1_w2(aura::test::CreateTestWindowWithDelegate( 416 &r1_d2, 0, gfx::Rect(10, 100, 100, 100), root_windows[0])); 417 scoped_ptr<aura::Window> r2_w1(aura::test::CreateTestWindowWithDelegate( 418 &r2_d1, 0, gfx::Rect(10, 10, 100, 100), root_windows[1])); 419 420 r1_w1->SetCapture(); 421 422 EXPECT_EQ(r1_w1.get(), 423 aura::client::GetCaptureWindow(r2_w1->GetRootWindow())); 424 425 aura::test::EventGenerator generator2(root_windows[1]); 426 generator2.MoveMouseToCenterOf(r2_w1.get()); 427 generator2.ClickLeftButton(); 428 EXPECT_EQ("0 0 0", r2_d1.GetMouseMotionCountsAndReset()); 429 EXPECT_EQ("0 0", r2_d1.GetMouseButtonCountsAndReset()); 430 // The mouse is outside. On chromeos, the mouse is warped to the 431 // dest root window, but it's not implemented on Win yet, so 432 // no mouse move event on Win. 433 EXPECT_EQ("1 1 0", r1_d1.GetMouseMotionCountsAndReset()); 434 EXPECT_EQ("1 1", r1_d1.GetMouseButtonCountsAndReset()); 435 // Emulate passive grab. (15,15) on 1st display is (-985,15) on 2nd 436 // display. 437 generator2.MoveMouseTo(-985, 15); 438 EXPECT_EQ("0 1 0", r1_d1.GetMouseMotionCountsAndReset()); 439 440 r1_w2->SetCapture(); 441 EXPECT_EQ(r1_w2.get(), 442 aura::client::GetCaptureWindow(r2_w1->GetRootWindow())); 443 generator2.MoveMouseBy(10, 10); 444 generator2.ClickLeftButton(); 445 EXPECT_EQ("0 0 0", r2_d1.GetMouseMotionCountsAndReset()); 446 EXPECT_EQ("0 0", r2_d1.GetMouseButtonCountsAndReset()); 447 // mouse is already entered. 448 EXPECT_EQ("0 1 0", r1_d2.GetMouseMotionCountsAndReset()); 449 EXPECT_EQ("1 1", r1_d2.GetMouseButtonCountsAndReset()); 450 r1_w2->ReleaseCapture(); 451 EXPECT_EQ(NULL, aura::client::GetCaptureWindow(r2_w1->GetRootWindow())); 452 generator2.MoveMouseTo(15, 15); 453 generator2.ClickLeftButton(); 454 EXPECT_EQ("1 1 0", r2_d1.GetMouseMotionCountsAndReset()); 455 EXPECT_EQ("1 1", r2_d1.GetMouseButtonCountsAndReset()); 456 // Make sure the mouse_moved_handler_ is properly reset. 457 EXPECT_EQ("0 0 0", r1_d2.GetMouseMotionCountsAndReset()); 458 EXPECT_EQ("0 0", r1_d2.GetMouseButtonCountsAndReset()); 459 } 460 461 TEST_F(ExtendedDesktopTest, MoveWindow) { 462 if (!SupportsMultipleDisplays()) 463 return; 464 465 UpdateDisplay("1000x600,600x400"); 466 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 467 views::Widget* d1 = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 468 469 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 470 471 d1->SetBounds(gfx::Rect(1010, 10, 100, 100)); 472 EXPECT_EQ("1010,10 100x100", 473 d1->GetWindowBoundsInScreen().ToString()); 474 475 EXPECT_EQ(root_windows[1], d1->GetNativeView()->GetRootWindow()); 476 477 d1->SetBounds(gfx::Rect(10, 10, 100, 100)); 478 EXPECT_EQ("10,10 100x100", 479 d1->GetWindowBoundsInScreen().ToString()); 480 481 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 482 483 // Make sure the bounds which doesn't fit to the root window 484 // works correctly. 485 d1->SetBounds(gfx::Rect(1560, 30, 100, 100)); 486 EXPECT_EQ(root_windows[1], d1->GetNativeView()->GetRootWindow()); 487 EXPECT_EQ("1560,30 100x100", 488 d1->GetWindowBoundsInScreen().ToString()); 489 490 // Setting outside of root windows will be moved to primary root window. 491 // TODO(oshima): This one probably should pick the closest root window. 492 d1->SetBounds(gfx::Rect(200, 10, 100, 100)); 493 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 494 } 495 496 // Verifies if the mouse event arrives to the window even when the window 497 // moves to another root in a pre-target handler. See: crbug.com/157583 498 TEST_F(ExtendedDesktopTest, MoveWindowByMouseClick) { 499 if (!SupportsMultipleDisplays()) 500 return; 501 502 UpdateDisplay("1000x600,600x400"); 503 504 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 505 aura::test::EventCountDelegate delegate; 506 scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithDelegate( 507 &delegate, 0, gfx::Rect(10, 10, 100, 100), root_windows[0])); 508 MoveWindowByClickEventHandler event_handler(window.get()); 509 window->AddPreTargetHandler(&event_handler); 510 511 aura::test::EventGenerator& event_generator(GetEventGenerator()); 512 513 event_generator.MoveMouseToCenterOf(window.get()); 514 event_generator.ClickLeftButton(); 515 // Both mouse pressed and released arrive at the window and its delegate. 516 EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset()); 517 // Also event_handler moves the window to another root at mouse release. 518 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 519 } 520 521 TEST_F(ExtendedDesktopTest, MoveWindowToDisplay) { 522 if (!SupportsMultipleDisplays()) 523 return; 524 525 UpdateDisplay("1000x1000,1000x1000"); 526 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 527 528 gfx::Display display0 = Shell::GetScreen()->GetDisplayMatching( 529 root_windows[0]->GetBoundsInScreen()); 530 gfx::Display display1 = Shell::GetScreen()->GetDisplayMatching( 531 root_windows[1]->GetBoundsInScreen()); 532 EXPECT_NE(display0.id(), display1.id()); 533 534 views::Widget* d1 = CreateTestWidget(gfx::Rect(10, 10, 1000, 100)); 535 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 536 537 // Move the window where the window spans both root windows. Since the second 538 // parameter is |display1|, the window should be shown on the secondary root. 539 d1->GetNativeWindow()->SetBoundsInScreen(gfx::Rect(500, 10, 1000, 100), 540 display1); 541 EXPECT_EQ("500,10 1000x100", 542 d1->GetWindowBoundsInScreen().ToString()); 543 EXPECT_EQ(root_windows[1], d1->GetNativeView()->GetRootWindow()); 544 545 // Move to the primary root. 546 d1->GetNativeWindow()->SetBoundsInScreen(gfx::Rect(500, 10, 1000, 100), 547 display0); 548 EXPECT_EQ("500,10 1000x100", 549 d1->GetWindowBoundsInScreen().ToString()); 550 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 551 } 552 553 TEST_F(ExtendedDesktopTest, MoveWindowWithTransient) { 554 if (!SupportsMultipleDisplays()) 555 return; 556 557 UpdateDisplay("1000x600,600x400"); 558 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 559 views::Widget* w1 = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 560 views::Widget* w1_t1 = CreateTestWidgetWithParent( 561 w1, gfx::Rect(50, 50, 50, 50), false /* transient */); 562 // Transient child of the transient child. 563 views::Widget* w1_t11 = CreateTestWidgetWithParent( 564 w1_t1, gfx::Rect(1200, 70, 30, 30), false /* transient */); 565 566 views::Widget* w11 = CreateTestWidgetWithParent( 567 w1, gfx::Rect(10, 10, 40, 40), true /* child */); 568 views::Widget* w11_t1 = CreateTestWidgetWithParent( 569 w1, gfx::Rect(1300, 100, 80, 80), false /* transient */); 570 571 EXPECT_EQ(root_windows[0], w1->GetNativeView()->GetRootWindow()); 572 EXPECT_EQ(root_windows[0], w11->GetNativeView()->GetRootWindow()); 573 EXPECT_EQ(root_windows[0], w1_t1->GetNativeView()->GetRootWindow()); 574 EXPECT_EQ(root_windows[0], w1_t11->GetNativeView()->GetRootWindow()); 575 EXPECT_EQ(root_windows[0], w11_t1->GetNativeView()->GetRootWindow()); 576 EXPECT_EQ("50,50 50x50", 577 w1_t1->GetWindowBoundsInScreen().ToString()); 578 EXPECT_EQ("1200,70 30x30", 579 w1_t11->GetWindowBoundsInScreen().ToString()); 580 EXPECT_EQ("20,20 40x40", 581 w11->GetWindowBoundsInScreen().ToString()); 582 EXPECT_EQ("1300,100 80x80", 583 w11_t1->GetWindowBoundsInScreen().ToString()); 584 585 w1->SetBounds(gfx::Rect(1100,10,100,100)); 586 587 EXPECT_EQ(root_windows[1], w1_t1->GetNativeView()->GetRootWindow()); 588 EXPECT_EQ(root_windows[1], w1_t1->GetNativeView()->GetRootWindow()); 589 EXPECT_EQ(root_windows[1], w1_t11->GetNativeView()->GetRootWindow()); 590 EXPECT_EQ(root_windows[1], w11->GetNativeView()->GetRootWindow()); 591 EXPECT_EQ(root_windows[1], w11_t1->GetNativeView()->GetRootWindow()); 592 593 EXPECT_EQ("1110,20 40x40", 594 w11->GetWindowBoundsInScreen().ToString()); 595 // Transient window's screen bounds stays the same. 596 EXPECT_EQ("50,50 50x50", 597 w1_t1->GetWindowBoundsInScreen().ToString()); 598 EXPECT_EQ("1200,70 30x30", 599 w1_t11->GetWindowBoundsInScreen().ToString()); 600 EXPECT_EQ("1300,100 80x80", 601 w11_t1->GetWindowBoundsInScreen().ToString()); 602 603 // Transient window doesn't move between root window unless 604 // its transient parent moves. 605 w1_t1->SetBounds(gfx::Rect(10, 50, 50, 50)); 606 EXPECT_EQ(root_windows[1], w1_t1->GetNativeView()->GetRootWindow()); 607 EXPECT_EQ("10,50 50x50", 608 w1_t1->GetWindowBoundsInScreen().ToString()); 609 } 610 611 // Test if the Window::ConvertPointToTarget works across root windows. 612 // TODO(oshima): Move multiple display suport and this test to aura. 613 TEST_F(ExtendedDesktopTest, ConvertPoint) { 614 if (!SupportsMultipleDisplays()) 615 return; 616 gfx::Screen* screen = Shell::GetInstance()->screen(); 617 UpdateDisplay("1000x600,600x400"); 618 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 619 gfx::Display display_1 = screen->GetDisplayNearestWindow(root_windows[0]); 620 EXPECT_EQ("0,0", display_1.bounds().origin().ToString()); 621 gfx::Display display_2 = screen->GetDisplayNearestWindow(root_windows[1]); 622 EXPECT_EQ("1000,0", display_2.bounds().origin().ToString()); 623 624 aura::Window* d1 = 625 CreateTestWidget(gfx::Rect(10, 10, 100, 100))->GetNativeView(); 626 aura::Window* d2 = 627 CreateTestWidget(gfx::Rect(1020, 20, 100, 100))->GetNativeView(); 628 EXPECT_EQ(root_windows[0], d1->GetRootWindow()); 629 EXPECT_EQ(root_windows[1], d2->GetRootWindow()); 630 631 // Convert point in the Root2's window to the Root1's window Coord. 632 gfx::Point p(0, 0); 633 aura::Window::ConvertPointToTarget(root_windows[1], root_windows[0], &p); 634 EXPECT_EQ("1000,0", p.ToString()); 635 p.SetPoint(0, 0); 636 aura::Window::ConvertPointToTarget(d2, d1, &p); 637 EXPECT_EQ("1010,10", p.ToString()); 638 639 // Convert point in the Root1's window to the Root2's window Coord. 640 p.SetPoint(0, 0); 641 aura::Window::ConvertPointToTarget(root_windows[0], root_windows[1], &p); 642 EXPECT_EQ("-1000,0", p.ToString()); 643 p.SetPoint(0, 0); 644 aura::Window::ConvertPointToTarget(d1, d2, &p); 645 EXPECT_EQ("-1010,-10", p.ToString()); 646 647 // Move the 2nd display to the bottom and test again. 648 SetSecondaryDisplayLayout(DisplayLayout::BOTTOM); 649 650 display_2 = screen->GetDisplayNearestWindow(root_windows[1]); 651 EXPECT_EQ("0,600", display_2.bounds().origin().ToString()); 652 653 // Convert point in Root2's window to Root1's window Coord. 654 p.SetPoint(0, 0); 655 aura::Window::ConvertPointToTarget(root_windows[1], root_windows[0], &p); 656 EXPECT_EQ("0,600", p.ToString()); 657 p.SetPoint(0, 0); 658 aura::Window::ConvertPointToTarget(d2, d1, &p); 659 EXPECT_EQ("10,610", p.ToString()); 660 661 // Convert point in Root1's window to Root2's window Coord. 662 p.SetPoint(0, 0); 663 aura::Window::ConvertPointToTarget(root_windows[0], root_windows[1], &p); 664 EXPECT_EQ("0,-600", p.ToString()); 665 p.SetPoint(0, 0); 666 aura::Window::ConvertPointToTarget(d1, d2, &p); 667 EXPECT_EQ("-10,-610", p.ToString()); 668 } 669 670 TEST_F(ExtendedDesktopTest, OpenSystemTray) { 671 if (!SupportsMultipleDisplays()) 672 return; 673 674 UpdateDisplay("500x600,600x400"); 675 SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray(); 676 ASSERT_FALSE(tray->HasSystemBubble()); 677 678 aura::test::EventGenerator& event_generator(GetEventGenerator()); 679 680 // Opens the tray by a dummy click event and makes sure that adding/removing 681 // displays doesn't break anything. 682 event_generator.MoveMouseToCenterOf(tray->GetWidget()->GetNativeWindow()); 683 event_generator.ClickLeftButton(); 684 EXPECT_TRUE(tray->HasSystemBubble()); 685 686 UpdateDisplay("500x600"); 687 EXPECT_TRUE(tray->HasSystemBubble()); 688 UpdateDisplay("500x600,600x400"); 689 EXPECT_TRUE(tray->HasSystemBubble()); 690 691 // Closes the tray and again makes sure that adding/removing displays doesn't 692 // break anything. 693 event_generator.ClickLeftButton(); 694 RunAllPendingInMessageLoop(); 695 696 EXPECT_FALSE(tray->HasSystemBubble()); 697 698 UpdateDisplay("500x600"); 699 EXPECT_FALSE(tray->HasSystemBubble()); 700 UpdateDisplay("500x600,600x400"); 701 EXPECT_FALSE(tray->HasSystemBubble()); 702 } 703 704 TEST_F(ExtendedDesktopTest, StayInSameRootWindow) { 705 if (!SupportsMultipleDisplays()) 706 return; 707 708 UpdateDisplay("100x100,200x200"); 709 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 710 views::Widget* w1 = CreateTestWidget(gfx::Rect(10, 10, 50, 50)); 711 EXPECT_EQ(root_windows[0], w1->GetNativeView()->GetRootWindow()); 712 w1->SetBounds(gfx::Rect(150, 10, 50, 50)); 713 EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow()); 714 715 // The widget stays in the same root if kStayInSameRootWindowKey is set to 716 // true. 717 w1->GetNativeView()->SetProperty(internal::kStayInSameRootWindowKey, true); 718 w1->SetBounds(gfx::Rect(10, 10, 50, 50)); 719 EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow()); 720 721 // The widget should now move to the 1st root window without the property. 722 w1->GetNativeView()->ClearProperty(internal::kStayInSameRootWindowKey); 723 w1->SetBounds(gfx::Rect(10, 10, 50, 50)); 724 EXPECT_EQ(root_windows[0], w1->GetNativeView()->GetRootWindow()); 725 726 // a window in SettingsBubbleContainer and StatusContainer should 727 // not move to another root window regardles of the bounds specified. 728 aura::Window* settings_bubble_container = 729 Shell::GetPrimaryRootWindowController()->GetContainer( 730 internal::kShellWindowId_SettingBubbleContainer); 731 aura::Window* window = aura::test::CreateTestWindowWithId( 732 100, settings_bubble_container); 733 window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50), 734 ScreenAsh::GetSecondaryDisplay()); 735 EXPECT_EQ(root_windows[0], window->GetRootWindow()); 736 737 aura::Window* status_container = 738 Shell::GetPrimaryRootWindowController()->GetContainer( 739 internal::kShellWindowId_StatusContainer); 740 window = aura::test::CreateTestWindowWithId(100, status_container); 741 window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50), 742 ScreenAsh::GetSecondaryDisplay()); 743 EXPECT_EQ(root_windows[0], window->GetRootWindow()); 744 } 745 746 TEST_F(ExtendedDesktopTest, KeyEventsOnLockScreen) { 747 if (!SupportsMultipleDisplays()) 748 return; 749 750 UpdateDisplay("100x100,200x200"); 751 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 752 753 // Create normal windows on both displays. 754 views::Widget* widget1 = CreateTestWidget( 755 Shell::GetScreen()->GetPrimaryDisplay().bounds()); 756 widget1->Show(); 757 EXPECT_EQ(root_windows[0], widget1->GetNativeView()->GetRootWindow()); 758 views::Widget* widget2 = CreateTestWidget( 759 ScreenAsh::GetSecondaryDisplay().bounds()); 760 widget2->Show(); 761 EXPECT_EQ(root_windows[1], widget2->GetNativeView()->GetRootWindow()); 762 763 // Create a LockScreen window. 764 views::Widget* lock_widget = CreateTestWidget( 765 Shell::GetScreen()->GetPrimaryDisplay().bounds()); 766 views::Textfield* textfield = new views::Textfield; 767 lock_widget->client_view()->AddChildView(textfield); 768 769 ash::Shell::GetContainer( 770 Shell::GetPrimaryRootWindow(), 771 ash::internal::kShellWindowId_LockScreenContainer)-> 772 AddChild(lock_widget->GetNativeView()); 773 lock_widget->Show(); 774 textfield->RequestFocus(); 775 776 aura::client::FocusClient* focus_client = 777 aura::client::GetFocusClient(root_windows[0]); 778 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 779 780 // The lock window should get events on both root windows. 781 aura::test::EventGenerator& event_generator(GetEventGenerator()); 782 783 event_generator.set_current_root_window(root_windows[0]); 784 event_generator.PressKey(ui::VKEY_A, 0); 785 event_generator.ReleaseKey(ui::VKEY_A, 0); 786 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 787 EXPECT_EQ("a", UTF16ToASCII(textfield->text())); 788 789 event_generator.set_current_root_window(root_windows[1]); 790 event_generator.PressKey(ui::VKEY_B, 0); 791 event_generator.ReleaseKey(ui::VKEY_B, 0); 792 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 793 EXPECT_EQ("ab", UTF16ToASCII(textfield->text())); 794 795 // Deleting 2nd display. The lock window still should get the events. 796 UpdateDisplay("100x100"); 797 event_generator.PressKey(ui::VKEY_C, 0); 798 event_generator.ReleaseKey(ui::VKEY_C, 0); 799 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 800 EXPECT_EQ("abc", UTF16ToASCII(textfield->text())); 801 802 // Creating 2nd display again, and lock window still should get events 803 // on both root windows. 804 UpdateDisplay("100x100,200x200"); 805 root_windows = Shell::GetAllRootWindows(); 806 event_generator.set_current_root_window(root_windows[0]); 807 event_generator.PressKey(ui::VKEY_D, 0); 808 event_generator.ReleaseKey(ui::VKEY_D, 0); 809 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 810 EXPECT_EQ("abcd", UTF16ToASCII(textfield->text())); 811 812 event_generator.set_current_root_window(root_windows[1]); 813 event_generator.PressKey(ui::VKEY_E, 0); 814 event_generator.ReleaseKey(ui::VKEY_E, 0); 815 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 816 EXPECT_EQ("abcde", UTF16ToASCII(textfield->text())); 817 } 818 819 TEST_F(ExtendedDesktopTest, PassiveGrab) { 820 if (!SupportsMultipleDisplays()) 821 return; 822 823 EventLocationRecordingEventHandler event_handler; 824 ash::Shell::GetInstance()->AddPreTargetHandler(&event_handler); 825 826 UpdateDisplay("300x300,200x200"); 827 828 views::Widget* widget = CreateTestWidget(gfx::Rect(50, 50, 200, 200)); 829 widget->Show(); 830 ASSERT_EQ("50,50 200x200", widget->GetWindowBoundsInScreen().ToString()); 831 832 aura::test::EventGenerator& generator(GetEventGenerator()); 833 generator.MoveMouseTo(150, 150); 834 EXPECT_EQ("100,100 150,150", event_handler.GetLocationsAndReset()); 835 836 generator.PressLeftButton(); 837 generator.MoveMouseTo(400, 150); 838 839 EXPECT_EQ("350,100 400,150", event_handler.GetLocationsAndReset()); 840 841 generator.ReleaseLeftButton(); 842 EXPECT_EQ("-999,-999 -999,-999", event_handler.GetLocationsAndReset()); 843 844 generator.MoveMouseTo(400, 150); 845 EXPECT_EQ("100,150 100,150", event_handler.GetLocationsAndReset()); 846 847 ash::Shell::GetInstance()->RemovePreTargetHandler(&event_handler); 848 } 849 850 } // namespace ash 851