summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormohsen <mohsen@chromium.org>2015-11-05 17:57:49 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-06 01:58:49 +0000
commita352204885ce8e8aebee2189bbed244a763aa7a2 (patch)
tree90824e0e19c7f23eb75f1c850b532544a98c0058
parent030eefd4458334797d2a6bf40dd174e168b089a7 (diff)
downloadchromium_src-a352204885ce8e8aebee2189bbed244a763aa7a2.zip
chromium_src-a352204885ce8e8aebee2189bbed244a763aa7a2.tar.gz
chromium_src-a352204885ce8e8aebee2189bbed244a763aa7a2.tar.bz2
Check if dispatcher is destroyed during PreDispatch
During pre-dispatch phase of an event, the dispatcher might get destroyed. We need to stop dispatching the event in that case. BUG=412019 Review URL: https://codereview.chromium.org/1417033007 Cr-Commit-Position: refs/heads/master@{#358229}
-rw-r--r--ui/aura/window_event_dispatcher.cc60
-rw-r--r--ui/aura/window_event_dispatcher.h9
-rw-r--r--ui/aura/window_event_dispatcher_unittest.cc74
3 files changed, 107 insertions, 36 deletions
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 5d0e73d..88ad086 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -462,14 +462,20 @@ ui::EventDispatchDetails WindowEventDispatcher::PreDispatchEvent(
}
}
+ DispatchDetails details;
if (event->IsMouseEvent()) {
- PreDispatchMouseEvent(target_window, static_cast<ui::MouseEvent*>(event));
+ details = PreDispatchMouseEvent(target_window,
+ static_cast<ui::MouseEvent*>(event));
} else if (event->IsScrollEvent()) {
- PreDispatchLocatedEvent(target_window,
- static_cast<ui::ScrollEvent*>(event));
+ details = PreDispatchLocatedEvent(target_window,
+ static_cast<ui::ScrollEvent*>(event));
} else if (event->IsTouchEvent()) {
- PreDispatchTouchEvent(target_window, static_cast<ui::TouchEvent*>(event));
+ details = PreDispatchTouchEvent(target_window,
+ static_cast<ui::TouchEvent*>(event));
}
+ if (details.dispatcher_destroyed || details.target_destroyed)
+ return details;
+
old_dispatch_target_ = event_dispatch_target_;
event_dispatch_target_ = target_window;
return DispatchDetails();
@@ -729,8 +735,9 @@ ui::EventDispatchDetails WindowEventDispatcher::SynthesizeMouseMoveEvent() {
return OnEventFromSource(&event);
}
-void WindowEventDispatcher::PreDispatchLocatedEvent(Window* target,
- ui::LocatedEvent* event) {
+DispatchDetails WindowEventDispatcher::PreDispatchLocatedEvent(
+ Window* target,
+ ui::LocatedEvent* event) {
int flags = event->flags();
if (IsNonClientLocation(target, event->location()))
flags |= ui::EF_IS_NON_CLIENT;
@@ -743,10 +750,13 @@ void WindowEventDispatcher::PreDispatchLocatedEvent(Window* target,
SetLastMouseLocation(window(), event->root_location());
synthesize_mouse_move_ = false;
}
+
+ return DispatchDetails();
}
-void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
- ui::MouseEvent* event) {
+DispatchDetails WindowEventDispatcher::PreDispatchMouseEvent(
+ Window* target,
+ ui::MouseEvent* event) {
client::CursorClient* cursor_client = client::GetCursorClient(window());
// We allow synthesized mouse exit events through even if mouse events are
// disabled. This ensures that hover state, etc on controls like buttons is
@@ -756,7 +766,7 @@ void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
(event->flags() & ui::EF_IS_SYNTHESIZED) &&
(event->type() != ui::ET_MOUSE_EXITED)) {
event->SetHandled();
- return;
+ return DispatchDetails();
}
if (IsEventCandidateForHold(*event) && !dispatching_held_event_) {
@@ -767,7 +777,7 @@ void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
}
held_move_event_.reset(new ui::MouseEvent(*event, target, window()));
event->SetHandled();
- return;
+ return DispatchDetails();
} else {
// We may have a held event for a period between the time move_hold_count_
// fell to 0 and the DispatchHeldEvents executes. Since we're going to
@@ -788,7 +798,7 @@ void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
DispatchMouseEnterOrExit(target, *event, ui::ET_MOUSE_EXITED);
if (details.dispatcher_destroyed) {
event->SetHandled();
- return;
+ return details;
}
mouse_moved_handler_ = NULL;
}
@@ -803,20 +813,25 @@ void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
live_window.Add(target);
DispatchDetails details =
DispatchMouseEnterOrExit(target, *event, ui::ET_MOUSE_EXITED);
+ // |details| contains information about |mouse_moved_handler_| being
+ // destroyed which is not our |target|. Return value of this function
+ // should be about our |target|.
+ DispatchDetails target_details = details;
+ target_details.target_destroyed = !live_window.Contains(target);
if (details.dispatcher_destroyed) {
event->SetHandled();
- return;
+ return target_details;
}
// If the |mouse_moved_handler_| changes out from under us, assume a
// nested message loop ran and we don't need to do anything.
if (mouse_moved_handler_ != old_mouse_moved_handler) {
event->SetHandled();
- return;
+ return target_details;
}
- if (!live_window.Contains(target) || details.target_destroyed) {
+ if (details.target_destroyed || target_details.target_destroyed) {
mouse_moved_handler_ = NULL;
event->SetHandled();
- return;
+ return target_details;
}
live_window.Remove(target);
@@ -825,7 +840,7 @@ void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
DispatchMouseEnterOrExit(target, *event, ui::ET_MOUSE_ENTERED);
if (details.dispatcher_destroyed || details.target_destroyed) {
event->SetHandled();
- return;
+ return details;
}
}
break;
@@ -848,11 +863,12 @@ void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
break;
}
- PreDispatchLocatedEvent(target, event);
+ return PreDispatchLocatedEvent(target, event);
}
-void WindowEventDispatcher::PreDispatchTouchEvent(Window* target,
- ui::TouchEvent* event) {
+DispatchDetails WindowEventDispatcher::PreDispatchTouchEvent(
+ Window* target,
+ ui::TouchEvent* event) {
switch (event->type()) {
case ui::ET_TOUCH_PRESSED:
touch_ids_down_ |= (1 << event->touch_id());
@@ -874,7 +890,7 @@ void WindowEventDispatcher::PreDispatchTouchEvent(Window* target,
if (move_hold_count_ && !dispatching_held_event_) {
held_move_event_.reset(new ui::TouchEvent(*event, target, window()));
event->SetHandled();
- return;
+ return DispatchDetails();
}
break;
@@ -889,14 +905,14 @@ void WindowEventDispatcher::PreDispatchTouchEvent(Window* target,
// The event is invalid - ignore it.
event->StopPropagation();
event->DisableSynchronousHandling();
- return;
+ return DispatchDetails();
}
// This flag is set depending on the gestures recognized in the call above,
// and needs to propagate with the forwarded event.
event->set_may_cause_scrolling(orig_event.may_cause_scrolling());
- PreDispatchLocatedEvent(target, event);
+ return PreDispatchLocatedEvent(target, event);
}
} // namespace aura
diff --git a/ui/aura/window_event_dispatcher.h b/ui/aura/window_event_dispatcher.h
index aa49e81..a44eace 100644
--- a/ui/aura/window_event_dispatcher.h
+++ b/ui/aura/window_event_dispatcher.h
@@ -234,9 +234,12 @@ class AURA_EXPORT WindowEventDispatcher : public ui::EventProcessor,
// the mouse cursor.
void SynthesizeMouseMoveAfterChangeToWindow(Window* window);
- void PreDispatchLocatedEvent(Window* target, ui::LocatedEvent* event);
- void PreDispatchMouseEvent(Window* target, ui::MouseEvent* event);
- void PreDispatchTouchEvent(Window* target, ui::TouchEvent* event);
+ ui::EventDispatchDetails PreDispatchLocatedEvent(Window* target,
+ ui::LocatedEvent* event);
+ ui::EventDispatchDetails PreDispatchMouseEvent(Window* target,
+ ui::MouseEvent* event);
+ ui::EventDispatchDetails PreDispatchTouchEvent(Window* target,
+ ui::TouchEvent* event);
WindowTreeHost* host_;
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index 38c5f2a..f9e80f4 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -1535,26 +1535,31 @@ TEST_F(WindowEventDispatcherTest, GestureRepostEventOrder) {
root_window()->RemovePreTargetHandler(&repost_event_recorder);
}
+// An event filter that deletes the specified object when sees a mouse-exited
+// event.
+template <class T>
class OnMouseExitDeletingEventFilter : public EventFilterRecorder {
public:
- OnMouseExitDeletingEventFilter() : window_to_delete_(NULL) {}
+ explicit OnMouseExitDeletingEventFilter(T* object_to_delete)
+ : object_to_delete_(object_to_delete) {}
+ OnMouseExitDeletingEventFilter() : object_to_delete_(nullptr) {}
~OnMouseExitDeletingEventFilter() override {}
- void set_window_to_delete(Window* window_to_delete) {
- window_to_delete_ = window_to_delete;
+ void set_object_to_delete(T* object_to_delete) {
+ object_to_delete_ = object_to_delete;
}
private:
- // Overridden from ui::EventHandler:
+ // Overridden from ui::EventFilterRecorder.
void OnMouseEvent(ui::MouseEvent* event) override {
EventFilterRecorder::OnMouseEvent(event);
- if (window_to_delete_) {
- delete window_to_delete_;
- window_to_delete_ = NULL;
+ if (object_to_delete_ && event->type() == ui::ET_MOUSE_EXITED) {
+ delete object_to_delete_;
+ object_to_delete_ = nullptr;
}
}
- Window* window_to_delete_;
+ T* object_to_delete_;
DISALLOW_COPY_AND_ASSIGN(OnMouseExitDeletingEventFilter);
};
@@ -1566,7 +1571,7 @@ TEST_F(WindowEventDispatcherTest, DeleteWindowDuringMouseMovedDispatch) {
// Create window 1 and set its event filter. Window 1 will take ownership of
// the event filter.
scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL));
- OnMouseExitDeletingEventFilter w1_filter;
+ OnMouseExitDeletingEventFilter<Window> w1_filter;
w1->AddPreTargetHandler(&w1_filter);
w1->SetBounds(gfx::Rect(20, 20, 60, 60));
EXPECT_EQ(NULL, host()->dispatcher()->mouse_moved_handler());
@@ -1585,9 +1590,9 @@ TEST_F(WindowEventDispatcherTest, DeleteWindowDuringMouseMovedDispatch) {
// Set window 2 as the window that is to be deleted when a mouse-exited event
// happens on window 1.
- w1_filter.set_window_to_delete(w2);
+ w1_filter.set_object_to_delete(w2);
- // Move mosue over window 2. This should generate a mouse-exited event for
+ // Move mouse over window 2. This should generate a mouse-exited event for
// window 1 resulting in deletion of window 2. The original mouse-moved event
// that was targeted to window 2 should be dropped since window 2 is
// destroyed. This test passes if no crash happens.
@@ -1599,6 +1604,53 @@ TEST_F(WindowEventDispatcherTest, DeleteWindowDuringMouseMovedDispatch) {
EventTypesToString(w1_filter.events()));
}
+// Tests the case where the event dispatcher is deleted during the pre-dispatch
+// phase of dispatching and event.
+TEST_F(WindowEventDispatcherTest, DeleteDispatcherDuringPreDispatch) {
+ // Create a host for the window hierarchy. This host will be destroyed later
+ // on.
+ WindowTreeHost* host = WindowTreeHost::Create(gfx::Rect(0, 0, 100, 100));
+ host->InitHost();
+
+ // Create two windows.
+ Window* w1 = CreateNormalWindow(1, host->window(), nullptr);
+ w1->SetBounds(gfx::Rect(20, 20, 60, 60));
+ Window* w2 = CreateNormalWindow(2, host->window(), nullptr);
+ w2->SetBounds(gfx::Rect(80, 20, 120, 60));
+ EXPECT_EQ(nullptr, host->dispatcher()->mouse_moved_handler());
+
+ ui::test::EventGenerator generator(host->window(), w1);
+
+ // Move mouse over window 1 to set it as the |mouse_moved_handler_| for the
+ // root window.
+ generator.MoveMouseTo(40, 40);
+ EXPECT_EQ(w1, host->dispatcher()->mouse_moved_handler());
+
+ // Set appropriate event filters for the two windows with the window tree host
+ // as the object that is to be deleted when a mouse-exited event happens on
+ // window 1. The windows will take ownership of the event filters.
+ OnMouseExitDeletingEventFilter<WindowTreeHost> w1_filter(host);
+ w1->AddPreTargetHandler(&w1_filter);
+ EventFilterRecorder w2_filter;
+ w2->AddPreTargetHandler(&w2_filter);
+
+ // Move mouse over window 2. This should generate a mouse-exited event for
+ // window 1 resulting in deletion of window tree host and its event
+ // dispatcher. The event dispatching should abort since the dispatcher is
+ // destroyed. This test passes if no crash happens.
+ // Here we can't use EventGenerator since it expects that the dispatcher is
+ // not destroyed at the end of the dispatch.
+ ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, gfx::Point(20, 20),
+ gfx::Point(20, 20), base::TimeDelta(), 0, 0);
+ ui::EventDispatchDetails details =
+ host->dispatcher()->DispatchEvent(w2, &mouse_move);
+ EXPECT_TRUE(details.dispatcher_destroyed);
+
+ // Check events received by the two windows.
+ EXPECT_EQ("MOUSE_EXITED", EventTypesToString(w1_filter.events()));
+ EXPECT_EQ(std::string(), EventTypesToString(w2_filter.events()));
+}
+
namespace {
// Used to track if OnWindowDestroying() is invoked and if there is a valid