summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
Diffstat (limited to 'views')
-rw-r--r--views/controls/menu/native_menu_gtk.cc18
-rw-r--r--views/controls/menu/native_menu_gtk.h7
-rw-r--r--views/controls/menu/nested_dispatcher_gtk.cc39
-rw-r--r--views/controls/menu/nested_dispatcher_gtk.h50
-rw-r--r--views/views.gyp2
5 files changed, 114 insertions, 2 deletions
diff --git a/views/controls/menu/native_menu_gtk.cc b/views/controls/menu/native_menu_gtk.cc
index 14c1f6d..31a8c3f 100644
--- a/views/controls/menu/native_menu_gtk.cc
+++ b/views/controls/menu/native_menu_gtk.cc
@@ -21,6 +21,7 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "views/accelerator.h"
#include "views/controls/menu/menu_2.h"
+#include "views/controls/menu/nested_dispatcher_gtk.h"
namespace {
@@ -63,10 +64,16 @@ NativeMenuGtk::NativeMenuGtk(Menu2* menu)
activated_index_(-1),
activate_factory_(this),
host_menu_(menu),
- menu_action_(MENU_ACTION_NONE) {
+ menu_action_(MENU_ACTION_NONE),
+ nested_dispatcher_(NULL) {
}
NativeMenuGtk::~NativeMenuGtk() {
+ if (nested_dispatcher_) {
+ // Menu is destroyed while its in message loop.
+ // Let nested dispatcher know the creator is deleted.
+ nested_dispatcher_->CreatorDestroyed();
+ }
if (menu_) {
// Don't call MenuDestroyed because menu2 has already been destroyed.
g_signal_handler_disconnect(menu_, destroy_handler_id_);
@@ -104,7 +111,14 @@ void NativeMenuGtk::RunMenuAt(const gfx::Point& point, int alignment) {
G_CALLBACK(OnMenuMoveCurrentThunk), this);
// Block until menu is no longer shown by running a nested message loop.
- MessageLoopForUI::current()->Run(this);
+ nested_dispatcher_ = new NestedDispatcherGtk(this, false);
+ bool deleted = nested_dispatcher_->RunAndSelfDestruct();
+ if (deleted) {
+ // The menu was destryed while menu is shown, so return immediately.
+ // Don't touch the instance which is already deleted.
+ return;
+ }
+ nested_dispatcher_ = NULL;
if (!menu_hidden_) {
// If this happens it means we haven't yet gotten the hide signal and
// someone else quit the message loop on us.
diff --git a/views/controls/menu/native_menu_gtk.h b/views/controls/menu/native_menu_gtk.h
index 79a6060..6145925 100644
--- a/views/controls/menu/native_menu_gtk.h
+++ b/views/controls/menu/native_menu_gtk.h
@@ -21,6 +21,8 @@ class MenuModel;
namespace views {
+class NestedDispatcherGtk;
+
// A Gtk implementation of MenuWrapper.
//
// NOTE: On windows the activate message is not sent immediately when an item
@@ -136,6 +138,11 @@ class NativeMenuGtk : public MenuWrapper,
// Vector of listeners to receive callbacks when the menu opens.
std::vector<MenuListener*> listeners_;
+ // Nested dispatcher object that can outlive this object.
+ // This is to deal with the menu being deleted while the nested
+ // message loop is handled. see http://crosbug.com/7228 .
+ NestedDispatcherGtk* nested_dispatcher_;
+
DISALLOW_COPY_AND_ASSIGN(NativeMenuGtk);
};
diff --git a/views/controls/menu/nested_dispatcher_gtk.cc b/views/controls/menu/nested_dispatcher_gtk.cc
new file mode 100644
index 0000000..856b0a8
--- /dev/null
+++ b/views/controls/menu/nested_dispatcher_gtk.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/menu/nested_dispatcher_gtk.h"
+
+namespace views {
+
+NestedDispatcherGtk::NestedDispatcherGtk(MessageLoopForUI::Dispatcher* creator,
+ bool allow_nested_task)
+ : creator_(creator),
+ allow_nested_task_(allow_nested_task) {
+}
+
+bool NestedDispatcherGtk::RunAndSelfDestruct() {
+ bool nestable = MessageLoopForUI::current()->NestableTasksAllowed();
+ if (allow_nested_task_)
+ MessageLoopForUI::current()->SetNestableTasksAllowed(true);
+ MessageLoopForUI::current()->Run(this);
+ if (allow_nested_task_)
+ MessageLoopForUI::current()->SetNestableTasksAllowed(nestable);
+ bool creator_is_deleted = creator_ == NULL;
+ delete this;
+ return creator_is_deleted;
+}
+
+void NestedDispatcherGtk::CreatorDestroyed() {
+ creator_ = NULL;
+}
+
+bool NestedDispatcherGtk::Dispatch(GdkEvent* event) {
+ if (creator_ != NULL) {
+ return creator_->Dispatch(event);
+ } else {
+ return false;
+ }
+}
+
+} // namespace views
diff --git a/views/controls/menu/nested_dispatcher_gtk.h b/views/controls/menu/nested_dispatcher_gtk.h
new file mode 100644
index 0000000..06f54fa
--- /dev/null
+++ b/views/controls/menu/nested_dispatcher_gtk.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VIEWS_CONTROLS_MENU_NESTED_DISPATCHER_GTK_H_
+#define VIEWS_CONTROLS_MENU_NATIVE_DISPATCHER_GTK_H_
+#pragma once
+
+#include "base/message_loop.h"
+
+namespace views {
+
+// A nested dispatcher that can out-live the creator of this
+// dispatcher. This is to deal with the scenario where a menu object
+// is deleted while it's handling the message loop. Note that
+// |RunAndSelfDestruct| method always delete itself regardless of
+// whether or not the menu object is deleted, so a menu object should
+// create a new instance for each open request.
+// http://crosbug.com/7228, http://crosbug.com/7929
+class NestedDispatcherGtk : public MessageLoopForUI::Dispatcher {
+ public:
+ NestedDispatcherGtk(MessageLoopForUI::Dispatcher* creator,
+ bool allow_nested_task);
+
+ // Run the messsage loop and returns if the menu has been
+ // deleted in the nested loop. Returns true if the menu is
+ // deleted, or false otherwise.
+ bool RunAndSelfDestruct();
+
+ // Tells the nested dispatcher that creator has been destroyed.
+ void CreatorDestroyed();
+
+ private:
+ virtual ~NestedDispatcherGtk() {}
+
+ // Overriden from MessageLoopForUI::Dispatcher:
+ virtual bool Dispatch(GdkEvent* event);
+
+ // Creator of the nested loop.
+ MessageLoopForUI::Dispatcher* creator_;
+
+ // True to allow nested task in the message loop.
+ bool allow_nested_task_;
+
+ DISALLOW_COPY_AND_ASSIGN(NestedDispatcherGtk);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_MENU_NESTED_DISPATCHER_GTK_H_
diff --git a/views/views.gyp b/views/views.gyp
index 14a6a5a..e011383 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -145,6 +145,8 @@
'controls/menu/native_menu_gtk.h',
'controls/menu/native_menu_win.cc',
'controls/menu/native_menu_win.h',
+ 'controls/menu/nested_dispatcher_gtk.cc',
+ 'controls/menu/nested_dispatcher_gtk.h',
'controls/menu/radio_button_image_gtk.cc',
'controls/menu/radio_button_image_gtk.h',
'controls/menu/submenu_view.cc',