• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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() == &currentTreeScope;
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 = &currentTreeScope;
254     }
255 }
256 
257 } // namespace
258