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_util.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_properties.h" 15 #include "ash/wm/window_util.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "ui/aura/client/capture_client.h" 19 #include "ui/aura/client/focus_client.h" 20 #include "ui/aura/test/event_generator.h" 21 #include "ui/aura/test/test_windows.h" 22 #include "ui/aura/test/window_test_api.h" 23 #include "ui/aura/window.h" 24 #include "ui/aura/window_event_dispatcher.h" 25 #include "ui/base/cursor/cursor.h" 26 #include "ui/events/event_handler.h" 27 #include "ui/gfx/display.h" 28 #include "ui/gfx/screen.h" 29 #include "ui/views/controls/textfield/textfield.h" 30 #include "ui/views/widget/widget.h" 31 #include "ui/views/widget/widget_delegate.h" 32 #include "ui/wm/public/activation_client.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(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::WindowTreeHost* host0 = root_windows[0]->GetHost(); 256 aura::WindowTreeHost* host1 = root_windows[1]->GetHost(); 257 EXPECT_EQ(ui::kCursorPointer, host0->last_cursor().native_type()); 258 EXPECT_EQ(ui::kCursorPointer, host1->last_cursor().native_type()); 259 Shell::GetInstance()->cursor_manager()->SetCursor(ui::kCursorCopy); 260 EXPECT_EQ(ui::kCursorCopy, host0->last_cursor().native_type()); 261 EXPECT_EQ(ui::kCursorCopy, host1->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, GetRootWindowAt) { 288 if (!SupportsMultipleDisplays()) 289 return; 290 291 UpdateDisplay("700x500,500x500"); 292 SetSecondaryDisplayLayout(DisplayLayout::LEFT); 293 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 294 295 EXPECT_EQ(root_windows[1], wm::GetRootWindowAt(gfx::Point(-400, 100))); 296 EXPECT_EQ(root_windows[1], wm::GetRootWindowAt(gfx::Point(-1, 100))); 297 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(0, 300))); 298 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(700,300))); 299 300 // Zero origin. 301 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(0, 0))); 302 303 // Out of range point should return the nearest root window 304 EXPECT_EQ(root_windows[1], wm::GetRootWindowAt(gfx::Point(-600, 0))); 305 EXPECT_EQ(root_windows[0], wm::GetRootWindowAt(gfx::Point(701, 100))); 306 } 307 308 TEST_F(ExtendedDesktopTest, GetRootWindowMatching) { 309 if (!SupportsMultipleDisplays()) 310 return; 311 312 UpdateDisplay("700x500,500x500"); 313 SetSecondaryDisplayLayout(DisplayLayout::LEFT); 314 315 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 316 317 // Containing rect. 318 EXPECT_EQ(root_windows[1], 319 wm::GetRootWindowMatching(gfx::Rect(-300, 10, 50, 50))); 320 EXPECT_EQ(root_windows[0], 321 wm::GetRootWindowMatching(gfx::Rect(100, 10, 50, 50))); 322 323 // Intersecting rect. 324 EXPECT_EQ(root_windows[1], 325 wm::GetRootWindowMatching(gfx::Rect(-200, 0, 300, 300))); 326 EXPECT_EQ(root_windows[0], 327 wm::GetRootWindowMatching(gfx::Rect(-100, 0, 300, 300))); 328 329 // Zero origin. 330 EXPECT_EQ(root_windows[0], 331 wm::GetRootWindowMatching(gfx::Rect(0, 0, 0, 0))); 332 EXPECT_EQ(root_windows[0], 333 wm::GetRootWindowMatching(gfx::Rect(0, 0, 1, 1))); 334 335 // Empty rect. 336 EXPECT_EQ(root_windows[1], 337 wm::GetRootWindowMatching(gfx::Rect(-400, 100, 0, 0))); 338 EXPECT_EQ(root_windows[0], 339 wm::GetRootWindowMatching(gfx::Rect(100, 100, 0, 0))); 340 341 // Out of range rect should return the primary root window. 342 EXPECT_EQ(root_windows[0], 343 wm::GetRootWindowMatching(gfx::Rect(-600, -300, 50, 50))); 344 EXPECT_EQ(root_windows[0], 345 wm::GetRootWindowMatching(gfx::Rect(0, 1000, 50, 50))); 346 } 347 348 TEST_F(ExtendedDesktopTest, Capture) { 349 if (!SupportsMultipleDisplays()) 350 return; 351 352 UpdateDisplay("1000x600,600x400"); 353 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 354 355 aura::test::EventCountDelegate r1_d1; 356 aura::test::EventCountDelegate r1_d2; 357 aura::test::EventCountDelegate r2_d1; 358 359 scoped_ptr<aura::Window> r1_w1(aura::test::CreateTestWindowWithDelegate( 360 &r1_d1, 0, gfx::Rect(10, 10, 100, 100), root_windows[0])); 361 scoped_ptr<aura::Window> r1_w2(aura::test::CreateTestWindowWithDelegate( 362 &r1_d2, 0, gfx::Rect(10, 100, 100, 100), root_windows[0])); 363 scoped_ptr<aura::Window> r2_w1(aura::test::CreateTestWindowWithDelegate( 364 &r2_d1, 0, gfx::Rect(10, 10, 100, 100), root_windows[1])); 365 366 r1_w1->SetCapture(); 367 368 EXPECT_EQ(r1_w1.get(), 369 aura::client::GetCaptureWindow(r2_w1->GetRootWindow())); 370 371 aura::test::EventGenerator& generator = GetEventGenerator(); 372 generator.MoveMouseToCenterOf(r2_w1.get()); 373 // |r1_w1| will receive the events because it has capture. 374 EXPECT_EQ("1 1 0", r1_d1.GetMouseMotionCountsAndReset()); 375 EXPECT_EQ("0 0 0", r1_d2.GetMouseMotionCountsAndReset()); 376 EXPECT_EQ("0 0 0", r2_d1.GetMouseMotionCountsAndReset()); 377 378 generator.ClickLeftButton(); 379 EXPECT_EQ("0 0 0", r2_d1.GetMouseMotionCountsAndReset()); 380 EXPECT_EQ("0 0", r2_d1.GetMouseButtonCountsAndReset()); 381 // The mouse is outside. On chromeos, the mouse is warped to the 382 // dest root window, but it's not implemented on Win yet, so 383 // no mouse move event on Win. 384 EXPECT_EQ("0 0 0", r1_d1.GetMouseMotionCountsAndReset()); 385 EXPECT_EQ("1 1", r1_d1.GetMouseButtonCountsAndReset()); 386 387 generator.MoveMouseTo(15, 15); 388 EXPECT_EQ("0 1 0", r1_d1.GetMouseMotionCountsAndReset()); 389 EXPECT_EQ("0 0 0", r1_d2.GetMouseMotionCountsAndReset()); 390 391 r1_w2->SetCapture(); 392 EXPECT_EQ(r1_w2.get(), 393 aura::client::GetCaptureWindow(r2_w1->GetRootWindow())); 394 generator.MoveMouseBy(10, 10); 395 // |r1_w2| has the capture. So it will receive the mouse-move event. 396 EXPECT_EQ("0 0 0", r1_d1.GetMouseMotionCountsAndReset()); 397 EXPECT_EQ("0 1 0", r1_d2.GetMouseMotionCountsAndReset()); 398 EXPECT_EQ("0 0 0", r2_d1.GetMouseMotionCountsAndReset()); 399 400 generator.ClickLeftButton(); 401 EXPECT_EQ("0 0 0", r2_d1.GetMouseMotionCountsAndReset()); 402 EXPECT_EQ("0 0", r2_d1.GetMouseButtonCountsAndReset()); 403 EXPECT_EQ("0 0 0", r1_d2.GetMouseMotionCountsAndReset()); 404 EXPECT_EQ("1 1", r1_d2.GetMouseButtonCountsAndReset()); 405 406 r1_w2->ReleaseCapture(); 407 EXPECT_EQ(NULL, aura::client::GetCaptureWindow(r2_w1->GetRootWindow())); 408 409 generator.MoveMouseToCenterOf(r2_w1.get()); 410 generator.ClickLeftButton(); 411 EXPECT_EQ("1 1 0", r2_d1.GetMouseMotionCountsAndReset()); 412 EXPECT_EQ("1 1", r2_d1.GetMouseButtonCountsAndReset()); 413 // Make sure the mouse_moved_handler_ is properly reset. 414 EXPECT_EQ("0 0 0", r1_d2.GetMouseMotionCountsAndReset()); 415 EXPECT_EQ("0 0", r1_d2.GetMouseButtonCountsAndReset()); 416 } 417 418 TEST_F(ExtendedDesktopTest, MoveWindow) { 419 if (!SupportsMultipleDisplays()) 420 return; 421 422 UpdateDisplay("1000x600,600x400"); 423 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 424 views::Widget* d1 = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 425 426 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 427 428 d1->SetBounds(gfx::Rect(1010, 10, 100, 100)); 429 EXPECT_EQ("1010,10 100x100", 430 d1->GetWindowBoundsInScreen().ToString()); 431 432 EXPECT_EQ(root_windows[1], d1->GetNativeView()->GetRootWindow()); 433 434 d1->SetBounds(gfx::Rect(10, 10, 100, 100)); 435 EXPECT_EQ("10,10 100x100", 436 d1->GetWindowBoundsInScreen().ToString()); 437 438 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 439 440 // Make sure the bounds which doesn't fit to the root window 441 // works correctly. 442 d1->SetBounds(gfx::Rect(1560, 30, 100, 100)); 443 EXPECT_EQ(root_windows[1], d1->GetNativeView()->GetRootWindow()); 444 EXPECT_EQ("1560,30 100x100", 445 d1->GetWindowBoundsInScreen().ToString()); 446 447 // Setting outside of root windows will be moved to primary root window. 448 // TODO(oshima): This one probably should pick the closest root window. 449 d1->SetBounds(gfx::Rect(200, 10, 100, 100)); 450 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 451 } 452 453 // Verifies if the mouse event arrives to the window even when the window 454 // moves to another root in a pre-target handler. See: crbug.com/157583 455 TEST_F(ExtendedDesktopTest, MoveWindowByMouseClick) { 456 if (!SupportsMultipleDisplays()) 457 return; 458 459 UpdateDisplay("1000x600,600x400"); 460 461 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 462 aura::test::EventCountDelegate delegate; 463 scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithDelegate( 464 &delegate, 0, gfx::Rect(10, 10, 100, 100), root_windows[0])); 465 MoveWindowByClickEventHandler event_handler(window.get()); 466 window->AddPreTargetHandler(&event_handler); 467 468 aura::test::EventGenerator& event_generator(GetEventGenerator()); 469 470 event_generator.MoveMouseToCenterOf(window.get()); 471 event_generator.ClickLeftButton(); 472 // Both mouse pressed and released arrive at the window and its delegate. 473 EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset()); 474 // Also event_handler moves the window to another root at mouse release. 475 EXPECT_EQ(root_windows[1], window->GetRootWindow()); 476 } 477 478 TEST_F(ExtendedDesktopTest, MoveWindowToDisplay) { 479 if (!SupportsMultipleDisplays()) 480 return; 481 482 UpdateDisplay("1000x1000,1000x1000"); 483 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 484 485 gfx::Display display0 = Shell::GetScreen()->GetDisplayMatching( 486 root_windows[0]->GetBoundsInScreen()); 487 gfx::Display display1 = Shell::GetScreen()->GetDisplayMatching( 488 root_windows[1]->GetBoundsInScreen()); 489 EXPECT_NE(display0.id(), display1.id()); 490 491 views::Widget* d1 = CreateTestWidget(gfx::Rect(10, 10, 1000, 100)); 492 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 493 494 // Move the window where the window spans both root windows. Since the second 495 // parameter is |display1|, the window should be shown on the secondary root. 496 d1->GetNativeWindow()->SetBoundsInScreen(gfx::Rect(500, 10, 1000, 100), 497 display1); 498 EXPECT_EQ("500,10 1000x100", 499 d1->GetWindowBoundsInScreen().ToString()); 500 EXPECT_EQ(root_windows[1], d1->GetNativeView()->GetRootWindow()); 501 502 // Move to the primary root. 503 d1->GetNativeWindow()->SetBoundsInScreen(gfx::Rect(500, 10, 1000, 100), 504 display0); 505 EXPECT_EQ("500,10 1000x100", 506 d1->GetWindowBoundsInScreen().ToString()); 507 EXPECT_EQ(root_windows[0], d1->GetNativeView()->GetRootWindow()); 508 } 509 510 TEST_F(ExtendedDesktopTest, MoveWindowWithTransient) { 511 if (!SupportsMultipleDisplays()) 512 return; 513 514 UpdateDisplay("1000x600,600x400"); 515 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 516 views::Widget* w1 = CreateTestWidget(gfx::Rect(10, 10, 100, 100)); 517 views::Widget* w1_t1 = CreateTestWidgetWithParent( 518 w1, gfx::Rect(50, 50, 50, 50), false /* transient */); 519 // Transient child of the transient child. 520 views::Widget* w1_t11 = CreateTestWidgetWithParent( 521 w1_t1, gfx::Rect(1200, 70, 35, 35), false /* transient */); 522 523 views::Widget* w11 = CreateTestWidgetWithParent( 524 w1, gfx::Rect(10, 10, 40, 40), true /* child */); 525 views::Widget* w11_t1 = CreateTestWidgetWithParent( 526 w1, gfx::Rect(1300, 100, 80, 80), false /* transient */); 527 528 EXPECT_EQ(root_windows[0], w1->GetNativeView()->GetRootWindow()); 529 EXPECT_EQ(root_windows[0], w11->GetNativeView()->GetRootWindow()); 530 EXPECT_EQ(root_windows[0], w1_t1->GetNativeView()->GetRootWindow()); 531 EXPECT_EQ(root_windows[0], w1_t11->GetNativeView()->GetRootWindow()); 532 EXPECT_EQ(root_windows[0], w11_t1->GetNativeView()->GetRootWindow()); 533 EXPECT_EQ("50,50 50x50", 534 w1_t1->GetWindowBoundsInScreen().ToString()); 535 EXPECT_EQ("1200,70 35x35", 536 w1_t11->GetWindowBoundsInScreen().ToString()); 537 EXPECT_EQ("20,20 40x40", 538 w11->GetWindowBoundsInScreen().ToString()); 539 EXPECT_EQ("1300,100 80x80", 540 w11_t1->GetWindowBoundsInScreen().ToString()); 541 542 w1->SetBounds(gfx::Rect(1100,10,100,100)); 543 544 EXPECT_EQ(root_windows[1], w1_t1->GetNativeView()->GetRootWindow()); 545 EXPECT_EQ(root_windows[1], w1_t1->GetNativeView()->GetRootWindow()); 546 EXPECT_EQ(root_windows[1], w1_t11->GetNativeView()->GetRootWindow()); 547 EXPECT_EQ(root_windows[1], w11->GetNativeView()->GetRootWindow()); 548 EXPECT_EQ(root_windows[1], w11_t1->GetNativeView()->GetRootWindow()); 549 550 EXPECT_EQ("1110,20 40x40", 551 w11->GetWindowBoundsInScreen().ToString()); 552 // Transient window's screen bounds stays the same. 553 EXPECT_EQ("50,50 50x50", 554 w1_t1->GetWindowBoundsInScreen().ToString()); 555 EXPECT_EQ("1200,70 35x35", 556 w1_t11->GetWindowBoundsInScreen().ToString()); 557 EXPECT_EQ("1300,100 80x80", 558 w11_t1->GetWindowBoundsInScreen().ToString()); 559 560 // Transient window doesn't move between root window unless 561 // its transient parent moves. 562 w1_t1->SetBounds(gfx::Rect(10, 50, 50, 50)); 563 EXPECT_EQ(root_windows[1], w1_t1->GetNativeView()->GetRootWindow()); 564 EXPECT_EQ("10,50 50x50", 565 w1_t1->GetWindowBoundsInScreen().ToString()); 566 } 567 568 // Test if the Window::ConvertPointToTarget works across root windows. 569 // TODO(oshima): Move multiple display suport and this test to aura. 570 TEST_F(ExtendedDesktopTest, ConvertPoint) { 571 if (!SupportsMultipleDisplays()) 572 return; 573 gfx::Screen* screen = Shell::GetScreen(); 574 UpdateDisplay("1000x600,600x400"); 575 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 576 gfx::Display display_1 = screen->GetDisplayNearestWindow(root_windows[0]); 577 EXPECT_EQ("0,0", display_1.bounds().origin().ToString()); 578 gfx::Display display_2 = screen->GetDisplayNearestWindow(root_windows[1]); 579 EXPECT_EQ("1000,0", display_2.bounds().origin().ToString()); 580 581 aura::Window* d1 = 582 CreateTestWidget(gfx::Rect(10, 10, 100, 100))->GetNativeView(); 583 aura::Window* d2 = 584 CreateTestWidget(gfx::Rect(1020, 20, 100, 100))->GetNativeView(); 585 EXPECT_EQ(root_windows[0], d1->GetRootWindow()); 586 EXPECT_EQ(root_windows[1], d2->GetRootWindow()); 587 588 // Convert point in the Root2's window to the Root1's window Coord. 589 gfx::Point p(0, 0); 590 aura::Window::ConvertPointToTarget(root_windows[1], root_windows[0], &p); 591 EXPECT_EQ("1000,0", p.ToString()); 592 p.SetPoint(0, 0); 593 aura::Window::ConvertPointToTarget(d2, d1, &p); 594 EXPECT_EQ("1010,10", p.ToString()); 595 596 // Convert point in the Root1's window to the Root2's window Coord. 597 p.SetPoint(0, 0); 598 aura::Window::ConvertPointToTarget(root_windows[0], root_windows[1], &p); 599 EXPECT_EQ("-1000,0", p.ToString()); 600 p.SetPoint(0, 0); 601 aura::Window::ConvertPointToTarget(d1, d2, &p); 602 EXPECT_EQ("-1010,-10", p.ToString()); 603 604 // Move the 2nd display to the bottom and test again. 605 SetSecondaryDisplayLayout(DisplayLayout::BOTTOM); 606 607 display_2 = screen->GetDisplayNearestWindow(root_windows[1]); 608 EXPECT_EQ("0,600", display_2.bounds().origin().ToString()); 609 610 // Convert point in Root2's window to Root1's window Coord. 611 p.SetPoint(0, 0); 612 aura::Window::ConvertPointToTarget(root_windows[1], root_windows[0], &p); 613 EXPECT_EQ("0,600", p.ToString()); 614 p.SetPoint(0, 0); 615 aura::Window::ConvertPointToTarget(d2, d1, &p); 616 EXPECT_EQ("10,610", p.ToString()); 617 618 // Convert point in Root1's window to Root2's window Coord. 619 p.SetPoint(0, 0); 620 aura::Window::ConvertPointToTarget(root_windows[0], root_windows[1], &p); 621 EXPECT_EQ("0,-600", p.ToString()); 622 p.SetPoint(0, 0); 623 aura::Window::ConvertPointToTarget(d1, d2, &p); 624 EXPECT_EQ("-10,-610", p.ToString()); 625 } 626 627 TEST_F(ExtendedDesktopTest, OpenSystemTray) { 628 if (!SupportsMultipleDisplays()) 629 return; 630 631 UpdateDisplay("500x600,600x400"); 632 SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray(); 633 ASSERT_FALSE(tray->HasSystemBubble()); 634 635 aura::test::EventGenerator& event_generator(GetEventGenerator()); 636 637 // Opens the tray by a dummy click event and makes sure that adding/removing 638 // displays doesn't break anything. 639 event_generator.MoveMouseToCenterOf(tray->GetWidget()->GetNativeWindow()); 640 event_generator.ClickLeftButton(); 641 EXPECT_TRUE(tray->HasSystemBubble()); 642 643 UpdateDisplay("500x600"); 644 EXPECT_TRUE(tray->HasSystemBubble()); 645 UpdateDisplay("500x600,600x400"); 646 EXPECT_TRUE(tray->HasSystemBubble()); 647 648 // Closes the tray and again makes sure that adding/removing displays doesn't 649 // break anything. 650 event_generator.ClickLeftButton(); 651 RunAllPendingInMessageLoop(); 652 653 EXPECT_FALSE(tray->HasSystemBubble()); 654 655 UpdateDisplay("500x600"); 656 EXPECT_FALSE(tray->HasSystemBubble()); 657 UpdateDisplay("500x600,600x400"); 658 EXPECT_FALSE(tray->HasSystemBubble()); 659 } 660 661 TEST_F(ExtendedDesktopTest, StayInSameRootWindow) { 662 if (!SupportsMultipleDisplays()) 663 return; 664 665 UpdateDisplay("100x100,200x200"); 666 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 667 views::Widget* w1 = CreateTestWidget(gfx::Rect(10, 10, 50, 50)); 668 EXPECT_EQ(root_windows[0], w1->GetNativeView()->GetRootWindow()); 669 w1->SetBounds(gfx::Rect(150, 10, 50, 50)); 670 EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow()); 671 672 // The widget stays in the same root if kStayInSameRootWindowKey is set to 673 // true. 674 w1->GetNativeView()->SetProperty(kStayInSameRootWindowKey, true); 675 w1->SetBounds(gfx::Rect(10, 10, 50, 50)); 676 EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow()); 677 678 // The widget should now move to the 1st root window without the property. 679 w1->GetNativeView()->ClearProperty(kStayInSameRootWindowKey); 680 w1->SetBounds(gfx::Rect(10, 10, 50, 50)); 681 EXPECT_EQ(root_windows[0], w1->GetNativeView()->GetRootWindow()); 682 683 // a window in SettingsBubbleContainer and StatusContainer should 684 // not move to another root window regardles of the bounds specified. 685 aura::Window* settings_bubble_container = 686 Shell::GetPrimaryRootWindowController()->GetContainer( 687 kShellWindowId_SettingBubbleContainer); 688 aura::Window* window = aura::test::CreateTestWindowWithId( 689 100, settings_bubble_container); 690 window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50), 691 ScreenUtil::GetSecondaryDisplay()); 692 EXPECT_EQ(root_windows[0], window->GetRootWindow()); 693 694 aura::Window* status_container = 695 Shell::GetPrimaryRootWindowController()->GetContainer( 696 kShellWindowId_StatusContainer); 697 window = aura::test::CreateTestWindowWithId(100, status_container); 698 window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50), 699 ScreenUtil::GetSecondaryDisplay()); 700 EXPECT_EQ(root_windows[0], window->GetRootWindow()); 701 } 702 703 TEST_F(ExtendedDesktopTest, KeyEventsOnLockScreen) { 704 if (!SupportsMultipleDisplays()) 705 return; 706 707 UpdateDisplay("100x100,200x200"); 708 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 709 710 // Create normal windows on both displays. 711 views::Widget* widget1 = CreateTestWidget( 712 Shell::GetScreen()->GetPrimaryDisplay().bounds()); 713 widget1->Show(); 714 EXPECT_EQ(root_windows[0], widget1->GetNativeView()->GetRootWindow()); 715 views::Widget* widget2 = CreateTestWidget( 716 ScreenUtil::GetSecondaryDisplay().bounds()); 717 widget2->Show(); 718 EXPECT_EQ(root_windows[1], widget2->GetNativeView()->GetRootWindow()); 719 720 // Create a LockScreen window. 721 views::Widget* lock_widget = CreateTestWidget( 722 Shell::GetScreen()->GetPrimaryDisplay().bounds()); 723 views::Textfield* textfield = new views::Textfield; 724 lock_widget->client_view()->AddChildView(textfield); 725 726 ash::Shell::GetContainer(Shell::GetPrimaryRootWindow(), 727 ash::kShellWindowId_LockScreenContainer) 728 ->AddChild(lock_widget->GetNativeView()); 729 lock_widget->Show(); 730 textfield->RequestFocus(); 731 732 aura::client::FocusClient* focus_client = 733 aura::client::GetFocusClient(root_windows[0]); 734 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 735 736 // The lock window should get events on both root windows. 737 aura::test::EventGenerator& event_generator(GetEventGenerator()); 738 739 event_generator.set_current_host(root_windows[0]->GetHost()); 740 event_generator.PressKey(ui::VKEY_A, 0); 741 event_generator.ReleaseKey(ui::VKEY_A, 0); 742 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 743 EXPECT_EQ("a", base::UTF16ToASCII(textfield->text())); 744 745 event_generator.set_current_host(root_windows[1]->GetHost()); 746 event_generator.PressKey(ui::VKEY_B, 0); 747 event_generator.ReleaseKey(ui::VKEY_B, 0); 748 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 749 EXPECT_EQ("ab", base::UTF16ToASCII(textfield->text())); 750 751 // Deleting 2nd display. The lock window still should get the events. 752 UpdateDisplay("100x100"); 753 event_generator.PressKey(ui::VKEY_C, 0); 754 event_generator.ReleaseKey(ui::VKEY_C, 0); 755 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 756 EXPECT_EQ("abc", base::UTF16ToASCII(textfield->text())); 757 758 // Creating 2nd display again, and lock window still should get events 759 // on both root windows. 760 UpdateDisplay("100x100,200x200"); 761 root_windows = Shell::GetAllRootWindows(); 762 event_generator.set_current_host(root_windows[0]->GetHost()); 763 event_generator.PressKey(ui::VKEY_D, 0); 764 event_generator.ReleaseKey(ui::VKEY_D, 0); 765 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 766 EXPECT_EQ("abcd", base::UTF16ToASCII(textfield->text())); 767 768 event_generator.set_current_host(root_windows[1]->GetHost()); 769 event_generator.PressKey(ui::VKEY_E, 0); 770 event_generator.ReleaseKey(ui::VKEY_E, 0); 771 EXPECT_EQ(lock_widget->GetNativeView(), focus_client->GetFocusedWindow()); 772 EXPECT_EQ("abcde", base::UTF16ToASCII(textfield->text())); 773 } 774 775 TEST_F(ExtendedDesktopTest, PassiveGrab) { 776 if (!SupportsMultipleDisplays()) 777 return; 778 779 EventLocationRecordingEventHandler event_handler; 780 ash::Shell::GetInstance()->AddPreTargetHandler(&event_handler); 781 782 UpdateDisplay("300x300,200x200"); 783 784 views::Widget* widget = CreateTestWidget(gfx::Rect(50, 50, 200, 200)); 785 widget->Show(); 786 ASSERT_EQ("50,50 200x200", widget->GetWindowBoundsInScreen().ToString()); 787 788 aura::test::EventGenerator& generator(GetEventGenerator()); 789 generator.MoveMouseTo(150, 150); 790 EXPECT_EQ("100,100 150,150", event_handler.GetLocationsAndReset()); 791 792 generator.PressLeftButton(); 793 generator.MoveMouseTo(400, 150); 794 795 EXPECT_EQ("350,100 400,150", event_handler.GetLocationsAndReset()); 796 797 generator.ReleaseLeftButton(); 798 EXPECT_EQ("-999,-999 -999,-999", event_handler.GetLocationsAndReset()); 799 800 generator.MoveMouseTo(400, 150); 801 EXPECT_EQ("100,150 100,150", event_handler.GetLocationsAndReset()); 802 803 ash::Shell::GetInstance()->RemovePreTargetHandler(&event_handler); 804 } 805 806 } // namespace ash 807