| 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" |
| 34 #include "ui/views/controls/menu/menu_key_event_handler.h" | 35 #include "ui/views/controls/menu/menu_pre_target_handler.h" |
| 35 #endif | 36 #endif |
| 36 | 37 |
| 37 #if defined(USE_X11) | 38 #if defined(USE_X11) |
| 38 #include <X11/Xlib.h> | 39 #include <X11/Xlib.h> |
| 39 #undef Bool | 40 #undef Bool |
| 40 #undef None | 41 #undef None |
| 41 #include "ui/events/test/events_test_utils_x11.h" | 42 #include "ui/events/test/events_test_utils_x11.h" |
| 42 #endif | 43 #endif |
| 43 | 44 |
| 44 namespace views { | 45 namespace views { |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 explicit TestMenuMessageLoop(std::unique_ptr<MenuMessageLoop> original); | 154 explicit TestMenuMessageLoop(std::unique_ptr<MenuMessageLoop> original); |
| 154 ~TestMenuMessageLoop() override; | 155 ~TestMenuMessageLoop() override; |
| 155 | 156 |
| 156 bool is_running() const { return is_running_; } | 157 bool is_running() const { return is_running_; } |
| 157 | 158 |
| 158 // MenuMessageLoop: | 159 // MenuMessageLoop: |
| 159 void QuitNow() override; | 160 void QuitNow() override; |
| 160 | 161 |
| 161 private: | 162 private: |
| 162 // MenuMessageLoop: | 163 // MenuMessageLoop: |
| 163 void Run(MenuController* controller, | 164 void Run() override; |
| 164 Widget* owner, | |
| 165 bool nested_menu) override; | |
| 166 void ClearOwner() override; | |
| 167 | 165 |
| 168 std::unique_ptr<MenuMessageLoop> original_; | 166 std::unique_ptr<MenuMessageLoop> original_; |
| 169 bool is_running_; | 167 bool is_running_; |
| 170 | 168 |
| 171 DISALLOW_COPY_AND_ASSIGN(TestMenuMessageLoop); | 169 DISALLOW_COPY_AND_ASSIGN(TestMenuMessageLoop); |
| 172 }; | 170 }; |
| 173 | 171 |
| 174 TestMenuMessageLoop::TestMenuMessageLoop( | 172 TestMenuMessageLoop::TestMenuMessageLoop( |
| 175 std::unique_ptr<MenuMessageLoop> original) | 173 std::unique_ptr<MenuMessageLoop> original) |
| 176 : original_(std::move(original)) { | 174 : original_(std::move(original)) { |
| 177 DCHECK(original_); | 175 DCHECK(original_); |
| 178 } | 176 } |
| 179 | 177 |
| 180 TestMenuMessageLoop::~TestMenuMessageLoop() {} | 178 TestMenuMessageLoop::~TestMenuMessageLoop() {} |
| 181 | 179 |
| 182 void TestMenuMessageLoop::Run(MenuController* controller, | 180 void TestMenuMessageLoop::Run() { |
| 183 Widget* owner, | |
| 184 bool nested_menu) { | |
| 185 is_running_ = true; | 181 is_running_ = true; |
| 186 original_->Run(controller, owner, nested_menu); | 182 original_->Run(); |
| 187 } | 183 } |
| 188 | 184 |
| 189 void TestMenuMessageLoop::QuitNow() { | 185 void TestMenuMessageLoop::QuitNow() { |
| 190 is_running_ = false; | 186 is_running_ = false; |
| 191 original_->QuitNow(); | 187 original_->QuitNow(); |
| 192 } | 188 } |
| 193 | 189 |
| 194 void TestMenuMessageLoop::ClearOwner() { | |
| 195 original_->ClearOwner(); | |
| 196 } | |
| 197 | |
| 198 } // namespace | 190 } // namespace |
| 199 | 191 |
| 200 class TestMenuItemViewShown : public MenuItemView { | 192 class TestMenuItemViewShown : public MenuItemView { |
| 201 public: | 193 public: |
| 202 TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) { | 194 TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) { |
| 203 submenu_ = new SubmenuViewShown(this); | 195 submenu_ = new SubmenuViewShown(this); |
| 204 } | 196 } |
| 205 ~TestMenuItemViewShown() override {} | 197 ~TestMenuItemViewShown() override {} |
| 206 | 198 |
| 207 void SetController(MenuController* controller) { | 199 void SetController(MenuController* controller) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 250 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); | 242 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
| 251 EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type()); | 243 EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type()); |
| 252 } | 244 } |
| 253 // Now that the targeter has been destroyed, expect to exit the menu | 245 // Now that the targeter has been destroyed, expect to exit the menu |
| 254 // normally when hitting escape. | 246 // normally when hitting escape. |
| 255 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); | 247 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
| 256 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); | 248 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); |
| 257 } | 249 } |
| 258 #endif // defined(OS_LINUX) && defined(USE_X11) | 250 #endif // defined(OS_LINUX) && defined(USE_X11) |
| 259 | 251 |
| 252 #if defined(USE_AURA) |
| 253 // Verifies that an open menu receives a cancel event, and closes. |
| 254 void TestCancelEvent() { |
| 255 EXPECT_EQ(MenuController::EXIT_NONE, menu_controller_->exit_type()); |
| 256 ui::CancelModeEvent cancel_event; |
| 257 event_generator_->Dispatch(&cancel_event); |
| 258 EXPECT_EQ(MenuController::EXIT_ALL, menu_controller_->exit_type()); |
| 259 } |
| 260 #endif // defined(USE_AURA) |
| 261 |
| 260 void TestAsynchronousNestedExitAll() { | 262 void TestAsynchronousNestedExitAll() { |
| 261 ASSERT_TRUE(test_message_loop_->is_running()); | 263 ASSERT_TRUE(test_message_loop_->is_running()); |
| 262 | 264 |
| 263 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( | 265 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( |
| 264 new TestMenuControllerDelegate()); | 266 new TestMenuControllerDelegate()); |
| 265 | 267 |
| 266 menu_controller()->AddNestedDelegate(nested_delegate.get()); | 268 menu_controller()->AddNestedDelegate(nested_delegate.get()); |
| 267 menu_controller()->SetAsyncRun(true); | 269 menu_controller()->SetAsyncRun(true); |
| 268 | 270 |
| 269 int mouse_event_flags = 0; | 271 int mouse_event_flags = 0; |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 } | 373 } |
| 372 | 374 |
| 373 internal::MenuControllerDelegate* GetCurrentDelegate() { | 375 internal::MenuControllerDelegate* GetCurrentDelegate() { |
| 374 return menu_controller_->delegate_; | 376 return menu_controller_->delegate_; |
| 375 } | 377 } |
| 376 | 378 |
| 377 bool IsAsyncRun() { return menu_controller_->async_run_; } | 379 bool IsAsyncRun() { return menu_controller_->async_run_; } |
| 378 | 380 |
| 379 bool IsShowing() { return menu_controller_->showing_; } | 381 bool IsShowing() { return menu_controller_->showing_; } |
| 380 | 382 |
| 383 MenuHost* GetMenuHost(SubmenuView* submenu) { return submenu->host_; } |
| 384 |
| 385 void MenuHostOnDragWillStart(MenuHost* host) { host->OnDragWillStart(); } |
| 386 |
| 387 void MenuHostOnDragComplete(MenuHost* host) { host->OnDragComplete(); } |
| 388 |
| 381 void SelectByChar(base::char16 character) { | 389 void SelectByChar(base::char16 character) { |
| 382 menu_controller_->SelectByChar(character); | 390 menu_controller_->SelectByChar(character); |
| 383 } | 391 } |
| 384 | 392 |
| 385 void SetDropMenuItem(MenuItemView* target, | 393 void SetDropMenuItem(MenuItemView* target, |
| 386 MenuDelegate::DropPosition position) { | 394 MenuDelegate::DropPosition position) { |
| 387 menu_controller_->SetDropMenuItem(target, position); | 395 menu_controller_->SetDropMenuItem(target, position); |
| 388 } | 396 } |
| 389 | 397 |
| 390 void SetIsCombobox(bool is_combobox) { | 398 void SetIsCombobox(bool is_combobox) { |
| 391 menu_controller_->set_is_combobox(is_combobox); | 399 menu_controller_->set_is_combobox(is_combobox); |
| 392 } | 400 } |
| 393 | 401 |
| 394 void SetSelectionOnPointerDown(SubmenuView* source, | 402 void SetSelectionOnPointerDown(SubmenuView* source, |
| 395 const ui::LocatedEvent* event) { | 403 const ui::LocatedEvent* event) { |
| 396 menu_controller_->SetSelectionOnPointerDown(source, event); | 404 menu_controller_->SetSelectionOnPointerDown(source, event); |
| 397 } | 405 } |
| 398 | 406 |
| 399 // Note that coordinates of events passed to MenuController must be in that of | 407 // Note that coordinates of events passed to MenuController must be in that of |
| 400 // the MenuScrollViewContainer. | 408 // the MenuScrollViewContainer. |
| 401 void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) { | 409 void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) { |
| 402 menu_controller_->OnMouseMoved(source, event); | 410 menu_controller_->OnMouseMoved(source, event); |
| 403 } | 411 } |
| 404 | 412 |
| 405 void RunMenu() { | 413 void RunMenu() { |
| 406 #if defined(USE_AURA) | 414 #if defined(USE_AURA) |
| 407 std::unique_ptr<MenuKeyEventHandler> key_event_handler( | 415 std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler( |
| 408 new MenuKeyEventHandler); | 416 new MenuPreTargetHandler(menu_controller_, owner_.get())); |
| 409 #endif | 417 #endif |
| 410 | 418 |
| 411 menu_controller_->message_loop_depth_++; | 419 menu_controller_->message_loop_depth_++; |
| 412 menu_controller_->RunMessageLoop(false); | 420 menu_controller_->RunMessageLoop(); |
| 413 menu_controller_->message_loop_depth_--; | 421 menu_controller_->message_loop_depth_--; |
| 414 } | 422 } |
| 415 | 423 |
| 416 void Accept(MenuItemView* item, int event_flags) { | 424 void Accept(MenuItemView* item, int event_flags) { |
| 417 menu_controller_->Accept(item, event_flags); | 425 menu_controller_->Accept(item, event_flags); |
| 418 } | 426 } |
| 419 | 427 |
| 420 void InstallTestMenuMessageLoop() { | 428 void InstallTestMenuMessageLoop() { |
| 421 test_message_loop_ = | 429 test_message_loop_ = |
| 422 new TestMenuMessageLoop(std::move(menu_controller_->message_loop_)); | 430 new TestMenuMessageLoop(std::move(menu_controller_->message_loop_)); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 444 for (int i = 0; i < 3; ++i) { | 452 for (int i = 0; i < 3; ++i) { |
| 445 LabelButton* button = | 453 LabelButton* button = |
| 446 new LabelButton(nullptr, base::ASCIIToUTF16("Label")); | 454 new LabelButton(nullptr, base::ASCIIToUTF16("Label")); |
| 447 // This is an in-menu button. Hence it must be always focusable. | 455 // This is an in-menu button. Hence it must be always focusable. |
| 448 button->SetFocusBehavior(View::FocusBehavior::ALWAYS); | 456 button->SetFocusBehavior(View::FocusBehavior::ALWAYS); |
| 449 item_view->AddChildView(button); | 457 item_view->AddChildView(button); |
| 450 } | 458 } |
| 451 menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); | 459 menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); |
| 452 } | 460 } |
| 453 | 461 |
| 462 void DestroyMenuItem() { menu_item_.reset(); } |
| 463 |
| 454 CustomButton* GetHotButton() { | 464 CustomButton* GetHotButton() { |
| 455 return menu_controller_->hot_button_; | 465 return menu_controller_->hot_button_; |
| 456 } | 466 } |
| 457 | 467 |
| 458 void SetHotTrackedButton(CustomButton* hot_button) { | 468 void SetHotTrackedButton(CustomButton* hot_button) { |
| 459 menu_controller_->SetHotTrackedButton(hot_button); | 469 menu_controller_->SetHotTrackedButton(hot_button); |
| 460 } | 470 } |
| 461 | 471 |
| 462 void ExitMenuRun() { | 472 void ExitMenuRun() { |
| 463 menu_controller_->SetExitType(MenuController::ExitType::EXIT_OUTERMOST); | 473 menu_controller_->SetExitType(MenuController::ExitType::EXIT_OUTERMOST); |
| (...skipping 548 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1012 | 1022 |
| 1013 EXPECT_FALSE(controller->drag_in_progress()); | 1023 EXPECT_FALSE(controller->drag_in_progress()); |
| 1014 TestMenuControllerDelegate* controller_delegate = menu_controller_delegate(); | 1024 TestMenuControllerDelegate* controller_delegate = menu_controller_delegate(); |
| 1015 EXPECT_EQ(1, controller_delegate->on_menu_closed_called()); | 1025 EXPECT_EQ(1, controller_delegate->on_menu_closed_called()); |
| 1016 EXPECT_EQ(nullptr, controller_delegate->on_menu_closed_menu()); | 1026 EXPECT_EQ(nullptr, controller_delegate->on_menu_closed_menu()); |
| 1017 EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, | 1027 EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, |
| 1018 controller_delegate->on_menu_closed_notify_type()); | 1028 controller_delegate->on_menu_closed_notify_type()); |
| 1019 EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); | 1029 EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); |
| 1020 } | 1030 } |
| 1021 | 1031 |
| 1032 // Tests that if a menu is destroyed while drag operations are occuring, that |
| 1033 // the MenuHost does not crash as the drag completes. |
| 1034 TEST_F(MenuControllerTest, AsynchronousDragHostDeleted) { |
| 1035 MenuController* controller = menu_controller(); |
| 1036 controller->SetAsyncRun(true); |
| 1037 |
| 1038 SubmenuView* submenu = menu_item()->GetSubmenu(); |
| 1039 submenu->ShowAt(owner(), menu_item()->bounds(), false); |
| 1040 MenuHost* host = GetMenuHost(submenu); |
| 1041 MenuHostOnDragWillStart(host); |
| 1042 submenu->Close(); |
| 1043 DestroyMenuItem(); |
| 1044 MenuHostOnDragComplete(host); |
| 1045 } |
| 1046 |
| 1022 // Tets that an asynchronous menu nested within an asynchronous menu closes both | 1047 // Tets that an asynchronous menu nested within an asynchronous menu closes both |
| 1023 // menus, and notifies both delegates. | 1048 // menus, and notifies both delegates. |
| 1024 TEST_F(MenuControllerTest, DoubleAsynchronousNested) { | 1049 TEST_F(MenuControllerTest, DoubleAsynchronousNested) { |
| 1025 MenuController* controller = menu_controller(); | 1050 MenuController* controller = menu_controller(); |
| 1026 TestMenuControllerDelegate* delegate = menu_controller_delegate(); | 1051 TestMenuControllerDelegate* delegate = menu_controller_delegate(); |
| 1027 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( | 1052 std::unique_ptr<TestMenuControllerDelegate> nested_delegate( |
| 1028 new TestMenuControllerDelegate()); | 1053 new TestMenuControllerDelegate()); |
| 1029 | 1054 |
| 1030 ASSERT_FALSE(IsAsyncRun()); | 1055 ASSERT_FALSE(IsAsyncRun()); |
| 1031 // Sets the run created in SetUp | 1056 // Sets the run created in SetUp |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1244 | 1269 |
| 1245 int result_event_flags = 0; | 1270 int result_event_flags = 0; |
| 1246 // This creates a nested message loop. | 1271 // This creates a nested message loop. |
| 1247 EXPECT_EQ(nullptr, menu_controller()->Run(owner(), nullptr, menu_item(), | 1272 EXPECT_EQ(nullptr, menu_controller()->Run(owner(), nullptr, menu_item(), |
| 1248 gfx::Rect(), MENU_ANCHOR_TOPLEFT, | 1273 gfx::Rect(), MENU_ANCHOR_TOPLEFT, |
| 1249 false, false, &result_event_flags)); | 1274 false, false, &result_event_flags)); |
| 1250 EXPECT_FALSE(menu_controller_delegate()->on_menu_closed_called()); | 1275 EXPECT_FALSE(menu_controller_delegate()->on_menu_closed_called()); |
| 1251 EXPECT_TRUE(nested_delegate->on_menu_closed_called()); | 1276 EXPECT_TRUE(nested_delegate->on_menu_closed_called()); |
| 1252 } | 1277 } |
| 1253 | 1278 |
| 1279 #if defined(USE_AURA) |
| 1280 // Tests that when a synchronous menu receives a cancel event, that it closes. |
| 1281 TEST_F(MenuControllerTest, SynchronousCancelEvent) { |
| 1282 ExitMenuRun(); |
| 1283 // Post actual test to run once the menu has created a nested message loop. |
| 1284 base::MessageLoopForUI::current()->PostTask( |
| 1285 FROM_HERE, |
| 1286 base::Bind(&MenuControllerTest::TestCancelEvent, base::Unretained(this))); |
| 1287 int mouse_event_flags = 0; |
| 1288 MenuItemView* run_result = menu_controller()->Run( |
| 1289 owner(), nullptr, menu_item(), gfx::Rect(), MENU_ANCHOR_TOPLEFT, false, |
| 1290 false, &mouse_event_flags); |
| 1291 EXPECT_EQ(run_result, nullptr); |
| 1292 } |
| 1293 |
| 1294 // Tests that when an asynchronous menu receives a cancel event, that it closes. |
| 1295 TEST_F(MenuControllerTest, AsynchronousCancelEvent) { |
| 1296 ExitMenuRun(); |
| 1297 MenuController* controller = menu_controller(); |
| 1298 controller->SetAsyncRun(true); |
| 1299 |
| 1300 int mouse_event_flags = 0; |
| 1301 MenuItemView* run_result = |
| 1302 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), |
| 1303 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); |
| 1304 EXPECT_EQ(run_result, nullptr); |
| 1305 TestCancelEvent(); |
| 1306 } |
| 1307 #endif // defined(USE_AURA) |
| 1308 |
| 1254 } // namespace test | 1309 } // namespace test |
| 1255 } // namespace views | 1310 } // namespace views |
| OLD | NEW |