#include "config.h"
#include "core/events/EventPath.h"
-#include "EventNames.h"
-#include "RuntimeEnabledFeatures.h"
-#include "SVGNames.h"
-#include "core/dom/FullscreenElementStack.h"
+#include "core/EventNames.h"
+#include "core/dom/Document.h"
#include "core/dom/Touch.h"
#include "core/dom/TouchList.h"
#include "core/dom/shadow/InsertionPoint.h"
#include "core/dom/shadow/ShadowRoot.h"
-#include "core/events/FocusEvent.h"
-#include "core/events/MouseEvent.h"
#include "core/events/TouchEvent.h"
#include "core/events/TouchEventContext.h"
-#include "core/svg/SVGElementInstance.h"
-#include "core/svg/SVGUseElement.h"
-namespace WebCore {
-
-Node* EventPath::parent(Node* node)
-{
- EventPath eventPath(node);
- return eventPath.size() > 1 ? eventPath[1].node() : 0;
-}
+namespace blink {
EventTarget* EventPath::eventTargetRespectingTargetRules(Node* referenceNode)
{
if (referenceNode->isPseudoElement())
return referenceNode->parentNode();
- if (!referenceNode->isSVGElement() || !referenceNode->isInShadowTree())
- return referenceNode;
-
- // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included
- // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects.
- Node& rootNode = referenceNode->treeScope().rootNode();
- Element* shadowHostElement = rootNode.isShadowRoot() ? toShadowRoot(rootNode).host() : 0;
- // At this time, SVG nodes are not supported in non-<use> shadow trees.
- if (!shadowHostElement || !shadowHostElement->hasTagName(SVGNames::useTag))
- return referenceNode;
- SVGUseElement* useElement = toSVGUseElement(shadowHostElement);
- if (SVGElementInstance* instance = useElement->instanceForShadowTreeElement(referenceNode))
- return instance;
-
return referenceNode;
}
-static inline bool inTheSameScope(ShadowRoot* shadowRoot, EventTarget* target)
-{
- return target->toNode() && target->toNode()->treeScope().rootNode() == shadowRoot;
-}
-
-static inline EventDispatchBehavior determineDispatchBehavior(Event* event, ShadowRoot* shadowRoot, EventTarget* target)
+static inline bool shouldStopAtShadowRoot(Event& event, ShadowRoot& shadowRoot, EventTarget& target)
{
- // Video-only full screen is a mode where we use the shadow DOM as an implementation
- // detail that should not be detectable by the web content.
- if (Element* element = FullscreenElementStack::currentFullScreenElementFrom(&target->toNode()->document())) {
- // FIXME: We assume that if the full screen element is a media element that it's
- // the video-only full screen. Both here and elsewhere. But that is probably wrong.
- if (element->isMediaElement() && shadowRoot && shadowRoot->host() == element)
- return StayInsideShadowDOM;
- }
-
// WebKit never allowed selectstart event to cross the the shadow DOM boundary.
// Changing this breaks existing sites.
// See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
- const AtomicString eventType = event->type();
- if (inTheSameScope(shadowRoot, target)
+ const AtomicString eventType = event.type();
+ return target.toNode() && target.toNode()->shadowHost() == shadowRoot.host()
&& (eventType == EventTypeNames::abort
|| eventType == EventTypeNames::change
|| eventType == EventTypeNames::error
|| eventType == EventTypeNames::resize
|| eventType == EventTypeNames::scroll
|| eventType == EventTypeNames::select
- || eventType == EventTypeNames::selectstart))
- return StayInsideShadowDOM;
-
- return RetargetEvent;
+ || eventType == EventTypeNames::selectstart);
}
EventPath::EventPath(Event* event)
- : m_node(0)
+ : m_node(nullptr)
, m_event(event)
{
}
EventPath::EventPath(Node* node)
: m_node(node)
- , m_event(0)
+ , m_event(nullptr)
{
resetWith(node);
}
m_treeScopeEventContexts.clear();
calculatePath();
calculateAdjustedTargets();
- calculateAdjustedEventPath();
+ calculateTreeScopePrePostOrderNumbers();
}
void EventPath::addNodeEventContext(Node* node)
{
ASSERT(m_node);
ASSERT(m_nodeEventContexts.isEmpty());
- m_node->document().updateDistributionForNodeIfNeeded(const_cast<Node*>(m_node));
+ m_node->document().updateDistributionForNodeIfNeeded(const_cast<Node*>(m_node.get()));
Node* current = m_node;
addNodeEventContext(current);
if (!m_node->inDocument())
return;
while (current) {
- if (current->isShadowRoot() && m_event && determineDispatchBehavior(m_event, toShadowRoot(current), m_node) == StayInsideShadowDOM)
+ if (m_event && current->keepEventInNode(m_event))
break;
- Vector<InsertionPoint*, 8> insertionPoints;
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
collectDestinationInsertionPoints(*current, insertionPoints);
if (!insertionPoints.isEmpty()) {
- for (size_t i = 0; i < insertionPoints.size(); ++i) {
- InsertionPoint* insertionPoint = insertionPoints[i];
+ for (const auto& insertionPoint : insertionPoints) {
if (insertionPoint->isShadowInsertionPoint()) {
ShadowRoot* containingShadowRoot = insertionPoint->containingShadowRoot();
ASSERT(containingShadowRoot);
continue;
}
if (current->isShadowRoot()) {
+ if (m_event && shouldStopAtShadowRoot(*m_event, *toShadowRoot(current), *m_node))
+ break;
current = current->shadowHost();
addNodeEventContext(current);
} else {
}
}
-void EventPath::calculateAdjustedEventPath()
+void EventPath::calculateTreeScopePrePostOrderNumbers()
{
- if (!RuntimeEnabledFeatures::shadowDOMEnabled())
- return;
- for (size_t i = 0; i < m_treeScopeEventContexts.size(); ++i) {
- TreeScopeEventContext* treeScopeEventContext = m_treeScopeEventContexts[i].get();
- Vector<RefPtr<Node> > nodes;
- nodes.reserveInitialCapacity(size());
- for (size_t i = 0; i < size(); ++i) {
- if (at(i).node()->treeScope().isInclusiveAncestorOf(treeScopeEventContext->treeScope()))
- nodes.append(at(i).node());
+ // Precondition:
+ // - TreeScopes in m_treeScopeEventContexts must be *connected* in the same tree of trees.
+ // - The root tree must be included.
+ WillBeHeapHashMap<RawPtrWillBeMember<const TreeScope>, RawPtrWillBeMember<TreeScopeEventContext>> treeScopeEventContextMap;
+ for (const auto& treeScopeEventContext : m_treeScopeEventContexts)
+ treeScopeEventContextMap.add(&treeScopeEventContext->treeScope(), treeScopeEventContext.get());
+ TreeScopeEventContext* rootTree = 0;
+ for (const auto& treeScopeEventContext : m_treeScopeEventContexts) {
+ // Use olderShadowRootOrParentTreeScope here for parent-child relationships.
+ // See the definition of trees of trees in the Shado DOM spec: http://w3c.github.io/webcomponents/spec/shadow/
+ TreeScope* parent = treeScopeEventContext.get()->treeScope().olderShadowRootOrParentTreeScope();
+ if (!parent) {
+ ASSERT(!rootTree);
+ rootTree = treeScopeEventContext.get();
+ continue;
}
- treeScopeEventContext->adoptEventPath(nodes);
+ ASSERT(treeScopeEventContextMap.find(parent) != treeScopeEventContextMap.end());
+ treeScopeEventContextMap.find(parent)->value->addChild(*treeScopeEventContext.get());
}
+ ASSERT(rootTree);
+ rootTree->calculatePrePostOrderNumber(0);
}
TreeScopeEventContext* EventPath::ensureTreeScopeEventContext(Node* currentTarget, TreeScope* treeScope, TreeScopeEventContextMap& treeScopeEventContextMap)
{
if (!treeScope)
return 0;
- TreeScopeEventContextMap::AddResult addResult = treeScopeEventContextMap.add(treeScope, TreeScopeEventContext::create(*treeScope));
- TreeScopeEventContext* treeScopeEventContext = addResult.iterator->value.get();
- if (addResult.isNewEntry) {
+ TreeScopeEventContext* treeScopeEventContext;
+ bool isNewEntry;
+ {
+ TreeScopeEventContextMap::AddResult addResult = treeScopeEventContextMap.add(treeScope, nullptr);
+ isNewEntry = addResult.isNewEntry;
+ if (isNewEntry)
+ addResult.storedValue->value = TreeScopeEventContext::create(*treeScope);
+ treeScopeEventContext = addResult.storedValue->value.get();
+ }
+ if (isNewEntry) {
TreeScopeEventContext* parentTreeScopeEventContext = ensureTreeScopeEventContext(0, treeScope->olderShadowRootOrParentTreeScope(), treeScopeEventContextMap);
if (parentTreeScopeEventContext && parentTreeScopeEventContext->target()) {
treeScopeEventContext->setTarget(parentTreeScopeEventContext->target());
void EventPath::calculateAdjustedTargets()
{
const TreeScope* lastTreeScope = 0;
- bool isSVGElement = at(0).node()->isSVGElement();
TreeScopeEventContextMap treeScopeEventContextMap;
TreeScopeEventContext* lastTreeScopeEventContext = 0;
Node* currentNode = at(i).node();
TreeScope& currentTreeScope = currentNode->treeScope();
if (lastTreeScope != ¤tTreeScope) {
- if (!isSVGElement) {
- lastTreeScopeEventContext = ensureTreeScopeEventContext(currentNode, ¤tTreeScope, treeScopeEventContextMap);
- } else {
- TreeScopeEventContextMap::AddResult addResult = treeScopeEventContextMap.add(¤tTreeScope, TreeScopeEventContext::create(currentTreeScope));
- lastTreeScopeEventContext = addResult.iterator->value.get();
- if (addResult.isNewEntry) {
- // Don't adjust an event target for SVG.
- lastTreeScopeEventContext->setTarget(eventTargetRespectingTargetRules(at(0).node()));
- }
- }
+ lastTreeScopeEventContext = ensureTreeScopeEventContext(currentNode, ¤tTreeScope, treeScopeEventContextMap);
}
ASSERT(lastTreeScopeEventContext);
at(i).setTreeScopeEventContext(lastTreeScopeEventContext);
void EventPath::buildRelatedNodeMap(const Node* relatedNode, RelatedTargetMap& relatedTargetMap)
{
- TreeScope* lastTreeScope = 0;
- EventPath eventPath(const_cast<Node*>(relatedNode));
- for (size_t i = 0; i < eventPath.size(); ++i) {
- TreeScope* treeScope = &eventPath[i].node()->treeScope();
- if (treeScope != lastTreeScope)
- relatedTargetMap.add(treeScope, eventPath[i].target());
- lastTreeScope = treeScope;
+ EventPath relatedTargetEventPath(const_cast<Node*>(relatedNode));
+ for (size_t i = 0; i < relatedTargetEventPath.m_treeScopeEventContexts.size(); ++i) {
+ TreeScopeEventContext* treeScopeEventContext = relatedTargetEventPath.m_treeScopeEventContexts[i].get();
+ relatedTargetMap.add(&treeScopeEventContext->treeScope(), treeScopeEventContext->target());
}
}
EventTarget* EventPath::findRelatedNode(TreeScope* scope, RelatedTargetMap& relatedTargetMap)
{
- Vector<TreeScope*, 32> parentTreeScopes;
+ WillBeHeapVector<RawPtrWillBeMember<TreeScope>, 32> parentTreeScopes;
EventTarget* relatedNode = 0;
while (scope) {
parentTreeScopes.append(scope);
- RelatedTargetMap::const_iterator found = relatedTargetMap.find(scope);
- if (found != relatedTargetMap.end()) {
- relatedNode = found->value;
+ RelatedTargetMap::const_iterator iter = relatedTargetMap.find(scope);
+ if (iter != relatedTargetMap.end() && iter->value) {
+ relatedNode = iter->value;
break;
}
scope = scope->olderShadowRootOrParentTreeScope();
}
- for (Vector<TreeScope*, 32>::iterator iter = parentTreeScopes.begin(); iter < parentTreeScopes.end(); ++iter)
+ ASSERT(relatedNode);
+ for (WillBeHeapVector<RawPtrWillBeMember<TreeScope>, 32>::iterator iter = parentTreeScopes.begin(); iter < parentTreeScopes.end(); ++iter)
relatedTargetMap.add(*iter, relatedNode);
return relatedNode;
}
Node* relatedNode = relatedTarget->toNode();
if (!relatedNode)
return;
+ if (target->document() != relatedNode->document())
+ return;
+ if (!target->inDocument() || !relatedNode->inDocument())
+ return;
+
RelatedTargetMap relatedNodeMap;
buildRelatedNodeMap(relatedNode, relatedNodeMap);
- for (size_t i = 0; i < m_treeScopeEventContexts.size(); ++i) {
- TreeScopeEventContext* treeScopeEventContext = m_treeScopeEventContexts[i].get();
- treeScopeEventContext->setRelatedTarget(findRelatedNode(&treeScopeEventContext->treeScope(), relatedNodeMap));
+ for (const auto& treeScopeEventContext : m_treeScopeEventContexts) {
+ EventTarget* adjustedRelatedTarget = findRelatedNode(&treeScopeEventContext.get()->treeScope(), relatedNodeMap);
+ ASSERT(adjustedRelatedTarget);
+ treeScopeEventContext.get()->setRelatedTarget(adjustedRelatedTarget);
}
shrinkIfNeeded(target, relatedTarget);
void EventPath::adjustForTouchEvent(Node* node, TouchEvent& touchEvent)
{
- Vector<TouchList*> adjustedTouches;
- Vector<TouchList*> adjustedTargetTouches;
- Vector<TouchList*> adjustedChangedTouches;
- Vector<TreeScope*> treeScopes;
+ WillBeHeapVector<RawPtrWillBeMember<TouchList>> adjustedTouches;
+ WillBeHeapVector<RawPtrWillBeMember<TouchList>> adjustedTargetTouches;
+ WillBeHeapVector<RawPtrWillBeMember<TouchList>> adjustedChangedTouches;
+ WillBeHeapVector<RawPtrWillBeMember<TreeScope>> treeScopes;
- for (size_t i = 0; i < m_treeScopeEventContexts.size(); ++i) {
- TouchEventContext* touchEventContext = m_treeScopeEventContexts[i]->ensureTouchEventContext();
+ for (const auto& treeScopeEventContext : m_treeScopeEventContexts) {
+ TouchEventContext* touchEventContext = treeScopeEventContext->ensureTouchEventContext();
adjustedTouches.append(&touchEventContext->touches());
adjustedTargetTouches.append(&touchEventContext->targetTouches());
adjustedChangedTouches.append(&touchEventContext->changedTouches());
- treeScopes.append(&m_treeScopeEventContexts[i]->treeScope());
+ treeScopes.append(&treeScopeEventContext->treeScope());
}
adjustTouchList(node, touchEvent.touches(), adjustedTouches, treeScopes);
adjustTouchList(node, touchEvent.targetTouches(), adjustedTargetTouches, treeScopes);
adjustTouchList(node, touchEvent.changedTouches(), adjustedChangedTouches, treeScopes);
-#ifndef NDEBUG
- for (size_t i = 0; i < m_treeScopeEventContexts.size(); ++i) {
- TreeScope& treeScope = m_treeScopeEventContexts[i]->treeScope();
- TouchEventContext* touchEventContext = m_treeScopeEventContexts[i]->touchEventContext();
+#if ENABLE(ASSERT)
+ for (const auto& treeScopeEventContext : m_treeScopeEventContexts) {
+ TreeScope& treeScope = treeScopeEventContext->treeScope();
+ TouchEventContext* touchEventContext = treeScopeEventContext->touchEventContext();
checkReachability(treeScope, touchEventContext->touches());
checkReachability(treeScope, touchEventContext->targetTouches());
checkReachability(treeScope, touchEventContext->changedTouches());
#endif
}
-void EventPath::adjustTouchList(const Node* node, const TouchList* touchList, Vector<TouchList*> adjustedTouchList, const Vector<TreeScope*>& treeScopes)
+void EventPath::adjustTouchList(const Node* node, const TouchList* touchList, WillBeHeapVector<RawPtrWillBeMember<TouchList>> adjustedTouchList, const WillBeHeapVector<RawPtrWillBeMember<TreeScope>>& treeScopes)
{
if (!touchList)
return;
}
}
-#ifndef NDEBUG
+#if ENABLE(ASSERT)
void EventPath::checkReachability(TreeScope& treeScope, TouchList& touchList)
{
for (size_t i = 0; i < touchList.length(); ++i)
}
#endif
+void EventPath::trace(Visitor* visitor)
+{
+ visitor->trace(m_nodeEventContexts);
+ visitor->trace(m_node);
+ visitor->trace(m_event);
+ visitor->trace(m_treeScopeEventContexts);
+}
+
} // namespace