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 "base/basictypes.h" 6 #include "base/bind.h" 7 #include "base/memory/scoped_ptr.h" 8 #include "base/message_loop/message_loop.h" 9 #include "base/run_loop.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 #include "ui/base/events/event_utils.h" 13 #include "ui/gfx/native_widget_types.h" 14 #include "ui/gfx/point.h" 15 #include "ui/views/bubble/bubble_delegate.h" 16 #include "ui/views/controls/textfield/textfield.h" 17 #include "ui/views/test/test_views_delegate.h" 18 #include "ui/views/test/views_test_base.h" 19 #include "ui/views/views_delegate.h" 20 #include "ui/views/widget/native_widget_delegate.h" 21 #include "ui/views/widget/root_view.h" 22 #include "ui/views/window/native_frame_view.h" 23 24 #if defined(USE_AURA) 25 #include "ui/aura/client/aura_constants.h" 26 #include "ui/aura/env.h" 27 #include "ui/aura/root_window.h" 28 #include "ui/aura/test/test_cursor_client.h" 29 #include "ui/aura/test/test_window_delegate.h" 30 #include "ui/aura/window.h" 31 #include "ui/views/widget/native_widget_aura.h" 32 #if !defined(OS_CHROMEOS) 33 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 34 #endif 35 #elif defined(OS_WIN) 36 #include "ui/views/widget/native_widget_win.h" 37 #endif 38 39 namespace views { 40 namespace test { 41 42 // A generic typedef to pick up relevant NativeWidget implementations. 43 #if defined(USE_AURA) 44 typedef NativeWidgetAura NativeWidgetPlatform; 45 #elif defined(OS_WIN) 46 typedef NativeWidgetWin NativeWidgetPlatform; 47 #endif 48 49 // A widget that assumes mouse capture always works. It won't on Aura in 50 // testing, so we mock it. 51 #if defined(USE_AURA) 52 class NativeWidgetCapture : public NativeWidgetPlatform { 53 public: 54 explicit NativeWidgetCapture(internal::NativeWidgetDelegate* delegate) 55 : NativeWidgetPlatform(delegate), 56 mouse_capture_(false) {} 57 virtual ~NativeWidgetCapture() {} 58 59 virtual void SetCapture() OVERRIDE { 60 mouse_capture_ = true; 61 } 62 virtual void ReleaseCapture() OVERRIDE { 63 if (mouse_capture_) 64 delegate()->OnMouseCaptureLost(); 65 mouse_capture_ = false; 66 } 67 virtual bool HasCapture() const OVERRIDE { 68 return mouse_capture_; 69 } 70 71 private: 72 bool mouse_capture_; 73 74 DISALLOW_COPY_AND_ASSIGN(NativeWidgetCapture); 75 }; 76 #endif 77 78 // A typedef that inserts our mock-capture NativeWidget implementation for 79 // relevant platforms. 80 #if defined(USE_AURA) 81 typedef NativeWidgetCapture NativeWidgetPlatformForTest; 82 #elif defined(OS_WIN) 83 typedef NativeWidgetWin NativeWidgetPlatformForTest; 84 #endif 85 86 // A view that always processes all mouse events. 87 class MouseView : public View { 88 public: 89 MouseView() 90 : View(), 91 entered_(0), 92 exited_(0), 93 pressed_(0) { 94 } 95 virtual ~MouseView() {} 96 97 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 98 pressed_++; 99 return true; 100 } 101 102 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { 103 entered_++; 104 } 105 106 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { 107 exited_++; 108 } 109 110 // Return the number of OnMouseEntered calls and reset the counter. 111 int EnteredCalls() { 112 int i = entered_; 113 entered_ = 0; 114 return i; 115 } 116 117 // Return the number of OnMouseExited calls and reset the counter. 118 int ExitedCalls() { 119 int i = exited_; 120 exited_ = 0; 121 return i; 122 } 123 124 int pressed() const { return pressed_; } 125 126 private: 127 int entered_; 128 int exited_; 129 130 int pressed_; 131 132 DISALLOW_COPY_AND_ASSIGN(MouseView); 133 }; 134 135 // A view that keeps track of the events it receives, but consumes no events. 136 class EventCountView : public View { 137 public: 138 EventCountView() {} 139 virtual ~EventCountView() {} 140 141 int GetEventCount(ui::EventType type) { 142 return event_count_[type]; 143 } 144 145 void ResetCounts() { 146 event_count_.clear(); 147 } 148 149 protected: 150 // Overridden from ui::EventHandler: 151 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { 152 RecordEvent(*event); 153 } 154 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 155 RecordEvent(*event); 156 } 157 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE { 158 RecordEvent(*event); 159 } 160 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE { 161 RecordEvent(*event); 162 } 163 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 164 RecordEvent(*event); 165 } 166 167 private: 168 void RecordEvent(const ui::Event& event) { 169 ++event_count_[event.type()]; 170 } 171 172 std::map<ui::EventType, int> event_count_; 173 174 DISALLOW_COPY_AND_ASSIGN(EventCountView); 175 }; 176 177 // A view that keeps track of the events it receives, and consumes all scroll 178 // gesture events. 179 class ScrollableEventCountView : public EventCountView { 180 public: 181 ScrollableEventCountView() {} 182 virtual ~ScrollableEventCountView() {} 183 184 private: 185 // Overridden from ui::EventHandler: 186 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 187 EventCountView::OnGestureEvent(event); 188 switch (event->type()) { 189 case ui::ET_GESTURE_SCROLL_BEGIN: 190 case ui::ET_GESTURE_SCROLL_UPDATE: 191 case ui::ET_GESTURE_SCROLL_END: 192 case ui::ET_SCROLL_FLING_START: 193 event->SetHandled(); 194 break; 195 default: 196 break; 197 } 198 } 199 200 DISALLOW_COPY_AND_ASSIGN(ScrollableEventCountView); 201 }; 202 203 // A view that does a capture on gesture-begin events. 204 class GestureCaptureView : public View { 205 public: 206 GestureCaptureView() {} 207 virtual ~GestureCaptureView() {} 208 209 private: 210 // Overridden from View: 211 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 212 if (event->type() == ui::ET_GESTURE_BEGIN) { 213 GetWidget()->SetCapture(this); 214 event->StopPropagation(); 215 } 216 } 217 218 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView); 219 }; 220 221 // A view that implements GetMinimumSize. 222 class MinimumSizeFrameView : public NativeFrameView { 223 public: 224 explicit MinimumSizeFrameView(Widget* frame): NativeFrameView(frame) {} 225 virtual ~MinimumSizeFrameView() {} 226 227 private: 228 // Overridden from View: 229 virtual gfx::Size GetMinimumSize() OVERRIDE { 230 return gfx::Size(300, 400); 231 } 232 233 DISALLOW_COPY_AND_ASSIGN(MinimumSizeFrameView); 234 }; 235 236 // An event handler that simply keeps a count of the different types of events 237 // it receives. 238 class EventCountHandler : public ui::EventHandler { 239 public: 240 EventCountHandler() {} 241 virtual ~EventCountHandler() {} 242 243 int GetEventCount(ui::EventType type) { 244 return event_count_[type]; 245 } 246 247 void ResetCounts() { 248 event_count_.clear(); 249 } 250 251 protected: 252 // Overridden from ui::EventHandler: 253 virtual void OnEvent(ui::Event* event) OVERRIDE { 254 RecordEvent(*event); 255 ui::EventHandler::OnEvent(event); 256 } 257 258 private: 259 void RecordEvent(const ui::Event& event) { 260 ++event_count_[event.type()]; 261 } 262 263 std::map<ui::EventType, int> event_count_; 264 265 DISALLOW_COPY_AND_ASSIGN(EventCountHandler); 266 }; 267 268 // A View that shows a different widget, sets capture on that widget, and 269 // initiates a nested message-loop when it receives a mouse-press event. 270 class NestedLoopCaptureView : public View { 271 public: 272 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {} 273 virtual ~NestedLoopCaptureView() {} 274 275 private: 276 // Overridden from View: 277 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 278 // Start a nested loop. 279 widget_->Show(); 280 widget_->SetCapture(widget_->GetContentsView()); 281 EXPECT_TRUE(widget_->HasCapture()); 282 283 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 284 base::MessageLoop::ScopedNestableTaskAllower allow(loop); 285 286 base::RunLoop run_loop; 287 #if defined(USE_AURA) 288 run_loop.set_dispatcher(aura::Env::GetInstance()->GetDispatcher()); 289 #endif 290 run_loop.Run(); 291 return true; 292 } 293 294 Widget* widget_; 295 296 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView); 297 }; 298 299 // A View that closes the Widget and exits the current message-loop when it 300 // receives a mouse-release event. 301 class ExitLoopOnRelease : public View { 302 public: 303 ExitLoopOnRelease() {} 304 virtual ~ExitLoopOnRelease() {} 305 306 private: 307 // Overridden from View: 308 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { 309 GetWidget()->Close(); 310 base::MessageLoop::current()->QuitNow(); 311 } 312 313 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease); 314 }; 315 316 class WidgetTest : public ViewsTestBase { 317 public: 318 WidgetTest() {} 319 virtual ~WidgetTest() {} 320 321 NativeWidget* CreatePlatformNativeWidget( 322 internal::NativeWidgetDelegate* delegate) { 323 return new NativeWidgetPlatformForTest(delegate); 324 } 325 326 Widget* CreateTopLevelPlatformWidget() { 327 Widget* toplevel = new Widget; 328 Widget::InitParams toplevel_params = 329 CreateParams(Widget::InitParams::TYPE_WINDOW); 330 toplevel_params.native_widget = CreatePlatformNativeWidget(toplevel); 331 toplevel->Init(toplevel_params); 332 return toplevel; 333 } 334 335 Widget* CreateTopLevelFramelessPlatformWidget() { 336 Widget* toplevel = new Widget; 337 Widget::InitParams toplevel_params = 338 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 339 toplevel_params.native_widget = CreatePlatformNativeWidget(toplevel); 340 toplevel->Init(toplevel_params); 341 return toplevel; 342 } 343 344 Widget* CreateChildPlatformWidget(gfx::NativeView parent_native_view) { 345 Widget* child = new Widget; 346 Widget::InitParams child_params = 347 CreateParams(Widget::InitParams::TYPE_CONTROL); 348 child_params.native_widget = CreatePlatformNativeWidget(child); 349 child_params.parent = parent_native_view; 350 child->Init(child_params); 351 child->SetContentsView(new View); 352 return child; 353 } 354 355 #if defined(OS_WIN) && !defined(USE_AURA) 356 // On Windows, it is possible for us to have a child window that is 357 // TYPE_POPUP. 358 Widget* CreateChildPopupPlatformWidget(gfx::NativeView parent_native_view) { 359 Widget* child = new Widget; 360 Widget::InitParams child_params = 361 CreateParams(Widget::InitParams::TYPE_POPUP); 362 child_params.child = true; 363 child_params.native_widget = CreatePlatformNativeWidget(child); 364 child_params.parent = parent_native_view; 365 child->Init(child_params); 366 child->SetContentsView(new View); 367 return child; 368 } 369 #endif 370 371 Widget* CreateTopLevelNativeWidget() { 372 Widget* toplevel = new Widget; 373 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); 374 toplevel->Init(params); 375 return toplevel; 376 } 377 378 Widget* CreateChildNativeWidgetWithParent(Widget* parent) { 379 Widget* child = new Widget; 380 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_CONTROL); 381 params.parent = parent->GetNativeView(); 382 child->Init(params); 383 child->SetContentsView(new View); 384 return child; 385 } 386 387 Widget* CreateChildNativeWidget() { 388 return CreateChildNativeWidgetWithParent(NULL); 389 } 390 391 View* GetMousePressedHandler(internal::RootView* root_view) { 392 return root_view->mouse_pressed_handler_; 393 } 394 395 View* GetMouseMoveHandler(internal::RootView* root_view) { 396 return root_view->mouse_move_handler_; 397 } 398 399 View* GetGestureHandler(internal::RootView* root_view) { 400 return root_view->gesture_handler_; 401 } 402 }; 403 404 bool WidgetHasMouseCapture(const Widget* widget) { 405 return static_cast<const internal::NativeWidgetPrivate*>(widget-> 406 native_widget())->HasCapture(); 407 } 408 409 ui::WindowShowState GetWidgetShowState(const Widget* widget) { 410 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement 411 // because the former is implemented on all platforms but the latter is not. 412 return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN : 413 widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED : 414 widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED : 415 ui::SHOW_STATE_NORMAL; 416 } 417 418 TEST_F(WidgetTest, WidgetInitParams) { 419 ASSERT_FALSE(views_delegate().UseTransparentWindows()); 420 421 // Widgets are not transparent by default. 422 Widget::InitParams init1; 423 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init1.opacity); 424 425 // Non-window widgets are not transparent either. 426 Widget::InitParams init2(Widget::InitParams::TYPE_MENU); 427 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init2.opacity); 428 429 // A ViewsDelegate can set windows transparent by default. 430 views_delegate().SetUseTransparentWindows(true); 431 Widget::InitParams init3; 432 EXPECT_EQ(Widget::InitParams::TRANSLUCENT_WINDOW, init3.opacity); 433 434 // Non-window widgets stay opaque. 435 Widget::InitParams init4(Widget::InitParams::TYPE_MENU); 436 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init4.opacity); 437 } 438 439 //////////////////////////////////////////////////////////////////////////////// 440 // Widget::GetTopLevelWidget tests. 441 442 TEST_F(WidgetTest, GetTopLevelWidget_Native) { 443 // Create a hierarchy of native widgets. 444 Widget* toplevel = CreateTopLevelPlatformWidget(); 445 gfx::NativeView parent = toplevel->GetNativeView(); 446 Widget* child = CreateChildPlatformWidget(parent); 447 448 EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget()); 449 EXPECT_EQ(toplevel, child->GetTopLevelWidget()); 450 451 toplevel->CloseNow(); 452 // |child| should be automatically destroyed with |toplevel|. 453 } 454 455 // Tests some grab/ungrab events. 456 TEST_F(WidgetTest, DISABLED_GrabUngrab) { 457 Widget* toplevel = CreateTopLevelPlatformWidget(); 458 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); 459 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); 460 461 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500)); 462 463 child1->SetBounds(gfx::Rect(10, 10, 300, 300)); 464 View* view = new MouseView(); 465 view->SetBounds(0, 0, 300, 300); 466 child1->GetRootView()->AddChildView(view); 467 468 child2->SetBounds(gfx::Rect(200, 10, 200, 200)); 469 view = new MouseView(); 470 view->SetBounds(0, 0, 200, 200); 471 child2->GetRootView()->AddChildView(view); 472 473 toplevel->Show(); 474 RunPendingMessages(); 475 476 // Click on child1 477 gfx::Point p1(45, 45); 478 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, 479 ui::EF_LEFT_MOUSE_BUTTON); 480 toplevel->OnMouseEvent(&pressed); 481 482 EXPECT_TRUE(WidgetHasMouseCapture(toplevel)); 483 EXPECT_TRUE(WidgetHasMouseCapture(child1)); 484 EXPECT_FALSE(WidgetHasMouseCapture(child2)); 485 486 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, 487 ui::EF_LEFT_MOUSE_BUTTON); 488 toplevel->OnMouseEvent(&released); 489 490 EXPECT_FALSE(WidgetHasMouseCapture(toplevel)); 491 EXPECT_FALSE(WidgetHasMouseCapture(child1)); 492 EXPECT_FALSE(WidgetHasMouseCapture(child2)); 493 494 RunPendingMessages(); 495 496 // Click on child2 497 gfx::Point p2(315, 45); 498 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, 499 ui::EF_LEFT_MOUSE_BUTTON); 500 toplevel->OnMouseEvent(&pressed2); 501 EXPECT_TRUE(pressed2.handled()); 502 EXPECT_TRUE(WidgetHasMouseCapture(toplevel)); 503 EXPECT_TRUE(WidgetHasMouseCapture(child2)); 504 EXPECT_FALSE(WidgetHasMouseCapture(child1)); 505 506 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, 507 ui::EF_LEFT_MOUSE_BUTTON); 508 toplevel->OnMouseEvent(&released2); 509 EXPECT_FALSE(WidgetHasMouseCapture(toplevel)); 510 EXPECT_FALSE(WidgetHasMouseCapture(child1)); 511 EXPECT_FALSE(WidgetHasMouseCapture(child2)); 512 513 toplevel->CloseNow(); 514 } 515 516 // Tests mouse move outside of the window into the "resize controller" and back 517 // will still generate an OnMouseEntered and OnMouseExited event.. 518 TEST_F(WidgetTest, CheckResizeControllerEvents) { 519 Widget* toplevel = CreateTopLevelPlatformWidget(); 520 521 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); 522 523 MouseView* view = new MouseView(); 524 view->SetBounds(90, 90, 10, 10); 525 toplevel->GetRootView()->AddChildView(view); 526 527 toplevel->Show(); 528 RunPendingMessages(); 529 530 // Move to an outside position. 531 gfx::Point p1(200, 200); 532 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE); 533 toplevel->OnMouseEvent(&moved_out); 534 EXPECT_EQ(0, view->EnteredCalls()); 535 EXPECT_EQ(0, view->ExitedCalls()); 536 537 // Move onto the active view. 538 gfx::Point p2(95, 95); 539 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE); 540 toplevel->OnMouseEvent(&moved_over); 541 EXPECT_EQ(1, view->EnteredCalls()); 542 EXPECT_EQ(0, view->ExitedCalls()); 543 544 // Move onto the outer resizing border. 545 gfx::Point p3(102, 95); 546 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE); 547 toplevel->OnMouseEvent(&moved_resizer); 548 EXPECT_EQ(0, view->EnteredCalls()); 549 EXPECT_EQ(1, view->ExitedCalls()); 550 551 // Move onto the view again. 552 toplevel->OnMouseEvent(&moved_over); 553 EXPECT_EQ(1, view->EnteredCalls()); 554 EXPECT_EQ(0, view->ExitedCalls()); 555 556 RunPendingMessages(); 557 558 toplevel->CloseNow(); 559 } 560 561 // Test if a focus manager and an inputmethod work without CHECK failure 562 // when window activation changes. 563 TEST_F(WidgetTest, ChangeActivation) { 564 Widget* top1 = CreateTopLevelPlatformWidget(); 565 // CreateInputMethod before activated 566 top1->GetInputMethod(); 567 top1->Show(); 568 RunPendingMessages(); 569 570 Widget* top2 = CreateTopLevelPlatformWidget(); 571 top2->Show(); 572 RunPendingMessages(); 573 574 top1->Activate(); 575 RunPendingMessages(); 576 577 // Create InputMethod after deactivated. 578 top2->GetInputMethod(); 579 top2->Activate(); 580 RunPendingMessages(); 581 582 top1->Activate(); 583 RunPendingMessages(); 584 585 top1->CloseNow(); 586 top2->CloseNow(); 587 } 588 589 // Tests visibility of child widgets. 590 TEST_F(WidgetTest, Visibility) { 591 Widget* toplevel = CreateTopLevelPlatformWidget(); 592 gfx::NativeView parent = toplevel->GetNativeView(); 593 Widget* child = CreateChildPlatformWidget(parent); 594 595 EXPECT_FALSE(toplevel->IsVisible()); 596 EXPECT_FALSE(child->IsVisible()); 597 598 child->Show(); 599 600 EXPECT_FALSE(toplevel->IsVisible()); 601 EXPECT_FALSE(child->IsVisible()); 602 603 toplevel->Show(); 604 605 EXPECT_TRUE(toplevel->IsVisible()); 606 EXPECT_TRUE(child->IsVisible()); 607 608 toplevel->CloseNow(); 609 // |child| should be automatically destroyed with |toplevel|. 610 } 611 612 #if defined(OS_WIN) && !defined(USE_AURA) 613 // On Windows, it is possible to have child window that are TYPE_POPUP. Unlike 614 // regular child windows, these should be created as hidden and must be shown 615 // explicitly. 616 TEST_F(WidgetTest, Visibility_ChildPopup) { 617 Widget* toplevel = CreateTopLevelPlatformWidget(); 618 Widget* child_popup = CreateChildPopupPlatformWidget( 619 toplevel->GetNativeView()); 620 621 EXPECT_FALSE(toplevel->IsVisible()); 622 EXPECT_FALSE(child_popup->IsVisible()); 623 624 toplevel->Show(); 625 626 EXPECT_TRUE(toplevel->IsVisible()); 627 EXPECT_FALSE(child_popup->IsVisible()); 628 629 child_popup->Show(); 630 631 EXPECT_TRUE(child_popup->IsVisible()); 632 633 toplevel->CloseNow(); 634 // |child_popup| should be automatically destroyed with |toplevel|. 635 } 636 #endif 637 638 //////////////////////////////////////////////////////////////////////////////// 639 // Widget ownership tests. 640 // 641 // Tests various permutations of Widget ownership specified in the 642 // InitParams::Ownership param. 643 644 // A WidgetTest that supplies a toplevel widget for NativeWidget to parent to. 645 class WidgetOwnershipTest : public WidgetTest { 646 public: 647 WidgetOwnershipTest() {} 648 virtual ~WidgetOwnershipTest() {} 649 650 virtual void SetUp() { 651 WidgetTest::SetUp(); 652 desktop_widget_ = CreateTopLevelPlatformWidget(); 653 } 654 655 virtual void TearDown() { 656 desktop_widget_->CloseNow(); 657 WidgetTest::TearDown(); 658 } 659 660 private: 661 Widget* desktop_widget_; 662 663 DISALLOW_COPY_AND_ASSIGN(WidgetOwnershipTest); 664 }; 665 666 // A bag of state to monitor destructions. 667 struct OwnershipTestState { 668 OwnershipTestState() : widget_deleted(false), native_widget_deleted(false) {} 669 670 bool widget_deleted; 671 bool native_widget_deleted; 672 }; 673 674 // A platform NativeWidget subclass that updates a bag of state when it is 675 // destroyed. 676 class OwnershipTestNativeWidget : public NativeWidgetPlatform { 677 public: 678 OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate, 679 OwnershipTestState* state) 680 : NativeWidgetPlatform(delegate), 681 state_(state) { 682 } 683 virtual ~OwnershipTestNativeWidget() { 684 state_->native_widget_deleted = true; 685 } 686 687 private: 688 OwnershipTestState* state_; 689 690 DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget); 691 }; 692 693 // A views NativeWidget subclass that updates a bag of state when it is 694 // destroyed. 695 class OwnershipTestNativeWidgetPlatform : public NativeWidgetPlatformForTest { 696 public: 697 OwnershipTestNativeWidgetPlatform(internal::NativeWidgetDelegate* delegate, 698 OwnershipTestState* state) 699 : NativeWidgetPlatformForTest(delegate), 700 state_(state) { 701 } 702 virtual ~OwnershipTestNativeWidgetPlatform() { 703 state_->native_widget_deleted = true; 704 } 705 706 private: 707 OwnershipTestState* state_; 708 709 DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetPlatform); 710 }; 711 712 // A Widget subclass that updates a bag of state when it is destroyed. 713 class OwnershipTestWidget : public Widget { 714 public: 715 explicit OwnershipTestWidget(OwnershipTestState* state) : state_(state) {} 716 virtual ~OwnershipTestWidget() { 717 state_->widget_deleted = true; 718 } 719 720 private: 721 OwnershipTestState* state_; 722 723 DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget); 724 }; 725 726 // Widget owns its NativeWidget, part 1: NativeWidget is a platform-native 727 // widget. 728 TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { 729 OwnershipTestState state; 730 731 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 732 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 733 params.native_widget = 734 new OwnershipTestNativeWidgetPlatform(widget.get(), &state); 735 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 736 widget->Init(params); 737 738 // Now delete the Widget, which should delete the NativeWidget. 739 widget.reset(); 740 741 EXPECT_TRUE(state.widget_deleted); 742 EXPECT_TRUE(state.native_widget_deleted); 743 744 // TODO(beng): write test for this ownership scenario and the NativeWidget 745 // being deleted out from under the Widget. 746 } 747 748 // Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget. 749 TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { 750 OwnershipTestState state; 751 752 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 753 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 754 params.native_widget = 755 new OwnershipTestNativeWidgetPlatform(widget.get(), &state); 756 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 757 widget->Init(params); 758 759 // Now delete the Widget, which should delete the NativeWidget. 760 widget.reset(); 761 762 EXPECT_TRUE(state.widget_deleted); 763 EXPECT_TRUE(state.native_widget_deleted); 764 765 // TODO(beng): write test for this ownership scenario and the NativeWidget 766 // being deleted out from under the Widget. 767 } 768 769 // Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget, 770 // destroy the parent view. 771 TEST_F(WidgetOwnershipTest, 772 Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) { 773 OwnershipTestState state; 774 775 Widget* toplevel = CreateTopLevelPlatformWidget(); 776 777 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 778 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 779 params.native_widget = 780 new OwnershipTestNativeWidgetPlatform(widget.get(), &state); 781 params.parent = toplevel->GetNativeView(); 782 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 783 widget->Init(params); 784 785 // Now close the toplevel, which deletes the view hierarchy. 786 toplevel->CloseNow(); 787 788 RunPendingMessages(); 789 790 // This shouldn't delete the widget because it shouldn't be deleted 791 // from the native side. 792 EXPECT_FALSE(state.widget_deleted); 793 EXPECT_FALSE(state.native_widget_deleted); 794 795 // Now delete it explicitly. 796 widget.reset(); 797 798 EXPECT_TRUE(state.widget_deleted); 799 EXPECT_TRUE(state.native_widget_deleted); 800 } 801 802 // NativeWidget owns its Widget, part 1: NativeWidget is a platform-native 803 // widget. 804 TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { 805 OwnershipTestState state; 806 807 Widget* widget = new OwnershipTestWidget(&state); 808 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 809 params.native_widget = 810 new OwnershipTestNativeWidgetPlatform(widget, &state); 811 widget->Init(params); 812 813 // Now destroy the native widget. 814 widget->CloseNow(); 815 816 EXPECT_TRUE(state.widget_deleted); 817 EXPECT_TRUE(state.native_widget_deleted); 818 } 819 820 // NativeWidget owns its Widget, part 2: NativeWidget is a NativeWidget. 821 TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { 822 OwnershipTestState state; 823 824 Widget* toplevel = CreateTopLevelPlatformWidget(); 825 826 Widget* widget = new OwnershipTestWidget(&state); 827 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 828 params.native_widget = 829 new OwnershipTestNativeWidgetPlatform(widget, &state); 830 params.parent = toplevel->GetNativeView(); 831 widget->Init(params); 832 833 // Now destroy the native widget. This is achieved by closing the toplevel. 834 toplevel->CloseNow(); 835 836 // The NativeWidget won't be deleted until after a return to the message loop 837 // so we have to run pending messages before testing the destruction status. 838 RunPendingMessages(); 839 840 EXPECT_TRUE(state.widget_deleted); 841 EXPECT_TRUE(state.native_widget_deleted); 842 } 843 844 // NativeWidget owns its Widget, part 3: NativeWidget is a platform-native 845 // widget, destroyed out from under it by the OS. 846 TEST_F(WidgetOwnershipTest, 847 Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy) { 848 OwnershipTestState state; 849 850 Widget* widget = new OwnershipTestWidget(&state); 851 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 852 params.native_widget = 853 new OwnershipTestNativeWidgetPlatform(widget, &state); 854 widget->Init(params); 855 856 // Now simulate a destroy of the platform native widget from the OS: 857 #if defined(USE_AURA) 858 delete widget->GetNativeView(); 859 #elif defined(OS_WIN) 860 DestroyWindow(widget->GetNativeView()); 861 #endif 862 863 EXPECT_TRUE(state.widget_deleted); 864 EXPECT_TRUE(state.native_widget_deleted); 865 } 866 867 // NativeWidget owns its Widget, part 4: NativeWidget is a NativeWidget, 868 // destroyed by the view hierarchy that contains it. 869 TEST_F(WidgetOwnershipTest, 870 Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) { 871 OwnershipTestState state; 872 873 Widget* toplevel = CreateTopLevelPlatformWidget(); 874 875 Widget* widget = new OwnershipTestWidget(&state); 876 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 877 params.native_widget = 878 new OwnershipTestNativeWidgetPlatform(widget, &state); 879 params.parent = toplevel->GetNativeView(); 880 widget->Init(params); 881 882 // Destroy the widget (achieved by closing the toplevel). 883 toplevel->CloseNow(); 884 885 // The NativeWidget won't be deleted until after a return to the message loop 886 // so we have to run pending messages before testing the destruction status. 887 RunPendingMessages(); 888 889 EXPECT_TRUE(state.widget_deleted); 890 EXPECT_TRUE(state.native_widget_deleted); 891 } 892 893 // NativeWidget owns its Widget, part 5: NativeWidget is a NativeWidget, 894 // we close it directly. 895 TEST_F(WidgetOwnershipTest, 896 Ownership_ViewsNativeWidgetOwnsWidget_Close) { 897 OwnershipTestState state; 898 899 Widget* toplevel = CreateTopLevelPlatformWidget(); 900 901 Widget* widget = new OwnershipTestWidget(&state); 902 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 903 params.native_widget = 904 new OwnershipTestNativeWidgetPlatform(widget, &state); 905 params.parent = toplevel->GetNativeView(); 906 widget->Init(params); 907 908 // Destroy the widget. 909 widget->Close(); 910 toplevel->CloseNow(); 911 912 // The NativeWidget won't be deleted until after a return to the message loop 913 // so we have to run pending messages before testing the destruction status. 914 RunPendingMessages(); 915 916 EXPECT_TRUE(state.widget_deleted); 917 EXPECT_TRUE(state.native_widget_deleted); 918 } 919 920 // Widget owns its NativeWidget and has a WidgetDelegateView as its contents. 921 TEST_F(WidgetOwnershipTest, 922 Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView) { 923 OwnershipTestState state; 924 925 WidgetDelegateView* delegate_view = new WidgetDelegateView; 926 927 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 928 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 929 params.native_widget = 930 new OwnershipTestNativeWidgetPlatform(widget.get(), &state); 931 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 932 params.delegate = delegate_view; 933 widget->Init(params); 934 widget->SetContentsView(delegate_view); 935 936 // Now delete the Widget. There should be no crash or use-after-free. 937 widget.reset(); 938 939 EXPECT_TRUE(state.widget_deleted); 940 EXPECT_TRUE(state.native_widget_deleted); 941 } 942 943 //////////////////////////////////////////////////////////////////////////////// 944 // Widget observer tests. 945 // 946 947 class WidgetObserverTest : public WidgetTest, public WidgetObserver { 948 public: 949 WidgetObserverTest() 950 : active_(NULL), 951 widget_closed_(NULL), 952 widget_activated_(NULL), 953 widget_shown_(NULL), 954 widget_hidden_(NULL), 955 widget_bounds_changed_(NULL) { 956 } 957 958 virtual ~WidgetObserverTest() {} 959 960 // Overridden from WidgetObserver: 961 virtual void OnWidgetDestroying(Widget* widget) OVERRIDE { 962 if (active_ == widget) 963 active_ = NULL; 964 widget_closed_ = widget; 965 } 966 967 virtual void OnWidgetActivationChanged(Widget* widget, 968 bool active) OVERRIDE { 969 if (active) { 970 if (widget_activated_) 971 widget_activated_->Deactivate(); 972 widget_activated_ = widget; 973 active_ = widget; 974 } else { 975 if (widget_activated_ == widget) 976 widget_activated_ = NULL; 977 widget_deactivated_ = widget; 978 } 979 } 980 981 virtual void OnWidgetVisibilityChanged(Widget* widget, 982 bool visible) OVERRIDE { 983 if (visible) 984 widget_shown_ = widget; 985 else 986 widget_hidden_ = widget; 987 } 988 989 virtual void OnWidgetBoundsChanged(Widget* widget, 990 const gfx::Rect& new_bounds) OVERRIDE { 991 widget_bounds_changed_ = widget; 992 } 993 994 void reset() { 995 active_ = NULL; 996 widget_closed_ = NULL; 997 widget_activated_ = NULL; 998 widget_deactivated_ = NULL; 999 widget_shown_ = NULL; 1000 widget_hidden_ = NULL; 1001 widget_bounds_changed_ = NULL; 1002 } 1003 1004 Widget* NewWidget() { 1005 Widget* widget = CreateTopLevelNativeWidget(); 1006 widget->AddObserver(this); 1007 return widget; 1008 } 1009 1010 const Widget* active() const { return active_; } 1011 const Widget* widget_closed() const { return widget_closed_; } 1012 const Widget* widget_activated() const { return widget_activated_; } 1013 const Widget* widget_deactivated() const { return widget_deactivated_; } 1014 const Widget* widget_shown() const { return widget_shown_; } 1015 const Widget* widget_hidden() const { return widget_hidden_; } 1016 const Widget* widget_bounds_changed() const { return widget_bounds_changed_; } 1017 1018 private: 1019 Widget* active_; 1020 1021 Widget* widget_closed_; 1022 Widget* widget_activated_; 1023 Widget* widget_deactivated_; 1024 Widget* widget_shown_; 1025 Widget* widget_hidden_; 1026 Widget* widget_bounds_changed_; 1027 }; 1028 1029 TEST_F(WidgetObserverTest, DISABLED_ActivationChange) { 1030 Widget* toplevel = CreateTopLevelPlatformWidget(); 1031 1032 Widget* toplevel1 = NewWidget(); 1033 Widget* toplevel2 = NewWidget(); 1034 1035 toplevel1->Show(); 1036 toplevel2->Show(); 1037 1038 reset(); 1039 1040 toplevel1->Activate(); 1041 1042 RunPendingMessages(); 1043 EXPECT_EQ(toplevel1, widget_activated()); 1044 1045 toplevel2->Activate(); 1046 RunPendingMessages(); 1047 EXPECT_EQ(toplevel1, widget_deactivated()); 1048 EXPECT_EQ(toplevel2, widget_activated()); 1049 EXPECT_EQ(toplevel2, active()); 1050 1051 toplevel->CloseNow(); 1052 } 1053 1054 TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) { 1055 Widget* toplevel = CreateTopLevelPlatformWidget(); 1056 1057 Widget* child1 = NewWidget(); 1058 Widget* child2 = NewWidget(); 1059 1060 toplevel->Show(); 1061 child1->Show(); 1062 child2->Show(); 1063 1064 reset(); 1065 1066 child1->Hide(); 1067 EXPECT_EQ(child1, widget_hidden()); 1068 1069 child2->Hide(); 1070 EXPECT_EQ(child2, widget_hidden()); 1071 1072 child1->Show(); 1073 EXPECT_EQ(child1, widget_shown()); 1074 1075 child2->Show(); 1076 EXPECT_EQ(child2, widget_shown()); 1077 1078 toplevel->CloseNow(); 1079 } 1080 1081 TEST_F(WidgetObserverTest, DestroyBubble) { 1082 Widget* anchor = CreateTopLevelPlatformWidget(); 1083 anchor->Show(); 1084 1085 BubbleDelegateView* bubble_delegate = 1086 new BubbleDelegateView(anchor->client_view(), BubbleBorder::NONE); 1087 Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); 1088 bubble_widget->Show(); 1089 bubble_widget->CloseNow(); 1090 1091 anchor->Hide(); 1092 anchor->CloseNow(); 1093 } 1094 1095 TEST_F(WidgetObserverTest, WidgetBoundsChanged) { 1096 Widget* child1 = NewWidget(); 1097 Widget* child2 = NewWidget(); 1098 1099 child1->OnNativeWidgetMove(); 1100 EXPECT_EQ(child1, widget_bounds_changed()); 1101 1102 child2->OnNativeWidgetMove(); 1103 EXPECT_EQ(child2, widget_bounds_changed()); 1104 1105 child1->OnNativeWidgetSizeChanged(gfx::Size()); 1106 EXPECT_EQ(child1, widget_bounds_changed()); 1107 1108 child2->OnNativeWidgetSizeChanged(gfx::Size()); 1109 EXPECT_EQ(child2, widget_bounds_changed()); 1110 } 1111 1112 #if !defined(USE_AURA) && defined(OS_WIN) 1113 // Aura needs shell to maximize/fullscreen window. 1114 // NativeWidgetGtk doesn't implement GetRestoredBounds. 1115 TEST_F(WidgetTest, GetRestoredBounds) { 1116 Widget* toplevel = CreateTopLevelPlatformWidget(); 1117 EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), 1118 toplevel->GetRestoredBounds().ToString()); 1119 toplevel->Show(); 1120 toplevel->Maximize(); 1121 RunPendingMessages(); 1122 EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), 1123 toplevel->GetRestoredBounds().ToString()); 1124 EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); 1125 EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); 1126 1127 toplevel->Restore(); 1128 RunPendingMessages(); 1129 EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), 1130 toplevel->GetRestoredBounds().ToString()); 1131 1132 toplevel->SetFullscreen(true); 1133 RunPendingMessages(); 1134 EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), 1135 toplevel->GetRestoredBounds().ToString()); 1136 EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); 1137 EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); 1138 } 1139 #endif 1140 1141 // Test that window state is not changed after getting out of full screen. 1142 TEST_F(WidgetTest, ExitFullscreenRestoreState) { 1143 Widget* toplevel = CreateTopLevelPlatformWidget(); 1144 1145 toplevel->Show(); 1146 RunPendingMessages(); 1147 1148 // This should be a normal state window. 1149 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); 1150 1151 toplevel->SetFullscreen(true); 1152 while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN) 1153 RunPendingMessages(); 1154 toplevel->SetFullscreen(false); 1155 while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN) 1156 RunPendingMessages(); 1157 1158 // And it should still be in normal state after getting out of full screen. 1159 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); 1160 1161 // Now, make it maximized. 1162 toplevel->Maximize(); 1163 while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_MAXIMIZED) 1164 RunPendingMessages(); 1165 1166 toplevel->SetFullscreen(true); 1167 while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN) 1168 RunPendingMessages(); 1169 toplevel->SetFullscreen(false); 1170 while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN) 1171 RunPendingMessages(); 1172 1173 // And it stays maximized after getting out of full screen. 1174 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); 1175 1176 // Clean up. 1177 toplevel->Close(); 1178 RunPendingMessages(); 1179 } 1180 1181 // Checks that if a mouse-press triggers a capture on a different widget (which 1182 // consumes the mouse-release event), then the target of the press does not have 1183 // capture. 1184 TEST_F(WidgetTest, CaptureWidgetFromMousePress) { 1185 // The test creates two widgets: |first| and |second|. 1186 // The View in |first| makes |second| visible, sets capture on it, and starts 1187 // a nested loop (like a menu does). The View in |second| terminates the 1188 // nested loop and closes the widget. 1189 // The test sends a mouse-press event to |first|, and posts a task to send a 1190 // release event to |second|, to make sure that the release event is 1191 // dispatched after the nested loop starts. 1192 1193 Widget* first = CreateTopLevelFramelessPlatformWidget(); 1194 Widget* second = CreateTopLevelFramelessPlatformWidget(); 1195 1196 View* container = new NestedLoopCaptureView(second); 1197 first->SetContentsView(container); 1198 1199 second->SetContentsView(new ExitLoopOnRelease()); 1200 1201 first->SetSize(gfx::Size(100, 100)); 1202 first->Show(); 1203 1204 gfx::Point location(20, 20); 1205 base::MessageLoop::current()->PostTask(FROM_HERE, 1206 base::Bind(&Widget::OnMouseEvent, 1207 base::Unretained(second), 1208 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, 1209 location, 1210 location, 1211 ui::EF_LEFT_MOUSE_BUTTON)))); 1212 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, 1213 ui::EF_LEFT_MOUSE_BUTTON); 1214 first->OnMouseEvent(&press); 1215 EXPECT_FALSE(first->HasCapture()); 1216 first->Close(); 1217 RunPendingMessages(); 1218 } 1219 1220 TEST_F(WidgetTest, ResetCaptureOnGestureEnd) { 1221 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 1222 View* container = new View; 1223 toplevel->SetContentsView(container); 1224 1225 View* gesture = new GestureCaptureView; 1226 gesture->SetBounds(0, 0, 30, 30); 1227 container->AddChildView(gesture); 1228 1229 MouseView* mouse = new MouseView; 1230 mouse->SetBounds(30, 0, 30, 30); 1231 container->AddChildView(mouse); 1232 1233 toplevel->SetSize(gfx::Size(100, 100)); 1234 toplevel->Show(); 1235 1236 // Start a gesture on |gesture|. 1237 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, 1238 15, 15, 0, base::TimeDelta(), 1239 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); 1240 ui::GestureEvent end(ui::ET_GESTURE_END, 1241 15, 15, 0, base::TimeDelta(), 1242 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); 1243 toplevel->OnGestureEvent(&begin); 1244 1245 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| 1246 // will not receive the event. 1247 gfx::Point click_location(45, 15); 1248 1249 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 1250 ui::EF_LEFT_MOUSE_BUTTON); 1251 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 1252 ui::EF_LEFT_MOUSE_BUTTON); 1253 1254 toplevel->OnMouseEvent(&press); 1255 toplevel->OnMouseEvent(&release); 1256 EXPECT_EQ(0, mouse->pressed()); 1257 1258 // The end of the gesture should release the capture, and pressing on |mouse| 1259 // should now reach |mouse|. 1260 toplevel->OnGestureEvent(&end); 1261 toplevel->OnMouseEvent(&press); 1262 toplevel->OnMouseEvent(&release); 1263 EXPECT_EQ(1, mouse->pressed()); 1264 1265 toplevel->Close(); 1266 RunPendingMessages(); 1267 } 1268 1269 #if defined(USE_AURA) 1270 // The key-event propagation from Widget happens differently on aura and 1271 // non-aura systems because of the difference in IME. So this test works only on 1272 // aura. 1273 TEST_F(WidgetTest, KeyboardInputEvent) { 1274 Widget* toplevel = CreateTopLevelPlatformWidget(); 1275 View* container = toplevel->client_view(); 1276 1277 Textfield* textfield = new Textfield(); 1278 textfield->SetText(ASCIIToUTF16("some text")); 1279 container->AddChildView(textfield); 1280 toplevel->Show(); 1281 textfield->RequestFocus(); 1282 1283 // The press gets handled. The release doesn't have an effect. 1284 ui::KeyEvent backspace_p(ui::ET_KEY_PRESSED, ui::VKEY_DELETE, 0, false); 1285 toplevel->OnKeyEvent(&backspace_p); 1286 EXPECT_TRUE(backspace_p.stopped_propagation()); 1287 ui::KeyEvent backspace_r(ui::ET_KEY_RELEASED, ui::VKEY_DELETE, 0, false); 1288 toplevel->OnKeyEvent(&backspace_r); 1289 EXPECT_FALSE(backspace_r.handled()); 1290 1291 toplevel->Close(); 1292 } 1293 1294 // Verifies bubbles result in a focus lost when shown. 1295 // TODO(msw): this tests relies on focus, it needs to be in 1296 // interactive_ui_tests. 1297 TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { 1298 // Create a widget, show and activate it and focus the contents view. 1299 View* contents_view = new View; 1300 contents_view->set_focusable(true); 1301 Widget widget; 1302 Widget::InitParams init_params = 1303 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1304 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1305 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1306 #if !defined(OS_CHROMEOS) 1307 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 1308 #endif 1309 widget.Init(init_params); 1310 widget.SetContentsView(contents_view); 1311 widget.Show(); 1312 widget.Activate(); 1313 contents_view->RequestFocus(); 1314 EXPECT_TRUE(contents_view->HasFocus()); 1315 1316 // Show a bubble. 1317 BubbleDelegateView* bubble_delegate_view = 1318 new BubbleDelegateView(contents_view, BubbleBorder::TOP_LEFT); 1319 bubble_delegate_view->set_focusable(true); 1320 BubbleDelegateView::CreateBubble(bubble_delegate_view)->Show(); 1321 bubble_delegate_view->RequestFocus(); 1322 1323 // |contents_view_| should no longer have focus. 1324 EXPECT_FALSE(contents_view->HasFocus()); 1325 EXPECT_TRUE(bubble_delegate_view->HasFocus()); 1326 1327 bubble_delegate_view->GetWidget()->CloseNow(); 1328 1329 // Closing the bubble should result in focus going back to the contents view. 1330 EXPECT_TRUE(contents_view->HasFocus()); 1331 } 1332 1333 // Desktop native widget Aura tests are for non Chrome OS platforms. 1334 #if !defined(OS_CHROMEOS) 1335 // Test to ensure that after minimize, view width is set to zero. 1336 TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { 1337 // Create a widget. 1338 Widget widget; 1339 Widget::InitParams init_params = 1340 CreateParams(Widget::InitParams::TYPE_WINDOW); 1341 init_params.show_state = ui::SHOW_STATE_NORMAL; 1342 gfx::Rect initial_bounds(0, 0, 300, 400); 1343 init_params.bounds = initial_bounds; 1344 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1345 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 1346 widget.Init(init_params); 1347 NonClientView* non_client_view = widget.non_client_view(); 1348 NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); 1349 non_client_view->SetFrameView(frame_view); 1350 widget.Show(); 1351 widget.Minimize(); 1352 EXPECT_EQ(0, frame_view->width()); 1353 } 1354 1355 // This class validates whether paints are received for a visible Widget. 1356 // To achieve this it overrides the Show and Close methods on the Widget class 1357 // and sets state whether subsequent paints are expected. 1358 class DesktopAuraTestValidPaintWidget : public views::Widget { 1359 public: 1360 DesktopAuraTestValidPaintWidget() 1361 : expect_paint_(true), 1362 received_paint_while_hidden_(false) { 1363 } 1364 1365 virtual ~DesktopAuraTestValidPaintWidget() { 1366 } 1367 1368 virtual void Show() OVERRIDE { 1369 expect_paint_ = true; 1370 views::Widget::Show(); 1371 } 1372 1373 virtual void Close() OVERRIDE { 1374 expect_paint_ = false; 1375 views::Widget::Close(); 1376 } 1377 1378 void Hide() { 1379 expect_paint_ = false; 1380 views::Widget::Hide(); 1381 } 1382 1383 virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE { 1384 EXPECT_TRUE(expect_paint_); 1385 if (!expect_paint_) 1386 received_paint_while_hidden_ = true; 1387 views::Widget::OnNativeWidgetPaint(canvas); 1388 } 1389 1390 bool received_paint_while_hidden() const { 1391 return received_paint_while_hidden_; 1392 } 1393 1394 private: 1395 bool expect_paint_; 1396 bool received_paint_while_hidden_; 1397 }; 1398 1399 TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterCloseTest) { 1400 View* contents_view = new View; 1401 contents_view->set_focusable(true); 1402 DesktopAuraTestValidPaintWidget widget; 1403 Widget::InitParams init_params = 1404 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1405 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1406 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1407 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 1408 widget.Init(init_params); 1409 widget.SetContentsView(contents_view); 1410 widget.Show(); 1411 widget.Activate(); 1412 RunPendingMessages(); 1413 widget.SchedulePaintInRect(init_params.bounds); 1414 widget.Close(); 1415 RunPendingMessages(); 1416 EXPECT_FALSE(widget.received_paint_while_hidden()); 1417 } 1418 1419 TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterHideTest) { 1420 View* contents_view = new View; 1421 contents_view->set_focusable(true); 1422 DesktopAuraTestValidPaintWidget widget; 1423 Widget::InitParams init_params = 1424 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1425 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1426 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1427 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 1428 widget.Init(init_params); 1429 widget.SetContentsView(contents_view); 1430 widget.Show(); 1431 widget.Activate(); 1432 RunPendingMessages(); 1433 widget.SchedulePaintInRect(init_params.bounds); 1434 widget.Hide(); 1435 RunPendingMessages(); 1436 EXPECT_FALSE(widget.received_paint_while_hidden()); 1437 widget.Close(); 1438 } 1439 1440 // This class provides functionality to test whether the destruction of full 1441 // screen child windows occurs correctly in desktop AURA without crashing. 1442 // It provides facilities to test the following cases:- 1443 // 1. Child window destroyed which should lead to the destruction of the 1444 // parent. 1445 // 2. Parent window destroyed which should lead to the child being destroyed. 1446 class DesktopAuraFullscreenChildWindowDestructionTest 1447 : public views::TestViewsDelegate, 1448 public aura::WindowObserver { 1449 public: 1450 DesktopAuraFullscreenChildWindowDestructionTest() 1451 : full_screen_widget_(NULL), 1452 child_window_(NULL), 1453 parent_destroyed_(false), 1454 child_destroyed_(false) {} 1455 1456 virtual ~DesktopAuraFullscreenChildWindowDestructionTest() { 1457 EXPECT_TRUE(parent_destroyed_); 1458 EXPECT_TRUE(child_destroyed_); 1459 full_screen_widget_ = NULL; 1460 child_window_ = NULL; 1461 } 1462 1463 // views::TestViewsDelegate overrides. 1464 virtual void OnBeforeWidgetInit( 1465 Widget::InitParams* params, 1466 internal::NativeWidgetDelegate* delegate) OVERRIDE { 1467 if (!params->native_widget) 1468 params->native_widget = new views::DesktopNativeWidgetAura(delegate); 1469 } 1470 1471 void CreateFullscreenChildWindow(const gfx::Rect& bounds) { 1472 Widget::InitParams init_params; 1473 init_params.type = Widget::InitParams::TYPE_WINDOW; 1474 init_params.bounds = bounds; 1475 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1476 init_params.layer_type = ui::LAYER_NOT_DRAWN; 1477 1478 widget_.Init(init_params); 1479 1480 child_window_ = new aura::Window(&child_window_delegate_); 1481 child_window_->SetType(aura::client::WINDOW_TYPE_NORMAL); 1482 child_window_->Init(ui::LAYER_TEXTURED); 1483 child_window_->SetName("TestFullscreenChildWindow"); 1484 child_window_->SetProperty(aura::client::kShowStateKey, 1485 ui::SHOW_STATE_FULLSCREEN); 1486 child_window_->SetDefaultParentByRootWindow( 1487 widget_.GetNativeView()->GetRootWindow(), gfx::Rect(0, 0, 1900, 1600)); 1488 child_window_->Show(); 1489 child_window_->AddObserver(this); 1490 1491 ASSERT_TRUE(child_window_->parent() != NULL); 1492 child_window_->parent()->AddObserver(this); 1493 1494 full_screen_widget_ = 1495 views::Widget::GetWidgetForNativeView(child_window_->parent()); 1496 ASSERT_TRUE(full_screen_widget_ != NULL); 1497 } 1498 1499 void DestroyChildWindow() { 1500 ASSERT_TRUE(child_window_ != NULL); 1501 delete child_window_; 1502 } 1503 1504 void DestroyParentWindow() { 1505 ASSERT_TRUE(full_screen_widget_ != NULL); 1506 full_screen_widget_->CloseNow(); 1507 } 1508 1509 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 1510 window->RemoveObserver(this); 1511 if (window == child_window_) { 1512 child_destroyed_ = true; 1513 } else if (window == full_screen_widget_->GetNativeView()) { 1514 parent_destroyed_ = true; 1515 } else { 1516 ADD_FAILURE() << "Unexpected window destroyed callback: " << window; 1517 } 1518 } 1519 1520 private: 1521 views::Widget widget_; 1522 views::Widget* full_screen_widget_; 1523 aura::Window* child_window_; 1524 bool parent_destroyed_; 1525 bool child_destroyed_; 1526 aura::test::TestWindowDelegate child_window_delegate_; 1527 1528 DISALLOW_COPY_AND_ASSIGN(DesktopAuraFullscreenChildWindowDestructionTest); 1529 }; 1530 1531 TEST_F(WidgetTest, DesktopAuraFullscreenChildDestroyedBeforeParentTest) { 1532 ViewsDelegate::views_delegate = NULL; 1533 DesktopAuraFullscreenChildWindowDestructionTest full_screen_child_test; 1534 ASSERT_NO_FATAL_FAILURE(full_screen_child_test.CreateFullscreenChildWindow( 1535 gfx::Rect(0, 0, 200, 200))); 1536 1537 RunPendingMessages(); 1538 ASSERT_NO_FATAL_FAILURE(full_screen_child_test.DestroyChildWindow()); 1539 RunPendingMessages(); 1540 } 1541 1542 TEST_F(WidgetTest, DesktopAuraFullscreenChildParentDestroyed) { 1543 ViewsDelegate::views_delegate = NULL; 1544 1545 DesktopAuraFullscreenChildWindowDestructionTest full_screen_child_test; 1546 ASSERT_NO_FATAL_FAILURE(full_screen_child_test.CreateFullscreenChildWindow( 1547 gfx::Rect(0, 0, 200, 200))); 1548 1549 RunPendingMessages(); 1550 ASSERT_NO_FATAL_FAILURE(full_screen_child_test.DestroyParentWindow()); 1551 RunPendingMessages(); 1552 } 1553 1554 // Test to ensure that the aura Window's visiblity state is set to visible if 1555 // the underlying widget is hidden and then shown. 1556 TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { 1557 // Create a widget. 1558 Widget widget; 1559 Widget::InitParams init_params = 1560 CreateParams(Widget::InitParams::TYPE_WINDOW); 1561 init_params.show_state = ui::SHOW_STATE_NORMAL; 1562 gfx::Rect initial_bounds(0, 0, 300, 400); 1563 init_params.bounds = initial_bounds; 1564 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1565 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 1566 widget.Init(init_params); 1567 NonClientView* non_client_view = widget.non_client_view(); 1568 NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); 1569 non_client_view->SetFrameView(frame_view); 1570 1571 widget.Hide(); 1572 EXPECT_FALSE(widget.GetNativeView()->IsVisible()); 1573 widget.Show(); 1574 EXPECT_TRUE(widget.GetNativeView()->IsVisible()); 1575 } 1576 1577 #endif // !defined(OS_CHROMEOS) 1578 1579 // Tests that wheel events generated from scroll events are targetted to the 1580 // views under the cursor when the focused view does not processed them. 1581 TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) { 1582 EventCountView* cursor_view = new EventCountView; 1583 cursor_view->SetBounds(60, 0, 50, 40); 1584 1585 Widget* widget = CreateTopLevelPlatformWidget(); 1586 widget->GetRootView()->AddChildView(cursor_view); 1587 1588 // Generate a scroll event on the cursor view. 1589 ui::ScrollEvent scroll(ui::ET_SCROLL, 1590 gfx::Point(65, 5), 1591 ui::EventTimeForNow(), 1592 0, 1593 0, 20, 1594 0, 20, 1595 2); 1596 widget->OnScrollEvent(&scroll); 1597 1598 EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_SCROLL)); 1599 EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); 1600 1601 cursor_view->ResetCounts(); 1602 1603 ui::ScrollEvent scroll2(ui::ET_SCROLL, 1604 gfx::Point(5, 5), 1605 ui::EventTimeForNow(), 1606 0, 1607 0, 20, 1608 0, 20, 1609 2); 1610 widget->OnScrollEvent(&scroll2); 1611 1612 EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_SCROLL)); 1613 EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); 1614 1615 widget->CloseNow(); 1616 } 1617 1618 #endif // defined(USE_AURA) 1619 1620 // Tests that if a scroll-begin gesture is not handled, then subsequent scroll 1621 // events are not dispatched to any view. 1622 TEST_F(WidgetTest, GestureScrollEventDispatching) { 1623 EventCountView* noscroll_view = new EventCountView; 1624 EventCountView* scroll_view = new ScrollableEventCountView; 1625 1626 noscroll_view->SetBounds(0, 0, 50, 40); 1627 scroll_view->SetBounds(60, 0, 40, 40); 1628 1629 Widget* widget = CreateTopLevelPlatformWidget(); 1630 widget->GetRootView()->AddChildView(noscroll_view); 1631 widget->GetRootView()->AddChildView(scroll_view); 1632 1633 { 1634 ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN, 1635 5, 5, 0, base::TimeDelta(), 1636 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), 1637 1); 1638 widget->OnGestureEvent(&begin); 1639 ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE, 1640 25, 15, 0, base::TimeDelta(), 1641 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), 1642 1); 1643 widget->OnGestureEvent(&update); 1644 ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END, 1645 25, 15, 0, base::TimeDelta(), 1646 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), 1647 1); 1648 widget->OnGestureEvent(&end); 1649 1650 EXPECT_EQ(1, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); 1651 EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); 1652 EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); 1653 } 1654 1655 { 1656 ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN, 1657 65, 5, 0, base::TimeDelta(), 1658 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), 1659 1); 1660 widget->OnGestureEvent(&begin); 1661 ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE, 1662 85, 15, 0, base::TimeDelta(), 1663 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), 1664 1); 1665 widget->OnGestureEvent(&update); 1666 ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END, 1667 85, 15, 0, base::TimeDelta(), 1668 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), 1669 1); 1670 widget->OnGestureEvent(&end); 1671 1672 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); 1673 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); 1674 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); 1675 } 1676 1677 widget->CloseNow(); 1678 } 1679 1680 // Tests that event-handlers installed on the RootView get triggered correctly. 1681 TEST_F(WidgetTest, EventHandlersOnRootView) { 1682 Widget* widget = CreateTopLevelNativeWidget(); 1683 View* root_view = widget->GetRootView(); 1684 1685 EventCountView* view = new EventCountView; 1686 view->SetBounds(0, 0, 20, 20); 1687 root_view->AddChildView(view); 1688 1689 EventCountHandler h1; 1690 root_view->AddPreTargetHandler(&h1); 1691 1692 EventCountHandler h2; 1693 root_view->AddPostTargetHandler(&h2); 1694 1695 widget->SetBounds(gfx::Rect(0, 0, 100, 100)); 1696 widget->Show(); 1697 1698 ui::TouchEvent pressed(ui::ET_TOUCH_PRESSED, 1699 gfx::Point(10, 10), 1700 0, 0, 1701 ui::EventTimeForNow(), 1702 1.0, 0.0, 1.0, 0.0); 1703 widget->OnTouchEvent(&pressed); 1704 EXPECT_EQ(1, h1.GetEventCount(ui::ET_TOUCH_PRESSED)); 1705 EXPECT_EQ(1, view->GetEventCount(ui::ET_TOUCH_PRESSED)); 1706 EXPECT_EQ(1, h2.GetEventCount(ui::ET_TOUCH_PRESSED)); 1707 1708 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, 1709 5, 5, 0, ui::EventTimeForNow(), 1710 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); 1711 ui::GestureEvent end(ui::ET_GESTURE_END, 1712 5, 5, 0, ui::EventTimeForNow(), 1713 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); 1714 widget->OnGestureEvent(&begin); 1715 EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_BEGIN)); 1716 EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_BEGIN)); 1717 EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_BEGIN)); 1718 1719 ui::TouchEvent released(ui::ET_TOUCH_RELEASED, 1720 gfx::Point(10, 10), 1721 0, 0, 1722 ui::EventTimeForNow(), 1723 1.0, 0.0, 1.0, 0.0); 1724 widget->OnTouchEvent(&released); 1725 EXPECT_EQ(1, h1.GetEventCount(ui::ET_TOUCH_RELEASED)); 1726 EXPECT_EQ(1, view->GetEventCount(ui::ET_TOUCH_RELEASED)); 1727 EXPECT_EQ(1, h2.GetEventCount(ui::ET_TOUCH_RELEASED)); 1728 1729 widget->OnGestureEvent(&end); 1730 EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_END)); 1731 EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_END)); 1732 EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_END)); 1733 1734 ui::ScrollEvent scroll(ui::ET_SCROLL, 1735 gfx::Point(5, 5), 1736 ui::EventTimeForNow(), 1737 0, 1738 0, 20, 1739 0, 20, 1740 2); 1741 widget->OnScrollEvent(&scroll); 1742 EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL)); 1743 EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL)); 1744 EXPECT_EQ(1, h2.GetEventCount(ui::ET_SCROLL)); 1745 1746 widget->CloseNow(); 1747 } 1748 1749 TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { 1750 Widget* widget = CreateTopLevelNativeWidget(); 1751 View* root_view = widget->GetRootView(); 1752 1753 EventCountView* v1 = new EventCountView(); 1754 v1->SetBounds(0, 0, 10, 10); 1755 root_view->AddChildView(v1); 1756 EventCountView* v2 = new EventCountView(); 1757 v2->SetBounds(0, 10, 10, 10); 1758 root_view->AddChildView(v2); 1759 1760 gfx::Point cursor_location(5, 5); 1761 ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, 1762 ui::EF_NONE); 1763 widget->OnMouseEvent(&move); 1764 1765 EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); 1766 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1767 1768 delete v1; 1769 v2->SetBounds(0, 0, 10, 10); 1770 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1771 1772 widget->SynthesizeMouseMoveEvent(); 1773 EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1774 } 1775 1776 TEST_F(WidgetTest, MouseEventsHandled) { 1777 Widget* widget = CreateTopLevelNativeWidget(); 1778 View* root_view = widget->GetRootView(); 1779 1780 #if defined(USE_AURA) 1781 aura::test::TestCursorClient cursor_client( 1782 widget->GetNativeView()->GetRootWindow()); 1783 #endif 1784 1785 EventCountView* v1 = new EventCountView(); 1786 v1->SetBounds(0, 0, 10, 10); 1787 root_view->AddChildView(v1); 1788 EventCountView* v2 = new EventCountView(); 1789 v2->SetBounds(0, 10, 10, 10); 1790 root_view->AddChildView(v2); 1791 1792 gfx::Point cursor_location1(5, 5); 1793 ui::MouseEvent move1(ui::ET_MOUSE_MOVED, cursor_location1, cursor_location1, 1794 ui::EF_NONE); 1795 widget->OnMouseEvent(&move1); 1796 EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); 1797 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1798 EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_EXITED)); 1799 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); 1800 v1->ResetCounts(); 1801 v2->ResetCounts(); 1802 1803 gfx::Point cursor_location2(5, 15); 1804 ui::MouseEvent move2(ui::ET_MOUSE_MOVED, cursor_location2, cursor_location2, 1805 ui::EF_NONE); 1806 widget->OnMouseEvent(&move2); 1807 EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); 1808 EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1809 EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_EXITED)); 1810 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); 1811 v1->ResetCounts(); 1812 v2->ResetCounts(); 1813 1814 #if defined(USE_AURA) 1815 // In Aura, we suppress mouse events if mouse events are disabled. 1816 cursor_client.DisableMouseEvents(); 1817 1818 widget->OnMouseEvent(&move1); 1819 EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); 1820 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1821 EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_EXITED)); 1822 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); 1823 v1->ResetCounts(); 1824 v2->ResetCounts(); 1825 1826 widget->OnMouseEvent(&move2); 1827 EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); 1828 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1829 EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_EXITED)); 1830 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); 1831 #endif 1832 } 1833 1834 // Used by SingleWindowClosing to count number of times WindowClosing() has 1835 // been invoked. 1836 class ClosingDelegate : public WidgetDelegate { 1837 public: 1838 ClosingDelegate() : count_(0), widget_(NULL) {} 1839 1840 int count() const { return count_; } 1841 1842 void set_widget(views::Widget* widget) { widget_ = widget; } 1843 1844 // WidgetDelegate overrides: 1845 virtual Widget* GetWidget() OVERRIDE { return widget_; } 1846 virtual const Widget* GetWidget() const OVERRIDE { return widget_; } 1847 virtual void WindowClosing() OVERRIDE { 1848 count_++; 1849 } 1850 1851 private: 1852 int count_; 1853 views::Widget* widget_; 1854 1855 DISALLOW_COPY_AND_ASSIGN(ClosingDelegate); 1856 }; 1857 1858 // Verifies WindowClosing() is invoked correctly on the delegate when a Widget 1859 // is closed. 1860 TEST_F(WidgetTest, SingleWindowClosing) { 1861 scoped_ptr<ClosingDelegate> delegate(new ClosingDelegate()); 1862 Widget* widget = new Widget(); // Destroyed by CloseNow() below. 1863 Widget::InitParams init_params = 1864 CreateParams(Widget::InitParams::TYPE_WINDOW); 1865 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1866 init_params.delegate = delegate.get(); 1867 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 1868 init_params.native_widget = new DesktopNativeWidgetAura(widget); 1869 #endif 1870 widget->Init(init_params); 1871 EXPECT_EQ(0, delegate->count()); 1872 widget->CloseNow(); 1873 EXPECT_EQ(1, delegate->count()); 1874 } 1875 1876 // Used by SetTopLevelCorrectly to track calls to OnBeforeWidgetInit(). 1877 class VerifyTopLevelDelegate : public TestViewsDelegate { 1878 public: 1879 VerifyTopLevelDelegate() 1880 : on_before_init_called_(false), 1881 is_top_level_(false) { 1882 } 1883 1884 bool on_before_init_called() const { return on_before_init_called_; } 1885 bool is_top_level() const { return is_top_level_; } 1886 1887 virtual void OnBeforeWidgetInit( 1888 Widget::InitParams* params, 1889 internal::NativeWidgetDelegate* delegate) OVERRIDE { 1890 on_before_init_called_ = true; 1891 is_top_level_ = params->top_level; 1892 } 1893 1894 private: 1895 bool on_before_init_called_; 1896 bool is_top_level_; 1897 1898 DISALLOW_COPY_AND_ASSIGN(VerifyTopLevelDelegate); 1899 }; 1900 1901 // Verifies |top_level| is correctly passed to 1902 // ViewsDelegate::OnBeforeWidgetInit(). 1903 TEST_F(WidgetTest, SetTopLevelCorrectly) { 1904 set_views_delegate(NULL); 1905 VerifyTopLevelDelegate* delegate = new VerifyTopLevelDelegate; 1906 set_views_delegate(delegate); // ViewsTestBase takes ownership. 1907 scoped_ptr<Widget> widget(new Widget); 1908 Widget::InitParams params = 1909 CreateParams(views::Widget::InitParams::TYPE_POPUP); 1910 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1911 widget->Init(params); 1912 EXPECT_TRUE(delegate->on_before_init_called()); 1913 EXPECT_TRUE(delegate->is_top_level()); 1914 } 1915 1916 // A scumbag View that deletes its owning widget OnMousePressed. 1917 class WidgetDeleterView : public View { 1918 public: 1919 WidgetDeleterView() : View() {} 1920 1921 // Overridden from View. 1922 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 1923 delete GetWidget(); 1924 return true; 1925 } 1926 1927 private: 1928 DISALLOW_COPY_AND_ASSIGN(WidgetDeleterView); 1929 }; 1930 1931 TEST_F(WidgetTest, TestWidgetDeletedInOnMousePressed) { 1932 Widget* widget = new Widget; 1933 Widget::InitParams params = 1934 CreateParams(views::Widget::InitParams::TYPE_POPUP); 1935 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1936 widget->Init(params); 1937 1938 widget->SetContentsView(new WidgetDeleterView); 1939 1940 widget->SetSize(gfx::Size(100, 100)); 1941 widget->Show(); 1942 1943 gfx::Point click_location(45, 15); 1944 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 1945 ui::EF_LEFT_MOUSE_BUTTON); 1946 widget->OnMouseEvent(&press); 1947 1948 // Yay we did not crash! 1949 } 1950 1951 // See description of RunGetNativeThemeFromDestructor() for details. 1952 class GetNativeThemeFromDestructorView : public WidgetDelegateView { 1953 public: 1954 GetNativeThemeFromDestructorView() {} 1955 virtual ~GetNativeThemeFromDestructorView() { 1956 VerifyNativeTheme(); 1957 } 1958 1959 virtual View* GetContentsView() OVERRIDE { 1960 return this; 1961 } 1962 1963 private: 1964 void VerifyNativeTheme() { 1965 ASSERT_TRUE(GetNativeTheme() != NULL); 1966 } 1967 1968 DISALLOW_COPY_AND_ASSIGN(GetNativeThemeFromDestructorView); 1969 }; 1970 1971 // Verifies GetNativeTheme() from the destructor of a WidgetDelegateView doesn't 1972 // crash. |is_first_run| is true if this is the first call. A return value of 1973 // true indicates this should be run again with a value of false. 1974 // First run uses DesktopNativeWidgetAura (if possible). Second run doesn't. 1975 bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params, 1976 bool is_first_run) { 1977 bool needs_second_run = false; 1978 // Destroyed by CloseNow() below. 1979 Widget* widget = new Widget; 1980 Widget::InitParams params(in_params); 1981 // Deletes itself when the Widget is destroyed. 1982 params.delegate = new GetNativeThemeFromDestructorView; 1983 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 1984 if (is_first_run) { 1985 params.native_widget = new DesktopNativeWidgetAura(widget); 1986 needs_second_run = true; 1987 } 1988 #endif 1989 widget->Init(params); 1990 widget->CloseNow(); 1991 return needs_second_run; 1992 } 1993 1994 // See description of RunGetNativeThemeFromDestructor() for details. 1995 TEST_F(WidgetTest, GetNativeThemeFromDestructor) { 1996 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1997 if (RunGetNativeThemeFromDestructor(params, true)) 1998 RunGetNativeThemeFromDestructor(params, false); 1999 } 2000 2001 // Used by HideCloseDestroy. Allows setting a boolean when the widget is 2002 // destroyed. 2003 class CloseDestroysWidget : public Widget { 2004 public: 2005 explicit CloseDestroysWidget(bool* destroyed) 2006 : destroyed_(destroyed) { 2007 } 2008 2009 virtual ~CloseDestroysWidget() { 2010 if (destroyed_) { 2011 *destroyed_ = true; 2012 base::MessageLoop::current()->QuitNow(); 2013 } 2014 } 2015 2016 void Detach() { destroyed_ = NULL; } 2017 2018 private: 2019 // If non-null set to true from destructor. 2020 bool* destroyed_; 2021 2022 DISALLOW_COPY_AND_ASSIGN(CloseDestroysWidget); 2023 }; 2024 2025 // Verifies Close() results in destroying. 2026 TEST_F(WidgetTest, CloseDestroys) { 2027 bool destroyed = false; 2028 CloseDestroysWidget* widget = new CloseDestroysWidget(&destroyed); 2029 Widget::InitParams params = 2030 CreateParams(views::Widget::InitParams::TYPE_MENU); 2031 params.opacity = Widget::InitParams::OPAQUE_WINDOW; 2032 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 2033 params.native_widget = new DesktopNativeWidgetAura(widget); 2034 #endif 2035 widget->Init(params); 2036 widget->Show(); 2037 widget->Hide(); 2038 widget->Close(); 2039 // Run the message loop as Close() asynchronously deletes. 2040 RunPendingMessages(); 2041 EXPECT_TRUE(destroyed); 2042 // Close() should destroy the widget. If not we'll cleanup to avoid leaks. 2043 if (!destroyed) { 2044 widget->Detach(); 2045 widget->CloseNow(); 2046 } 2047 } 2048 2049 // A view that consumes mouse-pressed event and gesture-tap-down events. 2050 class RootViewTestView : public View { 2051 public: 2052 RootViewTestView(): View() {} 2053 2054 private: 2055 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 2056 return true; 2057 } 2058 2059 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 2060 if (event->type() == ui::ET_GESTURE_TAP_DOWN) 2061 event->SetHandled(); 2062 } 2063 }; 2064 2065 // Checks if RootView::*_handler_ fields are unset when widget is hidden. 2066 TEST_F(WidgetTest, TestRootViewHandlersWhenHidden) { 2067 Widget* widget = CreateTopLevelNativeWidget(); 2068 widget->SetBounds(gfx::Rect(0, 0, 300, 300)); 2069 View* view = new RootViewTestView(); 2070 view->SetBounds(0, 0, 300, 300); 2071 internal::RootView* root_view = 2072 static_cast<internal::RootView*>(widget->GetRootView()); 2073 root_view->AddChildView(view); 2074 2075 // Check RootView::mouse_pressed_handler_. 2076 widget->Show(); 2077 EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); 2078 gfx::Point click_location(45, 15); 2079 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 2080 ui::EF_LEFT_MOUSE_BUTTON); 2081 widget->OnMouseEvent(&press); 2082 EXPECT_EQ(view, GetMousePressedHandler(root_view)); 2083 widget->Hide(); 2084 EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); 2085 2086 // Check RootView::mouse_move_handler_. 2087 widget->Show(); 2088 EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); 2089 gfx::Point move_location(45, 15); 2090 ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0); 2091 widget->OnMouseEvent(&move); 2092 EXPECT_EQ(view, GetMouseMoveHandler(root_view)); 2093 widget->Hide(); 2094 EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); 2095 2096 // Check RootView::gesture_handler_. 2097 widget->Show(); 2098 EXPECT_EQ(NULL, GetGestureHandler(root_view)); 2099 ui::GestureEvent tap_down( 2100 ui::ET_GESTURE_TAP_DOWN, 2101 15, 2102 15, 2103 0, 2104 base::TimeDelta(), 2105 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0), 2106 1); 2107 widget->OnGestureEvent(&tap_down); 2108 EXPECT_EQ(view, GetGestureHandler(root_view)); 2109 widget->Hide(); 2110 EXPECT_EQ(NULL, GetGestureHandler(root_view)); 2111 2112 widget->Close(); 2113 } 2114 2115 } // namespace test 2116 } // namespace views 2117