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

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

Issue 1664001: Fixes possible crash if the window hosting a menu was closed while the (Closed)
Patch Set: Incorporated review feedback Created 10 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
« no previous file with comments | « views/controls/menu/menu_controller.h ('k') | views/controls/menu/menu_host.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) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 "views/controls/menu/menu_controller.h" 5 #include "views/controls/menu/menu_controller.h"
6 6
7 #include "app/l10n_util.h" 7 #include "app/l10n_util.h"
8 #include "app/os_exchange_data.h" 8 #include "app/os_exchange_data.h"
9 #include "base/i18n/rtl.h" 9 #include "base/i18n/rtl.h"
10 #include "base/keyboard_codes.h" 10 #include "base/keyboard_codes.h"
11 #include "base/time.h" 11 #include "base/time.h"
12 #include "gfx/canvas.h" 12 #include "gfx/canvas.h"
13 #include "views/controls/button/menu_button.h" 13 #include "views/controls/button/menu_button.h"
14 #include "views/controls/menu/menu_scroll_view_container.h" 14 #include "views/controls/menu/menu_scroll_view_container.h"
15 #include "views/controls/menu/submenu_view.h" 15 #include "views/controls/menu/submenu_view.h"
16 #include "views/drag_utils.h" 16 #include "views/drag_utils.h"
17 #include "views/screen.h" 17 #include "views/screen.h"
18 #include "views/view_constants.h" 18 #include "views/view_constants.h"
19 #include "views/views_delegate.h"
19 #include "views/widget/root_view.h" 20 #include "views/widget/root_view.h"
20 #include "views/widget/widget.h" 21 #include "views/widget/widget.h"
22
21 #if defined(OS_LINUX) 23 #if defined(OS_LINUX)
22 #include "base/keyboard_code_conversion_gtk.h" 24 #include "base/keyboard_code_conversion_gtk.h"
23 #endif 25 #endif
24 26
25 using base::Time; 27 using base::Time;
26 using base::TimeDelta; 28 using base::TimeDelta;
27 29
28 // Period of the scroll timer (in milliseconds). 30 // Period of the scroll timer (in milliseconds).
29 static const int kScrollTimerMS = 30; 31 static const int kScrollTimerMS = 30;
30 32
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 return NULL; 186 return NULL;
185 } else if (button) { 187 } else if (button) {
186 menu_button_ = button; 188 menu_button_ = button;
187 } 189 }
188 190
189 #ifdef DEBUG_MENU 191 #ifdef DEBUG_MENU
190 nested_depth++; 192 nested_depth++;
191 DLOG(INFO) << " entering nested loop, depth=" << nested_depth; 193 DLOG(INFO) << " entering nested loop, depth=" << nested_depth;
192 #endif 194 #endif
193 195
196 // Make sure Chrome doesn't attempt to shut down while the menu is showing.
197 if (ViewsDelegate::views_delegate)
198 ViewsDelegate::views_delegate->AddRef();
199
194 MessageLoopForUI* loop = MessageLoopForUI::current(); 200 MessageLoopForUI* loop = MessageLoopForUI::current();
195 if (MenuItemView::allow_task_nesting_during_run_) { 201 if (MenuItemView::allow_task_nesting_during_run_) {
196 bool did_allow_task_nesting = loop->NestableTasksAllowed(); 202 bool did_allow_task_nesting = loop->NestableTasksAllowed();
197 loop->SetNestableTasksAllowed(true); 203 loop->SetNestableTasksAllowed(true);
198 loop->Run(this); 204 loop->Run(this);
199 loop->SetNestableTasksAllowed(did_allow_task_nesting); 205 loop->SetNestableTasksAllowed(did_allow_task_nesting);
200 } else { 206 } else {
201 loop->Run(this); 207 loop->Run(this);
202 } 208 }
203 209
210 if (ViewsDelegate::views_delegate)
211 ViewsDelegate::views_delegate->ReleaseRef();
212
204 #ifdef DEBUG_MENU 213 #ifdef DEBUG_MENU
205 nested_depth--; 214 nested_depth--;
206 DLOG(INFO) << " exiting nested loop, depth=" << nested_depth; 215 DLOG(INFO) << " exiting nested loop, depth=" << nested_depth;
207 #endif 216 #endif
208 217
209 // Close any open menus. 218 // Close any open menus.
210 SetSelection(NULL, false, true); 219 SetSelection(NULL, false, true);
211 220
212 if (nested_menu) { 221 if (nested_menu) {
213 DCHECK(!menu_stack_.empty()); 222 DCHECK(!menu_stack_.empty());
(...skipping 17 matching lines...) Expand all
231 if (exit_type_ == EXIT_OUTERMOST) { 240 if (exit_type_ == EXIT_OUTERMOST) {
232 exit_type_ = EXIT_NONE; 241 exit_type_ = EXIT_NONE;
233 } else { 242 } else {
234 if (nested_menu && result) { 243 if (nested_menu && result) {
235 // We're nested and about to return a value. The caller might enter 244 // We're nested and about to return a value. The caller might enter
236 // another blocking loop. We need to make sure all menus are hidden 245 // another blocking loop. We need to make sure all menus are hidden
237 // before that happens otherwise the menus will stay on screen. 246 // before that happens otherwise the menus will stay on screen.
238 CloseAllNestedMenus(); 247 CloseAllNestedMenus();
239 248
240 // Set exit_all_, which makes sure all nested loops exit immediately. 249 // Set exit_all_, which makes sure all nested loops exit immediately.
241 exit_type_ = EXIT_ALL; 250 if (exit_type_ != EXIT_DESTROYED)
251 exit_type_ = EXIT_ALL;
242 } 252 }
243 } 253 }
244 254
245 if (menu_button_) { 255 // If we stopped running because one of the menus was destroyed chances are
256 // the button was also destroyed.
257 if (exit_type_ != EXIT_DESTROYED && menu_button_) {
246 menu_button_->SetState(CustomButton::BS_NORMAL); 258 menu_button_->SetState(CustomButton::BS_NORMAL);
247 menu_button_->SchedulePaint(); 259 menu_button_->SchedulePaint();
248 } 260 }
249 261
250 return result; 262 return result;
251 } 263 }
252 264
253 void MenuController::SetSelection(MenuItemView* menu_item, 265 void MenuController::SetSelection(MenuItemView* menu_item,
254 bool open_submenu, 266 bool open_submenu,
255 bool update_immediately) { 267 bool update_immediately) {
(...skipping 23 matching lines...) Expand all
279 // Stop timers. 291 // Stop timers.
280 StopShowTimer(); 292 StopShowTimer();
281 StopCancelAllTimer(); 293 StopCancelAllTimer();
282 294
283 if (update_immediately) 295 if (update_immediately)
284 CommitPendingSelection(); 296 CommitPendingSelection();
285 else 297 else
286 StartShowTimer(); 298 StartShowTimer();
287 } 299 }
288 300
289 void MenuController::Cancel(bool all) { 301 void MenuController::Cancel(ExitType type) {
290 if (!showing_) { 302 if (!showing_) {
291 // This occurs if we're in the process of notifying the delegate for a drop 303 // This occurs if we're in the process of notifying the delegate for a drop
292 // and the delegate cancels us. 304 // and the delegate cancels us.
293 return; 305 return;
294 } 306 }
295 307
296 MenuItemView* selected = state_.item; 308 MenuItemView* selected = state_.item;
297 exit_type_ = all ? EXIT_ALL : EXIT_OUTERMOST; 309 exit_type_ = type;
298 310
299 // Hide windows immediately. 311 // Hide windows immediately.
300 SetSelection(NULL, false, true); 312 SetSelection(NULL, false, true);
301 313
302 if (!blocking_run_) { 314 if (!blocking_run_) {
303 // If we didn't block the caller we need to notify the menu, which 315 // If we didn't block the caller we need to notify the menu, which
304 // triggers deleting us. 316 // triggers deleting us.
305 DCHECK(selected); 317 DCHECK(selected);
306 showing_ = false; 318 showing_ = false;
307 selected->GetRootMenuItem()->DropMenuClosed(true); 319 selected->GetRootMenuItem()->DropMenuClosed(true);
(...skipping 21 matching lines...) Expand all
329 341
330 // We're going to close and we own the mouse capture. We need to repost the 342 // We're going to close and we own the mouse capture. We need to repost the
331 // mouse down, otherwise the window the user clicked on won't get the 343 // mouse down, otherwise the window the user clicked on won't get the
332 // event. 344 // event.
333 #if defined(OS_WIN) 345 #if defined(OS_WIN)
334 RepostEvent(source, event); 346 RepostEvent(source, event);
335 // NOTE: not reposting on linux seems fine. 347 // NOTE: not reposting on linux seems fine.
336 #endif 348 #endif
337 349
338 // And close. 350 // And close.
339 Cancel(true); 351 Cancel(EXIT_ALL);
340 return; 352 return;
341 } 353 }
342 354
343 bool open_submenu = false; 355 bool open_submenu = false;
344 if (!part.menu) { 356 if (!part.menu) {
345 part.menu = part.parent; 357 part.menu = part.parent;
346 open_submenu = true; 358 open_submenu = true;
347 } else { 359 } else {
348 if (part.menu->GetDelegate()->CanDrag(part.menu)) { 360 if (part.menu->GetDelegate()->CanDrag(part.menu)) {
349 possible_drag_ = true; 361 possible_drag_ = true;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 drag_utils::SetDragImageOnDataObject(canvas, item->size(), press_loc, 399 drag_utils::SetDragImageOnDataObject(canvas, item->size(), press_loc,
388 &data); 400 &data);
389 StopScrolling(); 401 StopScrolling();
390 int drag_ops = item->GetDelegate()->GetDragOperations(item); 402 int drag_ops = item->GetDelegate()->GetDragOperations(item);
391 item->GetRootView()->StartDragForViewFromMouseEvent( 403 item->GetRootView()->StartDragForViewFromMouseEvent(
392 NULL, data, drag_ops); 404 NULL, data, drag_ops);
393 if (GetActiveInstance() == this) { 405 if (GetActiveInstance() == this) {
394 if (showing_) { 406 if (showing_) {
395 // We're still showing, close all menus. 407 // We're still showing, close all menus.
396 CloseAllNestedMenus(); 408 CloseAllNestedMenus();
397 Cancel(true); 409 Cancel(EXIT_ALL);
398 } // else case, drop was on us. 410 } // else case, drop was on us.
399 } // else case, someone canceled us, don't do anything 411 } // else case, someone canceled us, don't do anything
400 } 412 }
401 return; 413 return;
402 } 414 }
403 if (part.type == MenuPart::MENU_ITEM) { 415 if (part.type == MenuPart::MENU_ITEM) {
404 if (!part.menu) 416 if (!part.menu)
405 part.menu = source->GetMenuItem(); 417 part.menu = source->GetMenuItem();
406 SetSelection(part.menu ? part.menu : state_.item, true, false); 418 SetSelection(part.menu ? part.menu : state_.item, true, false);
407 } else if (part.type == MenuPart::NONE) { 419 } else if (part.type == MenuPart::NONE) {
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
630 642
631 // static 643 // static
632 void MenuController::SetActiveInstance(MenuController* controller) { 644 void MenuController::SetActiveInstance(MenuController* controller) {
633 active_instance_ = controller; 645 active_instance_ = controller;
634 } 646 }
635 647
636 #if defined(OS_WIN) 648 #if defined(OS_WIN)
637 bool MenuController::Dispatch(const MSG& msg) { 649 bool MenuController::Dispatch(const MSG& msg) {
638 DCHECK(blocking_run_); 650 DCHECK(blocking_run_);
639 651
640 if (exit_type_ == EXIT_ALL) { 652 if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) {
641 // We must translate/dispatch the message here, otherwise we would drop 653 // We must translate/dispatch the message here, otherwise we would drop
642 // the message on the floor. 654 // the message on the floor.
643 TranslateMessage(&msg); 655 TranslateMessage(&msg);
644 DispatchMessage(&msg); 656 DispatchMessage(&msg);
645 return false; 657 return false;
646 } 658 }
647 659
648 // NOTE: we don't get WM_ACTIVATE or anything else interesting in here. 660 // NOTE: we don't get WM_ACTIVATE or anything else interesting in here.
649 switch (msg.message) { 661 switch (msg.message) {
650 case WM_CONTEXTMENU: { 662 case WM_CONTEXTMENU: {
(...skipping 20 matching lines...) Expand all
671 683
672 case WM_SYSKEYUP: 684 case WM_SYSKEYUP:
673 // We may have been shown on a system key, as such don't do anything 685 // We may have been shown on a system key, as such don't do anything
674 // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and 686 // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and
675 // close the menu. 687 // close the menu.
676 return true; 688 return true;
677 689
678 case WM_CANCELMODE: 690 case WM_CANCELMODE:
679 case WM_SYSKEYDOWN: 691 case WM_SYSKEYDOWN:
680 // Exit immediately on system keys. 692 // Exit immediately on system keys.
681 Cancel(true); 693 Cancel(EXIT_ALL);
682 return false; 694 return false;
683 695
684 default: 696 default:
685 break; 697 break;
686 } 698 }
687 TranslateMessage(&msg); 699 TranslateMessage(&msg);
688 DispatchMessage(&msg); 700 DispatchMessage(&msg);
689 return exit_type_ == EXIT_NONE; 701 return exit_type_ == EXIT_NONE;
690 } 702 }
691 703
692 #else 704 #else
693 bool MenuController::Dispatch(GdkEvent* event) { 705 bool MenuController::Dispatch(GdkEvent* event) {
694 gtk_main_do_event(event); 706 gtk_main_do_event(event);
695 707
696 if (exit_type_ == EXIT_ALL) 708 if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED)
697 return false; 709 return false;
698 710
699 switch (event->type) { 711 switch (event->type) {
700 case GDK_KEY_PRESS: { 712 case GDK_KEY_PRESS: {
701 base::KeyboardCode win_keycode = 713 base::KeyboardCode win_keycode =
702 base::WindowsKeyCodeForGdkKeyCode(event->key.keyval); 714 base::WindowsKeyCodeForGdkKeyCode(event->key.keyval);
703 if (!OnKeyDown(win_keycode)) 715 if (!OnKeyDown(win_keycode))
704 return false; 716 return false;
705 guint32 keycode = gdk_keyval_to_unicode(event->key.keyval); 717 guint32 keycode = gdk_keyval_to_unicode(event->key.keyval);
706 if (keycode) 718 if (keycode)
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
759 } 771 }
760 } 772 }
761 break; 773 break;
762 774
763 case base::VKEY_ESCAPE: 775 case base::VKEY_ESCAPE:
764 if (!state_.item->GetParentMenuItem() || 776 if (!state_.item->GetParentMenuItem() ||
765 (!state_.item->GetParentMenuItem()->GetParentMenuItem() && 777 (!state_.item->GetParentMenuItem()->GetParentMenuItem() &&
766 (!state_.item->HasSubmenu() || 778 (!state_.item->HasSubmenu() ||
767 !state_.item->GetSubmenu()->IsShowing()))) { 779 !state_.item->GetSubmenu()->IsShowing()))) {
768 // User pressed escape and only one menu is shown, cancel it. 780 // User pressed escape and only one menu is shown, cancel it.
769 Cancel(false); 781 Cancel(EXIT_OUTERMOST);
770 return false; 782 return false;
771 } else { 783 } else {
772 CloseSubmenu(); 784 CloseSubmenu();
773 } 785 }
774 break; 786 break;
775 787
776 #if defined(OS_WIN) 788 #if defined(OS_WIN)
777 case VK_APPS: 789 case VK_APPS:
778 break; 790 break;
779 #endif 791 #endif
(...skipping 721 matching lines...) Expand 10 before | Expand all | Expand 10 after
1501 if (!scroll_task_.get()) 1513 if (!scroll_task_.get())
1502 scroll_task_.reset(new MenuScrollTask()); 1514 scroll_task_.reset(new MenuScrollTask());
1503 scroll_task_->Update(part); 1515 scroll_task_->Update(part);
1504 } 1516 }
1505 1517
1506 void MenuController::StopScrolling() { 1518 void MenuController::StopScrolling() {
1507 scroll_task_.reset(NULL); 1519 scroll_task_.reset(NULL);
1508 } 1520 }
1509 1521
1510 } // namespace views 1522 } // namespace views
OLDNEW
« no previous file with comments | « views/controls/menu/menu_controller.h ('k') | views/controls/menu/menu_host.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698