2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Neither the name of Google Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "core/events/EventPath.h"
30 #include "core/EventNames.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Touch.h"
33 #include "core/dom/TouchList.h"
34 #include "core/dom/shadow/InsertionPoint.h"
35 #include "core/dom/shadow/ShadowRoot.h"
36 #include "core/events/TouchEvent.h"
37 #include "core/events/TouchEventContext.h"
41 EventTarget
* EventPath::eventTargetRespectingTargetRules(Node
& referenceNode
)
43 if (referenceNode
.isPseudoElement()) {
44 ASSERT(referenceNode
.parentNode());
45 return referenceNode
.parentNode();
48 return &referenceNode
;
51 static inline bool shouldStopAtShadowRoot(Event
& event
, ShadowRoot
& shadowRoot
, EventTarget
& target
)
53 // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
54 // Changing this breaks existing sites.
55 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
56 const AtomicString eventType
= event
.type();
57 return target
.toNode() && target
.toNode()->shadowHost() == shadowRoot
.host()
58 && (eventType
== EventTypeNames::abort
59 || eventType
== EventTypeNames::change
60 || eventType
== EventTypeNames::error
61 || eventType
== EventTypeNames::load
62 || eventType
== EventTypeNames::reset
63 || eventType
== EventTypeNames::resize
64 || eventType
== EventTypeNames::scroll
65 || eventType
== EventTypeNames::select
66 || eventType
== EventTypeNames::selectstart
);
69 EventPath::EventPath(Node
& node
, Event
* event
)
76 void EventPath::initializeWith(Node
& node
, Event
* event
)
80 m_windowEventContext
= nullptr;
81 m_nodeEventContexts
.clear();
82 m_treeScopeEventContexts
.clear();
86 static inline bool eventPathShouldBeEmptyFor(Node
& node
)
88 return node
.isPseudoElement() && !node
.parentElement();
91 void EventPath::initialize()
93 if (eventPathShouldBeEmptyFor(*m_node
))
96 calculateAdjustedTargets();
97 calculateTreeOrderAndSetNearestAncestorClosedTree();
100 void EventPath::calculatePath()
103 ASSERT(m_nodeEventContexts
.isEmpty());
104 m_node
->updateDistribution();
106 // For performance and memory usage reasons we want to store the
107 // path using as few bytes as possible and with as few allocations
108 // as possible which is why we gather the data on the stack before
109 // storing it in a perfectly sized m_nodeEventContexts Vector.
110 WillBeHeapVector
<RawPtrWillBeMember
<Node
>, 64> nodesInPath
;
111 Node
* current
= m_node
;
112 nodesInPath
.append(current
);
114 if (m_event
&& current
->keepEventInNode(m_event
))
116 WillBeHeapVector
<RawPtrWillBeMember
<InsertionPoint
>, 8> insertionPoints
;
117 collectDestinationInsertionPoints(*current
, insertionPoints
);
118 if (!insertionPoints
.isEmpty()) {
119 for (const auto& insertionPoint
: insertionPoints
) {
120 if (insertionPoint
->isShadowInsertionPoint()) {
121 ShadowRoot
* containingShadowRoot
= insertionPoint
->containingShadowRoot();
122 ASSERT(containingShadowRoot
);
123 if (!containingShadowRoot
->isOldest())
124 nodesInPath
.append(containingShadowRoot
->olderShadowRoot());
126 nodesInPath
.append(insertionPoint
);
128 current
= insertionPoints
.last();
131 if (current
->isShadowRoot()) {
132 if (m_event
&& shouldStopAtShadowRoot(*m_event
, *toShadowRoot(current
), *m_node
))
134 current
= current
->shadowHost();
136 // TODO(kochi): crbug.com/507413 This check is necessary when some asynchronous event
137 // is queued while its shadow host is removed and the shadow root gets the event
138 // immediately after it. When Oilpan is enabled, this situation does not happen.
139 // Except this case, shadow root's host is assumed to be non-null.
141 nodesInPath
.append(current
);
143 nodesInPath
.append(current
);
146 current
= current
->parentNode();
148 nodesInPath
.append(current
);
152 m_nodeEventContexts
.reserveCapacity(nodesInPath
.size());
153 for (Node
* nodeInPath
: nodesInPath
) {
154 m_nodeEventContexts
.append(NodeEventContext(nodeInPath
, eventTargetRespectingTargetRules(*nodeInPath
)));
158 void EventPath::calculateTreeOrderAndSetNearestAncestorClosedTree()
161 // - TreeScopes in m_treeScopeEventContexts must be *connected* in the same tree of trees.
162 // - The root tree must be included.
163 WillBeHeapHashMap
<RawPtrWillBeMember
<const TreeScope
>, RawPtrWillBeMember
<TreeScopeEventContext
>> treeScopeEventContextMap
;
164 for (const auto& treeScopeEventContext
: m_treeScopeEventContexts
)
165 treeScopeEventContextMap
.add(&treeScopeEventContext
->treeScope(), treeScopeEventContext
.get());
166 TreeScopeEventContext
* rootTree
= nullptr;
167 for (const auto& treeScopeEventContext
: m_treeScopeEventContexts
) {
168 // Use olderShadowRootOrParentTreeScope here for parent-child relationships.
169 // See the definition of trees of trees in the Shadow DOM spec:
170 // http://w3c.github.io/webcomponents/spec/shadow/
171 TreeScope
* parent
= treeScopeEventContext
.get()->treeScope().olderShadowRootOrParentTreeScope();
174 rootTree
= treeScopeEventContext
.get();
177 ASSERT(treeScopeEventContextMap
.find(parent
) != treeScopeEventContextMap
.end());
178 treeScopeEventContextMap
.find(parent
)->value
->addChild(*treeScopeEventContext
.get());
181 rootTree
->calculateTreeOrderAndSetNearestAncestorClosedTree(0, nullptr);
184 TreeScopeEventContext
* EventPath::ensureTreeScopeEventContext(Node
* currentTarget
, TreeScope
* treeScope
, TreeScopeEventContextMap
& treeScopeEventContextMap
)
188 TreeScopeEventContext
* treeScopeEventContext
;
191 TreeScopeEventContextMap::AddResult addResult
= treeScopeEventContextMap
.add(treeScope
, nullptr);
192 isNewEntry
= addResult
.isNewEntry
;
194 addResult
.storedValue
->value
= TreeScopeEventContext::create(*treeScope
);
195 treeScopeEventContext
= addResult
.storedValue
->value
.get();
198 TreeScopeEventContext
* parentTreeScopeEventContext
= ensureTreeScopeEventContext(0, treeScope
->olderShadowRootOrParentTreeScope(), treeScopeEventContextMap
);
199 if (parentTreeScopeEventContext
&& parentTreeScopeEventContext
->target()) {
200 treeScopeEventContext
->setTarget(parentTreeScopeEventContext
->target());
201 } else if (currentTarget
) {
202 treeScopeEventContext
->setTarget(eventTargetRespectingTargetRules(*currentTarget
));
204 } else if (!treeScopeEventContext
->target() && currentTarget
) {
205 treeScopeEventContext
->setTarget(eventTargetRespectingTargetRules(*currentTarget
));
207 return treeScopeEventContext
;
210 void EventPath::calculateAdjustedTargets()
212 const TreeScope
* lastTreeScope
= nullptr;
214 TreeScopeEventContextMap treeScopeEventContextMap
;
215 TreeScopeEventContext
* lastTreeScopeEventContext
= nullptr;
217 for (size_t i
= 0; i
< size(); ++i
) {
218 Node
* currentNode
= at(i
).node();
219 TreeScope
& currentTreeScope
= currentNode
->treeScope();
220 if (lastTreeScope
!= ¤tTreeScope
) {
221 lastTreeScopeEventContext
= ensureTreeScopeEventContext(currentNode
, ¤tTreeScope
, treeScopeEventContextMap
);
223 ASSERT(lastTreeScopeEventContext
);
224 at(i
).setTreeScopeEventContext(lastTreeScopeEventContext
);
225 lastTreeScope
= ¤tTreeScope
;
227 m_treeScopeEventContexts
.appendRange(treeScopeEventContextMap
.values().begin(), treeScopeEventContextMap
.values().end());
230 void EventPath::buildRelatedNodeMap(const Node
& relatedNode
, RelatedTargetMap
& relatedTargetMap
)
232 OwnPtrWillBeRawPtr
<EventPath
> relatedTargetEventPath
= adoptPtrWillBeNoop(new EventPath(const_cast<Node
&>(relatedNode
)));
233 for (size_t i
= 0; i
< relatedTargetEventPath
->m_treeScopeEventContexts
.size(); ++i
) {
234 TreeScopeEventContext
* treeScopeEventContext
= relatedTargetEventPath
->m_treeScopeEventContexts
[i
].get();
235 relatedTargetMap
.add(&treeScopeEventContext
->treeScope(), treeScopeEventContext
->target());
238 // Oilpan: It is important to explicitly clear the vectors to reuse
239 // the memory in subsequent event dispatchings.
240 relatedTargetEventPath
->clear();
244 EventTarget
* EventPath::findRelatedNode(TreeScope
& scope
, RelatedTargetMap
& relatedTargetMap
)
246 WillBeHeapVector
<RawPtrWillBeMember
<TreeScope
>, 32> parentTreeScopes
;
247 EventTarget
* relatedNode
= nullptr;
248 for (TreeScope
* current
= &scope
; current
; current
= current
->olderShadowRootOrParentTreeScope()) {
249 parentTreeScopes
.append(current
);
250 RelatedTargetMap::const_iterator iter
= relatedTargetMap
.find(current
);
251 if (iter
!= relatedTargetMap
.end() && iter
->value
) {
252 relatedNode
= iter
->value
;
257 for (const auto& entry
: parentTreeScopes
)
258 relatedTargetMap
.add(entry
, relatedNode
);
263 void EventPath::adjustForRelatedTarget(Node
& target
, EventTarget
* relatedTarget
)
267 Node
* relatedNode
= relatedTarget
->toNode();
270 if (target
.document() != relatedNode
->document())
272 if (!target
.inDocument() || !relatedNode
->inDocument())
275 RelatedTargetMap relatedNodeMap
;
276 buildRelatedNodeMap(*relatedNode
, relatedNodeMap
);
278 for (const auto& treeScopeEventContext
: m_treeScopeEventContexts
) {
279 EventTarget
* adjustedRelatedTarget
= findRelatedNode(treeScopeEventContext
->treeScope(), relatedNodeMap
);
280 ASSERT(adjustedRelatedTarget
);
281 treeScopeEventContext
.get()->setRelatedTarget(adjustedRelatedTarget
);
284 shrinkIfNeeded(target
, *relatedTarget
);
287 void EventPath::shrinkIfNeeded(const Node
& target
, const EventTarget
& relatedTarget
)
289 // Synthetic mouse events can have a relatedTarget which is identical to the target.
290 bool targetIsIdenticalToToRelatedTarget
= (&target
== &relatedTarget
);
292 for (size_t i
= 0; i
< size(); ++i
) {
293 if (targetIsIdenticalToToRelatedTarget
) {
294 if (target
.treeScope().rootNode() == at(i
).node()) {
298 } else if (at(i
).target() == at(i
).relatedTarget()) {
299 // Event dispatching should be stopped here.
306 void EventPath::adjustForTouchEvent(TouchEvent
& touchEvent
)
308 WillBeHeapVector
<RawPtrWillBeMember
<TouchList
>> adjustedTouches
;
309 WillBeHeapVector
<RawPtrWillBeMember
<TouchList
>> adjustedTargetTouches
;
310 WillBeHeapVector
<RawPtrWillBeMember
<TouchList
>> adjustedChangedTouches
;
311 WillBeHeapVector
<RawPtrWillBeMember
<TreeScope
>> treeScopes
;
313 for (const auto& treeScopeEventContext
: m_treeScopeEventContexts
) {
314 TouchEventContext
* touchEventContext
= treeScopeEventContext
->ensureTouchEventContext();
315 adjustedTouches
.append(&touchEventContext
->touches());
316 adjustedTargetTouches
.append(&touchEventContext
->targetTouches());
317 adjustedChangedTouches
.append(&touchEventContext
->changedTouches());
318 treeScopes
.append(&treeScopeEventContext
->treeScope());
321 adjustTouchList(touchEvent
.touches(), adjustedTouches
, treeScopes
);
322 adjustTouchList(touchEvent
.targetTouches(), adjustedTargetTouches
, treeScopes
);
323 adjustTouchList(touchEvent
.changedTouches(), adjustedChangedTouches
, treeScopes
);
326 for (const auto& treeScopeEventContext
: m_treeScopeEventContexts
) {
327 TreeScope
& treeScope
= treeScopeEventContext
->treeScope();
328 TouchEventContext
* touchEventContext
= treeScopeEventContext
->touchEventContext();
329 checkReachability(treeScope
, touchEventContext
->touches());
330 checkReachability(treeScope
, touchEventContext
->targetTouches());
331 checkReachability(treeScope
, touchEventContext
->changedTouches());
336 void EventPath::adjustTouchList(const TouchList
* touchList
, WillBeHeapVector
<RawPtrWillBeMember
<TouchList
>> adjustedTouchList
, const WillBeHeapVector
<RawPtrWillBeMember
<TreeScope
>>& treeScopes
)
340 for (size_t i
= 0; i
< touchList
->length(); ++i
) {
341 const Touch
& touch
= *touchList
->item(i
);
345 Node
* targetNode
= touch
.target()->toNode();
349 RelatedTargetMap relatedNodeMap
;
350 buildRelatedNodeMap(*targetNode
, relatedNodeMap
);
351 for (size_t j
= 0; j
< treeScopes
.size(); ++j
) {
352 adjustedTouchList
[j
]->append(touch
.cloneWithNewTarget(findRelatedNode(*treeScopes
[j
], relatedNodeMap
)));
357 const NodeEventContext
& EventPath::topNodeEventContext()
363 void EventPath::ensureWindowEventContext()
366 if (!m_windowEventContext
)
367 m_windowEventContext
= adoptPtrWillBeNoop(new WindowEventContext(*m_event
, topNodeEventContext()));
371 void EventPath::checkReachability(TreeScope
& treeScope
, TouchList
& touchList
)
373 for (size_t i
= 0; i
< touchList
.length(); ++i
)
374 ASSERT(touchList
.item(i
)->target()->toNode()->treeScope().isInclusiveOlderSiblingShadowRootOrAncestorTreeScopeOf(treeScope
));
378 DEFINE_TRACE(EventPath
)
381 visitor
->trace(m_nodeEventContexts
);
382 visitor
->trace(m_node
);
383 visitor
->trace(m_event
);
384 visitor
->trace(m_treeScopeEventContexts
);
385 visitor
->trace(m_windowEventContext
);