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