diff options
author | mohsen <mohsen@chromium.org> | 2015-11-05 17:57:49 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-06 01:58:49 +0000 |
commit | a352204885ce8e8aebee2189bbed244a763aa7a2 (patch) | |
tree | 90824e0e19c7f23eb75f1c850b532544a98c0058 | |
parent | 030eefd4458334797d2a6bf40dd174e168b089a7 (diff) | |
download | chromium_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.cc | 60 | ||||
-rw-r--r-- | ui/aura/window_event_dispatcher.h | 9 | ||||
-rw-r--r-- | ui/aura/window_event_dispatcher_unittest.cc | 74 |
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 |