- Revision
- 210564
- Author
- [email protected]
- Date
- 2017-01-10 15:34:51 -0800 (Tue, 10 Jan 2017)
Log Message
: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:
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (210563 => 210564)
--- trunk/LayoutTests/ChangeLog 2017-01-10 23:17:42 UTC (rev 210563)
+++ trunk/LayoutTests/ChangeLog 2017-01-10 23:34:51 UTC (rev 210564)
@@ -1,3 +1,20 @@
+2017-01-10 Ryosuke Niwa <[email protected]>
+
+ :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:
+
2017-01-10 Wenson Hsieh <[email protected]>
Implement "proximity" scroll snapping
Added: trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow-expected.html (0 => 210564)
--- 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)
@@ -0,0 +1,7 @@
+<!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>
Added: trunk/LayoutTests/fast/shadow-dom/clear-active-state-in-shadow.html (0 => 210564)
--- 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)
@@ -0,0 +1,37 @@
+<!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=""
+<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>
Added: trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content-expected.html (0 => 210564)
--- 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)
@@ -0,0 +1,7 @@
+<!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>
Added: trunk/LayoutTests/fast/shadow-dom/hover-over-nested-slotted-content.html (0 => 210564)
--- 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)
@@ -0,0 +1,37 @@
+<!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=""
+<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>
Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (210563 => 210564)
--- 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)
@@ -356,6 +356,8 @@
fast/shadow-dom/shadow-host-removal-crash.html [ Skip ]
fast/shadow-dom/input-element-in-shadow.html [ Skip ]
fast/shadow-dom/activate-over-slotted-content.html [ Skip ]
+fast/shadow-dom/clear-active-state-in-shadow.html [ Skip ]
+fast/shadow-dom/hover-over-nested-slotted-content.html [ Skip ]
fast/shadow-dom/hover-over-slotted-content.html [ Skip ]
fast/events/keyboardevent-key.html [ Skip ]
fast/events/keyboardevent-code.html [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (210563 => 210564)
--- 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)
@@ -1,3 +1,26 @@
+2017-01-10 Ryosuke Niwa <[email protected]>
+
+ :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:
+
2017-01-10 Keith Rollin <[email protected]>
Missing logging in IconLoader::startLoading
Modified: trunk/Source/WebCore/dom/Document.cpp (210563 => 210564)
--- 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)
@@ -6475,9 +6475,9 @@
Element* oldActiveElement = m_activeElement.get();
if (oldActiveElement && !request.active()) {
// We are clearing the :active chain because the mouse has been released.
- for (Element* curr = oldActiveElement; curr; curr = curr->parentOrShadowHostElement()) {
- curr->setActive(false);
- m_userActionElements.setInActiveChain(curr, false);
+ for (Element* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) {
+ currentElement->setActive(false);
+ m_userActionElements.setInActiveChain(currentElement, false);
}
m_activeElement = nullptr;
} else {
@@ -6515,7 +6515,7 @@
// If it hasn't, we do not need to do anything.
Element* newHoveredElement = innerElementInDocument;
while (newHoveredElement && !newHoveredElement->renderer())
- newHoveredElement = newHoveredElement->parentOrShadowHostElement();
+ newHoveredElement = newHoveredElement->parentElementInComposedTree();
m_hoveredElement = newHoveredElement;
@@ -6534,7 +6534,7 @@
// (for instance by setting display:none in the :hover pseudo-class). In this case, the old hovered element (and its ancestors)
// must be updated, to ensure it's normal style is re-applied.
if (oldHoveredElement && !oldHoverObj) {
- for (Element* element = oldHoveredElement.get(); element; element = element->parentElement()) {
+ for (Element* element = oldHoveredElement.get(); element; element = element->parentElementInComposedTree()) {
if (!mustBeInActiveChain || element->inActiveChain())
elementsToRemoveFromChain.append(element);
}
Modified: trunk/Source/WebCore/dom/Node.cpp (210563 => 210564)
--- 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)
@@ -1139,6 +1139,19 @@
return parentNode();
}
+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;
+}
+
bool Node::isInUserAgentShadowTree() const
{
auto* shadowRoot = containingShadowRoot();
Modified: trunk/Source/WebCore/dom/Node.h (210563 => 210564)
--- 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)
@@ -256,6 +256,7 @@
// Node's parent or shadow tree host.
ContainerNode* parentOrShadowHostNode() const;
ContainerNode* parentInComposedTree() const;
+ Element* parentElementInComposedTree() const;
Element* parentOrShadowHostElement() const;
void setParentNode(ContainerNode*);
Node& rootNode() const;