<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[200464] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/200464">200464</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-05-05 11:33:17 -0700 (Thu, 05 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>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:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastshadowdomeventinsideslottednodehtml">trunk/LayoutTests/fast/shadow-dom/event-inside-slotted-node.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomEventcpp">trunk/Source/WebCore/dom/Event.cpp</a></li>
<li><a href="#trunkSourceWebCoredomEventContexth">trunk/Source/WebCore/dom/EventContext.h</a></li>
<li><a href="#trunkSourceWebCoredomEventPathcpp">trunk/Source/WebCore/dom/EventPath.cpp</a></li>
<li><a href="#trunkSourceWebCoredomEventPathh">trunk/Source/WebCore/dom/EventPath.h</a></li>
<li><a href="#trunkSourceWebCoredomNodecpp">trunk/Source/WebCore/dom/Node.cpp</a></li>
<li><a href="#trunkSourceWebCoredomNodeh">trunk/Source/WebCore/dom/Node.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/LayoutTests/ChangeLog        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2016-05-05  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        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.
+
+        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:
+
</ins><span class="cx"> 2016-05-04  Alex Christensen  &lt;achristensen@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Blocked redirected main resource requests need descriptive errors
</span></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomeventinsideslottednodehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/shadow-dom/event-inside-slotted-node.html (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -80,9 +80,9 @@
</span><span class="cx">                 assert_equals(log.length, 6, 'EventPath must contain [target, target parent, slot, slot parent, shadow root, shadow host]');
</span><span class="cx">                 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
</span><span class="cx">                 assert_array_equals(log[1], [shadow.targetParent, shadow.target], 'EventPath[1] must be the parent of the target');
</span><del>-                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');
</del><ins>+                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');
</ins><span class="cx">                 assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
</span><span class="cx"> 
</span><span class="cx">             }, 'Firing an event inside a grand child of a detached ' + mode + ' mode shadow host');
</span><span class="lines">@@ -101,9 +101,9 @@
</span><span class="cx">                 assert_equals(log.length, 9, 'EventPath must contain [target, target parent, slot, slot parent, shadow root, shadow host, body, html, document]');
</span><span class="cx">                 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
</span><span class="cx">                 assert_array_equals(log[1], [shadow.targetParent, shadow.target], 'EventPath[1] must be the parent of the target');
</span><del>-                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');
</del><ins>+                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');
</ins><span class="cx">                 assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
</span><span class="cx">                 assert_array_equals(log[6], [document.body, shadow.target], 'EventPath[6] must be the body element');
</span><span class="cx">                 assert_array_equals(log[7], [document.documentElement, shadow.target], 'EventPath[7] must be the html element');
</span><span class="lines">@@ -179,19 +179,19 @@
</span><span class="cx">                 assert_equals(log.length, 15, 'EventPath must contain 15 targets');
</span><span class="cx"> 
</span><span class="cx">                 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target');
</span><del>-                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');
</del><ins>+                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');
</ins><span class="cx">                 assert_array_equals(log[9], [shadow.lowerShadow.host, shadow.target], 'EventPath[9] must be the lower shadow host');
</span><span class="cx">                 assert_array_equals(log[10], [shadow.host.firstChild, shadow.target], 'EventPath[10] must be the parent of the grand parent of the target');
</span><del>-                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');
</del><ins>+                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');
</ins><span class="cx">                 assert_array_equals(log[14], [shadow.host, shadow.target], 'EventPath[14] must be the host');
</span><span class="cx"> 
</span><span class="cx">             }, 'Firing an event on a node with two ancestors with a detached ' + outerUpperMode + ' and ' + outerLowerMode
</span><span class="lines">@@ -215,7 +215,7 @@
</span><span class="cx">             + a               + em (4)
</span><span class="cx">                                 + inner-host (3) -- (innerShadow; 2)
</span><span class="cx">                                   + span            + i (1)
</span><del>-                                    + slot            + slot (innerSlot; 0)
</del><ins>+                                    + slot            + slot (innerSlot, target; 0)
</ins><span class="cx">         */
</span><span class="cx"> 
</span><span class="cx">         function testEventInsideNestedShadowsUnderAnotherShadow(outerUpperMode, outerLowerMode, innerMode) {
</span><span class="lines">@@ -236,9 +236,9 @@
</span><span class="cx">                 assert_array_equals(log[5], [shadow.lowerShadow, shadow.innerShadow.host], 'EventPath[5] must be the lower (but outer) shadow root');
</span><span class="cx">                 assert_array_equals(log[6], [shadow.lowerShadow.host, shadow.lowerShadow.host], 'EventPath[6] must be the lower (but outer) shadow root');
</span><span class="cx">                 assert_array_equals(log[7], [shadow.host.firstChild, shadow.lowerShadow.host], 'EventPath[7] must be the slot inside the upper shadow tree');
</span><del>-                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');
</del><ins>+                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');
</ins><span class="cx">                 assert_array_equals(log[11], [shadow.upperShadow.host, shadow.lowerShadow.host], 'EventPath[11] must be the host');
</span><span class="cx"> 
</span><span class="cx">             }, 'Firing an event on a node with two ancestors with a detached ' + outerUpperMode + ' and ' + outerLowerMode
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/ChangeLog        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -1,3 +1,46 @@
</span><ins>+2016-05-05  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        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.
+
+        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:
+
</ins><span class="cx"> 2016-05-02  Sam Weinig  &lt;sam@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         On platforms that support it, use a SecTrustRef as the basis of CertificateInfo instead of a chain of SecCertificateRefs.
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Event.cpp (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Event.cpp        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/dom/Event.cpp        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -191,7 +191,7 @@
</span><span class="cx"> {
</span><span class="cx">     if (!m_eventPath)
</span><span class="cx">         return Vector&lt;EventTarget*&gt;();
</span><del>-    return m_eventPath-&gt;computePathDisclosedToTarget(*m_target);
</del><ins>+    return m_eventPath-&gt;computePathUnclosedToTarget(*m_currentTarget);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void Event::receivedTarget()
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventContexth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventContext.h (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/EventContext.h        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/dom/EventContext.h        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -57,7 +57,6 @@
</span><span class="cx"> protected:
</span><span class="cx"> #if !ASSERT_DISABLED
</span><span class="cx">     bool isUnreachableNode(EventTarget*);
</span><del>-    bool isReachable(Node*) const;
</del><span class="cx"> #endif
</span><span class="cx">     RefPtr&lt;Node&gt; m_node;
</span><span class="cx">     RefPtr&lt;EventTarget&gt; m_currentTarget;
</span><span class="lines">@@ -134,19 +133,8 @@
</span><span class="cx"> inline bool EventContext::isUnreachableNode(EventTarget* target)
</span><span class="cx"> {
</span><span class="cx">     // FIXME: Checks also for SVG elements.
</span><del>-    return target &amp;&amp; target-&gt;toNode() &amp;&amp; !target-&gt;toNode()-&gt;isSVGElement() &amp;&amp; !isReachable(target-&gt;toNode());
</del><ins>+    return target &amp;&amp; target-&gt;toNode() &amp;&amp; !target-&gt;toNode()-&gt;isSVGElement() &amp;&amp; !m_node-&gt;isUnclosedNode(*target-&gt;toNode());
</ins><span class="cx"> }
</span><del>-
-inline bool EventContext::isReachable(Node* target) const
-{
-    ASSERT(target);
-    TreeScope&amp; targetScope = target-&gt;treeScope();
-    for (TreeScope* scope = &amp;m_node-&gt;treeScope(); scope; scope = scope-&gt;parentTreeScope()) {
-        if (scope == &amp;targetScope)
-            return true;
-    }
-    return false;
-}
</del><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> inline void MouseOrFocusEventContext::setRelatedTarget(PassRefPtr&lt;EventTarget&gt; relatedTarget)
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventPathcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventPath.cpp (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -60,9 +60,9 @@
</span><span class="cx"> 
</span><span class="cx"> class RelatedNodeRetargeter {
</span><span class="cx"> public:
</span><del>-    RelatedNodeRetargeter(Node&amp; relatedNode, TreeScope&amp; targetTreeScope);
</del><ins>+    RelatedNodeRetargeter(Node&amp; relatedNode, Node&amp; target);
</ins><span class="cx"> 
</span><del>-    Node* currentNode(TreeScope&amp; currentTreeScope);
</del><ins>+    Node* currentNode(Node&amp; currentTreeScope);
</ins><span class="cx">     void moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope&amp; newTreeScope);
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="lines">@@ -71,9 +71,9 @@
</span><span class="cx">     void collectTreeScopes();
</span><span class="cx"> 
</span><span class="cx"> #if ASSERT_DISABLED
</span><del>-    void checkConsistency(TreeScope&amp;) { }
</del><ins>+    void checkConsistency(Node&amp;) { }
</ins><span class="cx"> #else
</span><del>-    void checkConsistency(TreeScope&amp; currentTreeScope);
</del><ins>+    void checkConsistency(Node&amp; currentTarget);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     Node&amp; m_relatedNode;
</span><span class="lines">@@ -86,22 +86,14 @@
</span><span class="cx"> EventPath::EventPath(Node&amp; originalTarget, Event&amp; event)
</span><span class="cx">     : m_event(event)
</span><span class="cx"> {
</span><del>-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
-    Vector&lt;EventTarget*, 16&gt; targetStack;
-#endif
-
</del><span class="cx">     bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent();
</span><span class="cx"> #if ENABLE(TOUCH_EVENTS)
</span><span class="cx">     bool isTouchEvent = event.isTouchEvent();
</span><span class="cx"> #endif
</span><del>-    EventTarget* target = nullptr;
-
</del><span class="cx">     Node* node = nodeOrHostIfPseudoElement(&amp;originalTarget);
</span><ins>+    Node* target = eventTargetRespectingTargetRules(*node);
</ins><span class="cx">     while (node) {
</span><del>-        if (!target)
-            target = eventTargetRespectingTargetRules(*node);
-        ContainerNode* parent;
-        for (; node; node = parent) {
</del><ins>+        while (node) {
</ins><span class="cx">             EventTarget* currentTarget = eventTargetRespectingTargetRules(*node);
</span><span class="cx"> 
</span><span class="cx">             if (isMouseOrFocusEvent)
</span><span class="lines">@@ -116,8 +108,7 @@
</span><span class="cx">             if (is&lt;ShadowRoot&gt;(*node))
</span><span class="cx">                 break;
</span><span class="cx"> 
</span><del>-            parent = node-&gt;parentNode();
-
</del><ins>+            ContainerNode* parent = node-&gt;parentNode();
</ins><span class="cx">             if (!parent)
</span><span class="cx">                 return;
</span><span class="cx"> 
</span><span class="lines">@@ -125,30 +116,21 @@
</span><span class="cx">             if (ShadowRoot* shadowRootOfParent = parent-&gt;shadowRoot()) {
</span><span class="cx">                 if (auto* assignedSlot = shadowRootOfParent-&gt;findAssignedSlot(*node)) {
</span><span class="cx">                     // node is assigned to a slot. Continue dispatching the event at this slot.
</span><del>-                    targetStack.append(target);
</del><span class="cx">                     parent = assignedSlot;
</span><del>-                    target = assignedSlot;
</del><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx"> #endif
</span><span class="cx">             node = parent;
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        bool exitingShadowTreeOfTarget = &amp;target-&gt;treeScope() == &amp;node-&gt;treeScope();
</ins><span class="cx">         ShadowRoot&amp; shadowRoot = downcast&lt;ShadowRoot&gt;(*node);
</span><del>-        // 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()-&gt;contains(target-&gt;toNode()));
-        } else
-#endif
-            target = nullptr;
-
</del><span class="cx">         if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget))
</span><span class="cx">             return;
</span><span class="cx">         node = shadowRoot.host();
</span><ins>+        if (exitingShadowTreeOfTarget)
+            target = eventTargetRespectingTargetRules(*node);
+
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -158,7 +140,7 @@
</span><span class="cx">     if (!relatedNode || m_path.isEmpty())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    RelatedNodeRetargeter retargeter(*relatedNode, downcast&lt;MouseOrFocusEventContext&gt;(*m_path[0]).node()-&gt;treeScope());
</del><ins>+    RelatedNodeRetargeter retargeter(*relatedNode, *m_path[0]-&gt;node());
</ins><span class="cx"> 
</span><span class="cx">     bool originIsRelatedTarget = &amp;origin == relatedNode;
</span><span class="cx">     bool relatedTargetScoped = m_event.relatedTargetScoped();
</span><span class="lines">@@ -168,11 +150,12 @@
</span><span class="cx">     for (unsigned contextIndex = 0; contextIndex &lt; originalEventPathSize; contextIndex++) {
</span><span class="cx">         auto&amp; context = downcast&lt;MouseOrFocusEventContext&gt;(*m_path[contextIndex]);
</span><span class="cx"> 
</span><del>-        TreeScope&amp; currentTreeScope = context.node()-&gt;treeScope();
</del><ins>+        Node&amp; currentTarget = *context.node();
+        TreeScope&amp; currentTreeScope = currentTarget.treeScope();
</ins><span class="cx">         if (UNLIKELY(previousTreeScope &amp;&amp; &amp;currentTreeScope != previousTreeScope))
</span><span class="cx">             retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
</span><span class="cx"> 
</span><del>-        Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
</del><ins>+        Node* currentRelatedNode = retargeter.currentNode(currentTarget);
</ins><span class="cx">         if (UNLIKELY(relatedTargetScoped &amp;&amp; !originIsRelatedTarget &amp;&amp; context.target() == currentRelatedNode)) {
</span><span class="cx">             m_path.shrink(contextIndex);
</span><span class="cx">             break;
</span><span class="lines">@@ -200,14 +183,15 @@
</span><span class="cx">     if (!targetNode)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    RelatedNodeRetargeter retargeter(*targetNode, m_path[0]-&gt;node()-&gt;treeScope());
</del><ins>+    RelatedNodeRetargeter retargeter(*targetNode, *m_path[0]-&gt;node());
</ins><span class="cx">     TreeScope* previousTreeScope = nullptr;
</span><span class="cx">     for (auto&amp; context : m_path) {
</span><del>-        TreeScope&amp; currentTreeScope = context-&gt;node()-&gt;treeScope();
</del><ins>+        Node&amp; currentTarget = *context-&gt;node();
+        TreeScope&amp; currentTreeScope = currentTarget.treeScope();
</ins><span class="cx">         if (UNLIKELY(previousTreeScope &amp;&amp; &amp;currentTreeScope != previousTreeScope))
</span><span class="cx">             retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
</span><span class="cx"> 
</span><del>-        Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
</del><ins>+        Node* currentRelatedNode = retargeter.currentNode(currentTarget);
</ins><span class="cx">         downcast&lt;TouchEventContext&gt;(*context).touchList(touchListType)-&gt;append(touch.cloneWithNewTarget(currentRelatedNode));
</span><span class="cx"> 
</span><span class="cx">         previousTreeScope = &amp;currentTreeScope;
</span><span class="lines">@@ -243,30 +227,8 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-// http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node
-static bool isUnclosedNodeOf(const Node&amp; a, const Node&amp; b)
</del><ins>+Vector&lt;EventTarget*&gt; EventPath::computePathUnclosedToTarget(const EventTarget&amp; target) const
</ins><span class="cx"> {
</span><del>-    // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small.
-    Vector&lt;TreeScope*, 8&gt; treeScopesOpenToB;
-
-    for (auto* scope = &amp;b.treeScope(); scope; scope = scope-&gt;parentTreeScope())
-        treeScopesOpenToB.append(scope);
-
-    for (auto* treeScopeThatCanAccessA = &amp;a.treeScope(); treeScopeThatCanAccessA; treeScopeThatCanAccessA = treeScopeThatCanAccessA-&gt;parentTreeScope()) {
-        for (auto* openToB : treeScopesOpenToB) {
-            if (openToB == treeScopeThatCanAccessA)
-                return true;
-        }
-        auto&amp; root = treeScopeThatCanAccessA-&gt;rootNode();
-        if (is&lt;ShadowRoot&gt;(root) &amp;&amp; downcast&lt;ShadowRoot&gt;(root).type() != ShadowRoot::Type::Open)
-            break;
-    }
-
-    return false;
-}
-
-Vector&lt;EventTarget*&gt; EventPath::computePathDisclosedToTarget(const EventTarget&amp; target) const
-{
</del><span class="cx">     Vector&lt;EventTarget*&gt; path;
</span><span class="cx">     const Node* targetNode = const_cast&lt;EventTarget&amp;&gt;(target).toNode();
</span><span class="cx">     if (!targetNode)
</span><span class="lines">@@ -274,7 +236,7 @@
</span><span class="cx"> 
</span><span class="cx">     for (auto&amp; context : m_path) {
</span><span class="cx">         if (Node* nodeInPath = context-&gt;currentTarget()-&gt;toNode()) {
</span><del>-            if (isUnclosedNodeOf(*nodeInPath, *targetNode))
</del><ins>+            if (targetNode-&gt;isUnclosedNode(*nodeInPath))
</ins><span class="cx">                 path.append(context-&gt;currentTarget());
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="lines">@@ -282,12 +244,21 @@
</span><span class="cx">     return path;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-RelatedNodeRetargeter::RelatedNodeRetargeter(Node&amp; relatedNode, TreeScope&amp; targetTreeScope)
</del><ins>+static Node* moveOutOfAllShadowRoots(Node&amp; startingNode)
+{
+    Node* node = &amp;startingNode;
+    while (node-&gt;isInShadowTree())
+        node = downcast&lt;ShadowRoot&gt;(node-&gt;treeScope().rootNode()).host();
+    return node;
+}
+
+RelatedNodeRetargeter::RelatedNodeRetargeter(Node&amp; relatedNode, Node&amp; target)
</ins><span class="cx">     : m_relatedNode(relatedNode)
</span><span class="cx">     , m_retargetedRelatedNode(&amp;relatedNode)
</span><span class="cx"> {
</span><ins>+    auto&amp; targetTreeScope = target.treeScope();
</ins><span class="cx">     TreeScope* currentTreeScope = &amp;m_relatedNode.treeScope();
</span><del>-    if (LIKELY(currentTreeScope == &amp;targetTreeScope))
</del><ins>+    if (LIKELY(currentTreeScope == &amp;targetTreeScope &amp;&amp; target.inDocument() &amp;&amp; m_relatedNode.inDocument()))
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (&amp;currentTreeScope-&gt;documentScope() != &amp;targetTreeScope.documentScope()) {
</span><span class="lines">@@ -295,10 +266,9 @@
</span><span class="cx">         m_retargetedRelatedNode = nullptr;
</span><span class="cx">         return;
</span><span class="cx">     }
</span><del>-    if (relatedNode.inDocument() != targetTreeScope.rootNode().inDocument()) {
</del><ins>+    if (relatedNode.inDocument() != target.inDocument()) {
</ins><span class="cx">         m_hasDifferentTreeRoot = true;
</span><del>-        while (m_retargetedRelatedNode-&gt;isInShadowTree())
-            m_retargetedRelatedNode = downcast&lt;ShadowRoot&gt;(m_retargetedRelatedNode-&gt;treeScope().rootNode()).host();
</del><ins>+        m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -320,13 +290,24 @@
</span><span class="cx">             break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool lowestCommonAncestorIsDocumentScope = i + 1 == m_ancestorTreeScopes.size();
+    if (lowestCommonAncestorIsDocumentScope &amp;&amp; !relatedNode.inDocument() &amp;&amp; !target.inDocument()) {
+        Node&amp; targetAncestorInDocumentScope = i ? *downcast&lt;ShadowRoot&gt;(m_ancestorTreeScopes[i - 1]-&gt;rootNode()).shadowHost() : target;
+        Node&amp; relatedNodeAncestorInDocumentScope = j ? *downcast&lt;ShadowRoot&gt;(targetTreeScopeAncestors[j - 1]-&gt;rootNode()).shadowHost() : relatedNode;
+        if (targetAncestorInDocumentScope.rootNode() != relatedNodeAncestorInDocumentScope.rootNode()) {
+            m_hasDifferentTreeRoot = true;
+            m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode);
+            return;
+        }
+    }
+
</ins><span class="cx">     m_lowestCommonAncestorIndex = i;
</span><span class="cx">     m_retargetedRelatedNode = nodeInLowestCommonAncestor();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-inline Node* RelatedNodeRetargeter::currentNode(TreeScope&amp; currentTreeScope)
</del><ins>+inline Node* RelatedNodeRetargeter::currentNode(Node&amp; currentTarget)
</ins><span class="cx"> {
</span><del>-    checkConsistency(currentTreeScope);
</del><ins>+    checkConsistency(currentTarget);
</ins><span class="cx">     return m_retargetedRelatedNode;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -381,17 +362,19 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if !ASSERT_DISABLED
</span><del>-void RelatedNodeRetargeter::checkConsistency(TreeScope&amp; currentTreeScope)
</del><ins>+void RelatedNodeRetargeter::checkConsistency(Node&amp; currentTarget)
</ins><span class="cx"> {
</span><del>-    for (auto* relatedNodeScope = &amp;m_relatedNode.treeScope(); relatedNodeScope; relatedNodeScope = relatedNodeScope-&gt;parentTreeScope()) {
-        for (auto* targetScope = &amp;currentTreeScope; targetScope; targetScope = targetScope-&gt;parentTreeScope()) {
-            if (targetScope == relatedNodeScope) {
-                ASSERT(&amp;m_retargetedRelatedNode-&gt;treeScope() == relatedNodeScope);
-                return;
-            }
</del><ins>+    ASSERT(!m_retargetedRelatedNode || currentTarget.isUnclosedNode(*m_retargetedRelatedNode));
+
+    // http://w3c.github.io/webcomponents/spec/shadow/#dfn-retargeting-algorithm
+    Node&amp; base = currentTarget;
+    for (Node* targetAncestor = &amp;m_relatedNode; targetAncestor; targetAncestor = targetAncestor-&gt;parentOrShadowHostNode()) {
+        if (targetAncestor-&gt;rootNode()-&gt;containsIncludingShadowDOM(&amp;base)) {
+            ASSERT(m_retargetedRelatedNode == targetAncestor);
+            return;
</ins><span class="cx">         }
</span><span class="cx">     }
</span><del>-    ASSERT(!m_retargetedRelatedNode);
</del><ins>+    ASSERT(!m_retargetedRelatedNode || m_hasDifferentTreeRoot);
</ins><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventPathh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventPath.h (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/EventPath.h        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/dom/EventPath.h        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -48,9 +48,9 @@
</span><span class="cx"> 
</span><span class="cx">     EventContext* lastContextIfExists() { return m_path.isEmpty() ? nullptr : m_path.last().get(); }
</span><span class="cx"> 
</span><del>-    Vector&lt;EventTarget*&gt; computePathDisclosedToTarget(const EventTarget&amp;) const;
</del><ins>+    Vector&lt;EventTarget*&gt; computePathUnclosedToTarget(const EventTarget&amp;) const;
</ins><span class="cx"> 
</span><del>-    static EventTarget* eventTargetRespectingTargetRules(Node&amp; referenceNode)
</del><ins>+    static Node* eventTargetRespectingTargetRules(Node&amp; referenceNode)
</ins><span class="cx">     {
</span><span class="cx">         if (is&lt;PseudoElement&gt;(referenceNode))
</span><span class="cx">             return downcast&lt;PseudoElement&gt;(referenceNode).hostElement();
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.cpp (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.cpp        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/dom/Node.cpp        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -1117,6 +1117,28 @@
</span><span class="cx">     return is&lt;ShadowRoot&gt;(root) ? downcast&lt;ShadowRoot&gt;(&amp;root) : nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node
+bool Node::isUnclosedNode(const Node&amp; otherNode) const
+{
+    // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small.
+    Vector&lt;TreeScope*, 8&gt; ancestorScopesOfThisNode;
+
+    for (auto* scope = &amp;treeScope(); scope; scope = scope-&gt;parentTreeScope())
+        ancestorScopesOfThisNode.append(scope);
+
+    for (auto* treeScopeThatCanAccessOtherNode = &amp;otherNode.treeScope(); treeScopeThatCanAccessOtherNode; treeScopeThatCanAccessOtherNode = treeScopeThatCanAccessOtherNode-&gt;parentTreeScope()) {
+        for (auto* scope : ancestorScopesOfThisNode) {
+            if (scope == treeScopeThatCanAccessOtherNode)
+                return true; // treeScopeThatCanAccessOtherNode is a shadow-including inclusive ancestor of this node.
+        }
+        auto&amp; root = treeScopeThatCanAccessOtherNode-&gt;rootNode();
+        if (is&lt;ShadowRoot&gt;(root) &amp;&amp; downcast&lt;ShadowRoot&gt;(root).type() != ShadowRoot::Type::Open)
+            break;
+    }
+
+    return false;
+}
+
</ins><span class="cx"> #if ENABLE(SHADOW_DOM)
</span><span class="cx"> HTMLSlotElement* Node::assignedSlot() const
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.h (200463 => 200464)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.h        2016-05-05 18:23:47 UTC (rev 200463)
+++ trunk/Source/WebCore/dom/Node.h        2016-05-05 18:33:17 UTC (rev 200464)
</span><span class="lines">@@ -259,6 +259,7 @@
</span><span class="cx">     WEBCORE_EXPORT Node* deprecatedShadowAncestorNode() const;
</span><span class="cx">     ShadowRoot* containingShadowRoot() const;
</span><span class="cx">     ShadowRoot* shadowRoot() const;
</span><ins>+    bool isUnclosedNode(const Node&amp;) const;
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(SHADOW_DOM)
</span><span class="cx">     HTMLSlotElement* assignedSlot() const;
</span></span></pre>
</div>
</div>

</body>
</html>