EventDispatcher.cpp [plain text]
#include "config.h"
#include "EventDispatcher.h"
#include "EventContext.h"
#include "EventDispatchMediator.h"
#include "FrameView.h"
#include "HTMLMediaElement.h"
#include "InspectorInstrumentation.h"
#include "MouseEvent.h"
#include "ScopedEventQueue.h"
#include "WindowEventContext.h"
#include <wtf/RefPtr.h>
#include <wtf/UnusedParam.h>
#if ENABLE(SVG)
#include "SVGElementInstance.h"
#include "SVGNames.h"
#include "SVGUseElement.h"
#endif
namespace WebCore {
static HashSet<Node*>* gNodesDispatchingSimulatedClicks = 0;
bool EventDispatcher::dispatchEvent(Node* node, PassRefPtr<EventDispatchMediator> mediator)
{
ASSERT(!eventDispatchForbidden());
EventDispatcher dispatcher(node);
return mediator->dispatchEvent(&dispatcher);
}
inline static EventTarget* eventTargetRespectingSVGTargetRules(Node* referenceNode)
{
ASSERT(referenceNode);
#if ENABLE(SVG)
if (!referenceNode->isSVGElement() || !referenceNode->isInShadowTree())
return referenceNode;
Element* shadowHostElement = referenceNode->treeScope()->rootNode()->shadowHost();
ASSERT(!shadowHostElement || shadowHostElement->hasTagName(SVGNames::useTag));
if (shadowHostElement && shadowHostElement->hasTagName(SVGNames::useTag)) {
SVGUseElement* useElement = static_cast<SVGUseElement*>(shadowHostElement);
if (SVGElementInstance* instance = useElement->instanceForShadowTreeElement(referenceNode))
return instance;
}
#endif
return referenceNode;
}
void EventDispatcher::dispatchScopedEvent(Node* node, PassRefPtr<EventDispatchMediator> mediator)
{
mediator->event()->setTarget(eventTargetRespectingSVGTargetRules(node));
ScopedEventQueue::instance()->enqueueEventDispatchMediator(mediator);
}
void EventDispatcher::dispatchSimulatedClick(Node* node, PassRefPtr<Event> underlyingEvent, bool sendMouseEvents, bool showPressedLook)
{
if (node->disabled())
return;
EventDispatcher dispatcher(node);
if (!gNodesDispatchingSimulatedClicks)
gNodesDispatchingSimulatedClicks = new HashSet<Node*>;
else if (gNodesDispatchingSimulatedClicks->contains(node))
return;
gNodesDispatchingSimulatedClicks->add(node);
if (sendMouseEvents)
dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().mousedownEvent, node->document()->defaultView(), underlyingEvent));
node->setActive(true, showPressedLook);
if (sendMouseEvents)
dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().mouseupEvent, node->document()->defaultView(), underlyingEvent));
node->setActive(false);
dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().clickEvent, node->document()->defaultView(), underlyingEvent));
gNodesDispatchingSimulatedClicks->remove(node);
}
static inline bool isShadowHost(Node* node)
{
return node->isElementNode() && toElement(node)->hasShadowRoot();
}
PassRefPtr<EventTarget> EventDispatcher::adjustToShadowBoundaries(PassRefPtr<Node> relatedTarget, const Vector<Node*> relatedTargetAncestors)
{
Vector<EventContext>::const_iterator lowestCommonBoundary = m_ancestors.end();
Vector<Node*>::const_iterator firstDivergentBoundary = relatedTargetAncestors.begin();
Vector<EventContext>::const_iterator targetAncestor = m_ancestors.end();
bool diverged = false;
for (Vector<Node*>::const_iterator i = relatedTargetAncestors.end() - 1; i >= relatedTargetAncestors.begin(); --i) {
if (diverged) {
if ((*i)->isShadowRoot()) {
firstDivergentBoundary = i + 1;
break;
}
continue;
}
if (targetAncestor == m_ancestors.begin()) {
diverged = true;
continue;
}
targetAncestor--;
if ((*i)->isShadowRoot())
lowestCommonBoundary = targetAncestor;
if ((*i) != (*targetAncestor).node())
diverged = true;
}
if (!diverged) {
if (m_node->shadowHost() == relatedTarget.get() || isShadowHost(relatedTarget.get())) {
Vector<EventContext>::const_iterator relatedTargetChild = targetAncestor - 1;
if (relatedTargetChild >= m_ancestors.begin() && relatedTargetChild->node()->isShadowRoot())
lowestCommonBoundary = relatedTargetChild;
}
} else if ((*firstDivergentBoundary) == m_node.get()) {
lowestCommonBoundary = m_ancestors.begin();
m_shouldPreventDispatch = true;
}
if (lowestCommonBoundary != m_ancestors.end()) {
m_ancestors.shrink(lowestCommonBoundary - m_ancestors.begin());
}
return firstDivergentBoundary != relatedTargetAncestors.begin() ? *firstDivergentBoundary : relatedTarget;
}
inline static bool ancestorsCrossShadowBoundaries(const Vector<EventContext>& ancestors)
{
return ancestors.isEmpty() || ancestors.first().node() == ancestors.last().node();
}
PassRefPtr<EventTarget> EventDispatcher::adjustRelatedTarget(Event* event, PassRefPtr<EventTarget> prpRelatedTarget)
{
if (!prpRelatedTarget)
return 0;
RefPtr<Node> relatedTarget = prpRelatedTarget->toNode();
if (!relatedTarget)
return 0;
Node* target = m_node.get();
if (!target)
return prpRelatedTarget;
ensureEventAncestors(event);
bool noCommonBoundary = ancestorsCrossShadowBoundaries(m_ancestors);
Vector<Node*> relatedTargetAncestors;
Node* outermostShadowBoundary = relatedTarget.get();
for (Node* n = outermostShadowBoundary; n; n = n->parentOrHostNode()) {
if (n->isShadowRoot())
outermostShadowBoundary = n->parentOrHostNode();
if (!noCommonBoundary)
relatedTargetAncestors.append(n);
}
if (noCommonBoundary)
return outermostShadowBoundary;
return adjustToShadowBoundaries(relatedTarget.release(), relatedTargetAncestors);
}
EventDispatcher::EventDispatcher(Node* node)
: m_node(node)
, m_ancestorsInitialized(false)
, m_shouldPreventDispatch(false)
{
ASSERT(node);
m_view = node->document()->view();
}
void EventDispatcher::ensureEventAncestors(Event* event)
{
if (!m_node->inDocument())
return;
if (m_ancestorsInitialized)
return;
m_ancestorsInitialized = true;
Node* ancestor = m_node.get();
EventTarget* target = eventTargetRespectingSVGTargetRules(ancestor);
while (true) {
if (ancestor->isShadowRoot()) {
if (determineDispatchBehavior(event, ancestor) == StayInsideShadowDOM)
return;
ancestor = ancestor->shadowHost();
if (!m_node->isSVGElement())
target = ancestor;
} else
ancestor = ancestor->parentNodeGuaranteedHostFree();
if (!ancestor)
return;
m_ancestors.append(EventContext(ancestor, eventTargetRespectingSVGTargetRules(ancestor), target));
}
}
bool EventDispatcher::dispatchEvent(PassRefPtr<Event> event)
{
event->setTarget(eventTargetRespectingSVGTargetRules(m_node.get()));
ASSERT(!eventDispatchForbidden());
ASSERT(event->target());
ASSERT(!event->type().isNull());
RefPtr<EventTarget> originalTarget = event->target();
ensureEventAncestors(event.get());
WindowEventContext windowContext(event.get(), m_node.get(), topEventContext());
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEvent(m_node->document(), *event, windowContext.window(), m_node.get(), m_ancestors);
void* data = m_node->preDispatchEventHandler(event.get());
if (m_shouldPreventDispatch || event->propagationStopped())
goto doneDispatching;
event->setEventPhase(Event::CAPTURING_PHASE);
if (windowContext.handleLocalEvents(event.get()) && event->propagationStopped())
goto doneDispatching;
for (size_t i = m_ancestors.size(); i; --i) {
const EventContext& eventContext = m_ancestors[i-1];
if (eventContext.currentTargetSameAsTarget()) {
if (event->bubbles())
continue;
event->setEventPhase(Event::AT_TARGET);
} else
event->setEventPhase(Event::CAPTURING_PHASE);
eventContext.handleLocalEvents(event.get());
if (event->propagationStopped())
goto doneDispatching;
}
event->setEventPhase(Event::AT_TARGET);
event->setTarget(originalTarget.get());
event->setCurrentTarget(eventTargetRespectingSVGTargetRules(m_node.get()));
m_node->handleLocalEvents(event.get());
if (event->propagationStopped())
goto doneDispatching;
if (event->bubbles() && !event->cancelBubble()) {
event->setEventPhase(Event::BUBBLING_PHASE);
size_t size = m_ancestors.size();
for (size_t i = 0; i < size; ++i) {
const EventContext& eventContext = m_ancestors[i];
if (eventContext.currentTargetSameAsTarget())
event->setEventPhase(Event::AT_TARGET);
else
event->setEventPhase(Event::BUBBLING_PHASE);
eventContext.handleLocalEvents(event.get());
if (event->propagationStopped() || event->cancelBubble())
goto doneDispatching;
}
windowContext.handleLocalEvents(event.get());
}
doneDispatching:
event->setTarget(originalTarget.get());
event->setCurrentTarget(0);
event->setEventPhase(0);
m_node->postDispatchEventHandler(event.get(), data);
if (!event->defaultPrevented() && !event->defaultHandled()) {
m_node->defaultEventHandler(event.get());
ASSERT(!event->defaultPrevented());
if (event->defaultHandled())
goto doneWithDefault;
if (event->bubbles()) {
size_t size = m_ancestors.size();
for (size_t i = 0; i < size; ++i) {
m_ancestors[i].node()->defaultEventHandler(event.get());
ASSERT(!event->defaultPrevented());
if (event->defaultHandled())
goto doneWithDefault;
}
}
}
doneWithDefault:
event->setTarget(windowContext.target());
event->setCurrentTarget(0);
InspectorInstrumentation::didDispatchEvent(cookie);
return !event->defaultPrevented();
}
const EventContext* EventDispatcher::topEventContext()
{
return m_ancestors.isEmpty() ? 0 : &m_ancestors.last();
}
EventDispatchBehavior EventDispatcher::determineDispatchBehavior(Event* event, Node* shadowRoot)
{
#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO)
if (Element* element = m_node->document()->webkitCurrentFullScreenElement()) {
if (element->isMediaElement() && shadowRoot && shadowRoot->shadowHost() == element)
return StayInsideShadowDOM;
}
#else
UNUSED_PARAM(shadowRoot);
#endif
if (event->hasInterface(eventNames().interfaceForMutationEvent))
return StayInsideShadowDOM;
if (event->type() == eventNames().selectstartEvent)
return StayInsideShadowDOM;
return RetargetEvent;
}
}