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

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

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