<!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>[210564] 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/210564">210564</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2017-01-10 15:34:51 -0800 (Tue, 10 Jan 2017)</dd>
</dl>
<h3>Log Message</h3>
<pre>:active and :hover states may not be updated across slots
https://bugs.webkit.org/show_bug.cgi?id=166881
<rdar://problem/29944582>
Reviewed by Antti Koivisto.
Source/WebCore:
The bug was caused by updateHoverActiveState not updating :hover and :active states on elements
when nodes are assigned to slots because they were walking up the tree using parentOrShadowHostElement
and parentNode. Fixed the bug by using parentElementInComposedTree instead since :hover and :active
states need to be updated in accordance with the render tree, which is created from the "flat tree"
or the "composed tree" in WebKit's terminology (this is old terminology in the spec).
Tests: fast/shadow-dom/clear-active-state-in-shadow.html
fast/shadow-dom/hover-over-nested-slotted-content.html
* dom/Document.cpp:
(WebCore::Document::updateHoverActiveState): Fixed the bug.
* dom/Node.cpp:
(WebCore::Node::parentElementInComposedTree): Added.
* dom/Node.h:
LayoutTests:
Added two regression tests; one for clearing :active state across a slot, and another one for clearing
a hover state on an ancestor of a slot to which a slot with the hovered element is assigned.
* fast/shadow-dom/clear-active-state-in-shadow-expected.html: Added.
* fast/shadow-dom/clear-active-state-in-shadow.html: Added.
* fast/shadow-dom/hover-over-nested-slotted-content-expected.html: Added.
* fast/shadow-dom/hover-over-nested-slotted-content.html: Added.
* platform/ios-simulator/TestExpectations:</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformiossimulatorTestExpectations">trunk/LayoutTests/platform/ios-simulator/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</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>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastshadowdomclearactivestateinshadowexpectedhtml">trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow-expected.html</a></li>
<li><a href="#trunkLayoutTestsfastshadowdomclearactivestateinshadowhtml">trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow.html</a></li>
<li><a href="#trunkLayoutTestsfastshadowdomhoverovernestedslottedcontentexpectedhtml">trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content-expected.html</a></li>
<li><a href="#trunkLayoutTestsfastshadowdomhoverovernestedslottedcontenthtml">trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (210563 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/LayoutTests/ChangeLog        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2017-01-10 Ryosuke Niwa <rniwa@webkit.org>
+
+ :active and :hover states may not be updated across slots
+ https://bugs.webkit.org/show_bug.cgi?id=166881
+ <rdar://problem/29944582>
+
+ Reviewed by Antti Koivisto.
+
+ Added two regression tests; one for clearing :active state across a slot, and another one for clearing
+ a hover state on an ancestor of a slot to which a slot with the hovered element is assigned.
+
+ * fast/shadow-dom/clear-active-state-in-shadow-expected.html: Added.
+ * fast/shadow-dom/clear-active-state-in-shadow.html: Added.
+ * fast/shadow-dom/hover-over-nested-slotted-content-expected.html: Added.
+ * fast/shadow-dom/hover-over-nested-slotted-content.html: Added.
+ * platform/ios-simulator/TestExpectations:
+
</ins><span class="cx"> 2017-01-10 Wenson Hsieh <wenson_hsieh@apple.com>
</span><span class="cx">
</span><span class="cx"> Implement "proximity" scroll snapping
</span></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomclearactivestateinshadowexpectedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow-expected.html (0 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow-expected.html         (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow-expected.html        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+<!DOCTYPE html>
+<html>
+ <body>
+ <p>Test passes if you see a single 100px by 100px green box below.</p>
+ <div style="width: 100px; height: 100px; background: green;"></div>
+ </body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomclearactivestateinshadowhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow.html (0 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow.html         (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow.html        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div><span>click here</span></div>
+<style>
+div {
+ display: block;
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+</style>
+<script src="../../resources/ui-helper.js"></script>
+<script>
+
+var div = document.querySelector('div');
+div.attachShadow({mode: 'closed'}).innerHTML = `
+ <style>
+ a, ::slotted(*) {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+
+ a:active {
+ background: red;
+ }
+ </style>
+ <a href="#" onclick="this.style.color = this.matches(':active') ? 'black' : 'green'"><slot></slot></a>`;
+
+if (window.testRunner)
+ UIHelper.wait(UIHelper.activateAt(div.offsetLeft + 5, div.offsetTop + 5));
+
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomhoverovernestedslottedcontentexpectedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content-expected.html (0 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content-expected.html         (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content-expected.html        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+<!DOCTYPE html>
+<html>
+ <body>
+ <p>Test passes if you see a single 100px by 100px green box below.</p>
+ <div style="width: 100px; height: 100px; background: green;"></div>
+ </body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomhoverovernestedslottedcontenthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content.html (0 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content.html         (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content.html        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div></div>
+<style>
+div {
+ display: block;
+ width: 100px;
+ height: 100px;
+ background: red;
+}
+</style>
+<script src="../../resources/ui-helper.js"></script>
+<script>
+
+var div = document.querySelector('div');
+var outerShadow = div.attachShadow({mode: 'closed'});
+outerShadow.innerHTML = `
+ <style>
+ div { width: 100%; height: 100%; }
+ </style>
+ <div><slot>hover over this text</slot></div>`;
+
+outerShadow.querySelector('div').attachShadow({mode: 'closed'}).innerHTML = `
+<style>
+div { background: yellow; width: 100%; height: 100%; }
+div:hover { background: green; color: green; }
+</style>
+<div><slot></slot></div>`;
+
+if (window.eventSender)
+ eventSender.mouseMoveTo(div.offsetLeft + 10, div.offsetTop + 10);
+
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformiossimulatorTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (210563 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-simulator/TestExpectations        2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -356,6 +356,8 @@
</span><span class="cx"> fast/shadow-dom/shadow-host-removal-crash.html [ Skip ]
</span><span class="cx"> fast/shadow-dom/input-element-in-shadow.html [ Skip ]
</span><span class="cx"> fast/shadow-dom/activate-over-slotted-content.html [ Skip ]
</span><ins>+fast/shadow-dom/clear-active-state-in-shadow.html [ Skip ]
+fast/shadow-dom/hover-over-nested-slotted-content.html [ Skip ]
</ins><span class="cx"> fast/shadow-dom/hover-over-slotted-content.html [ Skip ]
</span><span class="cx"> fast/events/keyboardevent-key.html [ Skip ]
</span><span class="cx"> fast/events/keyboardevent-code.html [ Skip ]
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (210563 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/Source/WebCore/ChangeLog        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2017-01-10 Ryosuke Niwa <rniwa@webkit.org>
+
+ :active and :hover states may not be updated across slots
+ https://bugs.webkit.org/show_bug.cgi?id=166881
+ <rdar://problem/29944582>
+
+ Reviewed by Antti Koivisto.
+
+ The bug was caused by updateHoverActiveState not updating :hover and :active states on elements
+ when nodes are assigned to slots because they were walking up the tree using parentOrShadowHostElement
+ and parentNode. Fixed the bug by using parentElementInComposedTree instead since :hover and :active
+ states need to be updated in accordance with the render tree, which is created from the "flat tree"
+ or the "composed tree" in WebKit's terminology (this is old terminology in the spec).
+
+ Tests: fast/shadow-dom/clear-active-state-in-shadow.html
+ fast/shadow-dom/hover-over-nested-slotted-content.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::updateHoverActiveState): Fixed the bug.
+ * dom/Node.cpp:
+ (WebCore::Node::parentElementInComposedTree): Added.
+ * dom/Node.h:
+
</ins><span class="cx"> 2017-01-10 Keith Rollin <krollin@apple.com>
</span><span class="cx">
</span><span class="cx"> Missing logging in IconLoader::startLoading
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (210563 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp        2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/Source/WebCore/dom/Document.cpp        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -6475,9 +6475,9 @@
</span><span class="cx"> Element* oldActiveElement = m_activeElement.get();
</span><span class="cx"> if (oldActiveElement && !request.active()) {
</span><span class="cx"> // We are clearing the :active chain because the mouse has been released.
</span><del>- for (Element* curr = oldActiveElement; curr; curr = curr->parentOrShadowHostElement()) {
- curr->setActive(false);
- m_userActionElements.setInActiveChain(curr, false);
</del><ins>+ for (Element* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) {
+ currentElement->setActive(false);
+ m_userActionElements.setInActiveChain(currentElement, false);
</ins><span class="cx"> }
</span><span class="cx"> m_activeElement = nullptr;
</span><span class="cx"> } else {
</span><span class="lines">@@ -6515,7 +6515,7 @@
</span><span class="cx"> // If it hasn't, we do not need to do anything.
</span><span class="cx"> Element* newHoveredElement = innerElementInDocument;
</span><span class="cx"> while (newHoveredElement && !newHoveredElement->renderer())
</span><del>- newHoveredElement = newHoveredElement->parentOrShadowHostElement();
</del><ins>+ newHoveredElement = newHoveredElement->parentElementInComposedTree();
</ins><span class="cx">
</span><span class="cx"> m_hoveredElement = newHoveredElement;
</span><span class="cx">
</span><span class="lines">@@ -6534,7 +6534,7 @@
</span><span class="cx"> // (for instance by setting display:none in the :hover pseudo-class). In this case, the old hovered element (and its ancestors)
</span><span class="cx"> // must be updated, to ensure it's normal style is re-applied.
</span><span class="cx"> if (oldHoveredElement && !oldHoverObj) {
</span><del>- for (Element* element = oldHoveredElement.get(); element; element = element->parentElement()) {
</del><ins>+ for (Element* element = oldHoveredElement.get(); element; element = element->parentElementInComposedTree()) {
</ins><span class="cx"> if (!mustBeInActiveChain || element->inActiveChain())
</span><span class="cx"> elementsToRemoveFromChain.append(element);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.cpp (210563 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.cpp        2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/Source/WebCore/dom/Node.cpp        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -1139,6 +1139,19 @@
</span><span class="cx"> return parentNode();
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+Element* Node::parentElementInComposedTree() const
+{
+ if (auto* slot = assignedSlot())
+ return slot;
+ if (auto* parent = parentNode()) {
+ if (is<ShadowRoot>(*parent))
+ return downcast<ShadowRoot>(*parent).host();
+ if (is<Element>(*parent))
+ return downcast<Element>(parent);
+ }
+ return nullptr;
+}
+
</ins><span class="cx"> bool Node::isInUserAgentShadowTree() const
</span><span class="cx"> {
</span><span class="cx"> auto* shadowRoot = containingShadowRoot();
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.h (210563 => 210564)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.h        2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/Source/WebCore/dom/Node.h        2017-01-10 23:34:51 UTC (rev 210564)
</span><span class="lines">@@ -256,6 +256,7 @@
</span><span class="cx"> // Node's parent or shadow tree host.
</span><span class="cx"> ContainerNode* parentOrShadowHostNode() const;
</span><span class="cx"> ContainerNode* parentInComposedTree() const;
</span><ins>+ Element* parentElementInComposedTree() const;
</ins><span class="cx"> Element* parentOrShadowHostElement() const;
</span><span class="cx"> void setParentNode(ContainerNode*);
</span><span class="cx"> Node& rootNode() const;
</span></span></pre>
</div>
</div>
</body>
</html>