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 <algorithm> 6 #include <set> 7 8 #include "base/basictypes.h" 9 #include "base/bind.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/run_loop.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "ui/aura/test/event_generator.h" 16 #include "ui/aura/window.h" 17 #include "ui/base/hit_test.h" 18 #include "ui/compositor/scoped_animation_duration_scale_mode.h" 19 #include "ui/compositor/scoped_layer_animation_settings.h" 20 #include "ui/events/event_processor.h" 21 #include "ui/events/event_utils.h" 22 #include "ui/gfx/native_widget_types.h" 23 #include "ui/gfx/point.h" 24 #include "ui/views/bubble/bubble_delegate.h" 25 #include "ui/views/controls/textfield/textfield.h" 26 #include "ui/views/test/test_views_delegate.h" 27 #include "ui/views/test/widget_test.h" 28 #include "ui/views/views_delegate.h" 29 #include "ui/views/widget/native_widget_delegate.h" 30 #include "ui/views/widget/root_view.h" 31 #include "ui/views/widget/widget_deletion_observer.h" 32 #include "ui/views/window/dialog_delegate.h" 33 #include "ui/views/window/native_frame_view.h" 34 35 #if defined(OS_WIN) 36 #include "ui/views/win/hwnd_util.h" 37 #endif 38 39 namespace views { 40 namespace test { 41 42 // A view that keeps track of the events it receives, but consumes no events. 43 class EventCountView : public View { 44 public: 45 EventCountView() {} 46 virtual ~EventCountView() {} 47 48 int GetEventCount(ui::EventType type) { 49 return event_count_[type]; 50 } 51 52 void ResetCounts() { 53 event_count_.clear(); 54 } 55 56 protected: 57 // Overridden from ui::EventHandler: 58 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { 59 RecordEvent(*event); 60 } 61 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 62 RecordEvent(*event); 63 } 64 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE { 65 RecordEvent(*event); 66 } 67 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 68 RecordEvent(*event); 69 } 70 71 private: 72 void RecordEvent(const ui::Event& event) { 73 ++event_count_[event.type()]; 74 } 75 76 std::map<ui::EventType, int> event_count_; 77 78 DISALLOW_COPY_AND_ASSIGN(EventCountView); 79 }; 80 81 // A view that keeps track of the events it receives, and consumes all scroll 82 // gesture events and ui::ET_SCROLL events. 83 class ScrollableEventCountView : public EventCountView { 84 public: 85 ScrollableEventCountView() {} 86 virtual ~ScrollableEventCountView() {} 87 88 private: 89 // Overridden from ui::EventHandler: 90 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 91 EventCountView::OnGestureEvent(event); 92 switch (event->type()) { 93 case ui::ET_GESTURE_SCROLL_BEGIN: 94 case ui::ET_GESTURE_SCROLL_UPDATE: 95 case ui::ET_GESTURE_SCROLL_END: 96 case ui::ET_SCROLL_FLING_START: 97 event->SetHandled(); 98 break; 99 default: 100 break; 101 } 102 } 103 104 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE { 105 EventCountView::OnScrollEvent(event); 106 if (event->type() == ui::ET_SCROLL) 107 event->SetHandled(); 108 } 109 110 DISALLOW_COPY_AND_ASSIGN(ScrollableEventCountView); 111 }; 112 113 // A view that implements GetMinimumSize. 114 class MinimumSizeFrameView : public NativeFrameView { 115 public: 116 explicit MinimumSizeFrameView(Widget* frame): NativeFrameView(frame) {} 117 virtual ~MinimumSizeFrameView() {} 118 119 private: 120 // Overridden from View: 121 virtual gfx::Size GetMinimumSize() const OVERRIDE { 122 return gfx::Size(300, 400); 123 } 124 125 DISALLOW_COPY_AND_ASSIGN(MinimumSizeFrameView); 126 }; 127 128 // An event handler that simply keeps a count of the different types of events 129 // it receives. 130 class EventCountHandler : public ui::EventHandler { 131 public: 132 EventCountHandler() {} 133 virtual ~EventCountHandler() {} 134 135 int GetEventCount(ui::EventType type) { 136 return event_count_[type]; 137 } 138 139 void ResetCounts() { 140 event_count_.clear(); 141 } 142 143 protected: 144 // Overridden from ui::EventHandler: 145 virtual void OnEvent(ui::Event* event) OVERRIDE { 146 RecordEvent(*event); 147 ui::EventHandler::OnEvent(event); 148 } 149 150 private: 151 void RecordEvent(const ui::Event& event) { 152 ++event_count_[event.type()]; 153 } 154 155 std::map<ui::EventType, int> event_count_; 156 157 DISALLOW_COPY_AND_ASSIGN(EventCountHandler); 158 }; 159 160 // Class that closes the widget (which ends up deleting it immediately) when the 161 // appropriate event is received. 162 class CloseWidgetView : public View { 163 public: 164 explicit CloseWidgetView(ui::EventType event_type) 165 : event_type_(event_type) { 166 } 167 168 // ui::EventHandler override: 169 virtual void OnEvent(ui::Event* event) OVERRIDE { 170 if (event->type() == event_type_) { 171 // Go through NativeWidgetPrivate to simulate what happens if the OS 172 // deletes the NativeWindow out from under us. 173 GetWidget()->native_widget_private()->CloseNow(); 174 } else { 175 View::OnEvent(event); 176 if (!event->IsTouchEvent()) 177 event->SetHandled(); 178 } 179 } 180 181 private: 182 const ui::EventType event_type_; 183 184 DISALLOW_COPY_AND_ASSIGN(CloseWidgetView); 185 }; 186 187 ui::WindowShowState GetWidgetShowState(const Widget* widget) { 188 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement 189 // because the former is implemented on all platforms but the latter is not. 190 return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN : 191 widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED : 192 widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED : 193 widget->IsActive() ? ui::SHOW_STATE_NORMAL : 194 ui::SHOW_STATE_INACTIVE; 195 } 196 197 TEST_F(WidgetTest, WidgetInitParams) { 198 // Widgets are not transparent by default. 199 Widget::InitParams init1; 200 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init1.opacity); 201 } 202 203 //////////////////////////////////////////////////////////////////////////////// 204 // Widget::GetTopLevelWidget tests. 205 206 TEST_F(WidgetTest, GetTopLevelWidget_Native) { 207 // Create a hierarchy of native widgets. 208 Widget* toplevel = CreateTopLevelPlatformWidget(); 209 gfx::NativeView parent = toplevel->GetNativeView(); 210 Widget* child = CreateChildPlatformWidget(parent); 211 212 EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget()); 213 EXPECT_EQ(toplevel, child->GetTopLevelWidget()); 214 215 toplevel->CloseNow(); 216 // |child| should be automatically destroyed with |toplevel|. 217 } 218 219 // Test if a focus manager and an inputmethod work without CHECK failure 220 // when window activation changes. 221 TEST_F(WidgetTest, ChangeActivation) { 222 Widget* top1 = CreateTopLevelPlatformWidget(); 223 // CreateInputMethod before activated 224 top1->GetInputMethod(); 225 top1->Show(); 226 RunPendingMessages(); 227 228 Widget* top2 = CreateTopLevelPlatformWidget(); 229 top2->Show(); 230 RunPendingMessages(); 231 232 top1->Activate(); 233 RunPendingMessages(); 234 235 // Create InputMethod after deactivated. 236 top2->GetInputMethod(); 237 top2->Activate(); 238 RunPendingMessages(); 239 240 top1->Activate(); 241 RunPendingMessages(); 242 243 top1->CloseNow(); 244 top2->CloseNow(); 245 } 246 247 // Tests visibility of child widgets. 248 TEST_F(WidgetTest, Visibility) { 249 Widget* toplevel = CreateTopLevelPlatformWidget(); 250 gfx::NativeView parent = toplevel->GetNativeView(); 251 Widget* child = CreateChildPlatformWidget(parent); 252 253 EXPECT_FALSE(toplevel->IsVisible()); 254 EXPECT_FALSE(child->IsVisible()); 255 256 child->Show(); 257 258 EXPECT_FALSE(toplevel->IsVisible()); 259 EXPECT_FALSE(child->IsVisible()); 260 261 toplevel->Show(); 262 263 EXPECT_TRUE(toplevel->IsVisible()); 264 EXPECT_TRUE(child->IsVisible()); 265 266 toplevel->CloseNow(); 267 // |child| should be automatically destroyed with |toplevel|. 268 } 269 270 //////////////////////////////////////////////////////////////////////////////// 271 // Widget ownership tests. 272 // 273 // Tests various permutations of Widget ownership specified in the 274 // InitParams::Ownership param. 275 276 // A WidgetTest that supplies a toplevel widget for NativeWidget to parent to. 277 class WidgetOwnershipTest : public WidgetTest { 278 public: 279 WidgetOwnershipTest() {} 280 virtual ~WidgetOwnershipTest() {} 281 282 virtual void SetUp() { 283 WidgetTest::SetUp(); 284 desktop_widget_ = CreateTopLevelPlatformWidget(); 285 } 286 287 virtual void TearDown() { 288 desktop_widget_->CloseNow(); 289 WidgetTest::TearDown(); 290 } 291 292 private: 293 Widget* desktop_widget_; 294 295 DISALLOW_COPY_AND_ASSIGN(WidgetOwnershipTest); 296 }; 297 298 // A bag of state to monitor destructions. 299 struct OwnershipTestState { 300 OwnershipTestState() : widget_deleted(false), native_widget_deleted(false) {} 301 302 bool widget_deleted; 303 bool native_widget_deleted; 304 }; 305 306 // A platform NativeWidget subclass that updates a bag of state when it is 307 // destroyed. 308 class OwnershipTestNativeWidget : public PlatformNativeWidget { 309 public: 310 OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate, 311 OwnershipTestState* state) 312 : PlatformNativeWidget(delegate), 313 state_(state) { 314 } 315 virtual ~OwnershipTestNativeWidget() { 316 state_->native_widget_deleted = true; 317 } 318 319 private: 320 OwnershipTestState* state_; 321 322 DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget); 323 }; 324 325 // A views NativeWidget subclass that updates a bag of state when it is 326 // destroyed. 327 class OwnershipTestNativeWidgetAura : public NativeWidgetCapture { 328 public: 329 OwnershipTestNativeWidgetAura(internal::NativeWidgetDelegate* delegate, 330 OwnershipTestState* state) 331 : NativeWidgetCapture(delegate), 332 state_(state) { 333 } 334 virtual ~OwnershipTestNativeWidgetAura() { 335 state_->native_widget_deleted = true; 336 } 337 338 private: 339 OwnershipTestState* state_; 340 341 DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetAura); 342 }; 343 344 // A Widget subclass that updates a bag of state when it is destroyed. 345 class OwnershipTestWidget : public Widget { 346 public: 347 explicit OwnershipTestWidget(OwnershipTestState* state) : state_(state) {} 348 virtual ~OwnershipTestWidget() { 349 state_->widget_deleted = true; 350 } 351 352 private: 353 OwnershipTestState* state_; 354 355 DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget); 356 }; 357 358 // Widget owns its NativeWidget, part 1: NativeWidget is a platform-native 359 // widget. 360 TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { 361 OwnershipTestState state; 362 363 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 364 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 365 params.native_widget = 366 new OwnershipTestNativeWidgetAura(widget.get(), &state); 367 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 368 widget->Init(params); 369 370 // Now delete the Widget, which should delete the NativeWidget. 371 widget.reset(); 372 373 EXPECT_TRUE(state.widget_deleted); 374 EXPECT_TRUE(state.native_widget_deleted); 375 376 // TODO(beng): write test for this ownership scenario and the NativeWidget 377 // being deleted out from under the Widget. 378 } 379 380 // Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget. 381 TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { 382 OwnershipTestState state; 383 384 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 385 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 386 params.native_widget = 387 new OwnershipTestNativeWidgetAura(widget.get(), &state); 388 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 389 widget->Init(params); 390 391 // Now delete the Widget, which should delete the NativeWidget. 392 widget.reset(); 393 394 EXPECT_TRUE(state.widget_deleted); 395 EXPECT_TRUE(state.native_widget_deleted); 396 397 // TODO(beng): write test for this ownership scenario and the NativeWidget 398 // being deleted out from under the Widget. 399 } 400 401 // Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget, 402 // destroy the parent view. 403 TEST_F(WidgetOwnershipTest, 404 Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) { 405 OwnershipTestState state; 406 407 Widget* toplevel = CreateTopLevelPlatformWidget(); 408 409 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 410 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 411 params.native_widget = 412 new OwnershipTestNativeWidgetAura(widget.get(), &state); 413 params.parent = toplevel->GetNativeView(); 414 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 415 widget->Init(params); 416 417 // Now close the toplevel, which deletes the view hierarchy. 418 toplevel->CloseNow(); 419 420 RunPendingMessages(); 421 422 // This shouldn't delete the widget because it shouldn't be deleted 423 // from the native side. 424 EXPECT_FALSE(state.widget_deleted); 425 EXPECT_FALSE(state.native_widget_deleted); 426 427 // Now delete it explicitly. 428 widget.reset(); 429 430 EXPECT_TRUE(state.widget_deleted); 431 EXPECT_TRUE(state.native_widget_deleted); 432 } 433 434 // NativeWidget owns its Widget, part 1: NativeWidget is a platform-native 435 // widget. 436 TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { 437 OwnershipTestState state; 438 439 Widget* widget = new OwnershipTestWidget(&state); 440 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 441 params.native_widget = 442 new OwnershipTestNativeWidgetAura(widget, &state); 443 widget->Init(params); 444 445 // Now destroy the native widget. 446 widget->CloseNow(); 447 448 EXPECT_TRUE(state.widget_deleted); 449 EXPECT_TRUE(state.native_widget_deleted); 450 } 451 452 // NativeWidget owns its Widget, part 2: NativeWidget is a NativeWidget. 453 TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { 454 OwnershipTestState state; 455 456 Widget* toplevel = CreateTopLevelPlatformWidget(); 457 458 Widget* widget = new OwnershipTestWidget(&state); 459 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 460 params.native_widget = 461 new OwnershipTestNativeWidgetAura(widget, &state); 462 params.parent = toplevel->GetNativeView(); 463 widget->Init(params); 464 465 // Now destroy the native widget. This is achieved by closing the toplevel. 466 toplevel->CloseNow(); 467 468 // The NativeWidget won't be deleted until after a return to the message loop 469 // so we have to run pending messages before testing the destruction status. 470 RunPendingMessages(); 471 472 EXPECT_TRUE(state.widget_deleted); 473 EXPECT_TRUE(state.native_widget_deleted); 474 } 475 476 // NativeWidget owns its Widget, part 3: NativeWidget is a platform-native 477 // widget, destroyed out from under it by the OS. 478 TEST_F(WidgetOwnershipTest, 479 Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy) { 480 OwnershipTestState state; 481 482 Widget* widget = new OwnershipTestWidget(&state); 483 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 484 params.native_widget = 485 new OwnershipTestNativeWidgetAura(widget, &state); 486 widget->Init(params); 487 488 // Now simulate a destroy of the platform native widget from the OS: 489 SimulateNativeDestroy(widget); 490 491 EXPECT_TRUE(state.widget_deleted); 492 EXPECT_TRUE(state.native_widget_deleted); 493 } 494 495 // NativeWidget owns its Widget, part 4: NativeWidget is a NativeWidget, 496 // destroyed by the view hierarchy that contains it. 497 TEST_F(WidgetOwnershipTest, 498 Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) { 499 OwnershipTestState state; 500 501 Widget* toplevel = CreateTopLevelPlatformWidget(); 502 503 Widget* widget = new OwnershipTestWidget(&state); 504 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 505 params.native_widget = 506 new OwnershipTestNativeWidgetAura(widget, &state); 507 params.parent = toplevel->GetNativeView(); 508 widget->Init(params); 509 510 // Destroy the widget (achieved by closing the toplevel). 511 toplevel->CloseNow(); 512 513 // The NativeWidget won't be deleted until after a return to the message loop 514 // so we have to run pending messages before testing the destruction status. 515 RunPendingMessages(); 516 517 EXPECT_TRUE(state.widget_deleted); 518 EXPECT_TRUE(state.native_widget_deleted); 519 } 520 521 // NativeWidget owns its Widget, part 5: NativeWidget is a NativeWidget, 522 // we close it directly. 523 TEST_F(WidgetOwnershipTest, 524 Ownership_ViewsNativeWidgetOwnsWidget_Close) { 525 OwnershipTestState state; 526 527 Widget* toplevel = CreateTopLevelPlatformWidget(); 528 529 Widget* widget = new OwnershipTestWidget(&state); 530 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 531 params.native_widget = 532 new OwnershipTestNativeWidgetAura(widget, &state); 533 params.parent = toplevel->GetNativeView(); 534 widget->Init(params); 535 536 // Destroy the widget. 537 widget->Close(); 538 toplevel->CloseNow(); 539 540 // The NativeWidget won't be deleted until after a return to the message loop 541 // so we have to run pending messages before testing the destruction status. 542 RunPendingMessages(); 543 544 EXPECT_TRUE(state.widget_deleted); 545 EXPECT_TRUE(state.native_widget_deleted); 546 } 547 548 // Widget owns its NativeWidget and has a WidgetDelegateView as its contents. 549 TEST_F(WidgetOwnershipTest, 550 Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView) { 551 OwnershipTestState state; 552 553 WidgetDelegateView* delegate_view = new WidgetDelegateView; 554 555 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); 556 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 557 params.native_widget = 558 new OwnershipTestNativeWidgetAura(widget.get(), &state); 559 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 560 params.delegate = delegate_view; 561 widget->Init(params); 562 widget->SetContentsView(delegate_view); 563 564 // Now delete the Widget. There should be no crash or use-after-free. 565 widget.reset(); 566 567 EXPECT_TRUE(state.widget_deleted); 568 EXPECT_TRUE(state.native_widget_deleted); 569 } 570 571 //////////////////////////////////////////////////////////////////////////////// 572 // Test to verify using various Widget methods doesn't crash when the underlying 573 // NativeView is destroyed. 574 // 575 class WidgetWithDestroyedNativeViewTest : public ViewsTestBase { 576 public: 577 WidgetWithDestroyedNativeViewTest() {} 578 virtual ~WidgetWithDestroyedNativeViewTest() {} 579 580 void InvokeWidgetMethods(Widget* widget) { 581 widget->GetNativeView(); 582 widget->GetNativeWindow(); 583 ui::Accelerator accelerator; 584 widget->GetAccelerator(0, &accelerator); 585 widget->GetTopLevelWidget(); 586 widget->GetWindowBoundsInScreen(); 587 widget->GetClientAreaBoundsInScreen(); 588 widget->SetBounds(gfx::Rect(0, 0, 100, 80)); 589 widget->SetSize(gfx::Size(10, 11)); 590 widget->SetBoundsConstrained(gfx::Rect(0, 0, 120, 140)); 591 widget->SetVisibilityChangedAnimationsEnabled(false); 592 widget->StackAtTop(); 593 widget->IsClosed(); 594 widget->Close(); 595 widget->Hide(); 596 widget->Activate(); 597 widget->Deactivate(); 598 widget->IsActive(); 599 widget->DisableInactiveRendering(); 600 widget->SetAlwaysOnTop(true); 601 widget->IsAlwaysOnTop(); 602 widget->Maximize(); 603 widget->Minimize(); 604 widget->Restore(); 605 widget->IsMaximized(); 606 widget->IsFullscreen(); 607 widget->SetOpacity(0); 608 widget->SetUseDragFrame(true); 609 widget->FlashFrame(true); 610 widget->IsVisible(); 611 widget->GetThemeProvider(); 612 widget->GetNativeTheme(); 613 widget->GetFocusManager(); 614 widget->GetInputMethod(); 615 widget->SchedulePaintInRect(gfx::Rect(0, 0, 1, 2)); 616 widget->IsMouseEventsEnabled(); 617 widget->SetNativeWindowProperty("xx", widget); 618 widget->GetNativeWindowProperty("xx"); 619 widget->GetFocusTraversable(); 620 widget->GetLayer(); 621 widget->ReorderNativeViews(); 622 widget->SetCapture(widget->GetRootView()); 623 widget->ReleaseCapture(); 624 widget->HasCapture(); 625 widget->GetWorkAreaBoundsInScreen(); 626 widget->IsTranslucentWindowOpacitySupported(); 627 } 628 629 private: 630 DISALLOW_COPY_AND_ASSIGN(WidgetWithDestroyedNativeViewTest); 631 }; 632 633 TEST_F(WidgetWithDestroyedNativeViewTest, Test) { 634 { 635 Widget widget; 636 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 637 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 638 widget.Init(params); 639 widget.Show(); 640 641 widget.native_widget_private()->CloseNow(); 642 InvokeWidgetMethods(&widget); 643 } 644 #if !defined(OS_CHROMEOS) 645 { 646 Widget widget; 647 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 648 params.native_widget = new PlatformDesktopNativeWidget(&widget); 649 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 650 widget.Init(params); 651 widget.Show(); 652 653 widget.native_widget_private()->CloseNow(); 654 InvokeWidgetMethods(&widget); 655 } 656 #endif 657 } 658 659 //////////////////////////////////////////////////////////////////////////////// 660 // Widget observer tests. 661 // 662 663 class WidgetObserverTest : public WidgetTest, public WidgetObserver { 664 public: 665 WidgetObserverTest() 666 : active_(NULL), 667 widget_closed_(NULL), 668 widget_activated_(NULL), 669 widget_shown_(NULL), 670 widget_hidden_(NULL), 671 widget_bounds_changed_(NULL) { 672 } 673 674 virtual ~WidgetObserverTest() {} 675 676 // Overridden from WidgetObserver: 677 virtual void OnWidgetDestroying(Widget* widget) OVERRIDE { 678 if (active_ == widget) 679 active_ = NULL; 680 widget_closed_ = widget; 681 } 682 683 virtual void OnWidgetActivationChanged(Widget* widget, 684 bool active) OVERRIDE { 685 if (active) { 686 if (widget_activated_) 687 widget_activated_->Deactivate(); 688 widget_activated_ = widget; 689 active_ = widget; 690 } else { 691 if (widget_activated_ == widget) 692 widget_activated_ = NULL; 693 widget_deactivated_ = widget; 694 } 695 } 696 697 virtual void OnWidgetVisibilityChanged(Widget* widget, 698 bool visible) OVERRIDE { 699 if (visible) 700 widget_shown_ = widget; 701 else 702 widget_hidden_ = widget; 703 } 704 705 virtual void OnWidgetBoundsChanged(Widget* widget, 706 const gfx::Rect& new_bounds) OVERRIDE { 707 widget_bounds_changed_ = widget; 708 } 709 710 void reset() { 711 active_ = NULL; 712 widget_closed_ = NULL; 713 widget_activated_ = NULL; 714 widget_deactivated_ = NULL; 715 widget_shown_ = NULL; 716 widget_hidden_ = NULL; 717 widget_bounds_changed_ = NULL; 718 } 719 720 Widget* NewWidget() { 721 Widget* widget = CreateTopLevelNativeWidget(); 722 widget->AddObserver(this); 723 return widget; 724 } 725 726 const Widget* active() const { return active_; } 727 const Widget* widget_closed() const { return widget_closed_; } 728 const Widget* widget_activated() const { return widget_activated_; } 729 const Widget* widget_deactivated() const { return widget_deactivated_; } 730 const Widget* widget_shown() const { return widget_shown_; } 731 const Widget* widget_hidden() const { return widget_hidden_; } 732 const Widget* widget_bounds_changed() const { return widget_bounds_changed_; } 733 734 private: 735 Widget* active_; 736 737 Widget* widget_closed_; 738 Widget* widget_activated_; 739 Widget* widget_deactivated_; 740 Widget* widget_shown_; 741 Widget* widget_hidden_; 742 Widget* widget_bounds_changed_; 743 }; 744 745 TEST_F(WidgetObserverTest, DISABLED_ActivationChange) { 746 Widget* toplevel = CreateTopLevelPlatformWidget(); 747 748 Widget* toplevel1 = NewWidget(); 749 Widget* toplevel2 = NewWidget(); 750 751 toplevel1->Show(); 752 toplevel2->Show(); 753 754 reset(); 755 756 toplevel1->Activate(); 757 758 RunPendingMessages(); 759 EXPECT_EQ(toplevel1, widget_activated()); 760 761 toplevel2->Activate(); 762 RunPendingMessages(); 763 EXPECT_EQ(toplevel1, widget_deactivated()); 764 EXPECT_EQ(toplevel2, widget_activated()); 765 EXPECT_EQ(toplevel2, active()); 766 767 toplevel->CloseNow(); 768 } 769 770 TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) { 771 Widget* toplevel = CreateTopLevelPlatformWidget(); 772 773 Widget* child1 = NewWidget(); 774 Widget* child2 = NewWidget(); 775 776 toplevel->Show(); 777 child1->Show(); 778 child2->Show(); 779 780 reset(); 781 782 child1->Hide(); 783 EXPECT_EQ(child1, widget_hidden()); 784 785 child2->Hide(); 786 EXPECT_EQ(child2, widget_hidden()); 787 788 child1->Show(); 789 EXPECT_EQ(child1, widget_shown()); 790 791 child2->Show(); 792 EXPECT_EQ(child2, widget_shown()); 793 794 toplevel->CloseNow(); 795 } 796 797 TEST_F(WidgetObserverTest, DestroyBubble) { 798 Widget* anchor = CreateTopLevelPlatformWidget(); 799 anchor->Show(); 800 801 BubbleDelegateView* bubble_delegate = 802 new BubbleDelegateView(anchor->client_view(), BubbleBorder::NONE); 803 Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); 804 bubble_widget->Show(); 805 bubble_widget->CloseNow(); 806 807 anchor->Hide(); 808 anchor->CloseNow(); 809 } 810 811 TEST_F(WidgetObserverTest, WidgetBoundsChanged) { 812 Widget* child1 = NewWidget(); 813 Widget* child2 = NewWidget(); 814 815 child1->OnNativeWidgetMove(); 816 EXPECT_EQ(child1, widget_bounds_changed()); 817 818 child2->OnNativeWidgetMove(); 819 EXPECT_EQ(child2, widget_bounds_changed()); 820 821 child1->OnNativeWidgetSizeChanged(gfx::Size()); 822 EXPECT_EQ(child1, widget_bounds_changed()); 823 824 child2->OnNativeWidgetSizeChanged(gfx::Size()); 825 EXPECT_EQ(child2, widget_bounds_changed()); 826 } 827 828 #if defined(false) 829 // Aura needs shell to maximize/fullscreen window. 830 // NativeWidgetGtk doesn't implement GetRestoredBounds. 831 TEST_F(WidgetTest, GetRestoredBounds) { 832 Widget* toplevel = CreateTopLevelPlatformWidget(); 833 EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), 834 toplevel->GetRestoredBounds().ToString()); 835 toplevel->Show(); 836 toplevel->Maximize(); 837 RunPendingMessages(); 838 EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), 839 toplevel->GetRestoredBounds().ToString()); 840 EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); 841 EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); 842 843 toplevel->Restore(); 844 RunPendingMessages(); 845 EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), 846 toplevel->GetRestoredBounds().ToString()); 847 848 toplevel->SetFullscreen(true); 849 RunPendingMessages(); 850 EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), 851 toplevel->GetRestoredBounds().ToString()); 852 EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); 853 EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); 854 } 855 #endif 856 857 // Test that window state is not changed after getting out of full screen. 858 TEST_F(WidgetTest, ExitFullscreenRestoreState) { 859 Widget* toplevel = CreateTopLevelPlatformWidget(); 860 861 toplevel->Show(); 862 RunPendingMessages(); 863 864 // This should be a normal state window. 865 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); 866 867 toplevel->SetFullscreen(true); 868 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); 869 toplevel->SetFullscreen(false); 870 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); 871 872 // And it should still be in normal state after getting out of full screen. 873 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); 874 875 // Now, make it maximized. 876 toplevel->Maximize(); 877 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); 878 879 toplevel->SetFullscreen(true); 880 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); 881 toplevel->SetFullscreen(false); 882 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); 883 884 // And it stays maximized after getting out of full screen. 885 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); 886 887 // Clean up. 888 toplevel->Close(); 889 RunPendingMessages(); 890 } 891 892 // The key-event propagation from Widget happens differently on aura and 893 // non-aura systems because of the difference in IME. So this test works only on 894 // aura. 895 TEST_F(WidgetTest, KeyboardInputEvent) { 896 Widget* toplevel = CreateTopLevelPlatformWidget(); 897 View* container = toplevel->client_view(); 898 899 Textfield* textfield = new Textfield(); 900 textfield->SetText(base::ASCIIToUTF16("some text")); 901 container->AddChildView(textfield); 902 toplevel->Show(); 903 textfield->RequestFocus(); 904 905 // The press gets handled. The release doesn't have an effect. 906 ui::KeyEvent backspace_p(ui::ET_KEY_PRESSED, ui::VKEY_DELETE, 0, false); 907 toplevel->OnKeyEvent(&backspace_p); 908 EXPECT_TRUE(backspace_p.stopped_propagation()); 909 ui::KeyEvent backspace_r(ui::ET_KEY_RELEASED, ui::VKEY_DELETE, 0, false); 910 toplevel->OnKeyEvent(&backspace_r); 911 EXPECT_FALSE(backspace_r.handled()); 912 913 toplevel->Close(); 914 } 915 916 // Verifies bubbles result in a focus lost when shown. 917 // TODO(msw): this tests relies on focus, it needs to be in 918 // interactive_ui_tests. 919 TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { 920 // Create a widget, show and activate it and focus the contents view. 921 View* contents_view = new View; 922 contents_view->SetFocusable(true); 923 Widget widget; 924 Widget::InitParams init_params = 925 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 926 init_params.bounds = gfx::Rect(0, 0, 200, 200); 927 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 928 #if !defined(OS_CHROMEOS) 929 init_params.native_widget = new PlatformDesktopNativeWidget(&widget); 930 #endif 931 widget.Init(init_params); 932 widget.SetContentsView(contents_view); 933 widget.Show(); 934 widget.Activate(); 935 contents_view->RequestFocus(); 936 EXPECT_TRUE(contents_view->HasFocus()); 937 938 // Show a bubble. 939 BubbleDelegateView* bubble_delegate_view = 940 new BubbleDelegateView(contents_view, BubbleBorder::TOP_LEFT); 941 bubble_delegate_view->SetFocusable(true); 942 BubbleDelegateView::CreateBubble(bubble_delegate_view)->Show(); 943 bubble_delegate_view->RequestFocus(); 944 945 // |contents_view_| should no longer have focus. 946 EXPECT_FALSE(contents_view->HasFocus()); 947 EXPECT_TRUE(bubble_delegate_view->HasFocus()); 948 949 bubble_delegate_view->GetWidget()->CloseNow(); 950 951 // Closing the bubble should result in focus going back to the contents view. 952 EXPECT_TRUE(contents_view->HasFocus()); 953 } 954 955 class TestBubbleDelegateView : public BubbleDelegateView { 956 public: 957 TestBubbleDelegateView(View* anchor) 958 : BubbleDelegateView(anchor, BubbleBorder::NONE), 959 reset_controls_called_(false) {} 960 virtual ~TestBubbleDelegateView() {} 961 962 virtual bool ShouldShowCloseButton() const OVERRIDE { 963 reset_controls_called_ = true; 964 return true; 965 } 966 967 mutable bool reset_controls_called_; 968 }; 969 970 TEST_F(WidgetTest, BubbleControlsResetOnInit) { 971 Widget* anchor = CreateTopLevelPlatformWidget(); 972 anchor->Show(); 973 974 TestBubbleDelegateView* bubble_delegate = 975 new TestBubbleDelegateView(anchor->client_view()); 976 Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); 977 EXPECT_TRUE(bubble_delegate->reset_controls_called_); 978 bubble_widget->Show(); 979 bubble_widget->CloseNow(); 980 981 anchor->Hide(); 982 anchor->CloseNow(); 983 } 984 985 // Desktop native widget Aura tests are for non Chrome OS platforms. 986 #if !defined(OS_CHROMEOS) 987 // Test to ensure that after minimize, view width is set to zero. 988 TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { 989 // Create a widget. 990 Widget widget; 991 Widget::InitParams init_params = 992 CreateParams(Widget::InitParams::TYPE_WINDOW); 993 init_params.show_state = ui::SHOW_STATE_NORMAL; 994 gfx::Rect initial_bounds(0, 0, 300, 400); 995 init_params.bounds = initial_bounds; 996 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 997 init_params.native_widget = new PlatformDesktopNativeWidget(&widget); 998 widget.Init(init_params); 999 NonClientView* non_client_view = widget.non_client_view(); 1000 NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); 1001 non_client_view->SetFrameView(frame_view); 1002 widget.Show(); 1003 widget.Minimize(); 1004 EXPECT_EQ(0, frame_view->width()); 1005 } 1006 1007 // This class validates whether paints are received for a visible Widget. 1008 // To achieve this it overrides the Show and Close methods on the Widget class 1009 // and sets state whether subsequent paints are expected. 1010 class DesktopAuraTestValidPaintWidget : public views::Widget { 1011 public: 1012 DesktopAuraTestValidPaintWidget() 1013 : expect_paint_(true), 1014 received_paint_while_hidden_(false) { 1015 } 1016 1017 virtual ~DesktopAuraTestValidPaintWidget() { 1018 } 1019 1020 virtual void Show() OVERRIDE { 1021 expect_paint_ = true; 1022 views::Widget::Show(); 1023 } 1024 1025 virtual void Close() OVERRIDE { 1026 expect_paint_ = false; 1027 views::Widget::Close(); 1028 } 1029 1030 void Hide() { 1031 expect_paint_ = false; 1032 views::Widget::Hide(); 1033 } 1034 1035 virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE { 1036 EXPECT_TRUE(expect_paint_); 1037 if (!expect_paint_) 1038 received_paint_while_hidden_ = true; 1039 views::Widget::OnNativeWidgetPaint(canvas); 1040 } 1041 1042 bool received_paint_while_hidden() const { 1043 return received_paint_while_hidden_; 1044 } 1045 1046 private: 1047 bool expect_paint_; 1048 bool received_paint_while_hidden_; 1049 }; 1050 1051 TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterCloseTest) { 1052 View* contents_view = new View; 1053 contents_view->SetFocusable(true); 1054 DesktopAuraTestValidPaintWidget widget; 1055 Widget::InitParams init_params = 1056 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1057 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1058 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1059 init_params.native_widget = new PlatformDesktopNativeWidget(&widget); 1060 widget.Init(init_params); 1061 widget.SetContentsView(contents_view); 1062 widget.Show(); 1063 widget.Activate(); 1064 RunPendingMessages(); 1065 widget.SchedulePaintInRect(init_params.bounds); 1066 widget.Close(); 1067 RunPendingMessages(); 1068 EXPECT_FALSE(widget.received_paint_while_hidden()); 1069 } 1070 1071 TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterHideTest) { 1072 View* contents_view = new View; 1073 contents_view->SetFocusable(true); 1074 DesktopAuraTestValidPaintWidget widget; 1075 Widget::InitParams init_params = 1076 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 1077 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1078 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1079 init_params.native_widget = new PlatformDesktopNativeWidget(&widget); 1080 widget.Init(init_params); 1081 widget.SetContentsView(contents_view); 1082 widget.Show(); 1083 widget.Activate(); 1084 RunPendingMessages(); 1085 widget.SchedulePaintInRect(init_params.bounds); 1086 widget.Hide(); 1087 RunPendingMessages(); 1088 EXPECT_FALSE(widget.received_paint_while_hidden()); 1089 widget.Close(); 1090 } 1091 1092 // Test to ensure that the aura Window's visiblity state is set to visible if 1093 // the underlying widget is hidden and then shown. 1094 TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { 1095 // Create a widget. 1096 Widget widget; 1097 Widget::InitParams init_params = 1098 CreateParams(Widget::InitParams::TYPE_WINDOW); 1099 init_params.show_state = ui::SHOW_STATE_NORMAL; 1100 gfx::Rect initial_bounds(0, 0, 300, 400); 1101 init_params.bounds = initial_bounds; 1102 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1103 init_params.native_widget = new PlatformDesktopNativeWidget(&widget); 1104 widget.Init(init_params); 1105 NonClientView* non_client_view = widget.non_client_view(); 1106 NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); 1107 non_client_view->SetFrameView(frame_view); 1108 1109 widget.Hide(); 1110 EXPECT_FALSE(IsNativeWindowVisible(widget.GetNativeWindow())); 1111 widget.Show(); 1112 EXPECT_TRUE(IsNativeWindowVisible(widget.GetNativeWindow())); 1113 } 1114 1115 // The following code verifies we can correctly destroy a Widget from a mouse 1116 // enter/exit. We could test move/drag/enter/exit but in general we don't run 1117 // nested message loops from such events, nor has the code ever really dealt 1118 // with this situation. 1119 1120 // Generates two moves (first generates enter, second real move), a press, drag 1121 // and release stopping at |last_event_type|. 1122 void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) { 1123 const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen()); 1124 ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(), 1125 screen_bounds.CenterPoint(), 0, 0); 1126 ui::EventProcessor* dispatcher = WidgetTest::GetEventProcessor(widget); 1127 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move_event); 1128 if (last_event_type == ui::ET_MOUSE_ENTERED || details.dispatcher_destroyed) 1129 return; 1130 details = dispatcher->OnEventFromSource(&move_event); 1131 if (last_event_type == ui::ET_MOUSE_MOVED || details.dispatcher_destroyed) 1132 return; 1133 1134 ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(), 1135 screen_bounds.CenterPoint(), 0, 0); 1136 details = dispatcher->OnEventFromSource(&press_event); 1137 if (last_event_type == ui::ET_MOUSE_PRESSED || details.dispatcher_destroyed) 1138 return; 1139 1140 gfx::Point end_point(screen_bounds.CenterPoint()); 1141 end_point.Offset(1, 1); 1142 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0, 0); 1143 details = dispatcher->OnEventFromSource(&drag_event); 1144 if (last_event_type == ui::ET_MOUSE_DRAGGED || details.dispatcher_destroyed) 1145 return; 1146 1147 ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0, 1148 0); 1149 details = dispatcher->OnEventFromSource(&release_event); 1150 if (details.dispatcher_destroyed) 1151 return; 1152 } 1153 1154 // Creates a widget and invokes GenerateMouseEvents() with |last_event_type|. 1155 void RunCloseWidgetDuringDispatchTest(WidgetTest* test, 1156 ui::EventType last_event_type) { 1157 // |widget| is deleted by CloseWidgetView. 1158 Widget* widget = new Widget; 1159 Widget::InitParams params = 1160 test->CreateParams(Widget::InitParams::TYPE_POPUP); 1161 params.native_widget = new PlatformDesktopNativeWidget(widget); 1162 params.bounds = gfx::Rect(0, 0, 50, 100); 1163 widget->Init(params); 1164 widget->SetContentsView(new CloseWidgetView(last_event_type)); 1165 widget->Show(); 1166 GenerateMouseEvents(widget, last_event_type); 1167 } 1168 1169 // Verifies deleting the widget from a mouse pressed event doesn't crash. 1170 TEST_F(WidgetTest, CloseWidgetDuringMousePress) { 1171 RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_PRESSED); 1172 } 1173 1174 // Verifies deleting the widget from a mouse released event doesn't crash. 1175 TEST_F(WidgetTest, CloseWidgetDuringMouseReleased) { 1176 RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED); 1177 } 1178 1179 #endif // !defined(OS_CHROMEOS) 1180 1181 // Tests that wheel events generated from scroll events are targetted to the 1182 // views under the cursor when the focused view does not processed them. 1183 TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) { 1184 EventCountView* cursor_view = new EventCountView; 1185 cursor_view->SetBounds(60, 0, 50, 40); 1186 1187 Widget* widget = CreateTopLevelPlatformWidget(); 1188 widget->GetRootView()->AddChildView(cursor_view); 1189 1190 // Generate a scroll event on the cursor view. 1191 ui::ScrollEvent scroll(ui::ET_SCROLL, 1192 gfx::Point(65, 5), 1193 ui::EventTimeForNow(), 1194 0, 1195 0, 20, 1196 0, 20, 1197 2); 1198 widget->OnScrollEvent(&scroll); 1199 1200 EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_SCROLL)); 1201 EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); 1202 1203 cursor_view->ResetCounts(); 1204 1205 ui::ScrollEvent scroll2(ui::ET_SCROLL, 1206 gfx::Point(5, 5), 1207 ui::EventTimeForNow(), 1208 0, 1209 0, 20, 1210 0, 20, 1211 2); 1212 widget->OnScrollEvent(&scroll2); 1213 1214 EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_SCROLL)); 1215 EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); 1216 1217 widget->CloseNow(); 1218 } 1219 1220 // Tests that if a scroll-begin gesture is not handled, then subsequent scroll 1221 // events are not dispatched to any view. 1222 TEST_F(WidgetTest, GestureScrollEventDispatching) { 1223 EventCountView* noscroll_view = new EventCountView; 1224 EventCountView* scroll_view = new ScrollableEventCountView; 1225 1226 noscroll_view->SetBounds(0, 0, 50, 40); 1227 scroll_view->SetBounds(60, 0, 40, 40); 1228 1229 Widget* widget = CreateTopLevelPlatformWidget(); 1230 widget->GetRootView()->AddChildView(noscroll_view); 1231 widget->GetRootView()->AddChildView(scroll_view); 1232 1233 { 1234 ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN, 1235 5, 5, 0, base::TimeDelta(), 1236 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), 1237 1); 1238 widget->OnGestureEvent(&begin); 1239 ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE, 1240 25, 15, 0, base::TimeDelta(), 1241 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), 1242 1); 1243 widget->OnGestureEvent(&update); 1244 ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END, 1245 25, 15, 0, base::TimeDelta(), 1246 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), 1247 1); 1248 widget->OnGestureEvent(&end); 1249 1250 EXPECT_EQ(1, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); 1251 EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); 1252 EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); 1253 } 1254 1255 { 1256 ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN, 1257 65, 5, 0, base::TimeDelta(), 1258 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), 1259 1); 1260 widget->OnGestureEvent(&begin); 1261 ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE, 1262 85, 15, 0, base::TimeDelta(), 1263 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), 1264 1); 1265 widget->OnGestureEvent(&update); 1266 ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END, 1267 85, 15, 0, base::TimeDelta(), 1268 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), 1269 1); 1270 widget->OnGestureEvent(&end); 1271 1272 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); 1273 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); 1274 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); 1275 } 1276 1277 widget->CloseNow(); 1278 } 1279 1280 // Tests that event-handlers installed on the RootView get triggered correctly. 1281 // TODO(tdanderson): Clean up this test as part of crbug.com/355680. 1282 TEST_F(WidgetTest, EventHandlersOnRootView) { 1283 Widget* widget = CreateTopLevelNativeWidget(); 1284 View* root_view = widget->GetRootView(); 1285 1286 scoped_ptr<EventCountView> view(new EventCountView()); 1287 view->set_owned_by_client(); 1288 view->SetBounds(0, 0, 20, 20); 1289 root_view->AddChildView(view.get()); 1290 1291 EventCountHandler h1; 1292 root_view->AddPreTargetHandler(&h1); 1293 1294 EventCountHandler h2; 1295 root_view->AddPostTargetHandler(&h2); 1296 1297 widget->SetBounds(gfx::Rect(0, 0, 100, 100)); 1298 widget->Show(); 1299 1300 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, 1301 5, 5, 0, ui::EventTimeForNow(), 1302 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); 1303 ui::GestureEvent end(ui::ET_GESTURE_END, 1304 5, 5, 0, ui::EventTimeForNow(), 1305 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); 1306 widget->OnGestureEvent(&begin); 1307 EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_BEGIN)); 1308 EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_BEGIN)); 1309 EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_BEGIN)); 1310 1311 widget->OnGestureEvent(&end); 1312 EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_END)); 1313 EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_END)); 1314 EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_END)); 1315 1316 ui::ScrollEvent scroll(ui::ET_SCROLL, 1317 gfx::Point(5, 5), 1318 ui::EventTimeForNow(), 1319 0, 1320 0, 20, 1321 0, 20, 1322 2); 1323 widget->OnScrollEvent(&scroll); 1324 EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL)); 1325 EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL)); 1326 EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL)); 1327 1328 // Unhandled scroll events are turned into wheel events and re-dispatched. 1329 EXPECT_EQ(1, h1.GetEventCount(ui::ET_MOUSEWHEEL)); 1330 EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSEWHEEL)); 1331 EXPECT_EQ(1, h2.GetEventCount(ui::ET_MOUSEWHEEL)); 1332 1333 h1.ResetCounts(); 1334 view->ResetCounts(); 1335 h2.ResetCounts(); 1336 1337 ui::ScrollEvent fling(ui::ET_SCROLL_FLING_START, 1338 gfx::Point(5, 5), 1339 ui::EventTimeForNow(), 1340 0, 1341 0, 20, 1342 0, 20, 1343 2); 1344 widget->OnScrollEvent(&fling); 1345 EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL_FLING_START)); 1346 EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL_FLING_START)); 1347 EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL_FLING_START)); 1348 1349 // Unhandled scroll events which are not of type ui::ET_SCROLL should not 1350 // be turned into wheel events and re-dispatched. 1351 EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL)); 1352 EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSEWHEEL)); 1353 EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL)); 1354 1355 h1.ResetCounts(); 1356 view->ResetCounts(); 1357 h2.ResetCounts(); 1358 1359 // Replace the child of |root_view| with a ScrollableEventCountView so that 1360 // ui::ET_SCROLL events are marked as handled at the target phase. 1361 root_view->RemoveChildView(view.get()); 1362 ScrollableEventCountView* scroll_view = new ScrollableEventCountView; 1363 scroll_view->SetBounds(0, 0, 20, 20); 1364 root_view->AddChildView(scroll_view); 1365 1366 ui::ScrollEvent consumed_scroll(ui::ET_SCROLL, 1367 gfx::Point(5, 5), 1368 ui::EventTimeForNow(), 1369 0, 1370 0, 20, 1371 0, 20, 1372 2); 1373 widget->OnScrollEvent(&consumed_scroll); 1374 1375 // The event is handled at the target phase and should not reach the 1376 // post-target handler. 1377 EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL)); 1378 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_SCROLL)); 1379 EXPECT_EQ(0, h2.GetEventCount(ui::ET_SCROLL)); 1380 1381 // Handled scroll events are not turned into wheel events and re-dispatched. 1382 EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL)); 1383 EXPECT_EQ(0, scroll_view->GetEventCount(ui::ET_MOUSEWHEEL)); 1384 EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL)); 1385 1386 widget->CloseNow(); 1387 } 1388 1389 TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { 1390 Widget* widget = CreateTopLevelNativeWidget(); 1391 View* root_view = widget->GetRootView(); 1392 1393 EventCountView* v1 = new EventCountView(); 1394 v1->SetBounds(0, 0, 10, 10); 1395 root_view->AddChildView(v1); 1396 EventCountView* v2 = new EventCountView(); 1397 v2->SetBounds(0, 10, 10, 10); 1398 root_view->AddChildView(v2); 1399 1400 gfx::Point cursor_location(5, 5); 1401 ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, 1402 ui::EF_NONE, ui::EF_NONE); 1403 widget->OnMouseEvent(&move); 1404 1405 EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); 1406 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1407 1408 delete v1; 1409 v2->SetBounds(0, 0, 10, 10); 1410 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1411 1412 widget->SynthesizeMouseMoveEvent(); 1413 EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); 1414 } 1415 1416 // Used by SingleWindowClosing to count number of times WindowClosing() has 1417 // been invoked. 1418 class ClosingDelegate : public WidgetDelegate { 1419 public: 1420 ClosingDelegate() : count_(0), widget_(NULL) {} 1421 1422 int count() const { return count_; } 1423 1424 void set_widget(views::Widget* widget) { widget_ = widget; } 1425 1426 // WidgetDelegate overrides: 1427 virtual Widget* GetWidget() OVERRIDE { return widget_; } 1428 virtual const Widget* GetWidget() const OVERRIDE { return widget_; } 1429 virtual void WindowClosing() OVERRIDE { 1430 count_++; 1431 } 1432 1433 private: 1434 int count_; 1435 views::Widget* widget_; 1436 1437 DISALLOW_COPY_AND_ASSIGN(ClosingDelegate); 1438 }; 1439 1440 // Verifies WindowClosing() is invoked correctly on the delegate when a Widget 1441 // is closed. 1442 TEST_F(WidgetTest, SingleWindowClosing) { 1443 scoped_ptr<ClosingDelegate> delegate(new ClosingDelegate()); 1444 Widget* widget = new Widget(); // Destroyed by CloseNow() below. 1445 Widget::InitParams init_params = 1446 CreateParams(Widget::InitParams::TYPE_WINDOW); 1447 init_params.bounds = gfx::Rect(0, 0, 200, 200); 1448 init_params.delegate = delegate.get(); 1449 #if !defined(OS_CHROMEOS) 1450 init_params.native_widget = new PlatformDesktopNativeWidget(widget); 1451 #endif 1452 widget->Init(init_params); 1453 EXPECT_EQ(0, delegate->count()); 1454 widget->CloseNow(); 1455 EXPECT_EQ(1, delegate->count()); 1456 } 1457 1458 class WidgetWindowTitleTest : public WidgetTest { 1459 protected: 1460 void RunTest(bool desktop_native_widget) { 1461 Widget* widget = new Widget(); // Destroyed by CloseNow() below. 1462 Widget::InitParams init_params = 1463 CreateParams(Widget::InitParams::TYPE_WINDOW); 1464 widget->Init(init_params); 1465 1466 #if !defined(OS_CHROMEOS) 1467 if (desktop_native_widget) 1468 init_params.native_widget = new PlatformDesktopNativeWidget(widget); 1469 #else 1470 DCHECK(!desktop_native_widget) 1471 << "DesktopNativeWidget does not exist on non-Aura or on ChromeOS."; 1472 #endif 1473 1474 internal::NativeWidgetPrivate* native_widget = 1475 widget->native_widget_private(); 1476 1477 base::string16 empty; 1478 base::string16 s1(base::UTF8ToUTF16("Title1")); 1479 base::string16 s2(base::UTF8ToUTF16("Title2")); 1480 base::string16 s3(base::UTF8ToUTF16("TitleLong")); 1481 1482 // The widget starts with no title, setting empty should not change 1483 // anything. 1484 EXPECT_FALSE(native_widget->SetWindowTitle(empty)); 1485 // Setting the title to something non-empty should cause a change. 1486 EXPECT_TRUE(native_widget->SetWindowTitle(s1)); 1487 // Setting the title to something else with the same length should cause a 1488 // change. 1489 EXPECT_TRUE(native_widget->SetWindowTitle(s2)); 1490 // Setting the title to something else with a different length should cause 1491 // a change. 1492 EXPECT_TRUE(native_widget->SetWindowTitle(s3)); 1493 // Setting the title to the same thing twice should not cause a change. 1494 EXPECT_FALSE(native_widget->SetWindowTitle(s3)); 1495 1496 widget->CloseNow(); 1497 } 1498 }; 1499 1500 TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_NativeWidget) { 1501 // Use the default NativeWidget. 1502 bool desktop_native_widget = false; 1503 RunTest(desktop_native_widget); 1504 } 1505 1506 // DesktopNativeWidget does not exist on non-Aura or on ChromeOS. 1507 #if !defined(OS_CHROMEOS) 1508 TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) { 1509 // Override to use a DesktopNativeWidget. 1510 bool desktop_native_widget = true; 1511 RunTest(desktop_native_widget); 1512 } 1513 #endif // !OS_CHROMEOS 1514 1515 TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { 1516 Widget* widget = new Widget; 1517 Widget::InitParams params = 1518 CreateParams(views::Widget::InitParams::TYPE_POPUP); 1519 widget->Init(params); 1520 1521 widget->SetContentsView(new CloseWidgetView(ui::ET_MOUSE_PRESSED)); 1522 1523 widget->SetSize(gfx::Size(100, 100)); 1524 widget->Show(); 1525 1526 aura::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); 1527 1528 WidgetDeletionObserver deletion_observer(widget); 1529 generator.ClickLeftButton(); 1530 EXPECT_FALSE(deletion_observer.IsWidgetAlive()); 1531 1532 // Yay we did not crash! 1533 } 1534 1535 TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) { 1536 Widget* widget = new Widget; 1537 Widget::InitParams params = 1538 CreateParams(views::Widget::InitParams::TYPE_POPUP); 1539 widget->Init(params); 1540 1541 widget->SetContentsView(new CloseWidgetView(ui::ET_GESTURE_TAP_DOWN)); 1542 1543 widget->SetSize(gfx::Size(100, 100)); 1544 widget->Show(); 1545 1546 aura::test::EventGenerator generator(GetContext()); 1547 1548 WidgetDeletionObserver deletion_observer(widget); 1549 generator.GestureTapAt(widget->GetWindowBoundsInScreen().CenterPoint()); 1550 EXPECT_FALSE(deletion_observer.IsWidgetAlive()); 1551 1552 // Yay we did not crash! 1553 } 1554 1555 // See description of RunGetNativeThemeFromDestructor() for details. 1556 class GetNativeThemeFromDestructorView : public WidgetDelegateView { 1557 public: 1558 GetNativeThemeFromDestructorView() {} 1559 virtual ~GetNativeThemeFromDestructorView() { 1560 VerifyNativeTheme(); 1561 } 1562 1563 virtual View* GetContentsView() OVERRIDE { 1564 return this; 1565 } 1566 1567 private: 1568 void VerifyNativeTheme() { 1569 ASSERT_TRUE(GetNativeTheme() != NULL); 1570 } 1571 1572 DISALLOW_COPY_AND_ASSIGN(GetNativeThemeFromDestructorView); 1573 }; 1574 1575 // Verifies GetNativeTheme() from the destructor of a WidgetDelegateView doesn't 1576 // crash. |is_first_run| is true if this is the first call. A return value of 1577 // true indicates this should be run again with a value of false. 1578 // First run uses DesktopNativeWidgetAura (if possible). Second run doesn't. 1579 bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params, 1580 bool is_first_run) { 1581 bool needs_second_run = false; 1582 // Destroyed by CloseNow() below. 1583 Widget* widget = new Widget; 1584 Widget::InitParams params(in_params); 1585 // Deletes itself when the Widget is destroyed. 1586 params.delegate = new GetNativeThemeFromDestructorView; 1587 #if !defined(OS_CHROMEOS) 1588 if (is_first_run) { 1589 params.native_widget = new PlatformDesktopNativeWidget(widget); 1590 needs_second_run = true; 1591 } 1592 #endif 1593 widget->Init(params); 1594 widget->CloseNow(); 1595 return needs_second_run; 1596 } 1597 1598 // See description of RunGetNativeThemeFromDestructor() for details. 1599 TEST_F(WidgetTest, GetNativeThemeFromDestructor) { 1600 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1601 if (RunGetNativeThemeFromDestructor(params, true)) 1602 RunGetNativeThemeFromDestructor(params, false); 1603 } 1604 1605 // Used by HideCloseDestroy. Allows setting a boolean when the widget is 1606 // destroyed. 1607 class CloseDestroysWidget : public Widget { 1608 public: 1609 explicit CloseDestroysWidget(bool* destroyed) 1610 : destroyed_(destroyed) { 1611 } 1612 1613 virtual ~CloseDestroysWidget() { 1614 if (destroyed_) { 1615 *destroyed_ = true; 1616 base::MessageLoop::current()->QuitNow(); 1617 } 1618 } 1619 1620 void Detach() { destroyed_ = NULL; } 1621 1622 private: 1623 // If non-null set to true from destructor. 1624 bool* destroyed_; 1625 1626 DISALLOW_COPY_AND_ASSIGN(CloseDestroysWidget); 1627 }; 1628 1629 // Verifies Close() results in destroying. 1630 TEST_F(WidgetTest, CloseDestroys) { 1631 bool destroyed = false; 1632 CloseDestroysWidget* widget = new CloseDestroysWidget(&destroyed); 1633 Widget::InitParams params = 1634 CreateParams(views::Widget::InitParams::TYPE_MENU); 1635 params.opacity = Widget::InitParams::OPAQUE_WINDOW; 1636 #if !defined(OS_CHROMEOS) 1637 params.native_widget = new PlatformDesktopNativeWidget(widget); 1638 #endif 1639 widget->Init(params); 1640 widget->Show(); 1641 widget->Hide(); 1642 widget->Close(); 1643 // Run the message loop as Close() asynchronously deletes. 1644 RunPendingMessages(); 1645 EXPECT_TRUE(destroyed); 1646 // Close() should destroy the widget. If not we'll cleanup to avoid leaks. 1647 if (!destroyed) { 1648 widget->Detach(); 1649 widget->CloseNow(); 1650 } 1651 } 1652 1653 // Tests that killing a widget while animating it does not crash. 1654 TEST_F(WidgetTest, CloseWidgetWhileAnimating) { 1655 scoped_ptr<Widget> widget(new Widget); 1656 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1657 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1658 widget->Init(params); 1659 1660 // Normal animations for tests have ZERO_DURATION, make sure we are actually 1661 // animating the movement. 1662 ui::ScopedAnimationDurationScaleMode animation_scale_mode( 1663 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 1664 ui::ScopedLayerAnimationSettings animation_settings( 1665 widget->GetLayer()->GetAnimator()); 1666 widget->Show(); 1667 // Animate the bounds change. 1668 widget->SetBounds(gfx::Rect(0, 0, 200, 200)); 1669 } 1670 1671 // A view that consumes mouse-pressed event and gesture-tap-down events. 1672 class RootViewTestView : public View { 1673 public: 1674 RootViewTestView(): View() {} 1675 1676 private: 1677 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 1678 return true; 1679 } 1680 1681 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 1682 if (event->type() == ui::ET_GESTURE_TAP_DOWN) 1683 event->SetHandled(); 1684 } 1685 }; 1686 1687 // Checks if RootView::*_handler_ fields are unset when widget is hidden. 1688 // Fails on chromium.webkit Windows bot, see crbug.com/264872. 1689 #if defined(OS_WIN) 1690 #define MAYBE_DisableTestRootViewHandlersWhenHidden\ 1691 DISABLED_TestRootViewHandlersWhenHidden 1692 #else 1693 #define MAYBE_DisableTestRootViewHandlersWhenHidden\ 1694 TestRootViewHandlersWhenHidden 1695 #endif 1696 TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) { 1697 Widget* widget = CreateTopLevelNativeWidget(); 1698 widget->SetBounds(gfx::Rect(0, 0, 300, 300)); 1699 View* view = new RootViewTestView(); 1700 view->SetBounds(0, 0, 300, 300); 1701 internal::RootView* root_view = 1702 static_cast<internal::RootView*>(widget->GetRootView()); 1703 root_view->AddChildView(view); 1704 1705 // Check RootView::mouse_pressed_handler_. 1706 widget->Show(); 1707 EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); 1708 gfx::Point click_location(45, 15); 1709 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 1710 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 1711 widget->OnMouseEvent(&press); 1712 EXPECT_EQ(view, GetMousePressedHandler(root_view)); 1713 widget->Hide(); 1714 EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); 1715 1716 // Check RootView::mouse_move_handler_. 1717 widget->Show(); 1718 EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); 1719 gfx::Point move_location(45, 15); 1720 ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0, 0); 1721 widget->OnMouseEvent(&move); 1722 EXPECT_EQ(view, GetMouseMoveHandler(root_view)); 1723 widget->Hide(); 1724 EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); 1725 1726 // Check RootView::gesture_handler_. 1727 widget->Show(); 1728 EXPECT_EQ(NULL, GetGestureHandler(root_view)); 1729 ui::GestureEvent tap_down( 1730 ui::ET_GESTURE_TAP_DOWN, 1731 15, 1732 15, 1733 0, 1734 base::TimeDelta(), 1735 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0), 1736 1); 1737 widget->OnGestureEvent(&tap_down); 1738 EXPECT_EQ(view, GetGestureHandler(root_view)); 1739 widget->Hide(); 1740 EXPECT_EQ(NULL, GetGestureHandler(root_view)); 1741 1742 widget->Close(); 1743 } 1744 1745 class GestureEndConsumerView : public View { 1746 private: 1747 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 1748 if (event->type() == ui::ET_GESTURE_END) 1749 event->SetHandled(); 1750 } 1751 }; 1752 1753 TEST_F(WidgetTest, GestureHandlerNotSetOnGestureEnd) { 1754 Widget* widget = CreateTopLevelNativeWidget(); 1755 widget->SetBounds(gfx::Rect(0, 0, 300, 300)); 1756 View* view = new GestureEndConsumerView(); 1757 view->SetBounds(0, 0, 300, 300); 1758 internal::RootView* root_view = 1759 static_cast<internal::RootView*>(widget->GetRootView()); 1760 root_view->AddChildView(view); 1761 1762 widget->Show(); 1763 EXPECT_EQ(NULL, GetGestureHandler(root_view)); 1764 ui::GestureEvent end(ui::ET_GESTURE_END, 15, 15, 0, base::TimeDelta(), 1765 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); 1766 widget->OnGestureEvent(&end); 1767 EXPECT_EQ(NULL, GetGestureHandler(root_view)); 1768 1769 widget->Close(); 1770 } 1771 1772 // Test the result of Widget::GetAllChildWidgets(). 1773 TEST_F(WidgetTest, GetAllChildWidgets) { 1774 // Create the following widget hierarchy: 1775 // 1776 // toplevel 1777 // +-- w1 1778 // +-- w11 1779 // +-- w2 1780 // +-- w21 1781 // +-- w22 1782 Widget* toplevel = CreateTopLevelPlatformWidget(); 1783 Widget* w1 = CreateChildPlatformWidget(toplevel->GetNativeView()); 1784 Widget* w11 = CreateChildPlatformWidget(w1->GetNativeView()); 1785 Widget* w2 = CreateChildPlatformWidget(toplevel->GetNativeView()); 1786 Widget* w21 = CreateChildPlatformWidget(w2->GetNativeView()); 1787 Widget* w22 = CreateChildPlatformWidget(w2->GetNativeView()); 1788 1789 std::set<Widget*> expected; 1790 expected.insert(toplevel); 1791 expected.insert(w1); 1792 expected.insert(w11); 1793 expected.insert(w2); 1794 expected.insert(w21); 1795 expected.insert(w22); 1796 1797 std::set<Widget*> widgets; 1798 Widget::GetAllChildWidgets(toplevel->GetNativeView(), &widgets); 1799 1800 EXPECT_EQ(expected.size(), widgets.size()); 1801 EXPECT_TRUE(std::equal(expected.begin(), expected.end(), widgets.begin())); 1802 } 1803 1804 // Used by DestroyChildWidgetsInOrder. On destruction adds the supplied name to 1805 // a vector. 1806 class DestroyedTrackingView : public View { 1807 public: 1808 DestroyedTrackingView(const std::string& name, 1809 std::vector<std::string>* add_to) 1810 : name_(name), 1811 add_to_(add_to) { 1812 } 1813 1814 virtual ~DestroyedTrackingView() { 1815 add_to_->push_back(name_); 1816 } 1817 1818 private: 1819 const std::string name_; 1820 std::vector<std::string>* add_to_; 1821 1822 DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingView); 1823 }; 1824 1825 class WidgetChildDestructionTest : public WidgetTest { 1826 public: 1827 WidgetChildDestructionTest() {} 1828 1829 // Creates a top level and a child, destroys the child and verifies the views 1830 // of the child are destroyed before the views of the parent. 1831 void RunDestroyChildWidgetsTest(bool top_level_has_desktop_native_widget_aura, 1832 bool child_has_desktop_native_widget_aura) { 1833 // When a View is destroyed its name is added here. 1834 std::vector<std::string> destroyed; 1835 1836 Widget* top_level = new Widget; 1837 Widget::InitParams params = 1838 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 1839 #if !defined(OS_CHROMEOS) 1840 if (top_level_has_desktop_native_widget_aura) 1841 params.native_widget = new PlatformDesktopNativeWidget(top_level); 1842 #endif 1843 top_level->Init(params); 1844 top_level->GetRootView()->AddChildView( 1845 new DestroyedTrackingView("parent", &destroyed)); 1846 top_level->Show(); 1847 1848 Widget* child = new Widget; 1849 Widget::InitParams child_params = 1850 CreateParams(views::Widget::InitParams::TYPE_POPUP); 1851 child_params.parent = top_level->GetNativeView(); 1852 #if !defined(OS_CHROMEOS) 1853 if (child_has_desktop_native_widget_aura) 1854 child_params.native_widget = new PlatformDesktopNativeWidget(child); 1855 #endif 1856 child->Init(child_params); 1857 child->GetRootView()->AddChildView( 1858 new DestroyedTrackingView("child", &destroyed)); 1859 child->Show(); 1860 1861 // Should trigger destruction of the child too. 1862 top_level->native_widget_private()->CloseNow(); 1863 1864 // Child should be destroyed first. 1865 ASSERT_EQ(2u, destroyed.size()); 1866 EXPECT_EQ("child", destroyed[0]); 1867 EXPECT_EQ("parent", destroyed[1]); 1868 } 1869 1870 private: 1871 DISALLOW_COPY_AND_ASSIGN(WidgetChildDestructionTest); 1872 }; 1873 1874 #if !defined(OS_CHROMEOS) 1875 // See description of RunDestroyChildWidgetsTest(). Parent uses 1876 // DesktopNativeWidgetAura. 1877 TEST_F(WidgetChildDestructionTest, 1878 DestroyChildWidgetsInOrderWithDesktopNativeWidget) { 1879 RunDestroyChildWidgetsTest(true, false); 1880 } 1881 1882 // See description of RunDestroyChildWidgetsTest(). Both parent and child use 1883 // DesktopNativeWidgetAura. 1884 TEST_F(WidgetChildDestructionTest, 1885 DestroyChildWidgetsInOrderWithDesktopNativeWidgetForBoth) { 1886 RunDestroyChildWidgetsTest(true, true); 1887 } 1888 #endif // !defined(OS_CHROMEOS) 1889 1890 // See description of RunDestroyChildWidgetsTest(). 1891 TEST_F(WidgetChildDestructionTest, DestroyChildWidgetsInOrder) { 1892 RunDestroyChildWidgetsTest(false, false); 1893 } 1894 1895 #if !defined(OS_CHROMEOS) 1896 // Provides functionality to create a window modal dialog. 1897 class ModalDialogDelegate : public DialogDelegateView { 1898 public: 1899 ModalDialogDelegate() {} 1900 virtual ~ModalDialogDelegate() {} 1901 1902 // WidgetDelegate overrides. 1903 virtual ui::ModalType GetModalType() const OVERRIDE { 1904 return ui::MODAL_TYPE_WINDOW; 1905 } 1906 1907 private: 1908 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); 1909 }; 1910 1911 // This test verifies that whether mouse events when a modal dialog is 1912 // displayed are eaten or recieved by the dialog. 1913 TEST_F(WidgetTest, WindowMouseModalityTest) { 1914 // Create a top level widget. 1915 Widget top_level_widget; 1916 Widget::InitParams init_params = 1917 CreateParams(Widget::InitParams::TYPE_WINDOW); 1918 init_params.show_state = ui::SHOW_STATE_NORMAL; 1919 gfx::Rect initial_bounds(0, 0, 500, 500); 1920 init_params.bounds = initial_bounds; 1921 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1922 init_params.native_widget = 1923 new PlatformDesktopNativeWidget(&top_level_widget); 1924 top_level_widget.Init(init_params); 1925 top_level_widget.Show(); 1926 EXPECT_TRUE(top_level_widget.IsVisible()); 1927 1928 // Create a view and validate that a mouse moves makes it to the view. 1929 EventCountView* widget_view = new EventCountView(); 1930 widget_view->SetBounds(0, 0, 10, 10); 1931 top_level_widget.GetRootView()->AddChildView(widget_view); 1932 1933 gfx::Point cursor_location_main(5, 5); 1934 ui::MouseEvent move_main(ui::ET_MOUSE_MOVED, 1935 cursor_location_main, 1936 cursor_location_main, 1937 ui::EF_NONE, 1938 ui::EF_NONE); 1939 ui::EventDispatchDetails details = 1940 GetEventProcessor(&top_level_widget)->OnEventFromSource(&move_main); 1941 ASSERT_FALSE(details.dispatcher_destroyed); 1942 1943 EXPECT_EQ(1, widget_view->GetEventCount(ui::ET_MOUSE_ENTERED)); 1944 widget_view->ResetCounts(); 1945 1946 // Create a modal dialog and validate that a mouse down message makes it to 1947 // the main view within the dialog. 1948 1949 // This instance will be destroyed when the dialog is destroyed. 1950 ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate; 1951 1952 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 1953 dialog_delegate, NULL, top_level_widget.GetNativeView()); 1954 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 1955 EventCountView* dialog_widget_view = new EventCountView(); 1956 dialog_widget_view->SetBounds(0, 0, 50, 50); 1957 modal_dialog_widget->GetRootView()->AddChildView(dialog_widget_view); 1958 modal_dialog_widget->Show(); 1959 EXPECT_TRUE(modal_dialog_widget->IsVisible()); 1960 1961 gfx::Point cursor_location_dialog(100, 100); 1962 ui::MouseEvent mouse_down_dialog(ui::ET_MOUSE_PRESSED, 1963 cursor_location_dialog, 1964 cursor_location_dialog, 1965 ui::EF_NONE, 1966 ui::EF_NONE); 1967 details = GetEventProcessor(&top_level_widget)->OnEventFromSource( 1968 &mouse_down_dialog); 1969 ASSERT_FALSE(details.dispatcher_destroyed); 1970 EXPECT_EQ(1, dialog_widget_view->GetEventCount(ui::ET_MOUSE_PRESSED)); 1971 1972 // Send a mouse move message to the main window. It should not be received by 1973 // the main window as the modal dialog is still active. 1974 gfx::Point cursor_location_main2(6, 6); 1975 ui::MouseEvent mouse_down_main(ui::ET_MOUSE_MOVED, 1976 cursor_location_main2, 1977 cursor_location_main2, 1978 ui::EF_NONE, 1979 ui::EF_NONE); 1980 details = GetEventProcessor(&top_level_widget)->OnEventFromSource( 1981 &mouse_down_main); 1982 ASSERT_FALSE(details.dispatcher_destroyed); 1983 EXPECT_EQ(0, widget_view->GetEventCount(ui::ET_MOUSE_MOVED)); 1984 1985 modal_dialog_widget->CloseNow(); 1986 top_level_widget.CloseNow(); 1987 } 1988 1989 // Verifies nativeview visbility matches that of Widget visibility when 1990 // SetFullscreen is invoked. 1991 TEST_F(WidgetTest, FullscreenStatePropagated) { 1992 Widget::InitParams init_params = 1993 CreateParams(Widget::InitParams::TYPE_WINDOW); 1994 init_params.show_state = ui::SHOW_STATE_NORMAL; 1995 init_params.bounds = gfx::Rect(0, 0, 500, 500); 1996 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1997 1998 { 1999 Widget top_level_widget; 2000 top_level_widget.Init(init_params); 2001 top_level_widget.SetFullscreen(true); 2002 EXPECT_EQ(top_level_widget.IsVisible(), 2003 IsNativeWindowVisible(top_level_widget.GetNativeWindow())); 2004 top_level_widget.CloseNow(); 2005 } 2006 2007 #if !defined(OS_CHROMEOS) 2008 { 2009 Widget top_level_widget; 2010 init_params.native_widget = 2011 new PlatformDesktopNativeWidget(&top_level_widget); 2012 top_level_widget.Init(init_params); 2013 top_level_widget.SetFullscreen(true); 2014 EXPECT_EQ(top_level_widget.IsVisible(), 2015 IsNativeWindowVisible(top_level_widget.GetNativeWindow())); 2016 top_level_widget.CloseNow(); 2017 } 2018 #endif 2019 } 2020 2021 #if defined(OS_WIN) 2022 2023 // Provides functionality to test widget activation via an activation flag 2024 // which can be set by an accessor. 2025 class ModalWindowTestWidgetDelegate : public WidgetDelegate { 2026 public: 2027 ModalWindowTestWidgetDelegate() 2028 : widget_(NULL), 2029 can_activate_(true) {} 2030 2031 virtual ~ModalWindowTestWidgetDelegate() {} 2032 2033 // Overridden from WidgetDelegate: 2034 virtual void DeleteDelegate() OVERRIDE { 2035 delete this; 2036 } 2037 virtual Widget* GetWidget() OVERRIDE { 2038 return widget_; 2039 } 2040 virtual const Widget* GetWidget() const OVERRIDE { 2041 return widget_; 2042 } 2043 virtual bool CanActivate() const OVERRIDE { 2044 return can_activate_; 2045 } 2046 virtual bool ShouldAdvanceFocusToTopLevelWidget() const OVERRIDE { 2047 return true; 2048 } 2049 2050 void set_can_activate(bool can_activate) { 2051 can_activate_ = can_activate; 2052 } 2053 2054 void set_widget(Widget* widget) { 2055 widget_ = widget; 2056 } 2057 2058 private: 2059 Widget* widget_; 2060 bool can_activate_; 2061 2062 DISALLOW_COPY_AND_ASSIGN(ModalWindowTestWidgetDelegate); 2063 }; 2064 2065 // Tests whether we can activate the top level widget when a modal dialog is 2066 // active. 2067 TEST_F(WidgetTest, WindowModalityActivationTest) { 2068 // Destroyed when the top level widget created below is destroyed. 2069 ModalWindowTestWidgetDelegate* widget_delegate = 2070 new ModalWindowTestWidgetDelegate; 2071 // Create a top level widget. 2072 Widget top_level_widget; 2073 Widget::InitParams init_params = 2074 CreateParams(Widget::InitParams::TYPE_WINDOW); 2075 init_params.show_state = ui::SHOW_STATE_NORMAL; 2076 gfx::Rect initial_bounds(0, 0, 500, 500); 2077 init_params.bounds = initial_bounds; 2078 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2079 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); 2080 init_params.delegate = widget_delegate; 2081 top_level_widget.Init(init_params); 2082 widget_delegate->set_widget(&top_level_widget); 2083 top_level_widget.Show(); 2084 EXPECT_TRUE(top_level_widget.IsVisible()); 2085 2086 HWND win32_window = views::HWNDForWidget(&top_level_widget); 2087 EXPECT_TRUE(::IsWindow(win32_window)); 2088 2089 // This instance will be destroyed when the dialog is destroyed. 2090 ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate; 2091 2092 // We should be able to activate the window even if the WidgetDelegate 2093 // says no, when a modal dialog is active. 2094 widget_delegate->set_can_activate(false); 2095 2096 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 2097 dialog_delegate, NULL, top_level_widget.GetNativeWindow()); 2098 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 2099 modal_dialog_widget->Show(); 2100 EXPECT_TRUE(modal_dialog_widget->IsVisible()); 2101 2102 LRESULT activate_result = ::SendMessage( 2103 win32_window, 2104 WM_MOUSEACTIVATE, 2105 reinterpret_cast<WPARAM>(win32_window), 2106 MAKELPARAM(WM_LBUTTONDOWN, HTCLIENT)); 2107 EXPECT_EQ(activate_result, MA_ACTIVATE); 2108 2109 modal_dialog_widget->CloseNow(); 2110 top_level_widget.CloseNow(); 2111 } 2112 #endif // defined(OS_WIN) 2113 #endif // !defined(OS_CHROMEOS) 2114 2115 TEST_F(WidgetTest, ShowCreatesActiveWindow) { 2116 Widget* widget = CreateTopLevelPlatformWidget(); 2117 2118 widget->Show(); 2119 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); 2120 2121 widget->CloseNow(); 2122 } 2123 2124 TEST_F(WidgetTest, ShowInactive) { 2125 Widget* widget = CreateTopLevelPlatformWidget(); 2126 2127 widget->ShowInactive(); 2128 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE); 2129 2130 widget->CloseNow(); 2131 } 2132 2133 TEST_F(WidgetTest, ShowInactiveAfterShow) { 2134 Widget* widget = CreateTopLevelPlatformWidget(); 2135 2136 widget->Show(); 2137 widget->ShowInactive(); 2138 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); 2139 2140 widget->CloseNow(); 2141 } 2142 2143 TEST_F(WidgetTest, ShowAfterShowInactive) { 2144 Widget* widget = CreateTopLevelPlatformWidget(); 2145 2146 widget->ShowInactive(); 2147 widget->Show(); 2148 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); 2149 2150 widget->CloseNow(); 2151 } 2152 2153 #if !defined(OS_CHROMEOS) 2154 TEST_F(WidgetTest, InactiveWidgetDoesNotGrabActivation) { 2155 Widget* widget = CreateTopLevelPlatformWidget(); 2156 widget->Show(); 2157 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); 2158 2159 Widget widget2; 2160 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 2161 params.native_widget = new PlatformDesktopNativeWidget(&widget2); 2162 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2163 widget2.Init(params); 2164 widget2.Show(); 2165 2166 EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE); 2167 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); 2168 2169 widget->CloseNow(); 2170 widget2.CloseNow(); 2171 } 2172 #endif // !defined(OS_CHROMEOS) 2173 2174 namespace { 2175 2176 class FullscreenAwareFrame : public views::NonClientFrameView { 2177 public: 2178 explicit FullscreenAwareFrame(views::Widget* widget) 2179 : widget_(widget), fullscreen_layout_called_(false) {} 2180 virtual ~FullscreenAwareFrame() {} 2181 2182 // views::NonClientFrameView overrides: 2183 virtual gfx::Rect GetBoundsForClientView() const OVERRIDE { 2184 return gfx::Rect(); 2185 } 2186 virtual gfx::Rect GetWindowBoundsForClientBounds( 2187 const gfx::Rect& client_bounds) const OVERRIDE { 2188 return gfx::Rect(); 2189 } 2190 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { 2191 return HTNOWHERE; 2192 } 2193 virtual void GetWindowMask(const gfx::Size& size, 2194 gfx::Path* window_mask) OVERRIDE {} 2195 virtual void ResetWindowControls() OVERRIDE {} 2196 virtual void UpdateWindowIcon() OVERRIDE {} 2197 virtual void UpdateWindowTitle() OVERRIDE {} 2198 2199 // views::View overrides: 2200 virtual void Layout() OVERRIDE { 2201 if (widget_->IsFullscreen()) 2202 fullscreen_layout_called_ = true; 2203 } 2204 2205 bool fullscreen_layout_called() const { return fullscreen_layout_called_; } 2206 2207 private: 2208 views::Widget* widget_; 2209 bool fullscreen_layout_called_; 2210 2211 DISALLOW_COPY_AND_ASSIGN(FullscreenAwareFrame); 2212 }; 2213 2214 } // namespace 2215 2216 // Tests that frame Layout is called when a widget goes fullscreen without 2217 // changing its size or title. 2218 TEST_F(WidgetTest, FullscreenFrameLayout) { 2219 Widget* widget = CreateTopLevelPlatformWidget(); 2220 FullscreenAwareFrame* frame = new FullscreenAwareFrame(widget); 2221 widget->non_client_view()->SetFrameView(frame); // Owns |frame|. 2222 2223 widget->Maximize(); 2224 RunPendingMessages(); 2225 2226 EXPECT_FALSE(frame->fullscreen_layout_called()); 2227 widget->SetFullscreen(true); 2228 widget->Show(); 2229 RunPendingMessages(); 2230 EXPECT_TRUE(frame->fullscreen_layout_called()); 2231 2232 widget->CloseNow(); 2233 } 2234 2235 #if !defined(OS_CHROMEOS) 2236 namespace { 2237 2238 // Trivial WidgetObserverTest that invokes Widget::IsActive() from 2239 // OnWindowDestroying. 2240 class IsActiveFromDestroyObserver : public WidgetObserver { 2241 public: 2242 IsActiveFromDestroyObserver() {} 2243 virtual ~IsActiveFromDestroyObserver() {} 2244 virtual void OnWidgetDestroying(Widget* widget) OVERRIDE { 2245 widget->IsActive(); 2246 } 2247 2248 private: 2249 DISALLOW_COPY_AND_ASSIGN(IsActiveFromDestroyObserver); 2250 }; 2251 2252 } // namespace 2253 2254 // Verifies Widget::IsActive() invoked from 2255 // WidgetObserver::OnWidgetDestroying() in a child widget doesn't crash. 2256 TEST_F(WidgetTest, IsActiveFromDestroy) { 2257 // Create two widgets, one a child of the other. 2258 IsActiveFromDestroyObserver observer; 2259 Widget parent_widget; 2260 Widget::InitParams parent_params = 2261 CreateParams(Widget::InitParams::TYPE_POPUP); 2262 parent_params.native_widget = new PlatformDesktopNativeWidget(&parent_widget); 2263 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2264 parent_widget.Init(parent_params); 2265 parent_widget.Show(); 2266 2267 Widget child_widget; 2268 Widget::InitParams child_params = 2269 CreateParams(Widget::InitParams::TYPE_POPUP); 2270 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2271 child_params.context = parent_widget.GetNativeView(); 2272 child_widget.Init(child_params); 2273 child_widget.AddObserver(&observer); 2274 child_widget.Show(); 2275 2276 parent_widget.CloseNow(); 2277 } 2278 #endif // !defined(OS_CHROMEOS) 2279 2280 } // namespace test 2281 } // namespace views 2282