Index: ui/views/widget/root_view_unittest.cc |
diff --git a/ui/views/widget/root_view_unittest.cc b/ui/views/widget/root_view_unittest.cc |
index fcefb7033020c19e0e19b8890838937b54e233f4..373fe2957f44129445701bea506ad534f64c17ff 100644 |
--- a/ui/views/widget/root_view_unittest.cc |
+++ b/ui/views/widget/root_view_unittest.cc |
@@ -4,6 +4,7 @@ |
#include "ui/views/widget/root_view.h" |
+#include "ui/events/event_utils.h" |
#include "ui/views/context_menu_controller.h" |
#include "ui/views/test/views_test_base.h" |
#include "ui/views/view_targeter.h" |
@@ -337,5 +338,67 @@ TEST_F(RootViewTest, ContextMenuFromLongPressOnDisabledView) { |
EXPECT_EQ(0, controller.show_context_menu_calls()); |
} |
+// This view class provides functionality to delete itself in the context of |
+// mouse exit event and helps test that we don't crash when we return from |
+// the mouse exit handler. |
+class DeleteViewOnMouseExit : public View { |
+ public: |
+ explicit DeleteViewOnMouseExit(bool* got_mouse_exit) |
+ : got_mouse_exit_(got_mouse_exit) { |
+ } |
+ |
+ ~DeleteViewOnMouseExit() override {} |
+ |
+ void OnMouseExited(const ui::MouseEvent& event) override { |
+ *got_mouse_exit_ = true; |
+ delete this; |
+ } |
+ |
+ private: |
+ // Set to true in OnMouseExited(). |
+ bool* got_mouse_exit_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DeleteViewOnMouseExit); |
+}; |
+ |
+// Verifies deleting a View in OnMouseExited() doesn't crash. |
+TEST_F(RootViewTest, DeleteViewOnMouseExitDispatch) { |
+ Widget widget; |
+ Widget::InitParams init_params = |
+ CreateParams(Widget::InitParams::TYPE_POPUP); |
+ init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
+ widget.Init(init_params); |
+ widget.SetBounds(gfx::Rect(10, 10, 500, 500)); |
+ |
+ View* content = new View; |
+ widget.SetContentsView(content); |
+ |
+ bool got_mouse_exit = false; |
+ View* child = new DeleteViewOnMouseExit(&got_mouse_exit); |
+ content->AddChildView(child); |
+ child->SetBounds(10, 10, 500, 500); |
+ |
+ internal::RootView* root_view = |
+ static_cast<internal::RootView*>(widget.GetRootView()); |
+ |
+ // Generate a mouse move event which ensures that the mouse_moved_handler_ |
+ // member is set in the RootView class. |
+ ui::MouseEvent moved_event(ui::ET_MOUSE_MOVED, gfx::Point(15, 15), |
+ gfx::Point(100, 100), ui::EventTimeForNow(), 0, |
+ 0); |
+ root_view->OnMouseMoved(moved_event); |
+ EXPECT_FALSE(got_mouse_exit); |
+ |
+ // Generate a mouse exit event which in turn will delete the child view which |
+ // was the target of the mouse move event above. This should not crash when |
+ // the mouse exit handler returns from the child. |
+ ui::MouseEvent exit_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), |
+ ui::EventTimeForNow(), 0, 0); |
+ root_view->OnMouseExited(exit_event); |
+ |
+ EXPECT_TRUE(got_mouse_exit); |
+ EXPECT_FALSE(content->has_children()); |
+} |
+ |
} // namespace test |
} // namespace views |