Modified: trunk/LayoutTests/fast/shadow-dom/event-inside-slotted-node.html (200463 => 200464)
--- trunk/LayoutTests/fast/shadow-dom/event-inside-slotted-node.html 2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/LayoutTests/fast/shadow-dom/event-inside-slotted-node.html 2016-05-05 18:33:17 UTC (rev 200464)
@@ -80,9 +80,9 @@
assert_equals(log.length, 6, 'EventPath must contain [target, target parent, slot, slot parent, shadow root, shadow host]');
assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
assert_array_equals(log[1], [shadow.targetParent, shadow.target], 'EventPath[1] must be the parent of the target');
- assert_array_equals(log[2], [shadow.slot, shadow.slot], 'EventPath[2] must be the slot');
- assert_array_equals(log[3], [shadow.slotParent, shadow.slot], 'EventPath[3] must be the parent of the slot');
- assert_array_equals(log[4], [shadow.root, shadow.slot], 'EventPath[4] must be the shadow root');
+ assert_array_equals(log[2], [shadow.slot, shadow.target], 'EventPath[2] must be the slot');
+ assert_array_equals(log[3], [shadow.slotParent, shadow.target], 'EventPath[3] must be the parent of the slot');
+ assert_array_equals(log[4], [shadow.root, shadow.target], 'EventPath[4] must be the shadow root');
assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
}, 'Firing an event inside a grand child of a detached ' + mode + ' mode shadow host');
@@ -101,9 +101,9 @@
assert_equals(log.length, 9, 'EventPath must contain [target, target parent, slot, slot parent, shadow root, shadow host, body, html, document]');
assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
assert_array_equals(log[1], [shadow.targetParent, shadow.target], 'EventPath[1] must be the parent of the target');
- assert_array_equals(log[2], [shadow.slot, shadow.slot], 'EventPath[2] must be the slot');
- assert_array_equals(log[3], [shadow.slotParent, shadow.slot], 'EventPath[3] must be the parent of the slot');
- assert_array_equals(log[4], [shadow.root, shadow.slot], 'EventPath[4] must be the shadow root');
+ assert_array_equals(log[2], [shadow.slot, shadow.target], 'EventPath[2] must be the slot');
+ assert_array_equals(log[3], [shadow.slotParent, shadow.target], 'EventPath[3] must be the parent of the slot');
+ assert_array_equals(log[4], [shadow.root, shadow.target], 'EventPath[4] must be the shadow root');
assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
assert_array_equals(log[6], [document.body, shadow.target], 'EventPath[6] must be the body element');
assert_array_equals(log[7], [document.documentElement, shadow.target], 'EventPath[7] must be the html element');
@@ -179,19 +179,19 @@
assert_equals(log.length, 15, 'EventPath must contain 15 targets');
assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
- assert_array_equals(log[1], [shadow.lowerSlot, shadow.lowerSlot], 'EventPath[1] must be the slot inside the lower shadow tree');
- assert_array_equals(log[2], [shadow.lowerSlot.parentNode, shadow.lowerSlot], 'EventPath[2] must be the parent of the slot inside the lower shadow tree');
- assert_array_equals(log[3], [shadow.innerSlot, shadow.innerSlot], 'EventPath[3] must be the slot inside the shadow tree inside the lower shadow tree');
- assert_array_equals(log[4], [shadow.innerSlot.parentNode, shadow.innerSlot], 'EventPath[4] must be the child of the inner shadow root');
- assert_array_equals(log[5], [shadow.innerShadow, shadow.innerSlot], 'EventPath[5] must be the inner shadow root');
- assert_array_equals(log[6], [shadow.innerShadow.host, shadow.lowerSlot], 'EventPath[6] must be the host of the inner shadow tree');
- assert_array_equals(log[7], [shadow.lowerShadow.firstChild, shadow.lowerSlot], 'EventPath[7] must be the parent of the inner shadow host');
- assert_array_equals(log[8], [shadow.lowerShadow, shadow.lowerSlot], 'EventPath[8] must be the lower shadow root');
+ assert_array_equals(log[1], [shadow.lowerSlot, shadow.target], 'EventPath[1] must be the slot inside the lower shadow tree');
+ assert_array_equals(log[2], [shadow.lowerSlot.parentNode, shadow.target], 'EventPath[2] must be the parent of the slot inside the lower shadow tree');
+ assert_array_equals(log[3], [shadow.innerSlot, shadow.target], 'EventPath[3] must be the slot inside the shadow tree inside the lower shadow tree');
+ assert_array_equals(log[4], [shadow.innerSlot.parentNode, shadow.target], 'EventPath[4] must be the child of the inner shadow root');
+ assert_array_equals(log[5], [shadow.innerShadow, shadow.target], 'EventPath[5] must be the inner shadow root');
+ assert_array_equals(log[6], [shadow.innerShadow.host, shadow.target], 'EventPath[6] must be the host of the inner shadow tree');
+ assert_array_equals(log[7], [shadow.lowerShadow.firstChild, shadow.target], 'EventPath[7] must be the parent of the inner shadow host');
+ assert_array_equals(log[8], [shadow.lowerShadow, shadow.target], 'EventPath[8] must be the lower shadow root');
assert_array_equals(log[9], [shadow.lowerShadow.host, shadow.target], 'EventPath[9] must be the lower shadow host');
assert_array_equals(log[10], [shadow.host.firstChild, shadow.target], 'EventPath[10] must be the parent of the grand parent of the target');
- assert_array_equals(log[11], [shadow.upperSlot, shadow.upperSlot], 'EventPath[11] must be the slot inside the upper shadow tree');
- assert_array_equals(log[12], [shadow.upperSlot.parentNode, shadow.upperSlot], 'EventPath[12] must be the parent of the slot inside the upper shadow tree');
- assert_array_equals(log[13], [shadow.upperShadow, shadow.upperSlot], 'EventPath[13] must be the upper shadow root');
+ assert_array_equals(log[11], [shadow.upperSlot, shadow.target], 'EventPath[11] must be the slot inside the upper shadow tree');
+ assert_array_equals(log[12], [shadow.upperSlot.parentNode, shadow.target], 'EventPath[12] must be the parent of the slot inside the upper shadow tree');
+ assert_array_equals(log[13], [shadow.upperShadow, shadow.target], 'EventPath[13] must be the upper shadow root');
assert_array_equals(log[14], [shadow.host, shadow.target], 'EventPath[14] must be the host');
}, 'Firing an event on a node with two ancestors with a detached ' + outerUpperMode + ' and ' + outerLowerMode
@@ -215,7 +215,7 @@
+ a + em (4)
+ inner-host (3) -- (innerShadow; 2)
+ span + i (1)
- + slot + slot (innerSlot; 0)
+ + slot + slot (innerSlot, target; 0)
*/
function testEventInsideNestedShadowsUnderAnotherShadow(outerUpperMode, outerLowerMode, innerMode) {
@@ -236,9 +236,9 @@
assert_array_equals(log[5], [shadow.lowerShadow, shadow.innerShadow.host], 'EventPath[5] must be the lower (but outer) shadow root');
assert_array_equals(log[6], [shadow.lowerShadow.host, shadow.lowerShadow.host], 'EventPath[6] must be the lower (but outer) shadow root');
assert_array_equals(log[7], [shadow.host.firstChild, shadow.lowerShadow.host], 'EventPath[7] must be the slot inside the upper shadow tree');
- assert_array_equals(log[8], [shadow.upperSlot, shadow.upperSlot], 'EventPath[8] must be the slot inside the upper shadow tree');
- assert_array_equals(log[9], [shadow.upperSlot.parentNode, shadow.upperSlot], 'EventPath[9] must be the parent of the slot inside the upper shadow tree');
- assert_array_equals(log[10], [shadow.upperShadow, shadow.upperSlot], 'EventPath[10] must be the upper shadow root');
+ assert_array_equals(log[8], [shadow.upperSlot, shadow.lowerShadow.host], 'EventPath[8] must be the slot inside the upper shadow tree');
+ assert_array_equals(log[9], [shadow.upperSlot.parentNode, shadow.lowerShadow.host], 'EventPath[9] must be the parent of the slot inside the upper shadow tree');
+ assert_array_equals(log[10], [shadow.upperShadow, shadow.lowerShadow.host], 'EventPath[10] must be the upper shadow root');
assert_array_equals(log[11], [shadow.upperShadow.host, shadow.lowerShadow.host], 'EventPath[11] must be the host');
}, 'Firing an event on a node with two ancestors with a detached ' + outerUpperMode + ' and ' + outerLowerMode
Modified: trunk/Source/WebCore/dom/EventPath.cpp (200463 => 200464)
--- trunk/Source/WebCore/dom/EventPath.cpp 2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/dom/EventPath.cpp 2016-05-05 18:33:17 UTC (rev 200464)
@@ -60,9 +60,9 @@
class RelatedNodeRetargeter {
public:
- RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope);
+ RelatedNodeRetargeter(Node& relatedNode, Node& target);
- Node* currentNode(TreeScope& currentTreeScope);
+ Node* currentNode(Node& currentTreeScope);
void moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope);
private:
@@ -71,9 +71,9 @@
void collectTreeScopes();
#if ASSERT_DISABLED
- void checkConsistency(TreeScope&) { }
+ void checkConsistency(Node&) { }
#else
- void checkConsistency(TreeScope& currentTreeScope);
+ void checkConsistency(Node& currentTarget);
#endif
Node& m_relatedNode;
@@ -86,22 +86,14 @@
EventPath::EventPath(Node& originalTarget, Event& event)
: m_event(event)
{
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
- Vector<EventTarget*, 16> targetStack;
-#endif
-
bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent();
#if ENABLE(TOUCH_EVENTS)
bool isTouchEvent = event.isTouchEvent();
#endif
- EventTarget* target = nullptr;
-
Node* node = nodeOrHostIfPseudoElement(&originalTarget);
+ Node* target = eventTargetRespectingTargetRules(*node);
while (node) {
- if (!target)
- target = eventTargetRespectingTargetRules(*node);
- ContainerNode* parent;
- for (; node; node = parent) {
+ while (node) {
EventTarget* currentTarget = eventTargetRespectingTargetRules(*node);
if (isMouseOrFocusEvent)
@@ -116,8 +108,7 @@
if (is<ShadowRoot>(*node))
break;
- parent = node->parentNode();
-
+ ContainerNode* parent = node->parentNode();
if (!parent)
return;
@@ -125,30 +116,21 @@
if (ShadowRoot* shadowRootOfParent = parent->shadowRoot()) {
if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) {
// node is assigned to a slot. Continue dispatching the event at this slot.
- targetStack.append(target);
parent = assignedSlot;
- target = assignedSlot;
}
}
#endif
node = parent;
}
+ bool exitingShadowTreeOfTarget = &target->treeScope() == &node->treeScope();
ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node);
- // At a shadow root. Continue dispatching the event at the shadow host.
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
- if (!targetStack.isEmpty()) {
- // Move target back to a descendant of the shadow host if the event did not originate in this shadow tree or its inner shadow trees.
- target = targetStack.last();
- targetStack.removeLast();
- ASSERT(shadowRoot.host()->contains(target->toNode()));
- } else
-#endif
- target = nullptr;
-
if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget))
return;
node = shadowRoot.host();
+ if (exitingShadowTreeOfTarget)
+ target = eventTargetRespectingTargetRules(*node);
+
}
}
@@ -158,7 +140,7 @@
if (!relatedNode || m_path.isEmpty())
return;
- RelatedNodeRetargeter retargeter(*relatedNode, downcast<MouseOrFocusEventContext>(*m_path[0]).node()->treeScope());
+ RelatedNodeRetargeter retargeter(*relatedNode, *m_path[0]->node());
bool originIsRelatedTarget = &origin == relatedNode;
bool relatedTargetScoped = m_event.relatedTargetScoped();
@@ -168,11 +150,12 @@
for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) {
auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]);
- TreeScope& currentTreeScope = context.node()->treeScope();
+ Node& currentTarget = *context.node();
+ TreeScope& currentTreeScope = currentTarget.treeScope();
if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope))
retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
- Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
+ Node* currentRelatedNode = retargeter.currentNode(currentTarget);
if (UNLIKELY(relatedTargetScoped && !originIsRelatedTarget && context.target() == currentRelatedNode)) {
m_path.shrink(contextIndex);
break;
@@ -200,14 +183,15 @@
if (!targetNode)
return;
- RelatedNodeRetargeter retargeter(*targetNode, m_path[0]->node()->treeScope());
+ RelatedNodeRetargeter retargeter(*targetNode, *m_path[0]->node());
TreeScope* previousTreeScope = nullptr;
for (auto& context : m_path) {
- TreeScope& currentTreeScope = context->node()->treeScope();
+ Node& currentTarget = *context->node();
+ TreeScope& currentTreeScope = currentTarget.treeScope();
if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope))
retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
- Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
+ Node* currentRelatedNode = retargeter.currentNode(currentTarget);
downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
previousTreeScope = ¤tTreeScope;
@@ -243,30 +227,8 @@
return false;
}
-// http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node
-static bool isUnclosedNodeOf(const Node& a, const Node& b)
+Vector<EventTarget*> EventPath::computePathUnclosedToTarget(const EventTarget& target) const
{
- // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small.
- Vector<TreeScope*, 8> treeScopesOpenToB;
-
- for (auto* scope = &b.treeScope(); scope; scope = scope->parentTreeScope())
- treeScopesOpenToB.append(scope);
-
- for (auto* treeScopeThatCanAccessA = &a.treeScope(); treeScopeThatCanAccessA; treeScopeThatCanAccessA = treeScopeThatCanAccessA->parentTreeScope()) {
- for (auto* openToB : treeScopesOpenToB) {
- if (openToB == treeScopeThatCanAccessA)
- return true;
- }
- auto& root = treeScopeThatCanAccessA->rootNode();
- if (is<ShadowRoot>(root) && downcast<ShadowRoot>(root).type() != ShadowRoot::Type::Open)
- break;
- }
-
- return false;
-}
-
-Vector<EventTarget*> EventPath::computePathDisclosedToTarget(const EventTarget& target) const
-{
Vector<EventTarget*> path;
const Node* targetNode = const_cast<EventTarget&>(target).toNode();
if (!targetNode)
@@ -274,7 +236,7 @@
for (auto& context : m_path) {
if (Node* nodeInPath = context->currentTarget()->toNode()) {
- if (isUnclosedNodeOf(*nodeInPath, *targetNode))
+ if (targetNode->isUnclosedNode(*nodeInPath))
path.append(context->currentTarget());
}
}
@@ -282,12 +244,21 @@
return path;
}
-RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope)
+static Node* moveOutOfAllShadowRoots(Node& startingNode)
+{
+ Node* node = &startingNode;
+ while (node->isInShadowTree())
+ node = downcast<ShadowRoot>(node->treeScope().rootNode()).host();
+ return node;
+}
+
+RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, Node& target)
: m_relatedNode(relatedNode)
, m_retargetedRelatedNode(&relatedNode)
{
+ auto& targetTreeScope = target.treeScope();
TreeScope* currentTreeScope = &m_relatedNode.treeScope();
- if (LIKELY(currentTreeScope == &targetTreeScope))
+ if (LIKELY(currentTreeScope == &targetTreeScope && target.inDocument() && m_relatedNode.inDocument()))
return;
if (¤tTreeScope->documentScope() != &targetTreeScope.documentScope()) {
@@ -295,10 +266,9 @@
m_retargetedRelatedNode = nullptr;
return;
}
- if (relatedNode.inDocument() != targetTreeScope.rootNode().inDocument()) {
+ if (relatedNode.inDocument() != target.inDocument()) {
m_hasDifferentTreeRoot = true;
- while (m_retargetedRelatedNode->isInShadowTree())
- m_retargetedRelatedNode = downcast<ShadowRoot>(m_retargetedRelatedNode->treeScope().rootNode()).host();
+ m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode);
return;
}
@@ -320,13 +290,24 @@
break;
}
+ bool lowestCommonAncestorIsDocumentScope = i + 1 == m_ancestorTreeScopes.size();
+ if (lowestCommonAncestorIsDocumentScope && !relatedNode.inDocument() && !target.inDocument()) {
+ Node& targetAncestorInDocumentScope = i ? *downcast<ShadowRoot>(m_ancestorTreeScopes[i - 1]->rootNode()).shadowHost() : target;
+ Node& relatedNodeAncestorInDocumentScope = j ? *downcast<ShadowRoot>(targetTreeScopeAncestors[j - 1]->rootNode()).shadowHost() : relatedNode;
+ if (targetAncestorInDocumentScope.rootNode() != relatedNodeAncestorInDocumentScope.rootNode()) {
+ m_hasDifferentTreeRoot = true;
+ m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode);
+ return;
+ }
+ }
+
m_lowestCommonAncestorIndex = i;
m_retargetedRelatedNode = nodeInLowestCommonAncestor();
}
-inline Node* RelatedNodeRetargeter::currentNode(TreeScope& currentTreeScope)
+inline Node* RelatedNodeRetargeter::currentNode(Node& currentTarget)
{
- checkConsistency(currentTreeScope);
+ checkConsistency(currentTarget);
return m_retargetedRelatedNode;
}
@@ -381,17 +362,19 @@
}
#if !ASSERT_DISABLED
-void RelatedNodeRetargeter::checkConsistency(TreeScope& currentTreeScope)
+void RelatedNodeRetargeter::checkConsistency(Node& currentTarget)
{
- for (auto* relatedNodeScope = &m_relatedNode.treeScope(); relatedNodeScope; relatedNodeScope = relatedNodeScope->parentTreeScope()) {
- for (auto* targetScope = ¤tTreeScope; targetScope; targetScope = targetScope->parentTreeScope()) {
- if (targetScope == relatedNodeScope) {
- ASSERT(&m_retargetedRelatedNode->treeScope() == relatedNodeScope);
- return;
- }
+ ASSERT(!m_retargetedRelatedNode || currentTarget.isUnclosedNode(*m_retargetedRelatedNode));
+
+ // http://w3c.github.io/webcomponents/spec/shadow/#dfn-retargeting-algorithm
+ Node& base = currentTarget;
+ for (Node* targetAncestor = &m_relatedNode; targetAncestor; targetAncestor = targetAncestor->parentOrShadowHostNode()) {
+ if (targetAncestor->rootNode()->containsIncludingShadowDOM(&base)) {
+ ASSERT(m_retargetedRelatedNode == targetAncestor);
+ return;
}
}
- ASSERT(!m_retargetedRelatedNode);
+ ASSERT(!m_retargetedRelatedNode || m_hasDifferentTreeRoot);
}
#endif