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