| OLD | NEW |
| 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/macros.h" | 8 #include "base/macros.h" |
| 9 #include "base/single_thread_task_runner.h" | 9 #include "base/single_thread_task_runner.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "build/build_config.h" | 11 #include "build/build_config.h" |
| 12 #include "ui/aura/scoped_window_targeter.h" | 12 #include "ui/aura/scoped_window_targeter.h" |
| 13 #include "ui/aura/window.h" | 13 #include "ui/aura/window.h" |
| 14 #include "ui/events/event.h" | 14 #include "ui/events/event.h" |
| 15 #include "ui/events/event_constants.h" | 15 #include "ui/events/event_constants.h" |
| 16 #include "ui/events/event_handler.h" | 16 #include "ui/events/event_handler.h" |
| 17 #include "ui/events/event_utils.h" | 17 #include "ui/events/event_utils.h" |
| 18 #include "ui/events/null_event_targeter.h" | 18 #include "ui/events/null_event_targeter.h" |
| 19 #include "ui/events/test/event_generator.h" | 19 #include "ui/events/test/event_generator.h" |
| 20 #include "ui/gfx/geometry/point.h" | 20 #include "ui/gfx/geometry/point.h" |
| 21 #include "ui/gfx/geometry/rect.h" | 21 #include "ui/gfx/geometry/rect.h" |
| 22 #include "ui/views/controls/menu/menu_controller_delegate.h" | 22 #include "ui/views/controls/menu/menu_controller_delegate.h" |
| 23 #include "ui/views/controls/menu/menu_delegate.h" | 23 #include "ui/views/controls/menu/menu_delegate.h" |
| 24 #include "ui/views/controls/menu/menu_host.h" |
| 24 #include "ui/views/controls/menu/menu_item_view.h" | 25 #include "ui/views/controls/menu/menu_item_view.h" |
| 25 #include "ui/views/controls/menu/menu_message_loop.h" | 26 #include "ui/views/controls/menu/menu_message_loop.h" |
| 26 #include "ui/views/controls/menu/menu_scroll_view_container.h" | 27 #include "ui/views/controls/menu/menu_scroll_view_container.h" |
| 27 #include "ui/views/controls/menu/submenu_view.h" | 28 #include "ui/views/controls/menu/submenu_view.h" |
| 28 #include "ui/views/test/menu_test_utils.h" | 29 #include "ui/views/test/menu_test_utils.h" |
| 29 #include "ui/views/test/views_test_base.h" | 30 #include "ui/views/test/views_test_base.h" |
| 30 | 31 |
| 31 #if defined(USE_AURA) | 32 #if defined(USE_AURA) |
| 32 #include "ui/aura/scoped_window_targeter.h" | 33 #include "ui/aura/scoped_window_targeter.h" |
| 33 #include "ui/aura/window.h" | 34 #include "ui/aura/window.h" |
| (...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 } | 372 } |
| 372 | 373 |
| 373 internal::MenuControllerDelegate* GetCurrentDelegate() { | 374 internal::MenuControllerDelegate* GetCurrentDelegate() { |
| 374 return menu_controller_->delegate_; | 375 return menu_controller_->delegate_; |
| 375 } | 376 } |
| 376 | 377 |
| 377 bool IsAsyncRun() { return menu_controller_->async_run_; } | 378 bool IsAsyncRun() { return menu_controller_->async_run_; } |
| 378 | 379 |
| 379 bool IsShowing() { return menu_controller_->showing_; } | 380 bool IsShowing() { return menu_controller_->showing_; } |
| 380 | 381 |
| 382 MenuHost* GetMenuHost(SubmenuView* submenu) { return submenu->host_; } |
| 383 |
| 384 void MenuHostOnDragWillStart(MenuHost* host) { host->OnDragWillStart(); } |
| 385 |
| 386 void MenuHostOnDragComplete(MenuHost* host) { host->OnDragComplete(); } |
| 387 |
| 381 void SelectByChar(base::char16 character) { | 388 void SelectByChar(base::char16 character) { |
| 382 menu_controller_->SelectByChar(character); | 389 menu_controller_->SelectByChar(character); |
| 383 } | 390 } |
| 384 | 391 |
| 385 void SetDropMenuItem(MenuItemView* target, | 392 void SetDropMenuItem(MenuItemView* target, |
| 386 MenuDelegate::DropPosition position) { | 393 MenuDelegate::DropPosition position) { |
| 387 menu_controller_->SetDropMenuItem(target, position); | 394 menu_controller_->SetDropMenuItem(target, position); |
| 388 } | 395 } |
| 389 | 396 |
| 390 void SetIsCombobox(bool is_combobox) { | 397 void SetIsCombobox(bool is_combobox) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 for (int i = 0; i < 3; ++i) { | 451 for (int i = 0; i < 3; ++i) { |
| 445 LabelButton* button = | 452 LabelButton* button = |
| 446 new LabelButton(nullptr, base::ASCIIToUTF16("Label")); | 453 new LabelButton(nullptr, base::ASCIIToUTF16("Label")); |
| 447 // This is an in-menu button. Hence it must be always focusable. | 454 // This is an in-menu button. Hence it must be always focusable. |
| 448 button->SetFocusBehavior(View::FocusBehavior::ALWAYS); | 455 button->SetFocusBehavior(View::FocusBehavior::ALWAYS); |
| 449 item_view->AddChildView(button); | 456 item_view->AddChildView(button); |
| 450 } | 457 } |
| 451 menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); | 458 menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); |
| 452 } | 459 } |
| 453 | 460 |
| 461 void DestroyMenuItem() { menu_item_.reset(); } |
| 462 |
| 454 CustomButton* GetHotButton() { | 463 CustomButton* GetHotButton() { |
| 455 return menu_controller_->hot_button_; | 464 return menu_controller_->hot_button_; |
| 456 } | 465 } |
| 457 | 466 |
| 458 void SetHotTrackedButton(CustomButton* hot_button) { | 467 void SetHotTrackedButton(CustomButton* hot_button) { |
| 459 menu_controller_->SetHotTrackedButton(hot_button); | 468 menu_controller_->SetHotTrackedButton(hot_button); |
| 460 } | 469 } |
| 461 | 470 |
| 462 void ExitMenuRun() { | 471 void ExitMenuRun() { |
| 463 menu_controller_->SetExitType(MenuController::ExitType::EXIT_OUTERMOST); | 472 menu_controller_->SetExitType(MenuController::ExitType::EXIT_OUTERMOST); |
| 464 menu_controller_->ExitMenuRun(); | 473 menu_controller_->ExitMenuRun(); |
| 465 } | 474 } |
| 466 | 475 |
| 467 private: | |
| 468 void DestroyMenuController() { | 476 void DestroyMenuController() { |
| 469 if (!menu_controller_) | 477 if (!menu_controller_) |
| 470 return; | 478 return; |
| 471 | 479 |
| 472 if (!owner_->IsClosed()) | 480 if (!owner_->IsClosed()) |
| 473 owner_->RemoveObserver(menu_controller_); | 481 owner_->RemoveObserver(menu_controller_); |
| 474 | 482 |
| 475 menu_controller_->showing_ = false; | 483 menu_controller_->showing_ = false; |
| 476 menu_controller_->owner_ = nullptr; | 484 menu_controller_->owner_ = nullptr; |
| 477 delete menu_controller_; | 485 delete menu_controller_; |
| 478 menu_controller_ = nullptr; | 486 menu_controller_ = nullptr; |
| 479 } | 487 } |
| 480 | 488 |
| 489 private: |
| 481 void Init() { | 490 void Init() { |
| 482 owner_.reset(new Widget); | 491 owner_.reset(new Widget); |
| 483 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 492 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
| 484 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 493 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 485 owner_->Init(params); | 494 owner_->Init(params); |
| 486 event_generator_.reset( | 495 event_generator_.reset( |
| 487 new ui::test::EventGenerator(owner_->GetNativeWindow())); | 496 new ui::test::EventGenerator(owner_->GetNativeWindow())); |
| 488 owner_->Show(); | 497 owner_->Show(); |
| 489 | 498 |
| 490 SetupMenuItem(); | 499 SetupMenuItem(); |
| (...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 936 | 945 |
| 937 controller->CancelAll(); | 946 controller->CancelAll(); |
| 938 EXPECT_EQ(1, delegate->on_menu_closed_called()); | 947 EXPECT_EQ(1, delegate->on_menu_closed_called()); |
| 939 EXPECT_EQ(nullptr, delegate->on_menu_closed_menu()); | 948 EXPECT_EQ(nullptr, delegate->on_menu_closed_menu()); |
| 940 EXPECT_EQ(0, delegate->on_menu_closed_mouse_event_flags()); | 949 EXPECT_EQ(0, delegate->on_menu_closed_mouse_event_flags()); |
| 941 EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, | 950 EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, |
| 942 delegate->on_menu_closed_notify_type()); | 951 delegate->on_menu_closed_notify_type()); |
| 943 EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); | 952 EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); |
| 944 } | 953 } |
| 945 | 954 |
| 955 // Tests that a when a MenuController has been destroyed, that a menu attempting |
| 956 // to cancel does not crash. |
| 957 TEST_F(MenuControllerTest, AsynchronousCancelAfterDeletion) { |
| 958 MenuController* controller = menu_controller(); |
| 959 controller->SetAsyncRun(true); |
| 960 |
| 961 int mouse_event_flags = 0; |
| 962 MenuItemView* run_result = |
| 963 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), |
| 964 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); |
| 965 EXPECT_EQ(run_result, nullptr); |
| 966 |
| 967 DestroyMenuController(); |
| 968 menu_item()->Cancel(); |
| 969 } |
| 970 |
| 946 // Tests that an asynchrnous menu nested within a synchronous menu restores the | 971 // Tests that an asynchrnous menu nested within a synchronous menu restores the |
| 947 // previous MenuControllerDelegate and synchronous settings. | 972 // previous MenuControllerDelegate and synchronous settings. |
| 948 TEST_F(MenuControllerTest, AsynchronousNestedDelegate) { | 973 TEST_F(MenuControllerTest, AsynchronousNestedDelegate) { |
| 949 MenuController* controller = menu_controller(); | 974 MenuController* controller = menu_controller(); |
| 950 TestMenuControllerDelegate* delegate = menu_controller_delegate(); | 975 TestMenuControllerDelegate* delegate = menu_controller_delegate(); |
| 951 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( | 976 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( |
| 952 new TestMenuControllerDelegate()); | 977 new TestMenuControllerDelegate()); |
| 953 | 978 |
| 954 ASSERT_FALSE(IsAsyncRun()); | 979 ASSERT_FALSE(IsAsyncRun()); |
| 955 controller->AddNestedDelegate(nested_delegate.get()); | 980 controller->AddNestedDelegate(nested_delegate.get()); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1012 | 1037 |
| 1013 EXPECT_FALSE(controller->drag_in_progress()); | 1038 EXPECT_FALSE(controller->drag_in_progress()); |
| 1014 TestMenuControllerDelegate* controller_delegate = menu_controller_delegate(); | 1039 TestMenuControllerDelegate* controller_delegate = menu_controller_delegate(); |
| 1015 EXPECT_EQ(1, controller_delegate->on_menu_closed_called()); | 1040 EXPECT_EQ(1, controller_delegate->on_menu_closed_called()); |
| 1016 EXPECT_EQ(nullptr, controller_delegate->on_menu_closed_menu()); | 1041 EXPECT_EQ(nullptr, controller_delegate->on_menu_closed_menu()); |
| 1017 EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, | 1042 EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, |
| 1018 controller_delegate->on_menu_closed_notify_type()); | 1043 controller_delegate->on_menu_closed_notify_type()); |
| 1019 EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); | 1044 EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); |
| 1020 } | 1045 } |
| 1021 | 1046 |
| 1047 // Tests that if a menu is destroyed while drag operations are occuring, that |
| 1048 // the MenuHost does not crash as the drag completes. |
| 1049 TEST_F(MenuControllerTest, AsynchronousDragHostDeleted) { |
| 1050 MenuController* controller = menu_controller(); |
| 1051 controller->SetAsyncRun(true); |
| 1052 |
| 1053 SubmenuView* submenu = menu_item()->GetSubmenu(); |
| 1054 submenu->ShowAt(owner(), menu_item()->bounds(), false); |
| 1055 MenuHost* host = GetMenuHost(submenu); |
| 1056 MenuHostOnDragWillStart(host); |
| 1057 submenu->Close(); |
| 1058 DestroyMenuItem(); |
| 1059 MenuHostOnDragComplete(host); |
| 1060 } |
| 1061 |
| 1022 // Tets that an asynchronous menu nested within an asynchronous menu closes both | 1062 // Tets that an asynchronous menu nested within an asynchronous menu closes both |
| 1023 // menus, and notifies both delegates. | 1063 // menus, and notifies both delegates. |
| 1024 TEST_F(MenuControllerTest, DoubleAsynchronousNested) { | 1064 TEST_F(MenuControllerTest, DoubleAsynchronousNested) { |
| 1025 MenuController* controller = menu_controller(); | 1065 MenuController* controller = menu_controller(); |
| 1026 TestMenuControllerDelegate* delegate = menu_controller_delegate(); | 1066 TestMenuControllerDelegate* delegate = menu_controller_delegate(); |
| 1027 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( | 1067 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( |
| 1028 new TestMenuControllerDelegate()); | 1068 new TestMenuControllerDelegate()); |
| 1029 | 1069 |
| 1030 ASSERT_FALSE(IsAsyncRun()); | 1070 ASSERT_FALSE(IsAsyncRun()); |
| 1031 // Sets the run created in SetUp | 1071 // Sets the run created in SetUp |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1246 // This creates a nested message loop. | 1286 // This creates a nested message loop. |
| 1247 EXPECT_EQ(nullptr, menu_controller()->Run(owner(), nullptr, menu_item(), | 1287 EXPECT_EQ(nullptr, menu_controller()->Run(owner(), nullptr, menu_item(), |
| 1248 gfx::Rect(), MENU_ANCHOR_TOPLEFT, | 1288 gfx::Rect(), MENU_ANCHOR_TOPLEFT, |
| 1249 false, false, &result_event_flags)); | 1289 false, false, &result_event_flags)); |
| 1250 EXPECT_FALSE(menu_controller_delegate()->on_menu_closed_called()); | 1290 EXPECT_FALSE(menu_controller_delegate()->on_menu_closed_called()); |
| 1251 EXPECT_TRUE(nested_delegate->on_menu_closed_called()); | 1291 EXPECT_TRUE(nested_delegate->on_menu_closed_called()); |
| 1252 } | 1292 } |
| 1253 | 1293 |
| 1254 } // namespace test | 1294 } // namespace test |
| 1255 } // namespace views | 1295 } // namespace views |
| OLD | NEW |