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

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

Issue 2654093005: Fix MenuRunner Releasing (Closed)
Patch Set: Clarification and renaming Created 3 years, 10 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
« no previous file with comments | « ui/views/controls/menu/menu_controller.h ('k') | ui/views/controls/menu/menu_runner_impl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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/i18n/case_conversion.h" 7 #include "base/i18n/case_conversion.h"
8 #include "base/i18n/rtl.h" 8 #include "base/i18n/rtl.h"
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
(...skipping 1008 matching lines...) Expand 10 before | Expand all | Expand 10 after
1019 drag_in_progress_ = false; 1019 drag_in_progress_ = false;
1020 // During a drag, mouse events are processed directly by the widget, and not 1020 // During a drag, mouse events are processed directly by the widget, and not
1021 // sent to the MenuController. At drag completion, reset pressed state and 1021 // sent to the MenuController. At drag completion, reset pressed state and
1022 // the event target. 1022 // the event target.
1023 current_mouse_pressed_state_ = 0; 1023 current_mouse_pressed_state_ = 0;
1024 current_mouse_event_target_ = nullptr; 1024 current_mouse_event_target_ = nullptr;
1025 1025
1026 // Only attempt to close if the MenuHost said to. 1026 // Only attempt to close if the MenuHost said to.
1027 if (should_close) { 1027 if (should_close) {
1028 if (showing_) { 1028 if (showing_) {
1029 // Close showing widgets. 1029 // During a drag operation there are several ways in which this can be
1030 // canceled and deleted. Verify that this is still active before closing
1031 // the widgets.
1030 if (GetActiveInstance() == this) { 1032 if (GetActiveInstance() == this) {
1033 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
1031 CloseAllNestedMenus(); 1034 CloseAllNestedMenus();
1032 Cancel(EXIT_ALL); 1035 Cancel(EXIT_ALL);
1036 // The above may have deleted us. If not perform a full shutdown.
1037 if (!this_ref)
1038 return;
1039 ExitAsyncRun();
1033 } 1040 }
1034 // The above may have deleted us. If not perform a full shutdown.
1035 if (GetActiveInstance() == this)
1036 ExitAsyncRun();
1037 } else if (exit_type_ == EXIT_ALL) { 1041 } else if (exit_type_ == EXIT_ALL) {
1038 // We may have been canceled during the drag. If so we still need to fully 1042 // We may have been canceled during the drag. If so we still need to fully
1039 // shutdown. 1043 // shutdown.
1040 ExitAsyncRun(); 1044 ExitAsyncRun();
1041 } 1045 }
1042 } 1046 }
1043 } 1047 }
1044 1048
1045 ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent( 1049 ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent(
1046 ui::KeyEvent* event) { 1050 ui::KeyEvent* event) {
1047 if (exit_type() == EXIT_ALL || exit_type() == EXIT_DESTROYED) { 1051 if (exit_type() == EXIT_ALL || exit_type() == EXIT_DESTROYED) {
1048 // If the event has arrived after the menu's exit type had changed but 1052 // If the event has arrived after the menu's exit type had changed but
1049 // before its message loop terminated, the event will continue its normal 1053 // before its message loop terminated, the event will continue its normal
1050 // propagation for the following reason: 1054 // propagation for the following reason:
1051 // If the user accepts a menu item in a nested menu, the menu item action is 1055 // If the user accepts a menu item in a nested menu, the menu item action is
1052 // run after the base::RunLoop for the innermost menu has quit but before 1056 // run after the base::RunLoop for the innermost menu has quit but before
1053 // the base::RunLoop for the outermost menu has quit. If the menu item 1057 // the base::RunLoop for the outermost menu has quit. If the menu item
1054 // action starts a base::RunLoop, the outermost menu's base::RunLoop will 1058 // action starts a base::RunLoop, the outermost menu's base::RunLoop will
1055 // not quit till the action's base::RunLoop ends. IDC_BOOKMARK_BAR_OPEN_ALL 1059 // not quit till the action's base::RunLoop ends. IDC_BOOKMARK_BAR_OPEN_ALL
1056 // sometimes opens a modal dialog. The modal dialog starts a base::RunLoop 1060 // sometimes opens a modal dialog. The modal dialog starts a base::RunLoop
1057 // and keeps the base::RunLoop running for the duration of its lifetime. 1061 // and keeps the base::RunLoop running for the duration of its lifetime.
1058 TerminateNestedMessageLoopIfNecessary(); 1062 TerminateNestedMessageLoopIfNecessary();
1059 return ui::POST_DISPATCH_PERFORM_DEFAULT; 1063 return ui::POST_DISPATCH_PERFORM_DEFAULT;
1060 } 1064 }
1061 1065
1062 event->StopPropagation(); 1066 event->StopPropagation();
1063 1067
1064 if (event->type() == ui::ET_KEY_PRESSED) { 1068 if (event->type() == ui::ET_KEY_PRESSED) {
1069 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
1065 OnKeyDown(event->key_code()); 1070 OnKeyDown(event->key_code());
1066 // Menu controller might have been deleted. 1071 // Key events can lead to this being deleted.
1067 if (!GetActiveInstance()) 1072 if (!this_ref)
1068 return ui::POST_DISPATCH_NONE; 1073 return ui::POST_DISPATCH_NONE;
1069 1074
1070 // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For 1075 // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For
1071 // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic. 1076 // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic.
1072 const int kKeyFlagsMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN; 1077 const int kKeyFlagsMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN;
1073 const int flags = event->flags(); 1078 const int flags = event->flags();
1074 if (exit_type() == EXIT_NONE && (flags & kKeyFlagsMask) == 0) { 1079 if (exit_type() == EXIT_NONE && (flags & kKeyFlagsMask) == 0) {
1075 base::char16 c = event->GetCharacter(); 1080 base::char16 c = event->GetCharacter();
1076 SelectByChar(c); 1081 SelectByChar(c);
1077 // Menu controller might have been deleted. 1082 // SelectByChar can lead to this being deleted.
1078 if (!GetActiveInstance()) 1083 if (!this_ref)
1079 return ui::POST_DISPATCH_NONE; 1084 return ui::POST_DISPATCH_NONE;
1080 } 1085 }
1081 } 1086 }
1082 1087
1083 if (!TerminateNestedMessageLoopIfNecessary()) { 1088 if (!TerminateNestedMessageLoopIfNecessary()) {
1084 ui::Accelerator accelerator(*event); 1089 ui::Accelerator accelerator(*event);
1085 ViewsDelegate::ProcessMenuAcceleratorResult result = 1090 ViewsDelegate::ProcessMenuAcceleratorResult result =
1086 ViewsDelegate::GetInstance()->ProcessAcceleratorWhileMenuShowing( 1091 ViewsDelegate::GetInstance()->ProcessAcceleratorWhileMenuShowing(
1087 accelerator); 1092 accelerator);
1088 if (result == ViewsDelegate::ProcessMenuAcceleratorResult::CLOSE_MENU) 1093 if (result == ViewsDelegate::ProcessMenuAcceleratorResult::CLOSE_MENU)
(...skipping 16 matching lines...) Expand all
1105 void MenuController::OnWidgetDestroying(Widget* widget) { 1110 void MenuController::OnWidgetDestroying(Widget* widget) {
1106 DCHECK_EQ(owner_, widget); 1111 DCHECK_EQ(owner_, widget);
1107 owner_->RemoveObserver(this); 1112 owner_->RemoveObserver(this);
1108 owner_ = NULL; 1113 owner_ = NULL;
1109 } 1114 }
1110 1115
1111 bool MenuController::IsCancelAllTimerRunningForTest() { 1116 bool MenuController::IsCancelAllTimerRunningForTest() {
1112 return cancel_all_timer_.IsRunning(); 1117 return cancel_all_timer_.IsRunning();
1113 } 1118 }
1114 1119
1120 void MenuController::ClearStateForTest() {
1121 state_ = State();
1122 pending_state_ = State();
1123 }
1124
1115 // static 1125 // static
1116 void MenuController::TurnOffMenuSelectionHoldForTest() { 1126 void MenuController::TurnOffMenuSelectionHoldForTest() {
1117 menu_selection_hold_time_ms = -1; 1127 menu_selection_hold_time_ms = -1;
1118 } 1128 }
1119 1129
1120 void MenuController::SetSelection(MenuItemView* menu_item, 1130 void MenuController::SetSelection(MenuItemView* menu_item,
1121 int selection_types) { 1131 int selection_types) {
1122 size_t paths_differ_at = 0; 1132 size_t paths_differ_at = 0;
1123 std::vector<MenuItemView*> current_path; 1133 std::vector<MenuItemView*> current_path;
1124 std::vector<MenuItemView*> new_path; 1134 std::vector<MenuItemView*> new_path;
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
1242 item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG); 1252 item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG);
1243 1253
1244 OSExchangeData data; 1254 OSExchangeData data;
1245 item->GetDelegate()->WriteDragData(item, &data); 1255 item->GetDelegate()->WriteDragData(item, &data);
1246 drag_utils::SetDragImageOnDataObject(*canvas, 1256 drag_utils::SetDragImageOnDataObject(*canvas,
1247 press_loc.OffsetFromOrigin(), 1257 press_loc.OffsetFromOrigin(),
1248 &data); 1258 &data);
1249 StopScrolling(); 1259 StopScrolling();
1250 int drag_ops = item->GetDelegate()->GetDragOperations(item); 1260 int drag_ops = item->GetDelegate()->GetDragOperations(item);
1251 did_initiate_drag_ = true; 1261 did_initiate_drag_ = true;
1262 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
1252 // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below. 1263 // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
1253 item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops, 1264 item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops,
1254 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); 1265 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
1255 // MenuController may have been deleted if |async_run_| so check for an active 1266 // MenuController may have been deleted if |async_run_| so check before
1256 // instance before accessing member variables. 1267 // accessing member variables.
1257 if (GetActiveInstance() == this) 1268 if (this_ref)
1258 did_initiate_drag_ = false; 1269 did_initiate_drag_ = false;
1259 } 1270 }
1260 1271
1261 void MenuController::OnKeyDown(ui::KeyboardCode key_code) { 1272 void MenuController::OnKeyDown(ui::KeyboardCode key_code) {
1262 // Do not process while performing drag-and-drop 1273 // Do not process while performing drag-and-drop
1263 if (!blocking_run_) 1274 if (!blocking_run_)
1264 return; 1275 return;
1265 1276
1266 switch (key_code) { 1277 switch (key_code) {
1267 case ui::VKEY_UP: 1278 case ui::VKEY_UP:
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
1408 1419
1409 void MenuController::RunMessageLoop() { 1420 void MenuController::RunMessageLoop() {
1410 message_loop_->Run(); 1421 message_loop_->Run();
1411 } 1422 }
1412 1423
1413 bool MenuController::SendAcceleratorToHotTrackedView() { 1424 bool MenuController::SendAcceleratorToHotTrackedView() {
1414 CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); 1425 CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item);
1415 if (!hot_view) 1426 if (!hot_view)
1416 return false; 1427 return false;
1417 1428
1429 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
1418 ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE); 1430 ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE);
1419 hot_view->AcceleratorPressed(accelerator); 1431 hot_view->AcceleratorPressed(accelerator);
1420 // An accelerator may have canceled the menu after activation. 1432 // An accelerator may have canceled the menu after activation.
1421 if (GetActiveInstance()) { 1433 if (this_ref) {
1422 CustomButton* button = static_cast<CustomButton*>(hot_view); 1434 CustomButton* button = static_cast<CustomButton*>(hot_view);
1423 SetHotTrackedButton(button); 1435 SetHotTrackedButton(button);
1424 } 1436 }
1425 return true; 1437 return true;
1426 } 1438 }
1427 1439
1428 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, 1440 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds,
1429 MenuAnchorPosition position, 1441 MenuAnchorPosition position,
1430 bool context_menu) { 1442 bool context_menu) {
1431 pending_state_.context_menu = context_menu; 1443 pending_state_.context_menu = context_menu;
(...skipping 945 matching lines...) Expand 10 before | Expand all | Expand 10 after
2377 gfx::NativeView native_view = source->GetWidget()->GetNativeView(); 2389 gfx::NativeView native_view = source->GetWidget()->GetNativeView();
2378 gfx::NativeWindow window = nullptr; 2390 gfx::NativeWindow window = nullptr;
2379 if (native_view) { 2391 if (native_view) {
2380 display::Screen* screen = display::Screen::GetScreen(); 2392 display::Screen* screen = display::Screen::GetScreen();
2381 window = screen->GetWindowAtScreenPoint(screen_loc); 2393 window = screen->GetWindowAtScreenPoint(screen_loc);
2382 } 2394 }
2383 #endif 2395 #endif
2384 2396
2385 #if defined(OS_WIN) 2397 #if defined(OS_WIN)
2386 if (event->IsMouseEvent() || event->IsTouchEvent()) { 2398 if (event->IsMouseEvent() || event->IsTouchEvent()) {
2399 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
2387 bool async_run = async_run_; 2400 bool async_run = async_run_;
2388 if (state_.item) { 2401 if (state_.item) {
2389 state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); 2402 state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture();
2403 // We're going to close and we own the event capture. We need to repost
2404 // the event, otherwise the window the user clicked on won't get the
2405 // event.
2390 RepostEventImpl(event, screen_loc, native_view, window); 2406 RepostEventImpl(event, screen_loc, native_view, window);
2391 } else { 2407 } else {
2392 // We some times get an event after closing all the menus. Ignore it. Make 2408 // We some times get an event after closing all the menus. Ignore it. Make
2393 // sure the menu is in fact not visible. If the menu is visible, then 2409 // sure the menu is in fact not visible. If the menu is visible, then
2394 // we're in a bad state where we think the menu isn't visibile but it is. 2410 // we're in a bad state where we think the menu isn't visibile but it is.
2395 DCHECK(!source->GetWidget()->IsVisible()); 2411 DCHECK(!source->GetWidget()->IsVisible());
2396 } 2412 }
2397 2413
2398 // We're going to close and we own the event capture. We need to repost the 2414 // Reposting the event may have deleted this, if so exit.
2399 // event, otherwise the window the user clicked on won't get the event. 2415 if (!this_ref) {
2400 // RepostEvent(source, event, screen_loc, native_view, window);
2401 // MenuController may have been deleted if |async_run_| so check for an
2402 // active
2403 // instance before accessing member variables.
2404 if (!GetActiveInstance()) {
2405 DCHECK(async_run); 2416 DCHECK(async_run);
2406 return; 2417 return;
2407 } 2418 }
2408 } 2419 }
2409 #endif 2420 #endif
2410 2421
2411 // Determine target to see if a complete or partial close of the menu should 2422 // Determine target to see if a complete or partial close of the menu should
2412 // occur. 2423 // occur.
2413 ExitType exit_type = EXIT_ALL; 2424 ExitType exit_type = EXIT_ALL;
2414 if (!menu_stack_.empty()) { 2425 if (!menu_stack_.empty()) {
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
2572 void MenuController::ExitAsyncRun() { 2583 void MenuController::ExitAsyncRun() {
2573 if (!async_run_) 2584 if (!async_run_)
2574 return; 2585 return;
2575 bool nested = delegate_stack_.size() > 1; 2586 bool nested = delegate_stack_.size() > 1;
2576 // ExitMenuRun unwinds nested delegates 2587 // ExitMenuRun unwinds nested delegates
2577 internal::MenuControllerDelegate* delegate = delegate_; 2588 internal::MenuControllerDelegate* delegate = delegate_;
2578 // MenuController may have been deleted when releasing ViewsDelegate ref. 2589 // MenuController may have been deleted when releasing ViewsDelegate ref.
2579 // However as |delegate| can outlive this, it must still be notified of the 2590 // However as |delegate| can outlive this, it must still be notified of the
2580 // menu closing so that it can perform teardown. 2591 // menu closing so that it can perform teardown.
2581 int accept_event_flags = accept_event_flags_; 2592 int accept_event_flags = accept_event_flags_;
2593 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
2582 MenuItemView* result = ExitMenuRun(); 2594 MenuItemView* result = ExitMenuRun();
2583 delegate->OnMenuClosed(internal::MenuControllerDelegate::NOTIFY_DELEGATE, 2595 delegate->OnMenuClosed(internal::MenuControllerDelegate::NOTIFY_DELEGATE,
2584 result, accept_event_flags); 2596 result, accept_event_flags);
2585 // MenuController may have been deleted by |delegate|. 2597 // |delegate| may have deleted this.
2586 if (GetActiveInstance() && nested && exit_type_ == EXIT_ALL) 2598 if (this_ref && nested && exit_type_ == EXIT_ALL)
2587 ExitAsyncRun(); 2599 ExitAsyncRun();
2588 } 2600 }
2589 2601
2590 MenuItemView* MenuController::ExitMenuRun() { 2602 MenuItemView* MenuController::ExitMenuRun() {
2603 base::WeakPtr<MenuController> this_ref = AsWeakPtr();
2604
2591 // Release the lock which prevents Chrome from shutting down while the menu is 2605 // Release the lock which prevents Chrome from shutting down while the menu is
2592 // showing. 2606 // showing.
2593 if (async_run_ && ViewsDelegate::GetInstance()) 2607 if (async_run_ && ViewsDelegate::GetInstance())
2594 ViewsDelegate::GetInstance()->ReleaseRef(); 2608 ViewsDelegate::GetInstance()->ReleaseRef();
2595 2609
2596 // Releasing the lock can result in Chrome shutting down, deleting this. 2610 // Releasing the lock can result in Chrome shutting down, deleting this.
2597 if (!GetActiveInstance()) 2611 if (!this_ref)
2598 return nullptr; 2612 return nullptr;
2599 2613
2600 // Close any open menus. 2614 // Close any open menus.
2601 SetSelection(nullptr, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); 2615 SetSelection(nullptr, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
2602 2616
2603 #if defined(OS_WIN) 2617 #if defined(OS_WIN)
2604 // On Windows, if we select the menu item by touch and if the window at the 2618 // On Windows, if we select the menu item by touch and if the window at the
2605 // location is another window on the same thread, that window gets a 2619 // location is another window on the same thread, that window gets a
2606 // WM_MOUSEACTIVATE message and ends up activating itself, which is not 2620 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
2607 // correct. We workaround this by setting a property on the window at the 2621 // correct. We workaround this by setting a property on the window at the
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
2732 if (hot_button_) 2746 if (hot_button_)
2733 hot_button_->SetHotTracked(false); 2747 hot_button_->SetHotTracked(false);
2734 hot_button_ = hot_button; 2748 hot_button_ = hot_button;
2735 if (hot_button) { 2749 if (hot_button) {
2736 hot_button->SetHotTracked(true); 2750 hot_button->SetHotTracked(true);
2737 hot_button->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); 2751 hot_button->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
2738 } 2752 }
2739 } 2753 }
2740 2754
2741 } // namespace views 2755 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/menu/menu_controller.h ('k') | ui/views/controls/menu/menu_runner_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698