1 // Copyright (c) 2013 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 "base/basictypes.h" 6 #include "base/bind.h" 7 #include "base/command_line.h" 8 #include "base/path_service.h" 9 #include "base/run_loop.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "ui/base/resource/resource_bundle.h" 13 #include "ui/base/ui_base_paths.h" 14 #include "ui/base/ui_base_switches.h" 15 #include "ui/events/event_processor.h" 16 #include "ui/events/test/event_generator.h" 17 #include "ui/gfx/native_widget_types.h" 18 #include "ui/gl/gl_surface.h" 19 #include "ui/views/controls/textfield/textfield.h" 20 #include "ui/views/controls/textfield/textfield_test_api.h" 21 #include "ui/views/focus/focus_manager.h" 22 #include "ui/views/test/focus_manager_test.h" 23 #include "ui/views/test/widget_test.h" 24 #include "ui/views/touchui/touch_selection_controller_impl.h" 25 #include "ui/views/widget/widget.h" 26 #include "ui/views/window/dialog_delegate.h" 27 #include "ui/wm/public/activation_client.h" 28 29 #if defined(OS_WIN) 30 #include "ui/aura/window.h" 31 #include "ui/aura/window_tree_host.h" 32 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 33 #include "ui/views/win/hwnd_util.h" 34 #endif 35 36 namespace views { 37 namespace test { 38 39 namespace { 40 41 // A View that closes the Widget and exits the current message-loop when it 42 // receives a mouse-release event. 43 class ExitLoopOnRelease : public View { 44 public: 45 ExitLoopOnRelease() {} 46 virtual ~ExitLoopOnRelease() {} 47 48 private: 49 // Overridden from View: 50 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { 51 GetWidget()->Close(); 52 base::MessageLoop::current()->QuitNow(); 53 } 54 55 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease); 56 }; 57 58 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events. 59 class GestureCaptureView : public View { 60 public: 61 GestureCaptureView() {} 62 virtual ~GestureCaptureView() {} 63 64 private: 65 // Overridden from View: 66 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 67 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { 68 GetWidget()->SetCapture(this); 69 event->StopPropagation(); 70 } 71 } 72 73 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView); 74 }; 75 76 // A view that always processes all mouse events. 77 class MouseView : public View { 78 public: 79 MouseView() 80 : View(), 81 entered_(0), 82 exited_(0), 83 pressed_(0) { 84 } 85 virtual ~MouseView() {} 86 87 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 88 pressed_++; 89 return true; 90 } 91 92 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { 93 entered_++; 94 } 95 96 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { 97 exited_++; 98 } 99 100 // Return the number of OnMouseEntered calls and reset the counter. 101 int EnteredCalls() { 102 int i = entered_; 103 entered_ = 0; 104 return i; 105 } 106 107 // Return the number of OnMouseExited calls and reset the counter. 108 int ExitedCalls() { 109 int i = exited_; 110 exited_ = 0; 111 return i; 112 } 113 114 int pressed() const { return pressed_; } 115 116 private: 117 int entered_; 118 int exited_; 119 120 int pressed_; 121 122 DISALLOW_COPY_AND_ASSIGN(MouseView); 123 }; 124 125 // A View that shows a different widget, sets capture on that widget, and 126 // initiates a nested message-loop when it receives a mouse-press event. 127 class NestedLoopCaptureView : public View { 128 public: 129 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {} 130 virtual ~NestedLoopCaptureView() {} 131 132 private: 133 // Overridden from View: 134 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 135 // Start a nested loop. 136 widget_->Show(); 137 widget_->SetCapture(widget_->GetContentsView()); 138 EXPECT_TRUE(widget_->HasCapture()); 139 140 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 141 base::MessageLoop::ScopedNestableTaskAllower allow(loop); 142 143 base::RunLoop run_loop; 144 run_loop.Run(); 145 return true; 146 } 147 148 Widget* widget_; 149 150 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView); 151 }; 152 153 } // namespace 154 155 class WidgetTestInteractive : public WidgetTest { 156 public: 157 WidgetTestInteractive() {} 158 virtual ~WidgetTestInteractive() {} 159 160 virtual void SetUp() OVERRIDE { 161 gfx::GLSurface::InitializeOneOffForTests(); 162 ui::RegisterPathProvider(); 163 base::FilePath ui_test_pak_path; 164 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); 165 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); 166 WidgetTest::SetUp(); 167 } 168 169 protected: 170 static void ShowQuickMenuImmediately( 171 TouchSelectionControllerImpl* controller) { 172 DCHECK(controller); 173 if (controller->context_menu_timer_.IsRunning()) { 174 controller->context_menu_timer_.Stop(); 175 // TODO(tapted): Enable this when porting ui/views/touchui to Mac. 176 #if !defined(OS_MACOSX) 177 controller->ContextMenuTimerFired(); 178 #endif 179 } 180 } 181 182 static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) { 183 DCHECK(controller); 184 return controller->context_menu_ && controller->context_menu_->visible(); 185 } 186 }; 187 188 #if defined(OS_WIN) 189 // Tests whether activation and focus change works correctly in Windows. 190 // We test the following:- 191 // 1. If the active aura window is correctly set when a top level widget is 192 // created. 193 // 2. If the active aura window in widget 1 created above, is set to NULL when 194 // another top level widget is created and focused. 195 // 3. On focusing the native platform window for widget 1, the active aura 196 // window for widget 1 should be set and that for widget 2 should reset. 197 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura. 198 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) { 199 // Create widget 1 and expect the active window to be its window. 200 View* contents_view1 = new View; 201 contents_view1->SetFocusable(true); 202 Widget widget1; 203 Widget::InitParams init_params = 204 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 205 init_params.bounds = gfx::Rect(0, 0, 200, 200); 206 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 207 init_params.native_widget = new DesktopNativeWidgetAura(&widget1); 208 widget1.Init(init_params); 209 widget1.SetContentsView(contents_view1); 210 widget1.Show(); 211 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow(); 212 contents_view1->RequestFocus(); 213 214 EXPECT_TRUE(root_window1 != NULL); 215 aura::client::ActivationClient* activation_client1 = 216 aura::client::GetActivationClient(root_window1); 217 EXPECT_TRUE(activation_client1 != NULL); 218 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); 219 220 // Create widget 2 and expect the active window to be its window. 221 View* contents_view2 = new View; 222 Widget widget2; 223 Widget::InitParams init_params2 = 224 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 225 init_params2.bounds = gfx::Rect(0, 0, 200, 200); 226 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 227 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2); 228 widget2.Init(init_params2); 229 widget2.SetContentsView(contents_view2); 230 widget2.Show(); 231 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow(); 232 contents_view2->RequestFocus(); 233 ::SetActiveWindow( 234 root_window2->GetHost()->GetAcceleratedWidget()); 235 236 aura::client::ActivationClient* activation_client2 = 237 aura::client::GetActivationClient(root_window2); 238 EXPECT_TRUE(activation_client2 != NULL); 239 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView()); 240 EXPECT_EQ(activation_client1->GetActiveWindow(), 241 reinterpret_cast<aura::Window*>(NULL)); 242 243 // Now set focus back to widget 1 and expect the active window to be its 244 // window. 245 contents_view1->RequestFocus(); 246 ::SetActiveWindow( 247 root_window1->GetHost()->GetAcceleratedWidget()); 248 EXPECT_EQ(activation_client2->GetActiveWindow(), 249 reinterpret_cast<aura::Window*>(NULL)); 250 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); 251 } 252 #endif // defined(OS_WIN) 253 254 TEST_F(WidgetTestInteractive, CaptureAutoReset) { 255 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 256 View* container = new View; 257 toplevel->SetContentsView(container); 258 259 EXPECT_FALSE(toplevel->HasCapture()); 260 toplevel->SetCapture(NULL); 261 EXPECT_TRUE(toplevel->HasCapture()); 262 263 // By default, mouse release removes capture. 264 gfx::Point click_location(45, 15); 265 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 266 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 267 toplevel->OnMouseEvent(&release); 268 EXPECT_FALSE(toplevel->HasCapture()); 269 270 // Now a mouse release shouldn't remove capture. 271 toplevel->set_auto_release_capture(false); 272 toplevel->SetCapture(NULL); 273 EXPECT_TRUE(toplevel->HasCapture()); 274 toplevel->OnMouseEvent(&release); 275 EXPECT_TRUE(toplevel->HasCapture()); 276 toplevel->ReleaseCapture(); 277 EXPECT_FALSE(toplevel->HasCapture()); 278 279 toplevel->Close(); 280 RunPendingMessages(); 281 } 282 283 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) { 284 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 285 View* container = new View; 286 toplevel->SetContentsView(container); 287 288 View* gesture = new GestureCaptureView; 289 gesture->SetBounds(0, 0, 30, 30); 290 container->AddChildView(gesture); 291 292 MouseView* mouse = new MouseView; 293 mouse->SetBounds(30, 0, 30, 30); 294 container->AddChildView(mouse); 295 296 toplevel->SetSize(gfx::Size(100, 100)); 297 toplevel->Show(); 298 299 // Start a gesture on |gesture|. 300 ui::GestureEvent tap_down(15, 301 15, 302 0, 303 base::TimeDelta(), 304 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN)); 305 ui::GestureEvent end(15, 306 15, 307 0, 308 base::TimeDelta(), 309 ui::GestureEventDetails(ui::ET_GESTURE_END)); 310 toplevel->OnGestureEvent(&tap_down); 311 312 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| 313 // will not receive the event. 314 gfx::Point click_location(45, 15); 315 316 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 317 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 318 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 319 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 320 321 EXPECT_TRUE(toplevel->HasCapture()); 322 323 toplevel->OnMouseEvent(&press); 324 toplevel->OnMouseEvent(&release); 325 EXPECT_EQ(0, mouse->pressed()); 326 327 EXPECT_FALSE(toplevel->HasCapture()); 328 329 // The end of the gesture should release the capture, and pressing on |mouse| 330 // should now reach |mouse|. 331 toplevel->OnGestureEvent(&end); 332 toplevel->OnMouseEvent(&press); 333 toplevel->OnMouseEvent(&release); 334 EXPECT_EQ(1, mouse->pressed()); 335 336 toplevel->Close(); 337 RunPendingMessages(); 338 } 339 340 // Checks that if a mouse-press triggers a capture on a different widget (which 341 // consumes the mouse-release event), then the target of the press does not have 342 // capture. 343 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) { 344 // The test creates two widgets: |first| and |second|. 345 // The View in |first| makes |second| visible, sets capture on it, and starts 346 // a nested loop (like a menu does). The View in |second| terminates the 347 // nested loop and closes the widget. 348 // The test sends a mouse-press event to |first|, and posts a task to send a 349 // release event to |second|, to make sure that the release event is 350 // dispatched after the nested loop starts. 351 352 Widget* first = CreateTopLevelFramelessPlatformWidget(); 353 Widget* second = CreateTopLevelFramelessPlatformWidget(); 354 355 View* container = new NestedLoopCaptureView(second); 356 first->SetContentsView(container); 357 358 second->SetContentsView(new ExitLoopOnRelease()); 359 360 first->SetSize(gfx::Size(100, 100)); 361 first->Show(); 362 363 gfx::Point location(20, 20); 364 base::MessageLoop::current()->PostTask(FROM_HERE, 365 base::Bind(&Widget::OnMouseEvent, 366 base::Unretained(second), 367 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, 368 location, 369 location, 370 ui::EF_LEFT_MOUSE_BUTTON, 371 ui::EF_LEFT_MOUSE_BUTTON)))); 372 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, 373 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 374 first->OnMouseEvent(&press); 375 EXPECT_FALSE(first->HasCapture()); 376 first->Close(); 377 RunPendingMessages(); 378 } 379 380 // Tests some grab/ungrab events. 381 // TODO(estade): can this be enabled now that this is an interactive ui test? 382 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) { 383 Widget* toplevel = CreateTopLevelPlatformWidget(); 384 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); 385 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); 386 387 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500)); 388 389 child1->SetBounds(gfx::Rect(10, 10, 300, 300)); 390 View* view = new MouseView(); 391 view->SetBounds(0, 0, 300, 300); 392 child1->GetRootView()->AddChildView(view); 393 394 child2->SetBounds(gfx::Rect(200, 10, 200, 200)); 395 view = new MouseView(); 396 view->SetBounds(0, 0, 200, 200); 397 child2->GetRootView()->AddChildView(view); 398 399 toplevel->Show(); 400 RunPendingMessages(); 401 402 // Click on child1 403 gfx::Point p1(45, 45); 404 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, 405 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 406 toplevel->OnMouseEvent(&pressed); 407 408 EXPECT_TRUE(toplevel->HasCapture()); 409 EXPECT_TRUE(child1->HasCapture()); 410 EXPECT_FALSE(child2->HasCapture()); 411 412 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, 413 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 414 toplevel->OnMouseEvent(&released); 415 416 EXPECT_FALSE(toplevel->HasCapture()); 417 EXPECT_FALSE(child1->HasCapture()); 418 EXPECT_FALSE(child2->HasCapture()); 419 420 RunPendingMessages(); 421 422 // Click on child2 423 gfx::Point p2(315, 45); 424 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, 425 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 426 toplevel->OnMouseEvent(&pressed2); 427 EXPECT_TRUE(pressed2.handled()); 428 EXPECT_TRUE(toplevel->HasCapture()); 429 EXPECT_TRUE(child2->HasCapture()); 430 EXPECT_FALSE(child1->HasCapture()); 431 432 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, 433 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 434 toplevel->OnMouseEvent(&released2); 435 EXPECT_FALSE(toplevel->HasCapture()); 436 EXPECT_FALSE(child1->HasCapture()); 437 EXPECT_FALSE(child2->HasCapture()); 438 439 toplevel->CloseNow(); 440 } 441 442 // Tests mouse move outside of the window into the "resize controller" and back 443 // will still generate an OnMouseEntered and OnMouseExited event.. 444 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) { 445 Widget* toplevel = CreateTopLevelPlatformWidget(); 446 447 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); 448 449 MouseView* view = new MouseView(); 450 view->SetBounds(90, 90, 10, 10); 451 toplevel->GetRootView()->AddChildView(view); 452 453 toplevel->Show(); 454 RunPendingMessages(); 455 456 // Move to an outside position. 457 gfx::Point p1(200, 200); 458 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE, 459 ui::EF_NONE); 460 toplevel->OnMouseEvent(&moved_out); 461 EXPECT_EQ(0, view->EnteredCalls()); 462 EXPECT_EQ(0, view->ExitedCalls()); 463 464 // Move onto the active view. 465 gfx::Point p2(95, 95); 466 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE, 467 ui::EF_NONE); 468 toplevel->OnMouseEvent(&moved_over); 469 EXPECT_EQ(1, view->EnteredCalls()); 470 EXPECT_EQ(0, view->ExitedCalls()); 471 472 // Move onto the outer resizing border. 473 gfx::Point p3(102, 95); 474 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE, 475 ui::EF_NONE); 476 toplevel->OnMouseEvent(&moved_resizer); 477 EXPECT_EQ(0, view->EnteredCalls()); 478 EXPECT_EQ(1, view->ExitedCalls()); 479 480 // Move onto the view again. 481 toplevel->OnMouseEvent(&moved_over); 482 EXPECT_EQ(1, view->EnteredCalls()); 483 EXPECT_EQ(0, view->ExitedCalls()); 484 485 RunPendingMessages(); 486 487 toplevel->CloseNow(); 488 } 489 490 // Test view focus restoration when a widget is deactivated and re-activated. 491 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { 492 Widget* widget1 = CreateTopLevelPlatformWidget(); 493 View* view1 = new View; 494 view1->SetFocusable(true); 495 widget1->GetContentsView()->AddChildView(view1); 496 497 Widget* widget2 = CreateTopLevelPlatformWidget(); 498 View* view2a = new View; 499 View* view2b = new View; 500 view2a->SetFocusable(true); 501 view2b->SetFocusable(true); 502 widget2->GetContentsView()->AddChildView(view2a); 503 widget2->GetContentsView()->AddChildView(view2b); 504 505 widget1->Show(); 506 EXPECT_TRUE(widget1->IsActive()); 507 view1->RequestFocus(); 508 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); 509 510 widget2->Show(); 511 EXPECT_TRUE(widget2->IsActive()); 512 EXPECT_FALSE(widget1->IsActive()); 513 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView()); 514 view2a->RequestFocus(); 515 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView()); 516 view2b->RequestFocus(); 517 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); 518 519 widget1->Activate(); 520 EXPECT_TRUE(widget1->IsActive()); 521 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); 522 EXPECT_FALSE(widget2->IsActive()); 523 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView()); 524 525 widget2->Activate(); 526 EXPECT_TRUE(widget2->IsActive()); 527 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); 528 EXPECT_FALSE(widget1->IsActive()); 529 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView()); 530 531 widget1->CloseNow(); 532 widget2->CloseNow(); 533 } 534 535 #if defined(OS_WIN) 536 537 // Test view focus retention when a widget's HWND is disabled and re-enabled. 538 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { 539 Widget* widget = CreateTopLevelFramelessPlatformWidget(); 540 widget->SetContentsView(new View); 541 for (size_t i = 0; i < 2; ++i) { 542 widget->GetContentsView()->AddChildView(new View); 543 widget->GetContentsView()->child_at(i)->SetFocusable(true); 544 } 545 546 widget->Show(); 547 const HWND hwnd = HWNDForWidget(widget); 548 EXPECT_TRUE(::IsWindow(hwnd)); 549 EXPECT_TRUE(::IsWindowEnabled(hwnd)); 550 EXPECT_EQ(hwnd, ::GetActiveWindow()); 551 552 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) { 553 SCOPED_TRACE(base::StringPrintf("Child view %d", i)); 554 View* view = widget->GetContentsView()->child_at(i); 555 556 view->RequestFocus(); 557 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); 558 EXPECT_FALSE(::EnableWindow(hwnd, FALSE)); 559 EXPECT_FALSE(::IsWindowEnabled(hwnd)); 560 561 // Oddly, disabling the HWND leaves it active with the focus unchanged. 562 EXPECT_EQ(hwnd, ::GetActiveWindow()); 563 EXPECT_TRUE(widget->IsActive()); 564 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); 565 566 EXPECT_TRUE(::EnableWindow(hwnd, TRUE)); 567 EXPECT_TRUE(::IsWindowEnabled(hwnd)); 568 EXPECT_EQ(hwnd, ::GetActiveWindow()); 569 EXPECT_TRUE(widget->IsActive()); 570 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); 571 } 572 573 widget->CloseNow(); 574 } 575 576 // This class subclasses the Widget class to listen for activation change 577 // notifications and provides accessors to return information as to whether 578 // the widget is active. We need this to ensure that users of the widget 579 // class activate the widget only when the underlying window becomes really 580 // active. Previously we would activate the widget in the WM_NCACTIVATE 581 // message which is incorrect because APIs like FlashWindowEx flash the 582 // window caption by sending fake WM_NCACTIVATE messages. 583 class WidgetActivationTest : public Widget { 584 public: 585 WidgetActivationTest() 586 : active_(false) {} 587 588 virtual ~WidgetActivationTest() {} 589 590 virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE { 591 active_ = active; 592 } 593 594 bool active() const { return active_; } 595 596 private: 597 bool active_; 598 599 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest); 600 }; 601 602 // Tests whether the widget only becomes active when the underlying window 603 // is really active. 604 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) { 605 WidgetActivationTest widget1; 606 Widget::InitParams init_params = 607 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 608 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 609 init_params.native_widget = new DesktopNativeWidgetAura(&widget1); 610 init_params.bounds = gfx::Rect(0, 0, 200, 200); 611 widget1.Init(init_params); 612 widget1.Show(); 613 EXPECT_EQ(true, widget1.active()); 614 615 WidgetActivationTest widget2; 616 init_params.native_widget = new DesktopNativeWidgetAura(&widget2); 617 widget2.Init(init_params); 618 widget2.Show(); 619 EXPECT_EQ(true, widget2.active()); 620 EXPECT_EQ(false, widget1.active()); 621 622 HWND win32_native_window1 = HWNDForWidget(&widget1); 623 EXPECT_TRUE(::IsWindow(win32_native_window1)); 624 625 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0); 626 EXPECT_EQ(false, widget1.active()); 627 EXPECT_EQ(true, widget2.active()); 628 629 ::SetActiveWindow(win32_native_window1); 630 EXPECT_EQ(true, widget1.active()); 631 EXPECT_EQ(false, widget2.active()); 632 } 633 #endif // defined(OS_WIN) 634 635 #if !defined(OS_CHROMEOS) 636 // Provides functionality to create a window modal dialog. 637 class ModalDialogDelegate : public DialogDelegateView { 638 public: 639 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {} 640 virtual ~ModalDialogDelegate() {} 641 642 // WidgetDelegate overrides. 643 virtual ui::ModalType GetModalType() const OVERRIDE { 644 return type_; 645 } 646 647 private: 648 ui::ModalType type_; 649 650 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); 651 }; 652 653 // Tests whether the focused window is set correctly when a modal window is 654 // created and destroyed. When it is destroyed it should focus the owner window. 655 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) { 656 TestWidgetFocusChangeListener focus_listener; 657 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); 658 const std::vector<NativeViewPair>& focus_changes = 659 focus_listener.focus_changes(); 660 661 // Create a top level widget. 662 Widget top_level_widget; 663 Widget::InitParams init_params = 664 CreateParams(Widget::InitParams::TYPE_WINDOW); 665 init_params.show_state = ui::SHOW_STATE_NORMAL; 666 gfx::Rect initial_bounds(0, 0, 500, 500); 667 init_params.bounds = initial_bounds; 668 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 669 init_params.native_widget = 670 new PlatformDesktopNativeWidget(&top_level_widget); 671 top_level_widget.Init(init_params); 672 top_level_widget.Show(); 673 674 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView(); 675 EXPECT_EQ(1u, focus_changes.size()); 676 EXPECT_EQ(NativeViewPair(NULL, top_level_native_view), focus_changes[0]); 677 678 // Create a modal dialog. 679 // This instance will be destroyed when the dialog is destroyed. 680 ModalDialogDelegate* dialog_delegate = 681 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW); 682 683 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 684 dialog_delegate, NULL, top_level_widget.GetNativeView()); 685 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 686 modal_dialog_widget->Show(); 687 688 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView(); 689 EXPECT_EQ(3u, focus_changes.size()); 690 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view), 691 focus_changes[1]); 692 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view), 693 focus_changes[2]); 694 695 modal_dialog_widget->CloseNow(); 696 697 EXPECT_EQ(5u, focus_changes.size()); 698 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view), 699 focus_changes[3]); 700 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view), 701 focus_changes[4]); 702 703 top_level_widget.CloseNow(); 704 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); 705 } 706 707 // Test that when opening a system-modal window, capture is released. 708 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) { 709 TestWidgetFocusChangeListener focus_listener; 710 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); 711 712 // Create a top level widget. 713 Widget top_level_widget; 714 Widget::InitParams init_params = 715 CreateParams(Widget::InitParams::TYPE_WINDOW); 716 init_params.show_state = ui::SHOW_STATE_NORMAL; 717 gfx::Rect initial_bounds(0, 0, 500, 500); 718 init_params.bounds = initial_bounds; 719 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 720 init_params.native_widget = 721 new PlatformDesktopNativeWidget(&top_level_widget); 722 top_level_widget.Init(init_params); 723 top_level_widget.Show(); 724 725 EXPECT_EQ(top_level_widget.GetNativeView(), 726 focus_listener.focus_changes().back().second);; 727 728 EXPECT_FALSE(top_level_widget.HasCapture()); 729 top_level_widget.SetCapture(NULL); 730 EXPECT_TRUE(top_level_widget.HasCapture()); 731 732 // Create a modal dialog. 733 ModalDialogDelegate* dialog_delegate = 734 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM); 735 736 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 737 dialog_delegate, NULL, top_level_widget.GetNativeView()); 738 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 739 modal_dialog_widget->Show(); 740 741 EXPECT_FALSE(top_level_widget.HasCapture()); 742 743 modal_dialog_widget->CloseNow(); 744 top_level_widget.CloseNow(); 745 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); 746 } 747 748 #endif // !defined(OS_CHROMEOS) 749 750 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) { 751 Widget widget; 752 Widget::InitParams init_params = 753 CreateParams(Widget::InitParams::TYPE_WINDOW); 754 init_params.bounds = gfx::Rect(0, 0, 200, 200); 755 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 756 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO; 757 #if !defined(OS_CHROMEOS) 758 init_params.native_widget = new PlatformDesktopNativeWidget(&widget); 759 #endif // !defined(OS_CHROMEOS) 760 widget.Init(init_params); 761 762 widget.Show(); 763 EXPECT_FALSE(widget.IsActive()); 764 } 765 766 // Test that touch selection quick menu is not activated when opened. 767 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) { 768 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing); 769 #if defined(OS_WIN) 770 views_delegate().set_use_desktop_native_widgets(true); 771 #endif // !defined(OS_WIN) 772 773 Widget widget; 774 Widget::InitParams init_params = 775 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 776 init_params.bounds = gfx::Rect(0, 0, 200, 200); 777 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 778 widget.Init(init_params); 779 780 Textfield* textfield = new Textfield; 781 textfield->SetBounds(0, 0, 200, 20); 782 textfield->SetText(base::ASCIIToUTF16("some text")); 783 widget.GetRootView()->AddChildView(textfield); 784 785 widget.Show(); 786 textfield->RequestFocus(); 787 textfield->SelectAll(true); 788 TextfieldTestApi textfield_test_api(textfield); 789 790 RunPendingMessages(); 791 792 ui::test::EventGenerator generator(widget.GetNativeWindow()); 793 generator.GestureTapAt(gfx::Point(10, 10)); 794 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>( 795 textfield_test_api.touch_selection_controller())); 796 797 EXPECT_TRUE(textfield->HasFocus()); 798 EXPECT_TRUE(widget.IsActive()); 799 EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>( 800 textfield_test_api.touch_selection_controller()))); 801 } 802 803 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) { 804 #if defined(OS_WIN) 805 views_delegate().set_use_desktop_native_widgets(true); 806 #endif // !defined(OS_WIN) 807 808 // Create first widget and view, activate the widget, and focus the view. 809 Widget widget1; 810 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP); 811 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 812 params1.activatable = Widget::InitParams::ACTIVATABLE_YES; 813 widget1.Init(params1); 814 815 View* view1 = new View(); 816 view1->SetFocusable(true); 817 widget1.GetRootView()->AddChildView(view1); 818 819 widget1.Activate(); 820 EXPECT_TRUE(widget1.IsActive()); 821 822 FocusManager* focus_manager1 = widget1.GetFocusManager(); 823 ASSERT_TRUE(focus_manager1); 824 focus_manager1->SetFocusedView(view1); 825 EXPECT_EQ(view1, focus_manager1->GetFocusedView()); 826 827 // Create second widget and view, activate the widget, and focus the view. 828 Widget widget2; 829 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP); 830 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 831 params2.activatable = Widget::InitParams::ACTIVATABLE_YES; 832 widget2.Init(params2); 833 834 View* view2 = new View(); 835 view2->SetFocusable(true); 836 widget2.GetRootView()->AddChildView(view2); 837 838 widget2.Activate(); 839 EXPECT_TRUE(widget2.IsActive()); 840 EXPECT_FALSE(widget1.IsActive()); 841 842 FocusManager* focus_manager2 = widget2.GetFocusManager(); 843 ASSERT_TRUE(focus_manager2); 844 focus_manager2->SetFocusedView(view2); 845 EXPECT_EQ(view2, focus_manager2->GetFocusedView()); 846 847 // Disable the first view and make sure it loses focus, but its widget is not 848 // activated. 849 view1->SetEnabled(false); 850 EXPECT_NE(view1, focus_manager1->GetFocusedView()); 851 EXPECT_FALSE(widget1.IsActive()); 852 EXPECT_TRUE(widget2.IsActive()); 853 } 854 855 namespace { 856 857 // Used to veirfy OnMouseCaptureLost() has been invoked. 858 class CaptureLostTrackingWidget : public Widget { 859 public: 860 CaptureLostTrackingWidget() : got_capture_lost_(false) {} 861 virtual ~CaptureLostTrackingWidget() {} 862 863 bool GetAndClearGotCaptureLost() { 864 bool value = got_capture_lost_; 865 got_capture_lost_ = false; 866 return value; 867 } 868 869 // Widget: 870 virtual void OnMouseCaptureLost() OVERRIDE { 871 got_capture_lost_ = true; 872 Widget::OnMouseCaptureLost(); 873 } 874 875 private: 876 bool got_capture_lost_; 877 878 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget); 879 }; 880 881 } // namespace 882 883 class WidgetCaptureTest : public ViewsTestBase { 884 public: 885 WidgetCaptureTest() { 886 } 887 888 virtual ~WidgetCaptureTest() { 889 } 890 891 virtual void SetUp() OVERRIDE { 892 gfx::GLSurface::InitializeOneOffForTests(); 893 ui::RegisterPathProvider(); 894 base::FilePath ui_test_pak_path; 895 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); 896 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); 897 ViewsTestBase::SetUp(); 898 } 899 900 // Verifies Widget::SetCapture() results in updating native capture along with 901 // invoking the right Widget function. 902 void TestCapture(bool use_desktop_native_widget) { 903 CaptureLostTrackingWidget widget1; 904 Widget::InitParams params1 = 905 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 906 params1.native_widget = CreateNativeWidget(use_desktop_native_widget, 907 &widget1); 908 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 909 widget1.Init(params1); 910 widget1.Show(); 911 912 CaptureLostTrackingWidget widget2; 913 Widget::InitParams params2 = 914 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 915 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 916 params2.native_widget = CreateNativeWidget(use_desktop_native_widget, 917 &widget2); 918 widget2.Init(params2); 919 widget2.Show(); 920 921 // Set capture to widget2 and verity it gets it. 922 widget2.SetCapture(widget2.GetRootView()); 923 EXPECT_FALSE(widget1.HasCapture()); 924 EXPECT_TRUE(widget2.HasCapture()); 925 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost()); 926 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); 927 928 // Set capture to widget1 and verify it gets it. 929 widget1.SetCapture(widget1.GetRootView()); 930 EXPECT_TRUE(widget1.HasCapture()); 931 EXPECT_FALSE(widget2.HasCapture()); 932 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost()); 933 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost()); 934 935 // Release and verify no one has it. 936 widget1.ReleaseCapture(); 937 EXPECT_FALSE(widget1.HasCapture()); 938 EXPECT_FALSE(widget2.HasCapture()); 939 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost()); 940 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); 941 } 942 943 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget, 944 Widget* widget) { 945 #if !defined(OS_CHROMEOS) 946 if (create_desktop_native_widget) 947 return new PlatformDesktopNativeWidget(widget); 948 #endif 949 return NULL; 950 } 951 952 private: 953 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest); 954 }; 955 956 // See description in TestCapture(). 957 TEST_F(WidgetCaptureTest, Capture) { 958 TestCapture(false); 959 } 960 961 #if !defined(OS_CHROMEOS) 962 // See description in TestCapture(). Creates DesktopNativeWidget. 963 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) { 964 TestCapture(true); 965 } 966 #endif 967 968 // Test that no state is set if capture fails. 969 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) { 970 Widget widget; 971 Widget::InitParams params = 972 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 973 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 974 params.bounds = gfx::Rect(400, 400); 975 widget.Init(params); 976 977 MouseView* mouse_view1 = new MouseView; 978 MouseView* mouse_view2 = new MouseView; 979 View* contents_view = new View; 980 contents_view->AddChildView(mouse_view1); 981 contents_view->AddChildView(mouse_view2); 982 widget.SetContentsView(contents_view); 983 984 mouse_view1->SetBounds(0, 0, 200, 400); 985 mouse_view2->SetBounds(200, 0, 200, 400); 986 987 // Setting capture should fail because |widget| is not visible. 988 widget.SetCapture(mouse_view1); 989 EXPECT_FALSE(widget.HasCapture()); 990 991 widget.Show(); 992 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow()); 993 generator.set_current_location(gfx::Point(300, 10)); 994 generator.PressLeftButton(); 995 996 EXPECT_FALSE(mouse_view1->pressed()); 997 EXPECT_TRUE(mouse_view2->pressed()); 998 } 999 1000 #if !defined(OS_CHROMEOS) && !defined(OS_WIN) 1001 // Test that a synthetic mouse exit is sent to the widget which was handling 1002 // mouse events when a different widget grabs capture. 1003 // TODO(pkotwicz): Make test pass on CrOS and Windows. 1004 TEST_F(WidgetCaptureTest, MouseExitOnCaptureGrab) { 1005 Widget widget1; 1006 Widget::InitParams params1 = 1007 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1008 params1.native_widget = CreateNativeWidget(true, &widget1); 1009 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1010 widget1.Init(params1); 1011 MouseView* mouse_view1 = new MouseView; 1012 widget1.SetContentsView(mouse_view1); 1013 widget1.Show(); 1014 widget1.SetBounds(gfx::Rect(300, 300)); 1015 1016 Widget widget2; 1017 Widget::InitParams params2 = 1018 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1019 params2.native_widget = CreateNativeWidget(true, &widget2); 1020 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1021 widget2.Init(params2); 1022 widget2.Show(); 1023 widget2.SetBounds(gfx::Rect(400, 0, 300, 300)); 1024 1025 ui::test::EventGenerator generator(widget1.GetNativeWindow()); 1026 generator.set_current_location(gfx::Point(100, 100)); 1027 generator.MoveMouseBy(0, 0); 1028 1029 EXPECT_EQ(1, mouse_view1->EnteredCalls()); 1030 EXPECT_EQ(0, mouse_view1->ExitedCalls()); 1031 1032 widget2.SetCapture(NULL); 1033 EXPECT_EQ(0, mouse_view1->EnteredCalls()); 1034 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event 1035 // in addition to the one generated by Chrome. 1036 EXPECT_LT(0, mouse_view1->ExitedCalls()); 1037 } 1038 #endif // !defined(OS_CHROMEOS) 1039 1040 namespace { 1041 1042 // Widget observer which grabs capture when the widget is activated. 1043 class CaptureOnActivationObserver : public WidgetObserver { 1044 public: 1045 CaptureOnActivationObserver() { 1046 } 1047 virtual ~CaptureOnActivationObserver() { 1048 } 1049 1050 // WidgetObserver: 1051 virtual void OnWidgetActivationChanged(Widget* widget, bool active) OVERRIDE { 1052 if (active) 1053 widget->SetCapture(NULL); 1054 } 1055 1056 private: 1057 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver); 1058 }; 1059 1060 } // namespace 1061 1062 // Test that setting capture on widget activation of a non-toplevel widget 1063 // (e.g. a bubble on Linux) succeeds. 1064 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) { 1065 Widget toplevel; 1066 Widget::InitParams toplevel_params = 1067 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1068 toplevel_params.native_widget = CreateNativeWidget(true, &toplevel); 1069 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1070 toplevel.Init(toplevel_params); 1071 toplevel.Show(); 1072 1073 Widget* child = new Widget; 1074 Widget::InitParams child_params = 1075 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1076 child_params.parent = toplevel.GetNativeView(); 1077 child_params.context = toplevel.GetNativeWindow(); 1078 child->Init(child_params); 1079 1080 CaptureOnActivationObserver observer; 1081 child->AddObserver(&observer); 1082 child->Show(); 1083 1084 EXPECT_TRUE(child->HasCapture()); 1085 } 1086 1087 1088 #if defined(OS_WIN) 1089 namespace { 1090 1091 // Used to verify OnMouseEvent() has been invoked. 1092 class MouseEventTrackingWidget : public Widget { 1093 public: 1094 MouseEventTrackingWidget() : got_mouse_event_(false) {} 1095 virtual ~MouseEventTrackingWidget() {} 1096 1097 bool GetAndClearGotMouseEvent() { 1098 bool value = got_mouse_event_; 1099 got_mouse_event_ = false; 1100 return value; 1101 } 1102 1103 // Widget: 1104 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 1105 got_mouse_event_ = true; 1106 Widget::OnMouseEvent(event); 1107 } 1108 1109 private: 1110 bool got_mouse_event_; 1111 1112 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget); 1113 }; 1114 1115 } // namespace 1116 1117 // Verifies if a mouse event is received on a widget that doesn't have capture 1118 // on Windows that it is correctly processed by the widget that doesn't have 1119 // capture. This behavior is not desired on OSes other than Windows. 1120 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) { 1121 MouseEventTrackingWidget widget1; 1122 Widget::InitParams params1 = 1123 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 1124 params1.native_widget = new DesktopNativeWidgetAura(&widget1); 1125 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1126 widget1.Init(params1); 1127 widget1.Show(); 1128 1129 MouseEventTrackingWidget widget2; 1130 Widget::InitParams params2 = 1131 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 1132 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1133 params2.native_widget = new DesktopNativeWidgetAura(&widget2); 1134 widget2.Init(params2); 1135 widget2.Show(); 1136 1137 // Set capture to widget2 and verity it gets it. 1138 widget2.SetCapture(widget2.GetRootView()); 1139 EXPECT_FALSE(widget1.HasCapture()); 1140 EXPECT_TRUE(widget2.HasCapture()); 1141 1142 widget1.GetAndClearGotMouseEvent(); 1143 widget2.GetAndClearGotMouseEvent(); 1144 // Send a mouse event to the RootWindow associated with |widget1|. Even though 1145 // |widget2| has capture, |widget1| should still get the event. 1146 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), 1147 ui::EF_NONE, ui::EF_NONE); 1148 ui::EventDispatchDetails details = widget1.GetNativeWindow()-> 1149 GetHost()->event_processor()->OnEventFromSource(&mouse_event); 1150 ASSERT_FALSE(details.dispatcher_destroyed); 1151 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent()); 1152 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent()); 1153 } 1154 #endif // defined(OS_WIN) 1155 1156 } // namespace test 1157 } // namespace views 1158