<!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>[203976] 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/203976">203976</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2016-08-01 11:00:48 -0700 (Mon, 01 Aug 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>REGRESSION (<a href="http://trac.webkit.org/projects/webkit/changeset/196383">r196383</a>): Drop down CSS menus not working on cnet.com, apmex.com
https://bugs.webkit.org/show_bug.cgi?id=160390

Reviewed by Simon Fraser.

Source/WebCore:

The case here is that we have a rule like

    .enableHover:hover .child { ... }

and the &quot;enableHover&quot; class is added dynamically. The class change invalidation optimization code would figure out
that nothing needs to be invalidated as the class change doesn't make the rule match (since :hover doesn't match).

However for event driven hover to actually work the hover element needs to have its childrenAffectedByHover bit set.
This bits is set when the selector match is attempted, whether it actually matches or not. Since we optimized away
the style invalidation we never set the bit either.

Fix by treating :hover as always matching (==ignored) when collecting rules for invalidation optimization purposes.
Dynamic pseudo elements are already treated this way for similar reasons.

Test: fast/selectors/hover-invalidation-descendant-dynamic.html

* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne):

    Match always in CollectingRulesIgnoringVirtualPseudoElements mode (now slightly misnamed).

    This mode is used for optimization purposes in StyleInvalidationAnalysis (which we care about here) and
    StyleSharingResolver. The change is fine for both.

* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsHovered):

    Same change for the slow path selector checker.

LayoutTests:

