Changeset 200464 in webkit


Ignore:
Timestamp:
May 5, 2016 11:33:17 AM (8 years ago)
Author:
rniwa@webkit.org
Message:

event.target shouldn't be retargeted as the event bubbles into a slot
https://bugs.webkit.org/show_bug.cgi?id=157369

Reviewed by Antti Koivisto.

Source/WebCore:

When an event bubbles up from an assigned node to its assigned slot, we shouldn't be adjusting
event.target to point to the slot. Since a shadow tree should have access to nodes outside
the shadow tree, event.target is accessible inside the shadow tree of such a slot.

New behavior matches the behavior of Google Chrome Canary as well as the shadow DOM specification:
http://w3c.github.io/webcomponents/spec/shadow/#dfn-retargeting-algorithm

Test: fast/shadow-dom/event-inside-slotted-node.html

  • dom/Event.cpp:

(WebCore::Event::deepPath):

  • dom/EventContext.h:

(WebCore::EventContext::isUnreachableNode): Use Node::isUnclosedNode instead of isReachable.
(WebCore::EventContext::isReachable): Deleted.

  • dom/EventPath.cpp:

(WebCore::EventPath::EventPath): Don't set the target to the slot when entering a slot. Also moved
the code to adjust the target as we exit a shadow tree to the end of the outer loop for clarity.
(WebCore::isUnclosedNodeOf): Deleted. Renamed to Node::isUnclosedNode.
(WebCore::EventPath::setRelatedTarget):
(WebCore::EventPath::computePathUnclosedToTarget): Renamed from computePathDisclosedToTarget.
(WebCore::moveOutOfAllShadowRoots): Extracted from RelatedNodeRetargeter::RelatedNodeRetargeter.
(WebCore::RelatedNodeRetargeter::RelatedNodeRetargeter): Fixed a bug that we were exiting early
without setting m_hasDifferentTreeRoot true when target and relatedNode are disconnected from
a document.
(WebCore::RelatedNodeRetargeter::currentNode):
(WebCore::RelatedNodeRetargeter::checkConsistency): Updated to match the spec with one exception.
We don't use null as the adjusted related target when the (original) related target and the target
are in two distinct disconnected trees since such a behavior is not Web compatible. This spec bug is
tracked by https://github.com/w3c/webcomponents/issues/494

  • dom/EventPath.h:

(WebCore::EventPath::eventTargetRespectingTargetRules): Returns Node* instead of EventTarget* since
we need a Node in RelatedNodeRetargeter::checkConsistency.

  • dom/Node.cpp:

(WebCore::Node::isUnclosedNode): Moved from RelatedNodeRetargeter.cpp

  • dom/Node.h:

LayoutTests:

