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/run_loop.h" 8 #include "ui/gfx/native_widget_types.h" 9 #include "ui/views/test/widget_test.h" 10 #include "ui/views/widget/widget.h" 11 #include "ui/views/window/dialog_delegate.h" 12 13 #if defined(USE_AURA) 14 #include "ui/aura/client/activation_client.h" 15 #include "ui/aura/client/focus_client.h" 16 #include "ui/aura/env.h" 17 #include "ui/aura/root_window.h" 18 #include "ui/aura/window.h" 19 #endif 20 21 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 22 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 23 #endif 24 25 #if defined(OS_WIN) 26 #include "ui/views/win/hwnd_util.h" 27 #endif 28 29 namespace views { 30 namespace test { 31 32 namespace { 33 34 // A View that closes the Widget and exits the current message-loop when it 35 // receives a mouse-release event. 36 class ExitLoopOnRelease : public View { 37 public: 38 ExitLoopOnRelease() {} 39 virtual ~ExitLoopOnRelease() {} 40 41 private: 42 // Overridden from View: 43 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { 44 GetWidget()->Close(); 45 base::MessageLoop::current()->QuitNow(); 46 } 47 48 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease); 49 }; 50 51 // A view that does a capture on gesture-begin events. 52 class GestureCaptureView : public View { 53 public: 54 GestureCaptureView() {} 55 virtual ~GestureCaptureView() {} 56 57 private: 58 // Overridden from View: 59 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 60 if (event->type() == ui::ET_GESTURE_BEGIN) { 61 GetWidget()->SetCapture(this); 62 event->StopPropagation(); 63 } 64 } 65 66 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView); 67 }; 68 69 // A view that always processes all mouse events. 70 class MouseView : public View { 71 public: 72 MouseView() 73 : View(), 74 entered_(0), 75 exited_(0), 76 pressed_(0) { 77 } 78 virtual ~MouseView() {} 79 80 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 81 pressed_++; 82 return true; 83 } 84 85 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { 86 entered_++; 87 } 88 89 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { 90 exited_++; 91 } 92 93 // Return the number of OnMouseEntered calls and reset the counter. 94 int EnteredCalls() { 95 int i = entered_; 96 entered_ = 0; 97 return i; 98 } 99 100 // Return the number of OnMouseExited calls and reset the counter. 101 int ExitedCalls() { 102 int i = exited_; 103 exited_ = 0; 104 return i; 105 } 106 107 int pressed() const { return pressed_; } 108 109 private: 110 int entered_; 111 int exited_; 112 113 int pressed_; 114 115 DISALLOW_COPY_AND_ASSIGN(MouseView); 116 }; 117 118 // A View that shows a different widget, sets capture on that widget, and 119 // initiates a nested message-loop when it receives a mouse-press event. 120 class NestedLoopCaptureView : public View { 121 public: 122 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {} 123 virtual ~NestedLoopCaptureView() {} 124 125 private: 126 // Overridden from View: 127 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 128 // Start a nested loop. 129 widget_->Show(); 130 widget_->SetCapture(widget_->GetContentsView()); 131 EXPECT_TRUE(widget_->HasCapture()); 132 133 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 134 base::MessageLoop::ScopedNestableTaskAllower allow(loop); 135 136 base::RunLoop run_loop; 137 #if defined(USE_AURA) 138 run_loop.set_dispatcher(aura::Env::GetInstance()->GetDispatcher()); 139 #endif 140 run_loop.Run(); 141 return true; 142 } 143 144 Widget* widget_; 145 146 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView); 147 }; 148 149 } // namespace 150 151 #if defined(OS_WIN) && defined(USE_AURA) 152 // Tests whether activation and focus change works correctly in Windows AURA. 153 // We test the following:- 154 // 1. If the active aura window is correctly set when a top level widget is 155 // created. 156 // 2. If the active aura window in widget 1 created above, is set to NULL when 157 // another top level widget is created and focused. 158 // 3. On focusing the native platform window for widget 1, the active aura 159 // window for widget 1 should be set and that for widget 2 should reset. 160 // TODO(ananta) 161 // Discuss with erg on how to write this test for linux x11 aura. 162 TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) { 163 // Create widget 1 and expect the active window to be its window. 164 View* contents_view1 = new View; 165 contents_view1->SetFocusable(true); 166 Widget widget1; 167 Widget::InitParams init_params = 168 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 169 init_params.bounds = gfx::Rect(0, 0, 200, 200); 170 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 171 init_params.native_widget = new DesktopNativeWidgetAura(&widget1); 172 widget1.Init(init_params); 173 widget1.SetContentsView(contents_view1); 174 widget1.Show(); 175 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow(); 176 contents_view1->RequestFocus(); 177 178 EXPECT_TRUE(root_window1 != NULL); 179 aura::client::ActivationClient* activation_client1 = 180 aura::client::GetActivationClient(root_window1); 181 EXPECT_TRUE(activation_client1 != NULL); 182 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); 183 184 // Create widget 2 and expect the active window to be its window. 185 View* contents_view2 = new View; 186 Widget widget2; 187 Widget::InitParams init_params2 = 188 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 189 init_params2.bounds = gfx::Rect(0, 0, 200, 200); 190 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 191 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2); 192 widget2.Init(init_params2); 193 widget2.SetContentsView(contents_view2); 194 widget2.Show(); 195 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow(); 196 contents_view2->RequestFocus(); 197 ::SetActiveWindow( 198 root_window2->GetDispatcher()->host()->GetAcceleratedWidget()); 199 200 aura::client::ActivationClient* activation_client2 = 201 aura::client::GetActivationClient(root_window2); 202 EXPECT_TRUE(activation_client2 != NULL); 203 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView()); 204 EXPECT_EQ(activation_client1->GetActiveWindow(), 205 reinterpret_cast<aura::Window*>(NULL)); 206 207 // Now set focus back to widget 1 and expect the active window to be its 208 // window. 209 contents_view1->RequestFocus(); 210 ::SetActiveWindow( 211 root_window1->GetDispatcher()->host()->GetAcceleratedWidget()); 212 EXPECT_EQ(activation_client2->GetActiveWindow(), 213 reinterpret_cast<aura::Window*>(NULL)); 214 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); 215 } 216 #endif 217 218 TEST_F(WidgetTest, CaptureAutoReset) { 219 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 220 View* container = new View; 221 toplevel->SetContentsView(container); 222 223 EXPECT_FALSE(toplevel->HasCapture()); 224 toplevel->SetCapture(NULL); 225 EXPECT_TRUE(toplevel->HasCapture()); 226 227 // By default, mouse release removes capture. 228 gfx::Point click_location(45, 15); 229 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 230 ui::EF_LEFT_MOUSE_BUTTON); 231 toplevel->OnMouseEvent(&release); 232 EXPECT_FALSE(toplevel->HasCapture()); 233 234 // Now a mouse release shouldn't remove capture. 235 toplevel->set_auto_release_capture(false); 236 toplevel->SetCapture(NULL); 237 EXPECT_TRUE(toplevel->HasCapture()); 238 toplevel->OnMouseEvent(&release); 239 EXPECT_TRUE(toplevel->HasCapture()); 240 toplevel->ReleaseCapture(); 241 EXPECT_FALSE(toplevel->HasCapture()); 242 243 toplevel->Close(); 244 RunPendingMessages(); 245 } 246 247 TEST_F(WidgetTest, ResetCaptureOnGestureEnd) { 248 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 249 View* container = new View; 250 toplevel->SetContentsView(container); 251 252 View* gesture = new GestureCaptureView; 253 gesture->SetBounds(0, 0, 30, 30); 254 container->AddChildView(gesture); 255 256 MouseView* mouse = new MouseView; 257 mouse->SetBounds(30, 0, 30, 30); 258 container->AddChildView(mouse); 259 260 toplevel->SetSize(gfx::Size(100, 100)); 261 toplevel->Show(); 262 263 // Start a gesture on |gesture|. 264 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, 265 15, 15, 0, base::TimeDelta(), 266 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); 267 ui::GestureEvent end(ui::ET_GESTURE_END, 268 15, 15, 0, base::TimeDelta(), 269 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); 270 toplevel->OnGestureEvent(&begin); 271 272 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| 273 // will not receive the event. 274 gfx::Point click_location(45, 15); 275 276 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 277 ui::EF_LEFT_MOUSE_BUTTON); 278 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 279 ui::EF_LEFT_MOUSE_BUTTON); 280 281 EXPECT_TRUE(toplevel->HasCapture()); 282 283 toplevel->OnMouseEvent(&press); 284 toplevel->OnMouseEvent(&release); 285 EXPECT_EQ(0, mouse->pressed()); 286 287 EXPECT_FALSE(toplevel->HasCapture()); 288 289 // The end of the gesture should release the capture, and pressing on |mouse| 290 // should now reach |mouse|. 291 toplevel->OnGestureEvent(&end); 292 toplevel->OnMouseEvent(&press); 293 toplevel->OnMouseEvent(&release); 294 EXPECT_EQ(1, mouse->pressed()); 295 296 toplevel->Close(); 297 RunPendingMessages(); 298 } 299 300 // Checks that if a mouse-press triggers a capture on a different widget (which 301 // consumes the mouse-release event), then the target of the press does not have 302 // capture. 303 TEST_F(WidgetTest, DisableCaptureWidgetFromMousePress) { 304 // The test creates two widgets: |first| and |second|. 305 // The View in |first| makes |second| visible, sets capture on it, and starts 306 // a nested loop (like a menu does). The View in |second| terminates the 307 // nested loop and closes the widget. 308 // The test sends a mouse-press event to |first|, and posts a task to send a 309 // release event to |second|, to make sure that the release event is 310 // dispatched after the nested loop starts. 311 312 Widget* first = CreateTopLevelFramelessPlatformWidget(); 313 Widget* second = CreateTopLevelFramelessPlatformWidget(); 314 315 View* container = new NestedLoopCaptureView(second); 316 first->SetContentsView(container); 317 318 second->SetContentsView(new ExitLoopOnRelease()); 319 320 first->SetSize(gfx::Size(100, 100)); 321 first->Show(); 322 323 gfx::Point location(20, 20); 324 base::MessageLoop::current()->PostTask(FROM_HERE, 325 base::Bind(&Widget::OnMouseEvent, 326 base::Unretained(second), 327 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, 328 location, 329 location, 330 ui::EF_LEFT_MOUSE_BUTTON)))); 331 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, 332 ui::EF_LEFT_MOUSE_BUTTON); 333 first->OnMouseEvent(&press); 334 EXPECT_FALSE(first->HasCapture()); 335 first->Close(); 336 RunPendingMessages(); 337 } 338 339 // Tests some grab/ungrab events. 340 // TODO(estade): can this be enabled now that this is an interactive ui test? 341 TEST_F(WidgetTest, DISABLED_GrabUngrab) { 342 Widget* toplevel = CreateTopLevelPlatformWidget(); 343 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); 344 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); 345 346 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500)); 347 348 child1->SetBounds(gfx::Rect(10, 10, 300, 300)); 349 View* view = new MouseView(); 350 view->SetBounds(0, 0, 300, 300); 351 child1->GetRootView()->AddChildView(view); 352 353 child2->SetBounds(gfx::Rect(200, 10, 200, 200)); 354 view = new MouseView(); 355 view->SetBounds(0, 0, 200, 200); 356 child2->GetRootView()->AddChildView(view); 357 358 toplevel->Show(); 359 RunPendingMessages(); 360 361 // Click on child1 362 gfx::Point p1(45, 45); 363 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, 364 ui::EF_LEFT_MOUSE_BUTTON); 365 toplevel->OnMouseEvent(&pressed); 366 367 EXPECT_TRUE(toplevel->HasCapture()); 368 EXPECT_TRUE(child1->HasCapture()); 369 EXPECT_FALSE(child2->HasCapture()); 370 371 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, 372 ui::EF_LEFT_MOUSE_BUTTON); 373 toplevel->OnMouseEvent(&released); 374 375 EXPECT_FALSE(toplevel->HasCapture()); 376 EXPECT_FALSE(child1->HasCapture()); 377 EXPECT_FALSE(child2->HasCapture()); 378 379 RunPendingMessages(); 380 381 // Click on child2 382 gfx::Point p2(315, 45); 383 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, 384 ui::EF_LEFT_MOUSE_BUTTON); 385 toplevel->OnMouseEvent(&pressed2); 386 EXPECT_TRUE(pressed2.handled()); 387 EXPECT_TRUE(toplevel->HasCapture()); 388 EXPECT_TRUE(child2->HasCapture()); 389 EXPECT_FALSE(child1->HasCapture()); 390 391 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, 392 ui::EF_LEFT_MOUSE_BUTTON); 393 toplevel->OnMouseEvent(&released2); 394 EXPECT_FALSE(toplevel->HasCapture()); 395 EXPECT_FALSE(child1->HasCapture()); 396 EXPECT_FALSE(child2->HasCapture()); 397 398 toplevel->CloseNow(); 399 } 400 401 // Tests mouse move outside of the window into the "resize controller" and back 402 // will still generate an OnMouseEntered and OnMouseExited event.. 403 TEST_F(WidgetTest, CheckResizeControllerEvents) { 404 Widget* toplevel = CreateTopLevelPlatformWidget(); 405 406 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); 407 408 MouseView* view = new MouseView(); 409 view->SetBounds(90, 90, 10, 10); 410 toplevel->GetRootView()->AddChildView(view); 411 412 toplevel->Show(); 413 RunPendingMessages(); 414 415 // Move to an outside position. 416 gfx::Point p1(200, 200); 417 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE); 418 toplevel->OnMouseEvent(&moved_out); 419 EXPECT_EQ(0, view->EnteredCalls()); 420 EXPECT_EQ(0, view->ExitedCalls()); 421 422 // Move onto the active view. 423 gfx::Point p2(95, 95); 424 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE); 425 toplevel->OnMouseEvent(&moved_over); 426 EXPECT_EQ(1, view->EnteredCalls()); 427 EXPECT_EQ(0, view->ExitedCalls()); 428 429 // Move onto the outer resizing border. 430 gfx::Point p3(102, 95); 431 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE); 432 toplevel->OnMouseEvent(&moved_resizer); 433 EXPECT_EQ(0, view->EnteredCalls()); 434 EXPECT_EQ(1, view->ExitedCalls()); 435 436 // Move onto the view again. 437 toplevel->OnMouseEvent(&moved_over); 438 EXPECT_EQ(1, view->EnteredCalls()); 439 EXPECT_EQ(0, view->ExitedCalls()); 440 441 RunPendingMessages(); 442 443 toplevel->CloseNow(); 444 } 445 446 #if defined(OS_WIN) 447 448 // This class subclasses the Widget class to listen for activation change 449 // notifications and provides accessors to return information as to whether 450 // the widget is active. We need this to ensure that users of the widget 451 // class activate the widget only when the underlying window becomes really 452 // active. Previously we would activate the widget in the WM_NCACTIVATE 453 // message which is incorrect because APIs like FlashWindowEx flash the 454 // window caption by sending fake WM_NCACTIVATE messages. 455 class WidgetActivationTest : public Widget { 456 public: 457 WidgetActivationTest() 458 : active_(false) {} 459 460 virtual ~WidgetActivationTest() {} 461 462 virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE { 463 active_ = active; 464 } 465 466 bool active() const { return active_; } 467 468 private: 469 bool active_; 470 471 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest); 472 }; 473 474 // Tests whether the widget only becomes active when the underlying window 475 // is really active. 476 TEST_F(WidgetTest, WidgetNotActivatedOnFakeActivationMessages) { 477 WidgetActivationTest widget1; 478 Widget::InitParams init_params = 479 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 480 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 481 #if defined(USE_AURA) 482 init_params.native_widget = new DesktopNativeWidgetAura(&widget1); 483 #endif 484 init_params.bounds = gfx::Rect(0, 0, 200, 200); 485 widget1.Init(init_params); 486 widget1.Show(); 487 EXPECT_EQ(true, widget1.active()); 488 489 WidgetActivationTest widget2; 490 #if defined(USE_AURA) 491 init_params.native_widget = new DesktopNativeWidgetAura(&widget2); 492 #endif 493 widget2.Init(init_params); 494 widget2.Show(); 495 EXPECT_EQ(true, widget2.active()); 496 EXPECT_EQ(false, widget1.active()); 497 498 HWND win32_native_window1 = HWNDForWidget(&widget1); 499 EXPECT_TRUE(::IsWindow(win32_native_window1)); 500 501 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0); 502 EXPECT_EQ(false, widget1.active()); 503 EXPECT_EQ(true, widget2.active()); 504 505 ::SetActiveWindow(win32_native_window1); 506 EXPECT_EQ(true, widget1.active()); 507 EXPECT_EQ(false, widget2.active()); 508 } 509 #endif 510 511 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 512 // Provides functionality to create a window modal dialog. 513 class ModalDialogDelegate : public DialogDelegateView { 514 public: 515 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {} 516 virtual ~ModalDialogDelegate() {} 517 518 // WidgetDelegate overrides. 519 virtual ui::ModalType GetModalType() const OVERRIDE { 520 return type_; 521 } 522 523 private: 524 ui::ModalType type_; 525 526 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); 527 }; 528 529 // Tests whether the focused window is set correctly when a modal window is 530 // created and destroyed. When it is destroyed it should focus the owner 531 // window. 532 TEST_F(WidgetTest, WindowModalWindowDestroyedActivationTest) { 533 // Create a top level widget. 534 Widget top_level_widget; 535 Widget::InitParams init_params = 536 CreateParams(Widget::InitParams::TYPE_WINDOW); 537 init_params.show_state = ui::SHOW_STATE_NORMAL; 538 gfx::Rect initial_bounds(0, 0, 500, 500); 539 init_params.bounds = initial_bounds; 540 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 541 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); 542 top_level_widget.Init(init_params); 543 top_level_widget.Show(); 544 545 aura::Window* top_level_window = top_level_widget.GetNativeWindow(); 546 EXPECT_EQ(top_level_window, aura::client::GetFocusClient( 547 top_level_window)->GetFocusedWindow()); 548 549 // Create a modal dialog. 550 // This instance will be destroyed when the dialog is destroyed. 551 ModalDialogDelegate* dialog_delegate = 552 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW); 553 554 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 555 dialog_delegate, NULL, top_level_widget.GetNativeWindow()); 556 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 557 modal_dialog_widget->Show(); 558 aura::Window* dialog_window = modal_dialog_widget->GetNativeWindow(); 559 EXPECT_EQ(dialog_window, aura::client::GetFocusClient( 560 top_level_window)->GetFocusedWindow()); 561 562 modal_dialog_widget->CloseNow(); 563 EXPECT_EQ(top_level_window, aura::client::GetFocusClient( 564 top_level_window)->GetFocusedWindow()); 565 top_level_widget.CloseNow(); 566 } 567 568 // Test that when opening a system-modal window, capture is released. 569 TEST_F(WidgetTest, SystemModalWindowReleasesCapture) { 570 // Create a top level widget. 571 Widget top_level_widget; 572 Widget::InitParams init_params = 573 CreateParams(Widget::InitParams::TYPE_WINDOW); 574 init_params.show_state = ui::SHOW_STATE_NORMAL; 575 gfx::Rect initial_bounds(0, 0, 500, 500); 576 init_params.bounds = initial_bounds; 577 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 578 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); 579 top_level_widget.Init(init_params); 580 top_level_widget.Show(); 581 582 aura::Window* top_level_window = top_level_widget.GetNativeWindow(); 583 EXPECT_EQ(top_level_window, aura::client::GetFocusClient( 584 top_level_window)->GetFocusedWindow()); 585 586 EXPECT_FALSE(top_level_window->HasCapture()); 587 top_level_window->SetCapture(); 588 EXPECT_TRUE(top_level_window->HasCapture()); 589 590 // Create a modal dialog. 591 ModalDialogDelegate* dialog_delegate = 592 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM); 593 594 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 595 dialog_delegate, NULL, top_level_widget.GetNativeWindow()); 596 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 597 modal_dialog_widget->Show(); 598 599 EXPECT_FALSE(top_level_window->HasCapture()); 600 601 modal_dialog_widget->CloseNow(); 602 top_level_widget.CloseNow(); 603 } 604 605 #endif 606 607 namespace { 608 609 // Used to veirfy OnMouseCaptureLost() has been invoked. 610 class CaptureLostTrackingWidget : public Widget { 611 public: 612 CaptureLostTrackingWidget() : got_capture_lost_(false) {} 613 virtual ~CaptureLostTrackingWidget() {} 614 615 bool GetAndClearGotCaptureLost() { 616 bool value = got_capture_lost_; 617 got_capture_lost_ = false; 618 return value; 619 } 620 621 // Widget: 622 virtual void OnMouseCaptureLost() OVERRIDE { 623 got_capture_lost_ = true; 624 Widget::OnMouseCaptureLost(); 625 } 626 627 private: 628 bool got_capture_lost_; 629 630 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget); 631 }; 632 633 } // namespace 634 635 class WidgetCaptureTest : public ViewsTestBase { 636 public: 637 WidgetCaptureTest() { 638 } 639 640 virtual ~WidgetCaptureTest() { 641 } 642 643 // Verifies Widget::SetCapture() results in updating native capture along with 644 // invoking the right Widget function. 645 void TestCapture(bool use_desktop_native_widget) { 646 CaptureLostTrackingWidget widget1; 647 Widget::InitParams params1 = 648 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 649 params1.native_widget = CreateNativeWidget(use_desktop_native_widget, 650 &widget1); 651 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 652 widget1.Init(params1); 653 widget1.Show(); 654 655 CaptureLostTrackingWidget widget2; 656 Widget::InitParams params2 = 657 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 658 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 659 params2.native_widget = CreateNativeWidget(use_desktop_native_widget, 660 &widget2); 661 widget2.Init(params2); 662 widget2.Show(); 663 664 // Set capture to widget2 and verity it gets it. 665 widget2.SetCapture(widget2.GetRootView()); 666 EXPECT_FALSE(widget1.HasCapture()); 667 EXPECT_TRUE(widget2.HasCapture()); 668 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost()); 669 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); 670 671 // Set capture to widget1 and verify it gets it. 672 widget1.SetCapture(widget1.GetRootView()); 673 EXPECT_TRUE(widget1.HasCapture()); 674 EXPECT_FALSE(widget2.HasCapture()); 675 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost()); 676 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost()); 677 678 // Release and verify no one has it. 679 widget1.ReleaseCapture(); 680 EXPECT_FALSE(widget1.HasCapture()); 681 EXPECT_FALSE(widget2.HasCapture()); 682 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost()); 683 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); 684 } 685 686 private: 687 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget, 688 Widget* widget) { 689 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 690 if (create_desktop_native_widget) 691 return new DesktopNativeWidgetAura(widget); 692 #endif 693 return NULL; 694 } 695 696 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest); 697 }; 698 699 // See description in TestCapture(). 700 TEST_F(WidgetCaptureTest, Capture) { 701 TestCapture(false); 702 } 703 704 #if defined(USE_AURA) && !defined(OS_LINUX) 705 // See description in TestCapture(). Creates DesktopNativeWidget. 706 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) { 707 TestCapture(true); 708 } 709 #endif 710 711 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 712 namespace { 713 714 // Used to veirfy OnMouseEvent() has been invoked. 715 class MouseEventTrackingWidget : public Widget { 716 public: 717 MouseEventTrackingWidget() : got_mouse_event_(false) {} 718 virtual ~MouseEventTrackingWidget() {} 719 720 bool GetAndClearGotMouseEvent() { 721 bool value = got_mouse_event_; 722 got_mouse_event_ = false; 723 return value; 724 } 725 726 // Widget: 727 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 728 got_mouse_event_ = true; 729 Widget::OnMouseEvent(event); 730 } 731 732 private: 733 bool got_mouse_event_; 734 735 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget); 736 }; 737 738 } // namespace 739 740 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA) 741 // TODO(erg): linux_aura bringup: http://crbug.com/163931 742 #define MAYBE_MouseEventDispatchedToRightWindow \ 743 DISABLED_MouseEventDispatchedToRightWindow 744 #else 745 #define MAYBE_MouseEventDispatchedToRightWindow \ 746 MouseEventDispatchedToRightWindow 747 #endif 748 749 // Verifies if a mouse event is received on a widget that doesn't have capture 750 // it is correctly processed by the widget that doesn't have capture. 751 TEST_F(WidgetCaptureTest, MAYBE_MouseEventDispatchedToRightWindow) { 752 MouseEventTrackingWidget widget1; 753 Widget::InitParams params1 = 754 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 755 params1.native_widget = new DesktopNativeWidgetAura(&widget1); 756 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 757 widget1.Init(params1); 758 widget1.Show(); 759 760 MouseEventTrackingWidget widget2; 761 Widget::InitParams params2 = 762 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 763 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 764 params2.native_widget = new DesktopNativeWidgetAura(&widget2); 765 widget2.Init(params2); 766 widget2.Show(); 767 768 // Set capture to widget2 and verity it gets it. 769 widget2.SetCapture(widget2.GetRootView()); 770 EXPECT_FALSE(widget1.HasCapture()); 771 EXPECT_TRUE(widget2.HasCapture()); 772 773 widget1.GetAndClearGotMouseEvent(); 774 widget2.GetAndClearGotMouseEvent(); 775 // Send a mouse event to the RootWindow associated with |widget1|. Even though 776 // |widget2| has capture, |widget1| should still get the event. 777 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), 778 ui::EF_NONE); 779 widget1.GetNativeWindow()->GetDispatcher()->AsRootWindowHostDelegate()-> 780 OnHostMouseEvent(&mouse_event); 781 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent()); 782 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent()); 783 } 784 #endif 785 786 } // namespace test 787 } // namespace views 788