* fast/selectors/hover-invalidation-descendant-dynamic-expected.txt: Added.
* fast/selectors/hover-invalidation-descendant-dynamic.html: Added.</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="#trunkSourceWebCorecssSelectorCheckercpp">trunk/Source/WebCore/css/SelectorChecker.cpp</a></li>
<li><a href="#trunkSourceWebCorecssjitSelectorCompilercpp">trunk/Source/WebCore/cssjit/SelectorCompiler.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastselectorshoverinvalidationdescendantdynamicexpectedtxt">trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastselectorshoverinvalidationdescendantdynamichtml">trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (203975 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-08-01 17:49:57 UTC (rev 203975)
+++ trunk/LayoutTests/ChangeLog        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2016-08-01  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        REGRESSION (r196383): Drop down CSS menus not working on cnet.com, apmex.com
+        https://bugs.webkit.org/show_bug.cgi?id=160390
+
+        Reviewed by Simon Fraser.
+
+        * fast/selectors/hover-invalidation-descendant-dynamic-expected.txt: Added.
+        * fast/selectors/hover-invalidation-descendant-dynamic.html: Added.
+
</ins><span class="cx"> 2016-07-31  Youenn Fablet  &lt;youenn@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fetch Response built-ins should use @makeThisTypeError
</span></span></pre></div>
<a id="trunkLayoutTestsfastselectorshoverinvalidationdescendantdynamicexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic-expected.txt (0 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic-expected.txt        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+Test that dynamically activated :hover rule affecting descendants works.
+Hover to see a green box below.
+Hover to see a green box below.
+Selector compiler: PASS
+Selector checker: PASS
</ins></span></pre></div>
<a id="trunkLayoutTestsfastselectorshoverinvalidationdescendantdynamichtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic.html (0 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic.html                                (rev 0)
+++ trunk/LayoutTests/fast/selectors/hover-invalidation-descendant-dynamic.html        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -0,0 +1,85 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;style&gt;
+.parent {
+    position: relative;
+    padding: 10px;
+    background-color: silver;
+}
+
+.child {
+    position: absolute;
+    width: 200px;
+    height: 200px;
+    top: 0px;
+    background-color: green;
+    display: none;
+}
+
+#target.enableHover:hover .child {
+    display: block;
+}
+
+/* :matches() is here to disable the selector compiler */
+#target2:matches(:root, :nth-of-type(n), :not(#specificity-trick), :nth-last-of-type(n)).enableHover:hover .child {
+    display: block;
+}
+
+&lt;/style&gt;
+&lt;script&gt;
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+function runTest(targetSelector) {
+    var target = document.querySelector(targetSelector);
+    var child = target.querySelector('.child');
+
+    target.classList.add('enableHover');
+
+    if (window.eventSender) {
+        var x = target.offsetLeft + target.offsetWidth / 2;
+        var y = target.offsetTop + target.offsetHeight / 2;
+        eventSender.mouseMoveTo(x, y);
+    }
+
+    return child.offsetWidth != 0;
+}
+
+function log(text, pass) {
+    var log = document.querySelector(&quot;#log&quot;);
+    var line = document.createElement(&quot;div&quot;);
+    line.innerHTML = text + &quot;: &quot; + (pass ? &quot;PASS&quot; : &quot;FAIL&quot;);
+    log.appendChild(line);
+}
+
+function runTests() {
+    var compilerPass = runTest(&quot;#target&quot;);
+    var checkerPass = runTest(&quot;#target2&quot;);
+    if (!window.testRunner)
+        return;
+    log(&quot;Selector compiler&quot;, compilerPass);
+    log(&quot;Selector checker&quot;, checkerPass);
+
+    testRunner.notifyDone();
+}
+
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTests()&quot;&gt;
+&lt;div&gt;
+Test that dynamically activated :hover rule affecting descendants works.
+&lt;/div&gt;
+&lt;div id=&quot;target&quot; class=&quot;parent&quot;&gt;
+    Hover to see a green box below.
+    &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
+&lt;/div&gt;
+&lt;div id=&quot;target2&quot; class=&quot;parent&quot;&gt;
+    Hover to see a green box below.
+    &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
+&lt;/div&gt;
+&lt;div id=log&gt;&lt;/div&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformiossimulatorTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (203975 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-simulator/TestExpectations        2016-08-01 17:49:57 UTC (rev 203975)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -288,6 +288,7 @@
</span><span class="cx"> fast/css/pseudo-active-style-sharing-6.html [ Skip ]
</span><span class="cx"> fast/css/pseudo-active-with-programmatic-focus.html [ Skip ]
</span><span class="cx"> http/tests/security/history-username-password.html [ Skip ]
</span><ins>+fast/selectors/hover-invalidation-descendant-dynamic.html [ Skip ]
</ins><span class="cx"> 
</span><span class="cx"> webkit.org/b/148695 fast/shadow-dom [ Pass ]
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (203975 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-08-01 17:49:57 UTC (rev 203975)
+++ trunk/Source/WebCore/ChangeLog        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -1,3 +1,39 @@
</span><ins>+2016-08-01  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        REGRESSION (r196383): Drop down CSS menus not working on cnet.com, apmex.com
+        https://bugs.webkit.org/show_bug.cgi?id=160390
+
+        Reviewed by Simon Fraser.
+
+        The case here is that we have a rule like
+
+            .enableHover:hover .child { ... }
+
+        and the &quot;enableHover&quot; class is added dynamically. The class change invalidation optimization code would figure out
+        that nothing needs to be invalidated as the class change doesn't make the rule match (since :hover doesn't match).
+
+        However for event driven hover to actually work the hover element needs to have its childrenAffectedByHover bit set.
+        This bits is set when the selector match is attempted, whether it actually matches or not. Since we optimized away
+        the style invalidation we never set the bit either.
+
+        Fix by treating :hover as always matching (==ignored) when collecting rules for invalidation optimization purposes.
+        Dynamic pseudo elements are already treated this way for similar reasons.
+
+        Test: fast/selectors/hover-invalidation-descendant-dynamic.html
+
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOne):
+
+            Match always in CollectingRulesIgnoringVirtualPseudoElements mode (now slightly misnamed).
+
+            This mode is used for optimization purposes in StyleInvalidationAnalysis (which we care about here) and
+            StyleSharingResolver. The change is fine for both.
+
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsHovered):
+
+            Same change for the slow path selector checker.
+
</ins><span class="cx"> 2016-08-01  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Cocoa] Freeze Objective-C bindings and stop autogenerating them: Step 1 - Convert a single file
</span></span></pre></div>
<a id="trunkSourceWebCorecssSelectorCheckercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (203975 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/SelectorChecker.cpp        2016-08-01 17:49:57 UTC (rev 203975)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -951,6 +951,10 @@
</span><span class="cx">             if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
</span><span class="cx">                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByHover);
</span><span class="cx"> 
</span><ins>+                // See the comment in generateElementIsHovered() in SelectorCompiler.
+                if (checkingContext.resolvingMode == SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements &amp;&amp; !context.isMatchElement)
+                    return true;
+
</ins><span class="cx">                 if (element.hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover))
</span><span class="cx">                     return true;
</span><span class="cx">             }
</span></span></pre></div>
<a id="trunkSourceWebCorecssjitSelectorCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (203975 => 203976)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp        2016-08-01 17:49:57 UTC (rev 203975)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp        2016-08-01 18:00:48 UTC (rev 203976)
</span><span class="lines">@@ -3214,10 +3214,22 @@
</span><span class="cx"> 
</span><span class="cx">     generateAddStyleRelationIfResolvingStyle(elementAddressRegister, Style::Relation::AffectedByHover);
</span><span class="cx"> 
</span><ins>+    Assembler::JumpList successCases;
+    if (m_selectorContext != SelectorContext::QuerySelector &amp;&amp; fragment.relationToRightFragment != FragmentRelation::Rightmost) {
+        // :hover always matches when not in rightmost position when collecting rules for descendant style invalidation optimization.
+        // Resolving style for a matching descendant will set parent childrenAffectedByHover bit even when the element is not currently hovered.
+        // This bit has to be set for the event based :hover invalidation to work.
+        // FIXME: We should just collect style relation bits and apply them as needed when computing style invalidation optimization.
+        LocalRegister checkingContext(m_registerAllocator);
+        successCases.append(branchOnResolvingMode(Assembler::Equal, SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements, checkingContext));
+    }
+
</ins><span class="cx">     FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
</span><span class="cx">     functionCall.setFunctionAddress(elementIsHovered);
</span><span class="cx">     functionCall.setOneArgument(elementAddressRegister);
</span><span class="cx">     failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
</span><ins>+
+    successCases.link(&amp;m_assembler);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SelectorCodeGenerator::generateElementIsInLanguage(Assembler::JumpList&amp; failureCases, const SelectorFragment&amp; fragment)
</span></span></pre>
</div>
</div>

</body>
</html>