1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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.
13 *
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.
25 */
26
27 #include "config.h"
28 #include "core/events/EventPath.h"
29
30 #include "EventNames.h"
31 #include "RuntimeEnabledFeatures.h"
32 #include "SVGNames.h"
33 #include "core/dom/FullscreenElementStack.h"
34 #include "core/dom/shadow/ElementShadow.h"
35 #include "core/dom/shadow/InsertionPoint.h"
36 #include "core/dom/shadow/ShadowRoot.h"
37 #include "core/html/shadow/HTMLShadowElement.h"
38 #include "core/svg/SVGElementInstance.h"
39 #include "core/svg/SVGUseElement.h"
40
41 namespace WebCore {
42
parent(Node * node)43 Node* EventPath::parent(Node* node)
44 {
45 EventPath eventPath(node);
46 return eventPath.size() > 1 ? eventPath[1].node() : 0;
47 }
48
eventTargetRespectingTargetRules(Node * referenceNode)49 EventTarget* EventPath::eventTargetRespectingTargetRules(Node* referenceNode)
50 {
51 ASSERT(referenceNode);
52
53 if (referenceNode->isPseudoElement())
54 return referenceNode->parentNode();
55
56 if (!referenceNode->isSVGElement() || !referenceNode->isInShadowTree())
57 return referenceNode;
58
59 // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included
60 // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects.
61 Node* rootNode = referenceNode->treeScope().rootNode();
62 Element* shadowHostElement = rootNode->isShadowRoot() ? toShadowRoot(rootNode)->host() : 0;
63 // At this time, SVG nodes are not supported in non-<use> shadow trees.
64 if (!shadowHostElement || !shadowHostElement->hasTagName(SVGNames::useTag))
65 return referenceNode;
66 SVGUseElement* useElement = toSVGUseElement(shadowHostElement);
67 if (SVGElementInstance* instance = useElement->instanceForShadowTreeElement(referenceNode))
68 return instance;
69
70 return referenceNode;
71 }
72
inTheSameScope(ShadowRoot * shadowRoot,EventTarget * target)73 static inline bool inTheSameScope(ShadowRoot* shadowRoot, EventTarget* target)
74 {
75 return target->toNode() && target->toNode()->treeScope().rootNode() == shadowRoot;
76 }
77
determineDispatchBehavior(Event * event,ShadowRoot * shadowRoot,EventTarget * target)78 static inline EventDispatchBehavior determineDispatchBehavior(Event* event, ShadowRoot* shadowRoot, EventTarget* target)
79 {
80 // Video-only full screen is a mode where we use the shadow DOM as an implementation
81 // detail that should not be detectable by the web content.
82 if (Element* element = FullscreenElementStack::currentFullScreenElementFrom(&target->toNode()->document())) {
83 // FIXME: We assume that if the full screen element is a media element that it's
84 // the video-only full screen. Both here and elsewhere. But that is probably wrong.
85 if (element->isMediaElement() && shadowRoot && shadowRoot->host() == element)
86 return StayInsideShadowDOM;
87 }
88
89 // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
90 // Changing this breaks existing sites.
91 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
92 const AtomicString eventType = event->type();
93 if (inTheSameScope(shadowRoot, target)
94 && (eventType == EventTypeNames::abort
95 || eventType == EventTypeNames::change
96 || eventType == EventTypeNames::error
97 || eventType == EventTypeNames::load
98 || eventType == EventTypeNames::reset
99 || eventType == EventTypeNames::resize
100 || eventType == EventTypeNames::scroll
101 || eventType == EventTypeNames::select
102 || eventType == EventTypeNames::selectstart))
103 return StayInsideShadowDOM;
104
105 return RetargetEvent;
106 }
107
EventPath(Event * event)108 EventPath::EventPath(Event* event)
109 : m_node(0)
110 , m_event(event)
111 {
112 }
113
EventPath(Node * node)114 EventPath::EventPath(Node* node)
115 : m_node(node)
116 , m_event(0)
117 {
118 resetWith(node);
119 }
120
resetWith(Node * node)121 void EventPath::resetWith(Node* node)
122 {
123 ASSERT(node);
124 m_node = node;
125 m_eventContexts.clear();
126 calculatePath();
127 calculateAdjustedTargets();
128 calculateAdjustedEventPathForEachNode();
129 }
130
addEventContext(Node * node)131 void EventPath::addEventContext(Node* node)
132 {
133 m_eventContexts.append(EventContext(node, eventTargetRespectingTargetRules(node)));
134 }
135
calculatePath()136 void EventPath::calculatePath()
137 {
138 ASSERT(m_node);
139 ASSERT(m_eventContexts.isEmpty());
140 m_node->document().updateDistributionForNodeIfNeeded(const_cast<Node*>(m_node));
141
142 Node* current = m_node;
143 addEventContext(current);
144 if (!m_node->inDocument())
145 return;
146 while (current) {
147 if (current->isShadowRoot() && m_event && determineDispatchBehavior(m_event, toShadowRoot(current), m_node) == StayInsideShadowDOM)
148 break;
149 Vector<InsertionPoint*, 8> insertionPoints;
150 collectDestinationInsertionPoints(*current, insertionPoints);
151 if (!insertionPoints.isEmpty()) {
152 for (size_t i = 0; i < insertionPoints.size(); ++i) {
153 InsertionPoint* insertionPoint = insertionPoints[i];
154 if (insertionPoint->isShadowInsertionPoint()) {
155 ShadowRoot* containingShadowRoot = insertionPoint->containingShadowRoot();
156 ASSERT(containingShadowRoot);
157 if (!containingShadowRoot->isOldest())
158 addEventContext(containingShadowRoot->olderShadowRoot());
159 }
160 addEventContext(insertionPoint);
161 }
162 current = insertionPoints.last();
163 continue;
164 }
165 if (current->isShadowRoot()) {
166 current = current->shadowHost();
167 addEventContext(current);
168 } else {
169 current = current->parentNode();
170 if (current)
171 addEventContext(current);
172 }
173 }
174 }
175
calculateAdjustedEventPathForEachNode()176 void EventPath::calculateAdjustedEventPathForEachNode()
177 {
178 if (!RuntimeEnabledFeatures::shadowDOMEnabled())
179 return;
180 TreeScope* lastScope = 0;
181 for (size_t i = 0; i < size(); ++i) {
182 TreeScope* currentScope = &at(i).node()->treeScope();
183 if (currentScope == lastScope) {
184 // Fast path.
185 at(i).setEventPath(at(i - 1).eventPath());
186 continue;
187 }
188 lastScope = currentScope;
189 Vector<RefPtr<Node> > nodes;
190 nodes.reserveInitialCapacity(size());
191 for (size_t j = 0; j < size(); ++j) {
192 if (at(j).node()->treeScope().isInclusiveAncestorOf(*currentScope))
193 nodes.append(at(j).node());
194 }
195 at(i).adoptEventPath(nodes);
196 }
197 }
198
199 #ifndef NDEBUG
movedFromOlderToYounger(const TreeScope & lastTreeScope,const TreeScope & currentTreeScope)200 static inline bool movedFromOlderToYounger(const TreeScope& lastTreeScope, const TreeScope& currentTreeScope)
201 {
202 Node* rootNode = lastTreeScope.rootNode();
203 return rootNode->isShadowRoot() && toShadowRoot(rootNode)->youngerShadowRoot() == currentTreeScope.rootNode();
204 }
205
movedFromYoungerToOlder(const TreeScope & lastTreeScope,const TreeScope & currentTreeScope)206 static inline bool movedFromYoungerToOlder(const TreeScope& lastTreeScope, const TreeScope& currentTreeScope)
207 {
208 Node* rootNode = lastTreeScope.rootNode();
209 return rootNode->isShadowRoot() && toShadowRoot(rootNode)->olderShadowRoot() == currentTreeScope.rootNode();
210 }
211 #endif
212
movedFromChildToParent(const TreeScope & lastTreeScope,const TreeScope & currentTreeScope)213 static inline bool movedFromChildToParent(const TreeScope& lastTreeScope, const TreeScope& currentTreeScope)
214 {
215 return lastTreeScope.parentTreeScope() == ¤tTreeScope;
216 }
217
movedFromParentToChild(const TreeScope & lastTreeScope,const TreeScope & currentTreeScope)218 static inline bool movedFromParentToChild(const TreeScope& lastTreeScope, const TreeScope& currentTreeScope)
219 {
220 return currentTreeScope.parentTreeScope() == &lastTreeScope;
221 }
222
calculateAdjustedTargets()223 void EventPath::calculateAdjustedTargets()
224 {
225 Vector<Node*, 32> targetStack;
226 const TreeScope* lastTreeScope = 0;
227 bool isSVGElement = at(0).node()->isSVGElement();
228
229 for (size_t i = 0; i < size(); ++i) {
230 Node* current = at(i).node();
231 const TreeScope& currentTreeScope = current->treeScope();
232 if (targetStack.isEmpty()) {
233 targetStack.append(current);
234 } else if (*lastTreeScope != currentTreeScope && !isSVGElement) {
235 if (movedFromParentToChild(*lastTreeScope, currentTreeScope)) {
236 targetStack.append(targetStack.last());
237 } else if (movedFromChildToParent(*lastTreeScope, currentTreeScope)) {
238 ASSERT(!targetStack.isEmpty());
239 targetStack.removeLast();
240 if (targetStack.isEmpty())
241 targetStack.append(current);
242 } else {
243 ASSERT(movedFromYoungerToOlder(*lastTreeScope, currentTreeScope) || movedFromOlderToYounger(*lastTreeScope, currentTreeScope));
244 ASSERT(!targetStack.isEmpty());
245 targetStack.removeLast();
246 if (targetStack.isEmpty())
247 targetStack.append(current);
248 else
249 targetStack.append(targetStack.last());
250 }
251 }
252 at(i).setTarget(eventTargetRespectingTargetRules(targetStack.last()));
253 lastTreeScope = ¤tTreeScope;
254 }
255 }
256
257 } // namespace
258