Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(205)

Side by Side Diff: ui/views/controls/menu/menu_controller_unittest.cc

Issue 2778383002: MenuController Do Not SetSelection to Empty Items on Drag (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/menu/menu_controller.h" 5 #include "ui/views/controls/menu/menu_controller.h"
6 6
7 #include "base/callback.h" 7 #include "base/callback.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "base/single_thread_task_runner.h" 10 #include "base/single_thread_task_runner.h"
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 public: 271 public:
272 TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) { 272 TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) {
273 submenu_ = new SubmenuViewShown(this); 273 submenu_ = new SubmenuViewShown(this);
274 } 274 }
275 ~TestMenuItemViewShown() override {} 275 ~TestMenuItemViewShown() override {}
276 276
277 void SetController(MenuController* controller) { 277 void SetController(MenuController* controller) {
278 set_controller(controller); 278 set_controller(controller);
279 } 279 }
280 280
281 void AddEmptyMenusForTest() { AddEmptyMenus(); }
282
281 private: 283 private:
282 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown); 284 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown);
283 }; 285 };
284 286
285 class MenuControllerTest : public ViewsTestBase { 287 class MenuControllerTest : public ViewsTestBase {
286 public: 288 public:
287 MenuControllerTest() : menu_controller_(nullptr) { 289 MenuControllerTest() : menu_controller_(nullptr) {
288 } 290 }
289 291
290 ~MenuControllerTest() override {} 292 ~MenuControllerTest() override {}
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 // Now that the targeter has been destroyed, expect to exit the menu 331 // Now that the targeter has been destroyed, expect to exit the menu
330 // normally when hitting escape. 332 // normally when hitting escape.
331 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); 333 event_generator_->PressKey(ui::VKEY_ESCAPE, 0);
332 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type()); 334 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type());
333 } 335 }
334 336
335 // Verifies that a non-nested menu fully closes when receiving an escape key. 337 // Verifies that a non-nested menu fully closes when receiving an escape key.
336 void TestAsyncEscapeKey() { 338 void TestAsyncEscapeKey() {
337 ui::KeyEvent event(ui::EventType::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0); 339 ui::KeyEvent event(ui::EventType::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0);
338 menu_controller_->OnWillDispatchKeyEvent(&event); 340 menu_controller_->OnWillDispatchKeyEvent(&event);
339 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type());
340 } 341 }
341 342
342 #endif // defined(OS_LINUX) && defined(USE_X11) 343 #endif // defined(OS_LINUX) && defined(USE_X11)
343 344
344 #if defined(USE_AURA) 345 #if defined(USE_AURA)
345 // Verifies that an open menu receives a cancel event, and closes. 346 // Verifies that an open menu receives a cancel event, and closes.
346 void TestCancelEvent() { 347 void TestCancelEvent() {
347 EXPECT_EQ(MenuController::EXIT_NONE, menu_controller_->exit_type()); 348 EXPECT_EQ(MenuController::EXIT_NONE, menu_controller_->exit_type());
348 ui::CancelModeEvent cancel_event; 349 ui::CancelModeEvent cancel_event;
349 event_generator_->Dispatch(&cancel_event); 350 event_generator_->Dispatch(&cancel_event);
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
452 &MenuControllerTest::DestroyMenuController, base::Unretained(this))); 453 &MenuControllerTest::DestroyMenuController, base::Unretained(this)));
453 menu_controller_->ExitAsyncRun(); 454 menu_controller_->ExitAsyncRun();
454 } 455 }
455 456
456 protected: 457 protected:
457 void SetPendingStateItem(MenuItemView* item) { 458 void SetPendingStateItem(MenuItemView* item) {
458 menu_controller_->pending_state_.item = item; 459 menu_controller_->pending_state_.item = item;
459 menu_controller_->pending_state_.submenu_open = true; 460 menu_controller_->pending_state_.submenu_open = true;
460 } 461 }
461 462
463 void SetState(MenuItemView* item) {
464 menu_controller_->state_.item = item;
465 menu_controller_->state_.submenu_open = true;
466 }
467
462 void ResetSelection() { 468 void ResetSelection() {
463 menu_controller_->SetSelection( 469 menu_controller_->SetSelection(
464 nullptr, 470 nullptr,
465 MenuController::SELECTION_EXIT | 471 MenuController::SELECTION_EXIT |
466 MenuController::SELECTION_UPDATE_IMMEDIATELY); 472 MenuController::SELECTION_UPDATE_IMMEDIATELY);
467 } 473 }
468 474
469 void IncrementSelection() { 475 void IncrementSelection() {
470 menu_controller_->IncrementSelection( 476 menu_controller_->IncrementSelection(
471 MenuController::INCREMENT_SELECTION_DOWN); 477 MenuController::INCREMENT_SELECTION_DOWN);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
537 menu_controller_->set_is_combobox(is_combobox); 543 menu_controller_->set_is_combobox(is_combobox);
538 } 544 }
539 545
540 void SetSelectionOnPointerDown(SubmenuView* source, 546 void SetSelectionOnPointerDown(SubmenuView* source,
541 const ui::LocatedEvent* event) { 547 const ui::LocatedEvent* event) {
542 menu_controller_->SetSelectionOnPointerDown(source, event); 548 menu_controller_->SetSelectionOnPointerDown(source, event);
543 } 549 }
544 550
545 // Note that coordinates of events passed to MenuController must be in that of 551 // Note that coordinates of events passed to MenuController must be in that of
546 // the MenuScrollViewContainer. 552 // the MenuScrollViewContainer.
553 void ProcessMousePressed(SubmenuView* source, const ui::MouseEvent& event) {
554 menu_controller_->OnMousePressed(source, event);
555 }
556
557 void ProcessMouseDragged(SubmenuView* source, const ui::MouseEvent& event) {
558 menu_controller_->OnMouseDragged(source, event);
559 }
560
547 void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) { 561 void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) {
548 menu_controller_->OnMouseMoved(source, event); 562 menu_controller_->OnMouseMoved(source, event);
549 } 563 }
550 564
565 void ProcessMouseReleased(SubmenuView* source, const ui::MouseEvent& event) {
566 menu_controller_->OnMouseReleased(source, event);
567 }
568
551 void RunMenu() { 569 void RunMenu() {
552 #if defined(USE_AURA) 570 #if defined(USE_AURA)
553 std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler( 571 std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler(
554 new MenuPreTargetHandler(menu_controller_, owner_.get())); 572 new MenuPreTargetHandler(menu_controller_, owner_.get()));
555 #endif 573 #endif
556 574
557 menu_controller_->message_loop_depth_++; 575 menu_controller_->message_loop_depth_++;
558 menu_controller_->RunMessageLoop(); 576 menu_controller_->RunMessageLoop();
559 menu_controller_->message_loop_depth_--; 577 menu_controller_->message_loop_depth_--;
560 } 578 }
(...skipping 13 matching lines...) Expand all
574 void StartDrag() { 592 void StartDrag() {
575 const gfx::Point location; 593 const gfx::Point location;
576 menu_controller_->state_.item = menu_item()->GetSubmenu()->GetMenuItemAt(0); 594 menu_controller_->state_.item = menu_item()->GetSubmenu()->GetMenuItemAt(0);
577 menu_controller_->StartDrag( 595 menu_controller_->StartDrag(
578 menu_item()->GetSubmenu()->GetMenuItemAt(0)->CreateSubmenu(), location); 596 menu_item()->GetSubmenu()->GetMenuItemAt(0)->CreateSubmenu(), location);
579 } 597 }
580 598
581 Widget* owner() { return owner_.get(); } 599 Widget* owner() { return owner_.get(); }
582 ui::test::EventGenerator* event_generator() { return event_generator_.get(); } 600 ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
583 TestMenuItemViewShown* menu_item() { return menu_item_.get(); } 601 TestMenuItemViewShown* menu_item() { return menu_item_.get(); }
602 TestMenuDelegate* menu_delegate() { return menu_delegate_.get(); }
584 TestMenuControllerDelegate* menu_controller_delegate() { 603 TestMenuControllerDelegate* menu_controller_delegate() {
585 return menu_controller_delegate_.get(); 604 return menu_controller_delegate_.get();
586 } 605 }
587 MenuController* menu_controller() { return menu_controller_; } 606 MenuController* menu_controller() { return menu_controller_; }
588 const MenuItemView* pending_state_item() const { 607 const MenuItemView* pending_state_item() const {
589 return menu_controller_->pending_state_.item; 608 return menu_controller_->pending_state_.item;
590 } 609 }
591 MenuController::ExitType menu_exit_type() const { 610 MenuController::ExitType menu_exit_type() const {
592 return menu_controller_->exit_type_; 611 return menu_controller_->exit_type_;
593 } 612 }
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
668 menu_item_->SetController(menu_controller_); 687 menu_item_->SetController(menu_controller_);
669 } 688 }
670 689
671 // Not owned. 690 // Not owned.
672 DestructingTestViewsDelegate* test_views_delegate_; 691 DestructingTestViewsDelegate* test_views_delegate_;
673 692
674 std::unique_ptr<Widget> owner_; 693 std::unique_ptr<Widget> owner_;
675 std::unique_ptr<ui::test::EventGenerator> event_generator_; 694 std::unique_ptr<ui::test::EventGenerator> event_generator_;
676 std::unique_ptr<TestMenuItemViewShown> menu_item_; 695 std::unique_ptr<TestMenuItemViewShown> menu_item_;
677 std::unique_ptr<TestMenuControllerDelegate> menu_controller_delegate_; 696 std::unique_ptr<TestMenuControllerDelegate> menu_controller_delegate_;
678 std::unique_ptr<MenuDelegate> menu_delegate_; 697 std::unique_ptr<TestMenuDelegate> menu_delegate_;
679 MenuController* menu_controller_; 698 MenuController* menu_controller_;
680 TestMenuMessageLoop* test_message_loop_; 699 TestMenuMessageLoop* test_message_loop_;
681 700
682 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); 701 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
683 }; 702 };
684 703
685 #if defined(OS_LINUX) && defined(USE_X11) 704 #if defined(OS_LINUX) && defined(USE_X11)
686 // Tests that an event targeter which blocks events will be honored by the menu 705 // Tests that an event targeter which blocks events will be honored by the menu
687 // event dispatcher. 706 // event dispatcher.
688 TEST_F(MenuControllerTest, EventTargeter) { 707 TEST_F(MenuControllerTest, EventTargeter) {
689 base::ThreadTaskRunnerHandle::Get()->PostTask( 708 base::ThreadTaskRunnerHandle::Get()->PostTask(
690 FROM_HERE, base::Bind(&MenuControllerTest::TestEventTargeter, 709 FROM_HERE, base::Bind(&MenuControllerTest::TestEventTargeter,
691 base::Unretained(this))); 710 base::Unretained(this)));
692 RunMenu(); 711 RunMenu();
693 } 712 }
694 713
695 // Tests that an non-nested menu receiving an escape key will fully shut. This 714 // Tests that an non-nested menu receiving an escape key will fully shut. This
696 // should not crash by attempting to retarget the key to an inner menu. 715 // should not crash by attempting to retarget the key to an inner menu.
697 TEST_F(MenuControllerTest, AsyncEscapeKey) { 716 TEST_F(MenuControllerTest, AsyncEscapeKey) {
698 menu_controller()->SetAsyncRun(true); 717 menu_controller()->SetAsyncRun(true);
699 TestAsyncEscapeKey(); 718 TestAsyncEscapeKey();
719 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type());
700 } 720 }
701 721
702 #endif // defined(OS_LINUX) && defined(USE_X11) 722 #endif // defined(OS_LINUX) && defined(USE_X11)
703 723
704 #if defined(USE_X11) 724 #if defined(USE_X11)
705 // Tests that touch event ids are released correctly. See crbug.com/439051 for 725 // Tests that touch event ids are released correctly. See crbug.com/439051 for
706 // details. When the ids aren't managed correctly, we get stuck down touches. 726 // details. When the ids aren't managed correctly, we get stuck down touches.
707 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { 727 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) {
708 TestEventHandler test_event_handler; 728 TestEventHandler test_event_handler;
709 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( 729 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler(
(...skipping 853 matching lines...) Expand 10 before | Expand all | Expand 10 after
1563 controller->SetAsyncRun(true); 1583 controller->SetAsyncRun(true);
1564 1584
1565 int mouse_event_flags = 0; 1585 int mouse_event_flags = 0;
1566 MenuItemView* run_result = 1586 MenuItemView* run_result =
1567 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), 1587 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(),
1568 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); 1588 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags);
1569 EXPECT_EQ(run_result, nullptr); 1589 EXPECT_EQ(run_result, nullptr);
1570 TestDestroyedDuringViewsRelease(); 1590 TestDestroyedDuringViewsRelease();
1571 } 1591 }
1572 1592
1593 // Tests that when a context menu is opened above an empty menu item, and a
1594 // right-click occurs over the empty item, that the bottom menu is not hidden,
1595 // that a request to relaunch the context menu is received, and that
1596 // subsequently pressing ESC does not crash the browser.
1597 TEST_F(MenuControllerTest, RepostEventToEmptyMenuItem) {
1598 // Setup a submenu. Additionally hook up appropriate Widget and View
1599 // containers, with bounds, so that hit testing works.
1600 MenuController* controller = menu_controller();
1601 controller->SetAsyncRun(true);
1602 MenuItemView* base_menu = menu_item();
1603 base_menu->SetBounds(0, 0, 200, 200);
1604 SubmenuView* base_submenu = base_menu->GetSubmenu();
1605 base_submenu->SetBounds(0, 0, 200, 200);
1606 base_submenu->ShowAt(owner(), gfx::Rect(0, 0, 200, 200), false);
1607 GetMenuHost(base_submenu)
1608 ->SetContentsView(base_submenu->GetScrollViewContainer());
1609
1610 // Build the submenu to have an empty menu item. Additionally hook up
1611 // appropriate Widget and View containersm with counds, so that hit testing
1612 // works.
1613 std::unique_ptr<TestMenuDelegate> sub_menu_item_delegate =
1614 base::MakeUnique<TestMenuDelegate>();
1615 std::unique_ptr<TestMenuItemViewShown> sub_menu_item =
1616 base::MakeUnique<TestMenuItemViewShown>(sub_menu_item_delegate.get());
1617 sub_menu_item->AddEmptyMenusForTest();
1618 sub_menu_item->SetController(controller);
1619 sub_menu_item->SetBounds(0, 50, 50, 50);
1620 base_submenu->AddChildView(sub_menu_item.get());
1621 SubmenuView* sub_menu_view = sub_menu_item->GetSubmenu();
1622 sub_menu_view->SetBounds(0, 50, 50, 50);
1623 sub_menu_view->ShowAt(owner(), gfx::Rect(0, 50, 50, 50), false);
1624 GetMenuHost(sub_menu_view)
1625 ->SetContentsView(sub_menu_view->GetScrollViewContainer());
1626
1627 // Set that the last selection target was the item which launches the submenu,
1628 // as the empty item can never become a target.
1629 SetPendingStateItem(sub_menu_item.get());
1630
1631 // Nest a context menu.
1632 std::unique_ptr<TestMenuDelegate> nested_menu_delegate_1 =
1633 base::MakeUnique<TestMenuDelegate>();
1634 std::unique_ptr<TestMenuItemViewShown> nested_menu_item_1 =
1635 base::MakeUnique<TestMenuItemViewShown>(nested_menu_delegate_1.get());
1636 nested_menu_item_1->SetBounds(0, 0, 100, 100);
1637 nested_menu_item_1->SetController(controller);
1638 std::unique_ptr<TestMenuControllerDelegate> nested_controller_delegate_1 =
1639 base::MakeUnique<TestMenuControllerDelegate>();
1640 controller->AddNestedDelegate(nested_controller_delegate_1.get());
1641 int mouse_event_flags = 0;
1642 MenuItemView* run_result = controller->Run(
1643 owner(), nullptr, nested_menu_item_1.get(), gfx::Rect(150, 50, 100, 100),
1644 MENU_ANCHOR_TOPLEFT, true, false, &mouse_event_flags);
1645 EXPECT_EQ(run_result, nullptr);
1646
1647 // Press down outside of the context menu, and within the empty menu item.
1648 // This should close the first context menu.
1649 gfx::Point press_location(sub_menu_view->bounds().CenterPoint());
1650 ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, press_location,
1651 press_location, ui::EventTimeForNow(),
1652 ui::EF_RIGHT_MOUSE_BUTTON, 0);
1653 ProcessMousePressed(nested_menu_item_1->GetSubmenu(), press_event);
1654 EXPECT_EQ(nested_controller_delegate_1->on_menu_closed_called(), 1);
1655 EXPECT_EQ(menu_controller_delegate(), GetCurrentDelegate());
1656
1657 // While the current state is the menu item which launched the sub menu, cause
1658 // a drag in the empty menu item. This should not hide the menu.
1659 SetState(sub_menu_item.get());
1660 press_location.Offset(-5, 0);
1661 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, press_location,
1662 press_location, ui::EventTimeForNow(),
1663 ui::EF_RIGHT_MOUSE_BUTTON, 0);
1664 ProcessMouseDragged(sub_menu_view, drag_event);
1665 EXPECT_EQ(menu_delegate()->will_hide_menu_count(), 0);
1666
1667 // Release the mouse in the empty menu item, triggering a context menu
1668 // request.
1669 ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, press_location,
1670 press_location, ui::EventTimeForNow(),
1671 ui::EF_RIGHT_MOUSE_BUTTON, 0);
1672 ProcessMouseReleased(sub_menu_view, release_event);
1673 EXPECT_EQ(sub_menu_item_delegate->show_context_menu_count(), 1);
1674 EXPECT_EQ(sub_menu_item_delegate->show_context_menu_source(),
1675 sub_menu_item.get());
1676
1677 // Nest a context menu.
1678 std::unique_ptr<TestMenuDelegate> nested_menu_delegate_2 =
1679 base::MakeUnique<TestMenuDelegate>();
1680 std::unique_ptr<TestMenuItemViewShown> nested_menu_item_2 =
1681 base::MakeUnique<TestMenuItemViewShown>(nested_menu_delegate_2.get());
1682 nested_menu_item_2->SetBounds(0, 0, 100, 100);
1683 nested_menu_item_2->SetController(controller);
1684
1685 std::unique_ptr<TestMenuControllerDelegate> nested_controller_delegate_2 =
1686 base::MakeUnique<TestMenuControllerDelegate>();
1687 controller->AddNestedDelegate(nested_controller_delegate_2.get());
1688 run_result = controller->Run(
1689 owner(), nullptr, nested_menu_item_2.get(), gfx::Rect(150, 50, 100, 100),
1690 MENU_ANCHOR_TOPLEFT, true, false, &mouse_event_flags);
1691 EXPECT_EQ(run_result, nullptr);
1692
1693 // The escapce key should only close the nested menu. SelectByChar should not
1694 // crash.
1695 TestAsyncEscapeKey();
1696 EXPECT_EQ(nested_controller_delegate_2->on_menu_closed_called(), 1);
1697 EXPECT_EQ(menu_controller_delegate(), GetCurrentDelegate());
1698 }
1699
1573 #endif // defined(USE_AURA) 1700 #endif // defined(USE_AURA)
1574 1701
1575 } // namespace test 1702 } // namespace test
1576 } // namespace views 1703 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698