Index: Source/core/events/EventPath.cpp |
diff --git a/Source/core/events/EventPath.cpp b/Source/core/events/EventPath.cpp |
index 9e53e718837b7f4b98bb0c5d7645cbec6efd6614..7857bd0f35b306c48eb3b6863b08f4017462c885 100644 |
--- a/Source/core/events/EventPath.cpp |
+++ b/Source/core/events/EventPath.cpp |
@@ -31,10 +31,12 @@ |
#include "core/dom/Document.h" |
#include "core/dom/Touch.h" |
#include "core/dom/TouchList.h" |
+#include "core/dom/shadow/ElementShadow.h" |
#include "core/dom/shadow/InsertionPoint.h" |
#include "core/dom/shadow/ShadowRoot.h" |
#include "core/events/TouchEvent.h" |
#include "core/events/TouchEventContext.h" |
+#include "core/html/HTMLShadowElement.h" |
namespace blink { |
@@ -46,6 +48,8 @@ EventTarget* EventPath::eventTargetRespectingTargetRules(Node& referenceNode) |
return &referenceNode; |
} |
+#define W3C23887 3 |
+#if !defined(W3C23887) || W3C23887 < 2 |
static inline bool shouldStopAtShadowRoot(Event& event, ShadowRoot& shadowRoot, EventTarget& target) |
{ |
// WebKit never allowed selectstart event to cross the the shadow DOM boundary. |
@@ -63,6 +67,24 @@ static inline bool shouldStopAtShadowRoot(Event& event, ShadowRoot& shadowRoot, |
|| eventType == EventTypeNames::select |
|| eventType == EventTypeNames::selectstart); |
} |
+#else |
+static inline bool shouldStopBeforeShadowHost(Event& event) |
+{ |
+ // 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(); |
+ return (eventType == EventTypeNames::abort |
+ || eventType == EventTypeNames::change |
+ || eventType == EventTypeNames::error |
+ || eventType == EventTypeNames::load |
+ || eventType == EventTypeNames::reset |
+ || eventType == EventTypeNames::resize |
+ || eventType == EventTypeNames::scroll |
+ || eventType == EventTypeNames::select |
+ || eventType == EventTypeNames::selectstart); |
+} |
+#endif |
EventPath::EventPath(Node& node, Event* event) |
: m_node(node) |
@@ -93,6 +115,46 @@ void EventPath::addNodeEventContext(Node& node) |
m_nodeEventContexts.append(NodeEventContext(&node, eventTargetRespectingTargetRules(node))); |
} |
+#if W3C23887 == 1 |
+static bool isDistributedToShadowInsertionPoint(const ShadowRoot& shadowRoot) |
+{ |
+ // 5.2.3. If CURRENT is not the youngest shadow root hosted by SHADOW-POOL-HOST: |
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c163 |
+ // and CURRENT is distributed to a shadow insertion point |
+ ElementShadow* owner = shadowRoot.owner(); |
+ if (!owner) |
+ return false; |
+ if (shadowRoot.firstChild()) |
+ return owner->finalDestinationInsertionPointFor(shadowRoot.firstChild()); |
+ // TODO(kojii): how can we determine if a shadow root is distributed? |
+ return false; |
+} |
+ |
+static ShadowRoot* shadowRootDistributedInto(InsertionPoint& insertionPoint) |
+{ |
+ // 5.4.4.1. Let PROJECTED-SHADOW be the shadow root projected into CURRENT. |
+ // TODO(kojii): is this correct way to determine that? |
+#ifndef A1 |
+ return insertionPoint.containingShadowRoot()->olderShadowRoot(); |
+#else |
+ // TODO(kojii): should it be first() or last()? |
+ Node* distributedNode = insertionPoint.first(); |
+ if (!distributedNode) |
+ return nullptr; |
+ return distributedNode->containingShadowRoot(); |
+#endif |
+} |
+#endif |
+#if W3C23887 >= 2 |
+static inline InsertionPoint* pop(WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8>& stack) |
+{ |
+ ASSERT(!stack.isEmpty()); |
+ InsertionPoint* last = stack.last(); |
+ stack.removeLast(); |
+ return last; |
+} |
+#endif |
+ |
void EventPath::calculatePath() |
{ |
ASSERT(m_node); |
@@ -100,6 +162,7 @@ void EventPath::calculatePath() |
m_node->updateDistribution(); |
Node* current = m_node; |
+#ifndef W3C23887 |
addNodeEventContext(*current); |
if (!m_node->inDocument()) |
return; |
@@ -132,6 +195,182 @@ void EventPath::calculatePath() |
addNodeEventContext(*current); |
} |
} |
+#elif W3C23887 == 3 |
+ if (!m_node->inDocument()) { |
+ addNodeEventContext(*current); |
+ return; |
+ } |
+ Element* stopBefore = m_event && shouldStopBeforeShadowHost(*m_event) ? m_node->shadowHost() : nullptr; |
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPointStack; |
+ for (;;) { |
+ ASSERT(current); |
+ addNodeEventContext(*current); |
+ if (m_event && current->keepEventInNode(m_event)) |
+ break; |
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
+ collectDestinationInsertionPoints(*current, insertionPoints); |
+ if (!insertionPoints.isEmpty()) { |
+ insertionPointStack.appendVector(insertionPoints); |
+ current = pop(insertionPointStack); |
+ continue; |
+ } |
+ if (current->isShadowRoot()) { |
+ ShadowRoot* shadowRoot = toShadowRoot(current); |
+ Node* olderShadowRootOrShadowHost = shadowRoot->olderShadowRoot(); |
+ if (!olderShadowRootOrShadowHost) |
+ olderShadowRootOrShadowHost = shadowRoot->host(); |
+ ASSERT(olderShadowRootOrShadowHost); |
+ if (!insertionPointStack.isEmpty() && insertionPointStack.last()->treeScope() == olderShadowRootOrShadowHost->treeScope()) { |
+ current = pop(insertionPointStack); |
+ continue; |
+ } |
+ if (olderShadowRootOrShadowHost == stopBefore) |
+ break; |
+ current = olderShadowRootOrShadowHost; |
+ continue; |
+ } |
+ current = current->parentNode(); |
+ if (!current) |
+ break; |
+ } |
+ // ASSERT(insertionPointStack.isEmpty()); |
+#elif W3C23887 == 2 |
+ if (!m_node->inDocument()) { |
+ addNodeEventContext(*current); |
+ return; |
+ } |
+ Element* stopBefore = m_event && shouldStopBeforeShadowHost(*m_event) ? m_node->shadowHost() : nullptr; |
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPointStack; |
+ for (;;) { |
+ ASSERT(current); |
+ addNodeEventContext(*current); |
+ if (m_event && current->keepEventInNode(m_event)) |
+ break; |
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
+ collectDestinationInsertionPoints(*current, insertionPoints); |
+ if (!insertionPoints.isEmpty()) { |
+ bool hasInsertionPoints = false; |
+ for (const auto& insertionPoint : insertionPoints) { |
+ if (insertionPoint->isShadowInsertionPoint() && !insertionPoint->containingShadowRoot()->isOldest()) |
+ continue; |
+ insertionPointStack.append(insertionPoint); |
+ hasInsertionPoints = true; |
+ } |
+ if (hasInsertionPoints) { |
+ current = pop(insertionPointStack); |
+ continue; |
+ } |
+ } |
+ if (current->isShadowRoot()) { |
+ ShadowRoot* shadowRoot = toShadowRoot(current); |
+ Element* shadowHost = shadowRoot->host(); |
+ ASSERT(shadowHost); |
+ if (!insertionPointStack.isEmpty() && insertionPointStack.last()->treeScope() == shadowHost->treeScope()) { |
+ current = pop(insertionPointStack); |
+ continue; |
+ } |
+ if (HTMLShadowElement* shadowInsertionPointOfYoungerShadowRoot = shadowRoot->shadowInsertionPointOfYoungerShadowRoot()) { |
+ current = shadowInsertionPointOfYoungerShadowRoot; |
+ continue; |
+ } |
+ if (shadowHost == stopBefore) |
+ break; |
+ current = shadowHost; |
+ continue; |
+ } |
+ current = current->parentNode(); |
+ if (!current) |
+ break; |
+ } |
+ ASSERT(insertionPointStack.isEmpty()); |
+#elif W3C23887 == 1 // https://bugzilla.mozilla.org/show_bug.cgi?id=1059989#c10 |
+ addNodeEventContext(*current); |
+ if (!m_node->inDocument()) |
+ return; |
+ // 3. Let INSERTION-POINTS be an empty stack of nodes. |
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPointStack; |
+ while (current) { |
+ if (m_event && current->keepEventInNode(m_event)) |
+ break; |
+ // 5.1. If the destination insertion points of CURRENT is not empty: |
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; |
+ collectDestinationInsertionPoints(*current, insertionPoints); |
+ bool removeShadowInsertionPoint = true; |
+ if (!insertionPoints.isEmpty()) { |
+ // 5.1.1. Push the destination insertion points into INSERTION-POINTS in order of first destination to final destination. |
+ insertionPointStack.appendVector(insertionPoints); |
+ // 5.1.2. Pop INSERTION-POINTS and set CURRENT to be the popped node. |
+ current = insertionPointStack.last(); |
+ insertionPointStack.removeLast(); |
+ // 5.2. Otherwise if CURRENT is a shadow root: |
+ } else if (current->isShadowRoot()) { |
+ // 5.2.2. If SHADOW-POOL-HOST hosts the node tree which NODE participates in and EVENT is one of the events which must be stopped: |
+ ShadowRoot* currentShadowRoot = toShadowRoot(current); |
+ // TODO(kojii): we should be able to check this once and determine where to stop for better performance |
+ if (m_event && shouldStopAtShadowRoot(*m_event, *currentShadowRoot, *m_node)) { |
+ // 5.2.2.1. Stop this algorithm |
+ break; |
+ } |
+ // 5.2.1. Let SHADOW-POOL-HOST be the shadow host which hosts CURRENT |
+ Element* shadowPoolHost = currentShadowRoot->host(); |
+ // 5.2.3. If CURRENT is not the youngest shadow root hosted by SHADOW-POOL-HOST: |
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c163 |
+ // and CURRENT is distributed to a shadow insertion point |
+ if (!currentShadowRoot->isYoungest() && isDistributedToShadowInsertionPoint(*currentShadowRoot)) { |
+ // 5.2.3.1. Let SHADOW-POOL-HOST be the shadow insertion point into which CURRENT shadow root is projected. |
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c122 |
+ // Let SHADOW-POOL-HOST be the shadow insertion point in the younger shadow tree relative to the shadow tree |
+ // whose root is CURRENT shadow root. |
+ shadowPoolHost = currentShadowRoot->shadowInsertionPointOfYoungerShadowRoot(); |
+ } |
+ // 5.2.4. If INSERTION-POINTS is not empty and |
+ // if the most recent node in the INSERTION-POINTS is in the same node tree as SHADOW-POOL-HOST: |
+ if (!insertionPointStack.isEmpty() && insertionPointStack.last()->treeScope() == shadowPoolHost->treeScope()) { |
+ // 5.2.4.1. Pop INSERTION-POINTS and set CURRENT to be the popped node. |
+ current = insertionPointStack.last(); |
+ insertionPointStack.removeLast(); |
+ // 5.2.5. Otherwise: |
+ } else { |
+ // 5.2.5.1. Set CURRENT to SHADOW-POOL-HOST and skip step 4 |
+ current = shadowPoolHost; |
+ removeShadowInsertionPoint = false; |
+ } |
+ // 5.3. Otherwise: |
+ } else { |
+ // 5.3.1. 1. Let CURRENT be the parent node of CURRENT. |
+ current = current->parentNode(); |
+ } |
+ if (removeShadowInsertionPoint) { |
+ // 5.4.4. Repeat while CURRENT is a shadow insertion point. |
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c170 |
+ // and there is a shadow root distributed to it (see the "if" within the "while" below) |
+ while (current && isActiveShadowInsertionPoint(*current)) { |
+ // 5.4.4.1. Let PROJECTED-SHADOW be the shadow root projected into CURRENT. |
+ ShadowRoot* distributedShadowRoot = shadowRootDistributedInto(toInsertionPoint(*current)); |
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c170 |
+ // 5.4.4. and there is a shadow root distributed to it |
+ if (!distributedShadowRoot) |
+ break; |
+ // 5.4.4.2. If INSERTION-POINTS is not empty and |
+ // if the most recent node in the INSERTION-POINTS is in the same node tree as PROJECTED-SHADOW: |
+ if (!insertionPointStack.isEmpty() && insertionPointStack.last()->treeScope() == distributedShadowRoot->treeScope()) { |
+ // 5.4.4.2.1. Pop INSERTION-POINTS and set CURRENT to be the popped node. |
+ current = insertionPointStack.last(); |
+ insertionPointStack.removeLast(); |
+ // 5.4.4.3. Otherwise: |
+ } else { |
+ // 5.4.4.3.1. Set CURRENT to PROJECTED-SHADOW. |
+ current = distributedShadowRoot; |
+ } |
+ } |
+ } |
+ // 5.5. If CURRENT exists: |
+ // 5.5.1. Add CURRENT to PATH. |
+ if (current) |
+ addNodeEventContext(*current); |
+ } |
+ ASSERT(insertionPointStack.isEmpty()); |
+#endif |
} |
void EventPath::calculateTreeScopePrePostOrderNumbers() |