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

Side by Side Diff: chrome/browser/ui/gtk/menu_gtk.cc

Issue 10966043: Linux: Fix a wide range of crashes in dynamic menu code on Ubuntu 12.04. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | 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 "chrome/browser/ui/gtk/menu_gtk.h" 5 #include "chrome/browser/ui/gtk/menu_gtk.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/i18n/rtl.h" 10 #include "base/i18n/rtl.h"
(...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after
505 505
506 if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) { 506 if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) {
507 GtkWidget* submenu = gtk_menu_new(); 507 GtkWidget* submenu = gtk_menu_new();
508 g_object_set_data(G_OBJECT(submenu), "menu-item", menu_item); 508 g_object_set_data(G_OBJECT(submenu), "menu-item", menu_item);
509 ui::MenuModel* submenu_model = model->GetSubmenuModelAt(i); 509 ui::MenuModel* submenu_model = model->GetSubmenuModelAt(i);
510 g_object_set_data(G_OBJECT(menu_item), "submenu-model", submenu_model); 510 g_object_set_data(G_OBJECT(menu_item), "submenu-model", submenu_model);
511 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); 511 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
512 // We will populate the submenu on demand when shown. 512 // We will populate the submenu on demand when shown.
513 g_signal_connect(submenu, "show", G_CALLBACK(OnSubMenuShowThunk), this); 513 g_signal_connect(submenu, "show", G_CALLBACK(OnSubMenuShowThunk), this);
514 g_signal_connect(submenu, "hide", G_CALLBACK(OnSubMenuHiddenThunk), this); 514 g_signal_connect(submenu, "hide", G_CALLBACK(OnSubMenuHiddenThunk), this);
515 connect_to_activate = false;
515 } 516 }
516 517
517 ui::AcceleratorGtk accelerator; 518 ui::AcceleratorGtk accelerator;
518 if (model->GetAcceleratorAt(i, &accelerator)) { 519 if (model->GetAcceleratorAt(i, &accelerator)) {
519 gtk_widget_add_accelerator(menu_item, 520 gtk_widget_add_accelerator(menu_item,
520 "activate", 521 "activate",
521 dummy_accel_group_, 522 dummy_accel_group_,
522 accelerator.GetGdkKeyCode(), 523 accelerator.GetGdkKeyCode(),
523 accelerator.gdk_modifier_type(), 524 accelerator.gdk_modifier_type(),
524 GTK_ACCEL_VISIBLE); 525 GTK_ACCEL_VISIBLE);
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 597
597 return menu_item; 598 return menu_item;
598 } 599 }
599 600
600 void MenuGtk::OnMenuItemActivated(GtkWidget* menu_item) { 601 void MenuGtk::OnMenuItemActivated(GtkWidget* menu_item) {
601 if (block_activation_) 602 if (block_activation_)
602 return; 603 return;
603 604
604 ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item)); 605 ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item));
605 606
606 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
607 if (submenu) {
608 // We receive activation messages when highlighting a menu that has a
609 // submenu. We build submenus on show, and tear them down on hide, to
610 // allow submenu models to be constructed and destroyed on demand. So,
611 // there's nothing further to do here in that case.
612 return;
613 }
614
615 // The activate signal is sent to radio items as they get deselected; 607 // The activate signal is sent to radio items as they get deselected;
616 // ignore it in this case. 608 // ignore it in this case.
617 if (GTK_IS_RADIO_MENU_ITEM(menu_item) && 609 if (GTK_IS_RADIO_MENU_ITEM(menu_item) &&
618 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) { 610 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) {
619 return; 611 return;
620 } 612 }
621 613
622 int id; 614 int id;
623 if (!GetMenuItemID(menu_item, &id)) 615 if (!GetMenuItemID(menu_item, &id))
624 return; 616 return;
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
753 } 745 }
754 746
755 gboolean MenuGtk::OnMenuFocusOut(GtkWidget* widget, GdkEventFocus* event) { 747 gboolean MenuGtk::OnMenuFocusOut(GtkWidget* widget, GdkEventFocus* event) {
756 gtk_widget_hide(menu_); 748 gtk_widget_hide(menu_);
757 return TRUE; 749 return TRUE;
758 } 750 }
759 751
760 void MenuGtk::OnSubMenuShow(GtkWidget* submenu) { 752 void MenuGtk::OnSubMenuShow(GtkWidget* submenu) {
761 GtkWidget* menu_item = static_cast<GtkWidget*>( 753 GtkWidget* menu_item = static_cast<GtkWidget*>(
762 g_object_get_data(G_OBJECT(submenu), "menu-item")); 754 g_object_get_data(G_OBJECT(submenu), "menu-item"));
763 755 // TODO(mdm): Figure out why this can sometimes be NULL. See bug 131974.
756 CHECK(menu_item);
Mike Mammarella 2012/09/24 10:05:48 This one is the same - originally I was adding mor
764 // Notify the submenu model that the menu will be shown. 757 // Notify the submenu model that the menu will be shown.
765 ui::MenuModel* submenu_model = static_cast<ui::MenuModel*>( 758 ui::MenuModel* submenu_model = static_cast<ui::MenuModel*>(
766 g_object_get_data(G_OBJECT(menu_item), "submenu-model")); 759 g_object_get_data(G_OBJECT(menu_item), "submenu-model"));
767 // TODO(mdm): Figure out why this can sometimes be NULL. See bug 131974. 760 // We're extra cautious here, and bail out if the submenu model is NULL. In
761 // some cases we clear it out from a parent menu; we shouldn't ever show the
762 // menu after that, but we play it safe since we're dealing with wacky
763 // injected libraries that toy with our menus. (See comments below.)
768 if (!submenu_model) 764 if (!submenu_model)
769 return; 765 return;
766
767 // If the submenu is already built, then return right away. This means we
768 // recently showed this submenu, and have not yet processed the fact that it
769 // was hidden before being shown again.
770 if (g_object_get_data(G_OBJECT(submenu), "submenu-built"))
771 return;
772 g_object_set_data(G_OBJECT(submenu), "submenu-built", GINT_TO_POINTER(1));
773
770 submenu_model->MenuWillShow(); 774 submenu_model->MenuWillShow();
771 775
772 // Actually build the submenu and attach it to the parent menu item. 776 // Actually build the submenu and attach it to the parent menu item.
773 BuildSubmenuFromModel(submenu_model, submenu); 777 BuildSubmenuFromModel(submenu_model, submenu);
774 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); 778 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
775 779
776 // Update all the menu item info in the newly-generated menu. 780 // Update all the menu item info in the newly-generated menu.
777 gtk_container_foreach(GTK_CONTAINER(submenu), SetMenuItemInfo, this); 781 gtk_container_foreach(GTK_CONTAINER(submenu), SetMenuItemInfo, this);
778 } 782 }
779 783
780 void MenuGtk::OnSubMenuHidden(GtkWidget* submenu) { 784 void MenuGtk::OnSubMenuHidden(GtkWidget* submenu) {
781 // Increase the reference count of the old submenu, and schedule it to be 785 // Increase the reference count of the old submenu, and schedule it to be
782 // deleted later. We get this hide notification before we've processed menu 786 // deleted later. We get this hide notification before we've processed menu
783 // activations, so if we were to delete the submenu now, we might lose the 787 // activations, so if we were to delete the submenu now, we might lose the
784 // activation. Note that we disconnect it from the parent menu item below. 788 // activation. This also lets us reuse the menu if it is shown again before
789 // it gets deleted; in that case, OnSubMenuHiddenCallback() just decrements
790 // the reference count again. Note that the delay is just an optimization; we
791 // could use PostTask() and this would still work correctly.
785 g_object_ref(G_OBJECT(submenu)); 792 g_object_ref(G_OBJECT(submenu));
786 MessageLoop::current()->PostTask( 793 MessageLoop::current()->PostDelayedTask(
787 FROM_HERE, 794 FROM_HERE,
788 base::Bind(&MenuGtk::OnSubMenuHiddenCallback, submenu)); 795 base::Bind(&MenuGtk::OnSubMenuHiddenCallback, submenu),
796 base::TimeDelta::FromSeconds(2));
797 }
789 798
790 // Build a new submenu, which will be populated when it is next shown. 799 namespace {
791 GtkWidget* menu_item = static_cast<GtkWidget*>( 800
792 g_object_get_data(G_OBJECT(submenu), "menu-item")); 801 // Remove all descendant submenu-model data pointers.
793 submenu = gtk_menu_new(); 802 void RemoveSubMenuModels(GtkWidget* menu_item, void* unused) {
Mike Mammarella 2012/09/24 10:05:48 This is the part that was causing the looping - pr
794 g_object_set_data(G_OBJECT(submenu), "menu-item", menu_item); 803 if (!GTK_IS_MENU_ITEM(menu_item))
795 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); 804 return;
796 g_signal_connect(submenu, "show", G_CALLBACK(OnSubMenuShowThunk), this); 805 g_object_steal_data(G_OBJECT(menu_item), "submenu-model");
797 g_signal_connect(submenu, "hide", G_CALLBACK(OnSubMenuHiddenThunk), this); 806 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
807 if (submenu)
808 gtk_container_foreach(GTK_CONTAINER(submenu), RemoveSubMenuModels, NULL);
798 } 809 }
799 810
811 } // namespace
812
800 // static 813 // static
801 void MenuGtk::OnSubMenuHiddenCallback(GtkWidget* submenu) { 814 void MenuGtk::OnSubMenuHiddenCallback(GtkWidget* submenu) {
802 // Notify the submenu model that the menu has been hidden. 815 if (!gtk_widget_get_visible(submenu)) {
803 GtkWidget* menu_item = static_cast<GtkWidget*>( 816 // Remove all the children of this menu, clearing out their submenu-model
804 g_object_get_data(G_OBJECT(submenu), "menu-item")); 817 // pointers in case they have pending calls to OnSubMenuHiddenCallback().
805 ui::MenuModel* submenu_model = static_cast<ui::MenuModel*>( 818 // (Normally that won't happen: we'd have hidden them first, and so they'd
806 g_object_get_data(G_OBJECT(menu_item), "submenu-model")); 819 // have already been deleted. But in some cases [e.g. on Ubuntu 12.04],
807 // TODO(mdm): Figure out why this can sometimes be NULL. See bug 124110. 820 // GTK menu operations may be hooked to allow external applications to
808 if (submenu_model) 821 // mirror the menu structure, and the hooks may show and hide menus in
809 submenu_model->MenuClosed(); 822 // order to trigger exactly the kind of dynamic menu building we're doing.
823 // The result is that we see show and hide events in strange orders.)
824 GList* children = gtk_container_get_children(GTK_CONTAINER(submenu));
825 for (GList* child = children; child; child = g_list_next(child)) {
826 RemoveSubMenuModels(GTK_WIDGET(child->data), NULL);
827 gtk_container_remove(GTK_CONTAINER(submenu), GTK_WIDGET(child->data));
828 }
829 g_list_free(children);
830
831 // Clear out the bit that says the menu is built.
832 // We'll rebuild it next time it is shown.
833 g_object_steal_data(G_OBJECT(submenu), "submenu-built");
834
835 // Notify the submenu model that the menu has been hidden. This may cause
836 // it to delete descendant submenu models, which is why we cleared those
837 // pointers out above.
838 GtkWidget* menu_item = static_cast<GtkWidget*>(
839 g_object_get_data(G_OBJECT(submenu), "menu-item"));
840 // TODO(mdm): Figure out why this can sometimes be NULL. See bug 124110.
Evan Stade 2012/09/24 08:49:09 because you got two hides in a row? (seems possibl
Mike Mammarella 2012/09/24 10:05:48 Well, we never clear this out on hide - it's only
841 CHECK(menu_item);
842 ui::MenuModel* submenu_model = static_cast<ui::MenuModel*>(
843 g_object_get_data(G_OBJECT(menu_item), "submenu-model"));
844 if (submenu_model)
845 submenu_model->MenuClosed();
846 }
810 847
811 // Remove the reference we grabbed in OnSubMenuHidden() above. 848 // Remove the reference we grabbed in OnSubMenuHidden() above.
812 g_object_unref(G_OBJECT(submenu)); 849 g_object_unref(G_OBJECT(submenu));
813 } 850 }
814 851
815 // static 852 // static
816 void MenuGtk::SetButtonItemInfo(GtkWidget* button, gpointer userdata) { 853 void MenuGtk::SetButtonItemInfo(GtkWidget* button, gpointer userdata) {
817 ui::ButtonMenuItemModel* model = 854 ui::ButtonMenuItemModel* model =
818 reinterpret_cast<ui::ButtonMenuItemModel*>( 855 reinterpret_cast<ui::ButtonMenuItemModel*>(
819 g_object_get_data(G_OBJECT(button), "button-model")); 856 g_object_get_data(G_OBJECT(button), "button-model"));
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
900 gtk_widget_hide(widget); 937 gtk_widget_hide(widget);
901 } 938 }
902 939
903 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget)); 940 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
904 if (submenu) { 941 if (submenu) {
905 gtk_container_foreach(GTK_CONTAINER(submenu), &SetMenuItemInfo, 942 gtk_container_foreach(GTK_CONTAINER(submenu), &SetMenuItemInfo,
906 userdata); 943 userdata);
907 } 944 }
908 } 945 }
909 } 946 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698