Updated test cases to expect the target to be not adjusted to a slot element when the event path
enters one as this didn't match the spec or the behavior of Google Chrome Canary. Both WebKit and
Chrome passes the test with this change.

  • fast/shadow-dom/event-inside-slotted-node.html:
Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r200461 r200464  
     12016-05-05  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        event.target shouldn't be retargeted as the event bubbles into a slot
     4        https://bugs.webkit.org/show_bug.cgi?id=157369
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Updated test cases to expect the target to be not adjusted to a slot element when the event path
     9        enters one as this didn't match the spec or the behavior of Google Chrome Canary. Both WebKit and
     10        Chrome passes the test with this change.
     11
     12        * fast/shadow-dom/event-inside-slotted-node.html:
     13
    1142016-05-04  Alex Christensen  <achristensen@webkit.org>
    215
  • trunk/LayoutTests/fast/shadow-dom/event-inside-slotted-node.html

    r190214 r200464  
    8181                assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
    8282                assert_array_equals(log[1], [shadow.targetParent, shadow.target], 'EventPath[1] must be the parent of the target');
    83                 assert_array_equals(log[2], [shadow.slot, shadow.slot], 'EventPath[2] must be the slot');
    84                 assert_array_equals(log[3], [shadow.slotParent, shadow.slot], 'EventPath[3] must be the parent of the slot');
    85                 assert_array_equals(log[4], [shadow.root, shadow.slot], 'EventPath[4] must be the shadow root');
     83                assert_array_equals(log[2], [shadow.slot, shadow.target], 'EventPath[2] must be the slot');
     84                assert_array_equals(log[3], [shadow.slotParent, shadow.target], 'EventPath[3] must be the parent of the slot');
     85                assert_array_equals(log[4], [shadow.root, shadow.target], 'EventPath[4] must be the shadow root');
    8686                assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
    8787
     
    102102                assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
    103103                assert_array_equals(log[1], [shadow.targetParent, shadow.target], 'EventPath[1] must be the parent of the target');
    104                 assert_array_equals(log[2], [shadow.slot, shadow.slot], 'EventPath[2] must be the slot');
    105                 assert_array_equals(log[3], [shadow.slotParent, shadow.slot], 'EventPath[3] must be the parent of the slot');
    106                 assert_array_equals(log[4], [shadow.root, shadow.slot], 'EventPath[4] must be the shadow root');
     104                assert_array_equals(log[2], [shadow.slot, shadow.target], 'EventPath[2] must be the slot');
     105                assert_array_equals(log[3], [shadow.slotParent, shadow.target], 'EventPath[3] must be the parent of the slot');
     106                assert_array_equals(log[4], [shadow.root, shadow.target], 'EventPath[4] must be the shadow root');
    107107                assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
    108108                assert_array_equals(log[6], [document.body, shadow.target], 'EventPath[6] must be the body element');
     
    180180
    181181                assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
    182                 assert_array_equals(log[1], [shadow.lowerSlot, shadow.lowerSlot], 'EventPath[1] must be the slot inside the lower shadow tree');
    183                 assert_array_equals(log[2], [shadow.lowerSlot.parentNode, shadow.lowerSlot], 'EventPath[2] must be the parent of the slot inside the lower shadow tree');
    184                 assert_array_equals(log[3], [shadow.innerSlot, shadow.innerSlot], 'EventPath[3] must be the slot inside the shadow tree inside the lower shadow tree');
    185                 assert_array_equals(log[4], [shadow.innerSlot.parentNode, shadow.innerSlot], 'EventPath[4] must be the child of the inner shadow root');
    186                 assert_array_equals(log[5], [shadow.innerShadow, shadow.innerSlot], 'EventPath[5] must be the inner shadow root');
    187                 assert_array_equals(log[6], [shadow.innerShadow.host, shadow.lowerSlot], 'EventPath[6] must be the host of the inner shadow tree');
    188                 assert_array_equals(log[7], [shadow.lowerShadow.firstChild, shadow.lowerSlot], 'EventPath[7] must be the parent of the inner shadow host');
    189                 assert_array_equals(log[8], [shadow.lowerShadow, shadow.lowerSlot], 'EventPath[8] must be the lower shadow root');
     182                assert_array_equals(log[1], [shadow.lowerSlot, shadow.target], 'EventPath[1] must be the slot inside the lower shadow tree');
     183                assert_array_equals(log[2], [shadow.lowerSlot.parentNode, shadow.target], 'EventPath[2] must be the parent of the slot inside the lower shadow tree');
     184                assert_array_equals(log[3], [shadow.innerSlot, shadow.target], 'EventPath[3] must be the slot inside the shadow tree inside the lower shadow tree');
     185                assert_array_equals(log[4], [shadow.innerSlot.parentNode, shadow.target], 'EventPath[4] must be the child of the inner shadow root');
     186                assert_array_equals(log[5], [shadow.innerShadow, shadow.target], 'EventPath[5] must be the inner shadow root');
     187                assert_array_equals(log[6], [shadow.innerShadow.host, shadow.target], 'EventPath[6] must be the host of the inner shadow tree');
     188                assert_array_equals(log[7], [shadow.lowerShadow.firstChild, shadow.target], 'EventPath[7] must be the parent of the inner shadow host');
     189                assert_array_equals(log[8], [shadow.lowerShadow, shadow.target], 'EventPath[8] must be the lower shadow root');
    190190                assert_array_equals(log[9], [shadow.lowerShadow.host, shadow.target], 'EventPath[9] must be the lower shadow host');
    191191                assert_array_equals(log[10], [shadow.host.firstChild, shadow.target], 'EventPath[10] must be the parent of the grand parent of the target');
    192                 assert_array_equals(log[11], [shadow.upperSlot, shadow.upperSlot], 'EventPath[11] must be the slot inside the upper shadow tree');
    193                 assert_array_equals(log[12], [shadow.upperSlot.parentNode, shadow.upperSlot], 'EventPath[12] must be the parent of the slot inside the upper shadow tree');
    194                 assert_array_equals(log[13], [shadow.upperShadow, shadow.upperSlot], 'EventPath[13] must be the upper shadow root');
     192                assert_array_equals(log[11], [shadow.upperSlot, shadow.target], 'EventPath[11] must be the slot inside the upper shadow tree');
     193                assert_array_equals(log[12], [shadow.upperSlot.parentNode, shadow.target], 'EventPath[12] must be the parent of the slot inside the upper shadow tree');
     194                assert_array_equals(log[13], [shadow.upperShadow, shadow.target], 'EventPath[13] must be the upper shadow root');
    195195                assert_array_equals(log[14], [shadow.host, shadow.target], 'EventPath[14] must be the host');
    196196
     
    216216                                + inner-host (3) -- (innerShadow; 2)
    217217                                  + span            + i (1)
    218                                     + slot            + slot (innerSlot; 0)
     218                                    + slot            + slot (innerSlot, target; 0)
    219219        */
    220220
     
    237237                assert_array_equals(log[6], [shadow.lowerShadow.host, shadow.lowerShadow.host], 'EventPath[6] must be the lower (but outer) shadow root');
    238238                assert_array_equals(log[7], [shadow.host.firstChild, shadow.lowerShadow.host], 'EventPath[7] must be the slot inside the upper shadow tree');
    239                 assert_array_equals(log[8], [shadow.upperSlot, shadow.upperSlot], 'EventPath[8] must be the slot inside the upper shadow tree');
    240                 assert_array_equals(log[9], [shadow.upperSlot.parentNode, shadow.upperSlot], 'EventPath[9] must be the parent of the slot inside the upper shadow tree');
    241                 assert_array_equals(log[10], [shadow.upperShadow, shadow.upperSlot], 'EventPath[10] must be the upper shadow root');
     239                assert_array_equals(log[8], [shadow.upperSlot, shadow.lowerShadow.host], 'EventPath[8] must be the slot inside the upper shadow tree');
     240                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');
     241                assert_array_equals(log[10], [shadow.upperShadow, shadow.lowerShadow.host], 'EventPath[10] must be the upper shadow root');
    242242                assert_array_equals(log[11], [shadow.upperShadow.host, shadow.lowerShadow.host], 'EventPath[11] must be the host');
    243243
  • trunk/Source/WebCore/ChangeLog

    r200463 r200464  
     12016-05-05  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        event.target shouldn't be retargeted as the event bubbles into a slot
     4        https://bugs.webkit.org/show_bug.cgi?id=157369
     5
     6        Reviewed by Antti Koivisto.
     7
     8        When an event bubbles up from an assigned node to its assigned slot, we shouldn't be adjusting
     9        event.target to point to the slot. Since a shadow tree should have access to nodes outside
     10        the shadow tree, event.target is accessible inside the shadow tree of such a slot.
     11
     12        New behavior matches the behavior of Google Chrome Canary as well as the shadow DOM specification:
     13        http://w3c.github.io/webcomponents/spec/shadow/#dfn-retargeting-algorithm
     14
     15        Test: fast/shadow-dom/event-inside-slotted-node.html
     16
     17        * dom/Event.cpp:
     18        (WebCore::Event::deepPath):
     19        * dom/EventContext.h:
     20        (WebCore::EventContext::isUnreachableNode): Use Node::isUnclosedNode instead of isReachable.
     21        (WebCore::EventContext::isReachable): Deleted.
     22        * dom/EventPath.cpp:
     23        (WebCore::EventPath::EventPath): Don't set the target to the slot when entering a slot. Also moved
     24        the code to adjust the target as we exit a shadow tree to the end of the outer loop for clarity.
     25        (WebCore::isUnclosedNodeOf): Deleted. Renamed to Node::isUnclosedNode.
     26        (WebCore::EventPath::setRelatedTarget):
     27        (WebCore::EventPath::computePathUnclosedToTarget): Renamed from computePathDisclosedToTarget.
     28        (WebCore::moveOutOfAllShadowRoots): Extracted from RelatedNodeRetargeter::RelatedNodeRetargeter.
     29        (WebCore::RelatedNodeRetargeter::RelatedNodeRetargeter): Fixed a bug that we were exiting early
     30        without setting m_hasDifferentTreeRoot true when target and relatedNode are disconnected from
     31        a document.
     32        (WebCore::RelatedNodeRetargeter::currentNode):
     33        (WebCore::RelatedNodeRetargeter::checkConsistency): Updated to match the spec with one exception.
     34        We don't use null as the adjusted related target when the (original) related target and the target
     35        are in two distinct disconnected trees since such a behavior is not Web compatible. This spec bug is
     36        tracked by https://github.com/w3c/webcomponents/issues/494
     37        * dom/EventPath.h:
     38        (WebCore::EventPath::eventTargetRespectingTargetRules): Returns Node* instead of EventTarget* since
     39        we need a Node in RelatedNodeRetargeter::checkConsistency.
     40        * dom/Node.cpp:
     41        (WebCore::Node::isUnclosedNode): Moved from RelatedNodeRetargeter.cpp
     42        * dom/Node.h:
     43
    1442016-05-02  Sam Weinig  <sam@webkit.org>
    245
  • trunk/Source/WebCore/dom/Event.cpp

    r198115 r200464  
    192192    if (!m_eventPath)
    193193        return Vector<EventTarget*>();
    194     return m_eventPath->computePathDisclosedToTarget(*m_target);
     194    return m_eventPath->computePathUnclosedToTarget(*m_currentTarget);
    195195}
    196196
  • trunk/Source/WebCore/dom/EventContext.h

    r198483 r200464  
    5858#if !ASSERT_DISABLED
    5959    bool isUnreachableNode(EventTarget*);
    60     bool isReachable(Node*) const;
    6160#endif
    6261    RefPtr<Node> m_node;
     
    135134{
    136135    // FIXME: Checks also for SVG elements.
    137     return target && target->toNode() && !target->toNode()->isSVGElement() && !isReachable(target->toNode());
    138 }
    139 
    140 inline bool EventContext::isReachable(Node* target) const
    141 {
    142     ASSERT(target);
    143     TreeScope& targetScope = target->treeScope();
    144     for (TreeScope* scope = &m_node->treeScope(); scope; scope = scope->parentTreeScope()) {
    145         if (scope == &targetScope)
    146             return true;
    147     }
    148     return false;
     136    return target && target->toNode() && !target->toNode()->isSVGElement() && !m_node->isUnclosedNode(*target->toNode());
    149137}
    150138#endif
  • trunk/Source/WebCore/dom/EventPath.cpp

    r198056 r200464  
    6161class RelatedNodeRetargeter {
    6262public:
    63     RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope);
    64 
    65     Node* currentNode(TreeScope& currentTreeScope);
     63    RelatedNodeRetargeter(Node& relatedNode, Node& target);
     64
     65    Node* currentNode(Node& currentTreeScope);
    6666    void moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope);
    6767
     
    7272
    7373#if ASSERT_DISABLED
    74     void checkConsistency(TreeScope&) { }
     74    void checkConsistency(Node&) { }
    7575#else
    76     void checkConsistency(TreeScope& currentTreeScope);
     76    void checkConsistency(Node& currentTarget);
    7777#endif
    7878
     
    8787    : m_event(event)
    8888{
    89 #if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
    90     Vector<EventTarget*, 16> targetStack;
    91 #endif
    92 
    9389    bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent();
    9490#if ENABLE(TOUCH_EVENTS)
    9591    bool isTouchEvent = event.isTouchEvent();
    9692#endif
    97     EventTarget* target = nullptr;
    98 
    9993    Node* node = nodeOrHostIfPseudoElement(&originalTarget);
     94    Node* target = eventTargetRespectingTargetRules(*node);
    10095    while (node) {
    101         if (!target)
    102             target = eventTargetRespectingTargetRules(*node);
    103         ContainerNode* parent;
    104         for (; node; node = parent) {
     96        while (node) {
    10597            EventTarget* currentTarget = eventTargetRespectingTargetRules(*node);
    10698
     
    117109                break;
    118110
    119             parent = node->parentNode();
    120 
     111            ContainerNode* parent = node->parentNode();
    121112            if (!parent)
    122113                return;
     
    126117                if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) {
    127118                    // node is assigned to a slot. Continue dispatching the event at this slot.
    128                     targetStack.append(target);
    129119                    parent = assignedSlot;
    130                     target = assignedSlot;
    131120                }
    132121            }
     
    135124        }
    136125
     126        bool exitingShadowTreeOfTarget = &target->treeScope() == &node->treeScope();
    137127        ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node);
    138         // At a shadow root. Continue dispatching the event at the shadow host.
    139 #if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
    140         if (!targetStack.isEmpty()) {
    141             // 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.
    142             target = targetStack.last();
    143             targetStack.removeLast();
    144             ASSERT(shadowRoot.host()->contains(target->toNode()));
    145         } else
    146 #endif
    147             target = nullptr;
    148 
    149128        if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget))
    150129            return;
    151130        node = shadowRoot.host();
     131        if (exitingShadowTreeOfTarget)
     132            target = eventTargetRespectingTargetRules(*node);
     133
    152134    }
    153135}
     
    159141        return;
    160142
    161     RelatedNodeRetargeter retargeter(*relatedNode, downcast<MouseOrFocusEventContext>(*m_path[0]).node()->treeScope());
     143    RelatedNodeRetargeter retargeter(*relatedNode, *m_path[0]->node());
    162144
    163145    bool originIsRelatedTarget = &origin == relatedNode;
     
    169151        auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]);
    170152
    171         TreeScope& currentTreeScope = context.node()->treeScope();
     153        Node& currentTarget = *context.node();
     154        TreeScope& currentTreeScope = currentTarget.treeScope();
    172155        if (UNLIKELY(previousTreeScope && &currentTreeScope != previousTreeScope))
    173156            retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
    174157
    175         Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
     158        Node* currentRelatedNode = retargeter.currentNode(currentTarget);
    176159        if (UNLIKELY(relatedTargetScoped && !originIsRelatedTarget && context.target() == currentRelatedNode)) {
    177160            m_path.shrink(contextIndex);
     
    201184        return;
    202185
    203     RelatedNodeRetargeter retargeter(*targetNode, m_path[0]->node()->treeScope());
     186    RelatedNodeRetargeter retargeter(*targetNode, *m_path[0]->node());
    204187    TreeScope* previousTreeScope = nullptr;
    205188    for (auto& context : m_path) {
    206         TreeScope& currentTreeScope = context->node()->treeScope();
     189        Node& currentTarget = *context->node();
     190        TreeScope& currentTreeScope = currentTarget.treeScope();
    207191        if (UNLIKELY(previousTreeScope && &currentTreeScope != previousTreeScope))
    208192            retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
    209193
    210         Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
     194        Node* currentRelatedNode = retargeter.currentNode(currentTarget);
    211195        downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
    212196
     
    244228}
    245229
    246 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node
    247 static bool isUnclosedNodeOf(const Node& a, const Node& b)
    248 {
    249     // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small.
    250     Vector<TreeScope*, 8> treeScopesOpenToB;
    251 
    252     for (auto* scope = &b.treeScope(); scope; scope = scope->parentTreeScope())
    253         treeScopesOpenToB.append(scope);
    254 
    255     for (auto* treeScopeThatCanAccessA = &a.treeScope(); treeScopeThatCanAccessA; treeScopeThatCanAccessA = treeScopeThatCanAccessA->parentTreeScope()) {
    256         for (auto* openToB : treeScopesOpenToB) {
    257             if (openToB == treeScopeThatCanAccessA)
    258                 return true;
    259         }
    260         auto& root = treeScopeThatCanAccessA->rootNode();
    261         if (is<ShadowRoot>(root) && downcast<ShadowRoot>(root).type() != ShadowRoot::Type::Open)
    262             break;
    263     }
    264 
    265     return false;
    266 }
    267 
    268 Vector<EventTarget*> EventPath::computePathDisclosedToTarget(const EventTarget& target) const
     230Vector<EventTarget*> EventPath::computePathUnclosedToTarget(const EventTarget& target) const
    269231{
    270232    Vector<EventTarget*> path;
     
    275237    for (auto& context : m_path) {
    276238        if (Node* nodeInPath = context->currentTarget()->toNode()) {
    277             if (isUnclosedNodeOf(*nodeInPath, *targetNode))
     239            if (targetNode->isUnclosedNode(*nodeInPath))
    278240                path.append(context->currentTarget());
    279241        }
     
    283245}
    284246
    285 RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope)
     247static Node* moveOutOfAllShadowRoots(Node& startingNode)
     248{
     249    Node* node = &startingNode;
     250    while (node->isInShadowTree())
     251        node = downcast<ShadowRoot>(node->treeScope().rootNode()).host();
     252    return node;
     253}
     254
     255RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, Node& target)
    286256    : m_relatedNode(relatedNode)
    287257    , m_retargetedRelatedNode(&relatedNode)
    288258{
     259    auto& targetTreeScope = target.treeScope();
    289260    TreeScope* currentTreeScope = &m_relatedNode.treeScope();
    290     if (LIKELY(currentTreeScope == &targetTreeScope))
     261    if (LIKELY(currentTreeScope == &targetTreeScope && target.inDocument() && m_relatedNode.inDocument()))
    291262        return;
    292263
     
    296267        return;
    297268    }
    298     if (relatedNode.inDocument() != targetTreeScope.rootNode().inDocument()) {
     269    if (relatedNode.inDocument() != target.inDocument()) {
    299270        m_hasDifferentTreeRoot = true;
    300         while (m_retargetedRelatedNode->isInShadowTree())
    301             m_retargetedRelatedNode = downcast<ShadowRoot>(m_retargetedRelatedNode->treeScope().rootNode()).host();
     271        m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode);
    302272        return;
    303273    }
     
    321291    }
    322292
     293    bool lowestCommonAncestorIsDocumentScope = i + 1 == m_ancestorTreeScopes.size();
     294    if (lowestCommonAncestorIsDocumentScope && !relatedNode.inDocument() && !target.inDocument()) {
     295        Node& targetAncestorInDocumentScope = i ? *downcast<ShadowRoot>(m_ancestorTreeScopes[i - 1]->rootNode()).shadowHost() : target;
     296        Node& relatedNodeAncestorInDocumentScope = j ? *downcast<ShadowRoot>(targetTreeScopeAncestors[j - 1]->rootNode()).shadowHost() : relatedNode;
     297        if (targetAncestorInDocumentScope.rootNode() != relatedNodeAncestorInDocumentScope.rootNode()) {
     298            m_hasDifferentTreeRoot = true;
     299            m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode);
     300            return;
     301        }
     302    }
     303
    323304    m_lowestCommonAncestorIndex = i;
    324305    m_retargetedRelatedNode = nodeInLowestCommonAncestor();
    325306}
    326307
    327 inline Node* RelatedNodeRetargeter::currentNode(TreeScope& currentTreeScope)
    328 {
    329     checkConsistency(currentTreeScope);
     308inline Node* RelatedNodeRetargeter::currentNode(Node& currentTarget)
     309{
     310    checkConsistency(currentTarget);
    330311    return m_retargetedRelatedNode;
    331312}
     
    382363
    383364#if !ASSERT_DISABLED
    384 void RelatedNodeRetargeter::checkConsistency(TreeScope& currentTreeScope)
    385 {
    386     for (auto* relatedNodeScope = &m_relatedNode.treeScope(); relatedNodeScope; relatedNodeScope = relatedNodeScope->parentTreeScope()) {
    387         for (auto* targetScope = &currentTreeScope; targetScope; targetScope = targetScope->parentTreeScope()) {
    388             if (targetScope == relatedNodeScope) {
    389                 ASSERT(&m_retargetedRelatedNode->treeScope() == relatedNodeScope);
    390                 return;
    391             }
    392         }
    393     }
    394     ASSERT(!m_retargetedRelatedNode);
    395 }
    396 #endif
    397 
    398 }
     365void RelatedNodeRetargeter::checkConsistency(Node& currentTarget)
     366{
     367    ASSERT(!m_retargetedRelatedNode || currentTarget.isUnclosedNode(*m_retargetedRelatedNode));
     368
     369    // http://w3c.github.io/webcomponents/spec/shadow/#dfn-retargeting-algorithm
     370    Node& base = currentTarget;
     371    for (Node* targetAncestor = &m_relatedNode; targetAncestor; targetAncestor = targetAncestor->parentOrShadowHostNode()) {
     372        if (targetAncestor->rootNode()->containsIncludingShadowDOM(&base)) {
     373            ASSERT(m_retargetedRelatedNode == targetAncestor);
     374            return;
     375        }
     376    }
     377    ASSERT(!m_retargetedRelatedNode || m_hasDifferentTreeRoot);
     378}
     379#endif
     380
     381}
  • trunk/Source/WebCore/dom/EventPath.h

    r198056 r200464  
    4949    EventContext* lastContextIfExists() { return m_path.isEmpty() ? nullptr : m_path.last().get(); }
    5050
    51     Vector<EventTarget*> computePathDisclosedToTarget(const EventTarget&) const;
     51    Vector<EventTarget*> computePathUnclosedToTarget(const EventTarget&) const;
    5252
    53     static EventTarget* eventTargetRespectingTargetRules(Node& referenceNode)
     53    static Node* eventTargetRespectingTargetRules(Node& referenceNode)
    5454    {
    5555        if (is<PseudoElement>(referenceNode))
  • trunk/Source/WebCore/dom/Node.cpp

    r200414 r200464  
    11181118}
    11191119
     1120// http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node
     1121bool Node::isUnclosedNode(const Node& otherNode) const
     1122{
     1123    // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small.
     1124    Vector<TreeScope*, 8> ancestorScopesOfThisNode;
     1125
     1126    for (auto* scope = &treeScope(); scope; scope = scope->parentTreeScope())
     1127        ancestorScopesOfThisNode.append(scope);
     1128
     1129    for (auto* treeScopeThatCanAccessOtherNode = &otherNode.treeScope(); treeScopeThatCanAccessOtherNode; treeScopeThatCanAccessOtherNode = treeScopeThatCanAccessOtherNode->parentTreeScope()) {
     1130        for (auto* scope : ancestorScopesOfThisNode) {
     1131            if (scope == treeScopeThatCanAccessOtherNode)
     1132                return true; // treeScopeThatCanAccessOtherNode is a shadow-including inclusive ancestor of this node.
     1133        }
     1134        auto& root = treeScopeThatCanAccessOtherNode->rootNode();
     1135        if (is<ShadowRoot>(root) && downcast<ShadowRoot>(root).type() != ShadowRoot::Type::Open)
     1136            break;
     1137    }
     1138
     1139    return false;
     1140}
     1141
    11201142#if ENABLE(SHADOW_DOM)
    11211143HTMLSlotElement* Node::assignedSlot() const
  • trunk/Source/WebCore/dom/Node.h

    r200414 r200464  
    260260    ShadowRoot* containingShadowRoot() const;
    261261    ShadowRoot* shadowRoot() const;
     262    bool isUnclosedNode(const Node&) const;
    262263
    263264#if ENABLE(SHADOW_DOM)
Note: See TracChangeset for help on using the changeset viewer.