// 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 "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)) { dispatch_helper.set_phase(EP_TARGET); DispatchEvent(target, 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