OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Neither the name of Google Inc. nor the names of its | 10 * * Neither the name of Google Inc. nor the names of its |
(...skipping 13 matching lines...) Expand all Loading... |
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 */ | 25 */ |
26 | 26 |
27 #include "config.h" | 27 #include "config.h" |
28 #include "core/events/EventPath.h" | 28 #include "core/events/EventPath.h" |
29 | 29 |
30 #include "core/EventNames.h" | 30 #include "core/EventNames.h" |
31 #include "core/dom/Document.h" | 31 #include "core/dom/Document.h" |
32 #include "core/dom/Touch.h" | 32 #include "core/dom/Touch.h" |
33 #include "core/dom/TouchList.h" | 33 #include "core/dom/TouchList.h" |
| 34 #include "core/dom/shadow/ElementShadow.h" |
34 #include "core/dom/shadow/InsertionPoint.h" | 35 #include "core/dom/shadow/InsertionPoint.h" |
35 #include "core/dom/shadow/ShadowRoot.h" | 36 #include "core/dom/shadow/ShadowRoot.h" |
36 #include "core/events/TouchEvent.h" | 37 #include "core/events/TouchEvent.h" |
37 #include "core/events/TouchEventContext.h" | 38 #include "core/events/TouchEventContext.h" |
| 39 #include "core/html/HTMLShadowElement.h" |
38 | 40 |
39 namespace blink { | 41 namespace blink { |
40 | 42 |
41 EventTarget* EventPath::eventTargetRespectingTargetRules(Node& referenceNode) | 43 EventTarget* EventPath::eventTargetRespectingTargetRules(Node& referenceNode) |
42 { | 44 { |
43 if (referenceNode.isPseudoElement()) | 45 if (referenceNode.isPseudoElement()) |
44 return referenceNode.parentNode(); | 46 return referenceNode.parentNode(); |
45 | 47 |
46 return &referenceNode; | 48 return &referenceNode; |
47 } | 49 } |
48 | 50 |
| 51 #define W3C23887 3 |
| 52 #if !defined(W3C23887) || W3C23887 < 2 |
49 static inline bool shouldStopAtShadowRoot(Event& event, ShadowRoot& shadowRoot,
EventTarget& target) | 53 static inline bool shouldStopAtShadowRoot(Event& event, ShadowRoot& shadowRoot,
EventTarget& target) |
50 { | 54 { |
51 // WebKit never allowed selectstart event to cross the the shadow DOM bounda
ry. | 55 // WebKit never allowed selectstart event to cross the the shadow DOM bounda
ry. |
52 // Changing this breaks existing sites. | 56 // Changing this breaks existing sites. |
53 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. | 57 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. |
54 const AtomicString eventType = event.type(); | 58 const AtomicString eventType = event.type(); |
55 return target.toNode() && target.toNode()->shadowHost() == shadowRoot.host() | 59 return target.toNode() && target.toNode()->shadowHost() == shadowRoot.host() |
56 && (eventType == EventTypeNames::abort | 60 && (eventType == EventTypeNames::abort |
57 || eventType == EventTypeNames::change | 61 || eventType == EventTypeNames::change |
58 || eventType == EventTypeNames::error | 62 || eventType == EventTypeNames::error |
59 || eventType == EventTypeNames::load | 63 || eventType == EventTypeNames::load |
60 || eventType == EventTypeNames::reset | 64 || eventType == EventTypeNames::reset |
61 || eventType == EventTypeNames::resize | 65 || eventType == EventTypeNames::resize |
62 || eventType == EventTypeNames::scroll | 66 || eventType == EventTypeNames::scroll |
63 || eventType == EventTypeNames::select | 67 || eventType == EventTypeNames::select |
64 || eventType == EventTypeNames::selectstart); | 68 || eventType == EventTypeNames::selectstart); |
65 } | 69 } |
| 70 #else |
| 71 static inline bool shouldStopBeforeShadowHost(Event& event) |
| 72 { |
| 73 // WebKit never allowed selectstart event to cross the the shadow DOM bounda
ry. |
| 74 // Changing this breaks existing sites. |
| 75 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. |
| 76 const AtomicString eventType = event.type(); |
| 77 return (eventType == EventTypeNames::abort |
| 78 || eventType == EventTypeNames::change |
| 79 || eventType == EventTypeNames::error |
| 80 || eventType == EventTypeNames::load |
| 81 || eventType == EventTypeNames::reset |
| 82 || eventType == EventTypeNames::resize |
| 83 || eventType == EventTypeNames::scroll |
| 84 || eventType == EventTypeNames::select |
| 85 || eventType == EventTypeNames::selectstart); |
| 86 } |
| 87 #endif |
66 | 88 |
67 EventPath::EventPath(Node& node, Event* event) | 89 EventPath::EventPath(Node& node, Event* event) |
68 : m_node(node) | 90 : m_node(node) |
69 , m_event(event) | 91 , m_event(event) |
70 { | 92 { |
71 initialize(); | 93 initialize(); |
72 } | 94 } |
73 | 95 |
74 void EventPath::initializeWith(Node& node, Event* event) | 96 void EventPath::initializeWith(Node& node, Event* event) |
75 { | 97 { |
(...skipping 10 matching lines...) Expand all Loading... |
86 calculatePath(); | 108 calculatePath(); |
87 calculateAdjustedTargets(); | 109 calculateAdjustedTargets(); |
88 calculateTreeScopePrePostOrderNumbers(); | 110 calculateTreeScopePrePostOrderNumbers(); |
89 } | 111 } |
90 | 112 |
91 void EventPath::addNodeEventContext(Node& node) | 113 void EventPath::addNodeEventContext(Node& node) |
92 { | 114 { |
93 m_nodeEventContexts.append(NodeEventContext(&node, eventTargetRespectingTarg
etRules(node))); | 115 m_nodeEventContexts.append(NodeEventContext(&node, eventTargetRespectingTarg
etRules(node))); |
94 } | 116 } |
95 | 117 |
| 118 #if W3C23887 == 1 |
| 119 static bool isDistributedToShadowInsertionPoint(const ShadowRoot& shadowRoot) |
| 120 { |
| 121 // 5.2.3. If CURRENT is not the youngest shadow root hosted by SHADOW-POOL-H
OST: |
| 122 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c163 |
| 123 // and CURRENT is distributed to a shadow insertion point |
| 124 ElementShadow* owner = shadowRoot.owner(); |
| 125 if (!owner) |
| 126 return false; |
| 127 if (shadowRoot.firstChild()) |
| 128 return owner->finalDestinationInsertionPointFor(shadowRoot.firstChild())
; |
| 129 // TODO(kojii): how can we determine if a shadow root is distributed? |
| 130 return false; |
| 131 } |
| 132 |
| 133 static ShadowRoot* shadowRootDistributedInto(InsertionPoint& insertionPoint) |
| 134 { |
| 135 // 5.4.4.1. Let PROJECTED-SHADOW be the shadow root projected into CURRENT. |
| 136 // TODO(kojii): is this correct way to determine that? |
| 137 #ifndef A1 |
| 138 return insertionPoint.containingShadowRoot()->olderShadowRoot(); |
| 139 #else |
| 140 // TODO(kojii): should it be first() or last()? |
| 141 Node* distributedNode = insertionPoint.first(); |
| 142 if (!distributedNode) |
| 143 return nullptr; |
| 144 return distributedNode->containingShadowRoot(); |
| 145 #endif |
| 146 } |
| 147 #endif |
| 148 #if W3C23887 >= 2 |
| 149 static inline InsertionPoint* pop(WillBeHeapVector<RawPtrWillBeMember<InsertionP
oint>, 8>& stack) |
| 150 { |
| 151 ASSERT(!stack.isEmpty()); |
| 152 InsertionPoint* last = stack.last(); |
| 153 stack.removeLast(); |
| 154 return last; |
| 155 } |
| 156 #endif |
| 157 |
96 void EventPath::calculatePath() | 158 void EventPath::calculatePath() |
97 { | 159 { |
98 ASSERT(m_node); | 160 ASSERT(m_node); |
99 ASSERT(m_nodeEventContexts.isEmpty()); | 161 ASSERT(m_nodeEventContexts.isEmpty()); |
100 m_node->updateDistribution(); | 162 m_node->updateDistribution(); |
101 | 163 |
102 Node* current = m_node; | 164 Node* current = m_node; |
| 165 #ifndef W3C23887 |
103 addNodeEventContext(*current); | 166 addNodeEventContext(*current); |
104 if (!m_node->inDocument()) | 167 if (!m_node->inDocument()) |
105 return; | 168 return; |
106 while (current) { | 169 while (current) { |
107 if (m_event && current->keepEventInNode(m_event)) | 170 if (m_event && current->keepEventInNode(m_event)) |
108 break; | 171 break; |
109 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; | 172 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
110 collectDestinationInsertionPoints(*current, insertionPoints); | 173 collectDestinationInsertionPoints(*current, insertionPoints); |
111 if (!insertionPoints.isEmpty()) { | 174 if (!insertionPoints.isEmpty()) { |
112 for (const auto& insertionPoint : insertionPoints) { | 175 for (const auto& insertionPoint : insertionPoints) { |
(...skipping 12 matching lines...) Expand all Loading... |
125 if (m_event && shouldStopAtShadowRoot(*m_event, *toShadowRoot(curren
t), *m_node)) | 188 if (m_event && shouldStopAtShadowRoot(*m_event, *toShadowRoot(curren
t), *m_node)) |
126 break; | 189 break; |
127 current = current->shadowHost(); | 190 current = current->shadowHost(); |
128 addNodeEventContext(*current); | 191 addNodeEventContext(*current); |
129 } else { | 192 } else { |
130 current = current->parentNode(); | 193 current = current->parentNode(); |
131 if (current) | 194 if (current) |
132 addNodeEventContext(*current); | 195 addNodeEventContext(*current); |
133 } | 196 } |
134 } | 197 } |
| 198 #elif W3C23887 == 3 |
| 199 if (!m_node->inDocument()) { |
| 200 addNodeEventContext(*current); |
| 201 return; |
| 202 } |
| 203 Element* stopBefore = m_event && shouldStopBeforeShadowHost(*m_event) ? m_no
de->shadowHost() : nullptr; |
| 204 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPointStack; |
| 205 for (;;) { |
| 206 ASSERT(current); |
| 207 addNodeEventContext(*current); |
| 208 if (m_event && current->keepEventInNode(m_event)) |
| 209 break; |
| 210 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
| 211 collectDestinationInsertionPoints(*current, insertionPoints); |
| 212 if (!insertionPoints.isEmpty()) { |
| 213 insertionPointStack.appendVector(insertionPoints); |
| 214 current = pop(insertionPointStack); |
| 215 continue; |
| 216 } |
| 217 if (current->isShadowRoot()) { |
| 218 ShadowRoot* shadowRoot = toShadowRoot(current); |
| 219 Node* olderShadowRootOrShadowHost = shadowRoot->olderShadowRoot(); |
| 220 if (!olderShadowRootOrShadowHost) |
| 221 olderShadowRootOrShadowHost = shadowRoot->host(); |
| 222 ASSERT(olderShadowRootOrShadowHost); |
| 223 if (!insertionPointStack.isEmpty() && insertionPointStack.last()->tr
eeScope() == olderShadowRootOrShadowHost->treeScope()) { |
| 224 current = pop(insertionPointStack); |
| 225 continue; |
| 226 } |
| 227 if (olderShadowRootOrShadowHost == stopBefore) |
| 228 break; |
| 229 current = olderShadowRootOrShadowHost; |
| 230 continue; |
| 231 } |
| 232 current = current->parentNode(); |
| 233 if (!current) |
| 234 break; |
| 235 } |
| 236 // ASSERT(insertionPointStack.isEmpty()); |
| 237 #elif W3C23887 == 2 |
| 238 if (!m_node->inDocument()) { |
| 239 addNodeEventContext(*current); |
| 240 return; |
| 241 } |
| 242 Element* stopBefore = m_event && shouldStopBeforeShadowHost(*m_event) ? m_no
de->shadowHost() : nullptr; |
| 243 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPointStack; |
| 244 for (;;) { |
| 245 ASSERT(current); |
| 246 addNodeEventContext(*current); |
| 247 if (m_event && current->keepEventInNode(m_event)) |
| 248 break; |
| 249 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
| 250 collectDestinationInsertionPoints(*current, insertionPoints); |
| 251 if (!insertionPoints.isEmpty()) { |
| 252 bool hasInsertionPoints = false; |
| 253 for (const auto& insertionPoint : insertionPoints) { |
| 254 if (insertionPoint->isShadowInsertionPoint() && !insertionPoint-
>containingShadowRoot()->isOldest()) |
| 255 continue; |
| 256 insertionPointStack.append(insertionPoint); |
| 257 hasInsertionPoints = true; |
| 258 } |
| 259 if (hasInsertionPoints) { |
| 260 current = pop(insertionPointStack); |
| 261 continue; |
| 262 } |
| 263 } |
| 264 if (current->isShadowRoot()) { |
| 265 ShadowRoot* shadowRoot = toShadowRoot(current); |
| 266 Element* shadowHost = shadowRoot->host(); |
| 267 ASSERT(shadowHost); |
| 268 if (!insertionPointStack.isEmpty() && insertionPointStack.last()->tr
eeScope() == shadowHost->treeScope()) { |
| 269 current = pop(insertionPointStack); |
| 270 continue; |
| 271 } |
| 272 if (HTMLShadowElement* shadowInsertionPointOfYoungerShadowRoot = sha
dowRoot->shadowInsertionPointOfYoungerShadowRoot()) { |
| 273 current = shadowInsertionPointOfYoungerShadowRoot; |
| 274 continue; |
| 275 } |
| 276 if (shadowHost == stopBefore) |
| 277 break; |
| 278 current = shadowHost; |
| 279 continue; |
| 280 } |
| 281 current = current->parentNode(); |
| 282 if (!current) |
| 283 break; |
| 284 } |
| 285 ASSERT(insertionPointStack.isEmpty()); |
| 286 #elif W3C23887 == 1 // https://bugzilla.mozilla.org/show_bug.cgi?id=1059989#c10 |
| 287 addNodeEventContext(*current); |
| 288 if (!m_node->inDocument()) |
| 289 return; |
| 290 // 3. Let INSERTION-POINTS be an empty stack of nodes. |
| 291 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPointStack; |
| 292 while (current) { |
| 293 if (m_event && current->keepEventInNode(m_event)) |
| 294 break; |
| 295 // 5.1. If the destination insertion points of CURRENT is not empty: |
| 296 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
| 297 collectDestinationInsertionPoints(*current, insertionPoints); |
| 298 bool removeShadowInsertionPoint = true; |
| 299 if (!insertionPoints.isEmpty()) { |
| 300 // 5.1.1. Push the destination insertion points into INSERTION-POINT
S in order of first destination to final destination. |
| 301 insertionPointStack.appendVector(insertionPoints); |
| 302 // 5.1.2. Pop INSERTION-POINTS and set CURRENT to be the popped node
. |
| 303 current = insertionPointStack.last(); |
| 304 insertionPointStack.removeLast(); |
| 305 // 5.2. Otherwise if CURRENT is a shadow root: |
| 306 } else if (current->isShadowRoot()) { |
| 307 // 5.2.2. If SHADOW-POOL-HOST hosts the node tree which NODE partici
pates in and EVENT is one of the events which must be stopped: |
| 308 ShadowRoot* currentShadowRoot = toShadowRoot(current); |
| 309 // TODO(kojii): we should be able to check this once and determine w
here to stop for better performance |
| 310 if (m_event && shouldStopAtShadowRoot(*m_event, *currentShadowRoot,
*m_node)) { |
| 311 // 5.2.2.1. Stop this algorithm |
| 312 break; |
| 313 } |
| 314 // 5.2.1. Let SHADOW-POOL-HOST be the shadow host which hosts CURREN
T |
| 315 Element* shadowPoolHost = currentShadowRoot->host(); |
| 316 // 5.2.3. If CURRENT is not the youngest shadow root hosted by SHADO
W-POOL-HOST: |
| 317 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c163 |
| 318 // and CURRENT is distributed to a shadow insertion point |
| 319 if (!currentShadowRoot->isYoungest() && isDistributedToShadowInserti
onPoint(*currentShadowRoot)) { |
| 320 // 5.2.3.1. Let SHADOW-POOL-HOST be the shadow insertion point i
nto which CURRENT shadow root is projected. |
| 321 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c122 |
| 322 // Let SHADOW-POOL-HOST be the shadow insertion point in the you
nger shadow tree relative to the shadow tree |
| 323 // whose root is CURRENT shadow root. |
| 324 shadowPoolHost = currentShadowRoot->shadowInsertionPointOfYounge
rShadowRoot(); |
| 325 } |
| 326 // 5.2.4. If INSERTION-POINTS is not empty and |
| 327 // if the most recent node in the INSERTION-POINTS is in the same no
de tree as SHADOW-POOL-HOST: |
| 328 if (!insertionPointStack.isEmpty() && insertionPointStack.last()->tr
eeScope() == shadowPoolHost->treeScope()) { |
| 329 // 5.2.4.1. Pop INSERTION-POINTS and set CURRENT to be the poppe
d node. |
| 330 current = insertionPointStack.last(); |
| 331 insertionPointStack.removeLast(); |
| 332 // 5.2.5. Otherwise: |
| 333 } else { |
| 334 // 5.2.5.1. Set CURRENT to SHADOW-POOL-HOST and skip step 4 |
| 335 current = shadowPoolHost; |
| 336 removeShadowInsertionPoint = false; |
| 337 } |
| 338 // 5.3. Otherwise: |
| 339 } else { |
| 340 // 5.3.1. 1. Let CURRENT be the parent node of CURRENT. |
| 341 current = current->parentNode(); |
| 342 } |
| 343 if (removeShadowInsertionPoint) { |
| 344 // 5.4.4. Repeat while CURRENT is a shadow insertion point. |
| 345 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c170 |
| 346 // and there is a shadow root distributed to it (see the "if" within
the "while" below) |
| 347 while (current && isActiveShadowInsertionPoint(*current)) { |
| 348 // 5.4.4.1. Let PROJECTED-SHADOW be the shadow root projected in
to CURRENT. |
| 349 ShadowRoot* distributedShadowRoot = shadowRootDistributedInto(to
InsertionPoint(*current)); |
| 350 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c170 |
| 351 // 5.4.4. and there is a shadow root distributed to it |
| 352 if (!distributedShadowRoot) |
| 353 break; |
| 354 // 5.4.4.2. If INSERTION-POINTS is not empty and |
| 355 // if the most recent node in the INSERTION-POINTS is in the sam
e node tree as PROJECTED-SHADOW: |
| 356 if (!insertionPointStack.isEmpty() && insertionPointStack.last()
->treeScope() == distributedShadowRoot->treeScope()) { |
| 357 // 5.4.4.2.1. Pop INSERTION-POINTS and set CURRENT to be the
popped node. |
| 358 current = insertionPointStack.last(); |
| 359 insertionPointStack.removeLast(); |
| 360 // 5.4.4.3. Otherwise: |
| 361 } else { |
| 362 // 5.4.4.3.1. Set CURRENT to PROJECTED-SHADOW. |
| 363 current = distributedShadowRoot; |
| 364 } |
| 365 } |
| 366 } |
| 367 // 5.5. If CURRENT exists: |
| 368 // 5.5.1. Add CURRENT to PATH. |
| 369 if (current) |
| 370 addNodeEventContext(*current); |
| 371 } |
| 372 ASSERT(insertionPointStack.isEmpty()); |
| 373 #endif |
135 } | 374 } |
136 | 375 |
137 void EventPath::calculateTreeScopePrePostOrderNumbers() | 376 void EventPath::calculateTreeScopePrePostOrderNumbers() |
138 { | 377 { |
139 // Precondition: | 378 // Precondition: |
140 // - TreeScopes in m_treeScopeEventContexts must be *connected* in the sam
e tree of trees. | 379 // - TreeScopes in m_treeScopeEventContexts must be *connected* in the sam
e tree of trees. |
141 // - The root tree must be included. | 380 // - The root tree must be included. |
142 WillBeHeapHashMap<RawPtrWillBeMember<const TreeScope>, RawPtrWillBeMember<Tr
eeScopeEventContext>> treeScopeEventContextMap; | 381 WillBeHeapHashMap<RawPtrWillBeMember<const TreeScope>, RawPtrWillBeMember<Tr
eeScopeEventContext>> treeScopeEventContextMap; |
143 for (const auto& treeScopeEventContext : m_treeScopeEventContexts) | 382 for (const auto& treeScopeEventContext : m_treeScopeEventContexts) |
144 treeScopeEventContextMap.add(&treeScopeEventContext->treeScope(), treeSc
opeEventContext.get()); | 383 treeScopeEventContextMap.add(&treeScopeEventContext->treeScope(), treeSc
opeEventContext.get()); |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 DEFINE_TRACE(EventPath) | 587 DEFINE_TRACE(EventPath) |
349 { | 588 { |
350 visitor->trace(m_nodeEventContexts); | 589 visitor->trace(m_nodeEventContexts); |
351 visitor->trace(m_node); | 590 visitor->trace(m_node); |
352 visitor->trace(m_event); | 591 visitor->trace(m_event); |
353 visitor->trace(m_treeScopeEventContexts); | 592 visitor->trace(m_treeScopeEventContexts); |
354 visitor->trace(m_windowEventContext); | 593 visitor->trace(m_windowEventContext); |
355 } | 594 } |
356 | 595 |
357 } // namespace | 596 } // namespace |
OLD | NEW |