| // Copyright (c) 2012 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 "ui/events/event_dispatcher.h" |
| |
| #include <algorithm> |
| |
| #include "base/macros.h" |
| #include "ui/events/event_target.h" |
| #include "ui/events/event_targeter.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| class ScopedDispatchHelper : public Event::DispatcherApi { |
| public: |
| explicit ScopedDispatchHelper(Event* event) |
| : Event::DispatcherApi(event) { |
| set_result(ui::ER_UNHANDLED); |
| } |
| |
| virtual ~ScopedDispatchHelper() { |
| set_phase(EP_POSTDISPATCH); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ScopedDispatchHelper); |
| }; |
| |
| } // namespace |
| |
| EventDispatcherDelegate::EventDispatcherDelegate() |
| : dispatcher_(NULL) { |
| } |
| |
| EventDispatcherDelegate::~EventDispatcherDelegate() { |
| if (dispatcher_) |
| dispatcher_->OnDispatcherDelegateDestroyed(); |
| } |
| |
| Event* EventDispatcherDelegate::current_event() { |
| return dispatcher_ ? dispatcher_->current_event() : NULL; |
| } |
| |
| EventDispatchDetails EventDispatcherDelegate::DispatchEvent(EventTarget* target, |
| Event* event) { |
| CHECK(target); |
| Event::DispatcherApi dispatch_helper(event); |
| dispatch_helper.set_phase(EP_PREDISPATCH); |
| dispatch_helper.set_result(ER_UNHANDLED); |
| |
| EventDispatchDetails details = PreDispatchEvent(target, event); |
| if (!event->handled() && |
| !details.dispatcher_destroyed && |
| !details.target_destroyed) { |
| details = DispatchEventToTarget(target, event); |
| } |
| bool target_destroyed_during_dispatch = details.target_destroyed; |
| if (!details.dispatcher_destroyed) { |
| details = PostDispatchEvent(target_destroyed_during_dispatch ? |
| NULL : target, *event); |
| } |
| |
| details.target_destroyed |= target_destroyed_during_dispatch; |
| return details; |
| } |
| |
| EventDispatchDetails EventDispatcherDelegate::PreDispatchEvent( |
| EventTarget* target, Event* event) { |
| return EventDispatchDetails(); |
| } |
| |
| EventDispatchDetails EventDispatcherDelegate::PostDispatchEvent( |
| EventTarget* target, const Event& event) { |
| return EventDispatchDetails(); |
| } |
| |
| EventDispatchDetails EventDispatcherDelegate::DispatchEventToTarget( |
| EventTarget* target, |
| Event* event) { |
| EventDispatcher* old_dispatcher = dispatcher_; |
| EventDispatcher dispatcher(this); |
| dispatcher_ = &dispatcher; |
| dispatcher.ProcessEvent(target, event); |
| if (!dispatcher.delegate_destroyed()) |
| dispatcher_ = old_dispatcher; |
| else if (old_dispatcher) |
| old_dispatcher->OnDispatcherDelegateDestroyed(); |
| |
| EventDispatchDetails details; |
| details.dispatcher_destroyed = dispatcher.delegate_destroyed(); |
| details.target_destroyed = |
| (!details.dispatcher_destroyed && !CanDispatchToTarget(target)); |
| return details; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // EventDispatcher: |
| |
| EventDispatcher::EventDispatcher(EventDispatcherDelegate* delegate) |
| : delegate_(delegate), |
| current_event_(NULL) { |
| } |
| |
| EventDispatcher::~EventDispatcher() { |
| } |
| |
| void EventDispatcher::OnHandlerDestroyed(EventHandler* handler) { |
| handler_list_.erase(std::find(handler_list_.begin(), |
| handler_list_.end(), |
| handler)); |
| } |
| |
| void EventDispatcher::ProcessEvent(EventTarget* target, Event* event) { |
| if (!target || !target->CanAcceptEvent(*event)) |
| return; |
| |
| ScopedDispatchHelper dispatch_helper(event); |
| dispatch_helper.set_target(target); |
| |
| handler_list_.clear(); |
| target->GetPreTargetHandlers(&handler_list_); |
| |
| dispatch_helper.set_phase(EP_PRETARGET); |
| DispatchEventToEventHandlers(&handler_list_, event); |
| if (event->handled()) |
| return; |
| |
| // If the event hasn't been consumed, trigger the default handler. Note that |
| // even if the event has already been handled (i.e. return result has |
| // ER_HANDLED set), that means that the event should still be processed at |
| // this layer, however it should not be processed in the next layer of |
| // abstraction. |
| if (delegate_ && delegate_->CanDispatchToTarget(target) && |
| target->target_handler()) { |
| dispatch_helper.set_phase(EP_TARGET); |
| DispatchEvent(target->target_handler(), event); |
| if (event->handled()) |
| return; |
| } |
| |
| if (!delegate_ || !delegate_->CanDispatchToTarget(target)) |
| return; |
| |
| handler_list_.clear(); |
| target->GetPostTargetHandlers(&handler_list_); |
| dispatch_helper.set_phase(EP_POSTTARGET); |
| DispatchEventToEventHandlers(&handler_list_, event); |
| } |
| |
| void EventDispatcher::OnDispatcherDelegateDestroyed() { |
| delegate_ = NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // EventDispatcher, private: |
| |
| void EventDispatcher::DispatchEventToEventHandlers(EventHandlerList* list, |
| Event* event) { |
| for (EventHandlerList::const_iterator it = list->begin(), |
| end = list->end(); it != end; ++it) { |
| (*it)->dispatchers_.push(this); |
| } |
| |
| while (!list->empty()) { |
| EventHandler* handler = (*list->begin()); |
| if (delegate_ && !event->stopped_propagation()) |
| DispatchEvent(handler, event); |
| |
| if (!list->empty() && *list->begin() == handler) { |
| // The handler has not been destroyed (because if it were, then it would |
| // have been removed from the list). |
| CHECK(handler->dispatchers_.top() == this); |
| handler->dispatchers_.pop(); |
| list->erase(list->begin()); |
| } |
| } |
| } |
| |
| void EventDispatcher::DispatchEvent(EventHandler* handler, Event* event) { |
| // If the target has been invalidated or deleted, don't dispatch the event. |
| if (!delegate_->CanDispatchToTarget(event->target())) { |
| if (event->cancelable()) |
| event->StopPropagation(); |
| return; |
| } |
| |
| base::AutoReset<Event*> event_reset(¤t_event_, event); |
| handler->OnEvent(event); |
| if (!delegate_ && event->cancelable()) |
| event->StopPropagation(); |
| } |
| |
| } // namespace ui |