Title: [175018] trunk
Revision
175018
Author
[email protected]
Date
2014-10-21 20:28:29 -0700 (Tue, 21 Oct 2014)

Log Message

CSS Rule features are ignored for nested CSS Selector lists
https://bugs.webkit.org/show_bug.cgi?id=137908

Patch by Benjamin Poulain <[email protected]> on 2014-10-21
Reviewed by Andreas Kling.

Source/WebCore:

When Rule feature sets were collected, any selector list nested inside an other
selector list was ignored when collecting properties of the CSS Selector.

As a result, style was not invalidated properly when any property listed in
the nested selectors.

This patch fixes the issue by make RuleFeatureSet::collectFeaturesFromSelector()
recursive, evaluating every chain of every selector lists.

Tests: fast/css/class-style-invalidation-optimization.html
       fast/css/direct-adjacent-style-sharing-1.html
       fast/css/direct-adjacent-style-sharing-2.html
       fast/css/direct-adjacent-style-sharing-3.html
       fast/css/id-style-invalidation-optimization.html
       fast/selectors/class-style-update-with-not.html
       fast/selectors/class-style-update-with-nth-child-of.html
       fast/selectors/class-style-update.html

* css/RuleFeature.cpp:
(WebCore::recursivelyCollectFeaturesFromSelector):
(WebCore::RuleFeatureSet::collectFeaturesFromSelector):
* css/RuleFeature.h:
* css/RuleSet.cpp:
(WebCore::collectFeaturesFromRuleData):

LayoutTests:

* fast/selectors/class-style-update-with-not-expected.txt: Added.
* fast/selectors/class-style-update-with-not.html: Added.
Parts of this test fail due to a bug with specificity. This will be addressed
separately.

* fast/css/class-style-invalidation-optimization-expected.txt: Added.
* fast/css/class-style-invalidation-optimization.html: Added.
* fast/css/direct-adjacent-style-sharing-1-expected.html: Added.
* fast/css/direct-adjacent-style-sharing-1.html: Added.
* fast/css/direct-adjacent-style-sharing-2-expected.html: Added.
* fast/css/direct-adjacent-style-sharing-2.html: Added.
* fast/css/direct-adjacent-style-sharing-3-expected.html: Added.
* fast/css/direct-adjacent-style-sharing-3.html: Added.
* fast/css/id-style-invalidation-optimization-expected.txt: Added.
* fast/css/id-style-invalidation-optimization.html: Added.
* fast/selectors/class-style-update-expected.txt: Added.
* fast/selectors/class-style-update-with-nth-child-of-expected.txt: Added.
* fast/selectors/class-style-update-with-nth-child-of.html: Added.
* fast/selectors/class-style-update.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (175017 => 175018)


--- trunk/LayoutTests/ChangeLog	2014-10-22 02:56:54 UTC (rev 175017)
+++ trunk/LayoutTests/ChangeLog	2014-10-22 03:28:29 UTC (rev 175018)
@@ -1,3 +1,30 @@
+2014-10-21  Benjamin Poulain  <[email protected]>
+
+        CSS Rule features are ignored for nested CSS Selector lists
+        https://bugs.webkit.org/show_bug.cgi?id=137908
+
+        Reviewed by Andreas Kling.
+
+        * fast/selectors/class-style-update-with-not-expected.txt: Added.
+        * fast/selectors/class-style-update-with-not.html: Added.
+        Parts of this test fail due to a bug with specificity. This will be addressed
+        separately.
+
+        * fast/css/class-style-invalidation-optimization-expected.txt: Added.
+        * fast/css/class-style-invalidation-optimization.html: Added.
+        * fast/css/direct-adjacent-style-sharing-1-expected.html: Added.
+        * fast/css/direct-adjacent-style-sharing-1.html: Added.
+        * fast/css/direct-adjacent-style-sharing-2-expected.html: Added.
+        * fast/css/direct-adjacent-style-sharing-2.html: Added.
+        * fast/css/direct-adjacent-style-sharing-3-expected.html: Added.
+        * fast/css/direct-adjacent-style-sharing-3.html: Added.
+        * fast/css/id-style-invalidation-optimization-expected.txt: Added.
+        * fast/css/id-style-invalidation-optimization.html: Added.
+        * fast/selectors/class-style-update-expected.txt: Added.
+        * fast/selectors/class-style-update-with-nth-child-of-expected.txt: Added.
+        * fast/selectors/class-style-update-with-nth-child-of.html: Added.
+        * fast/selectors/class-style-update.html: Added.
+
 2014-10-21  Brent Fulgham  <[email protected]>
 
         [Win] Unreviewed test updates after switching to Windows theme for tests.

Added: trunk/LayoutTests/fast/css/class-style-invalidation-optimization-expected.txt (0 => 175018)


--- trunk/LayoutTests/fast/css/class-style-invalidation-optimization-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/css/class-style-invalidation-optimization-expected.txt	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,45 @@
+Test that we do not invalidate the style of an element if we are changing a class that is not used by the stylesheet.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/css/class-style-invalidation-optimization.html (0 => 175018)


--- trunk/LayoutTests/fast/css/class-style-invalidation-optimization.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/class-style-invalidation-optimization.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+    color: black;
+}
+target.Case1 {
+    color: rgb(0, 1, 2);
+}
+target:matches(foo, .Case2, bar) {
+    color: rgb(3, 4, 5);
+}
+target:matches(foo1, :matches(foo2, .Case3, bar1), bar2) {
+    color: rgb(6, 7, 8);
+}
+target:matches(foo1, :matches(foo2, :matches(foo3, .Case4, bar1), bar2), bar3) {
+    color: rgb(9, 10, 11);
+}
+</style>
+</head>
+<body>
+    <div>
+        <!-- With renderer -->
+        <target></target>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <target></target>
+    </div>
+</body>
+<script>
+
+description('Test that we do not invalidate the style of an element if we are changing a class that is not used by the stylesheet.');
+
+function shouldNeedStyleRecalc(expected) {
+    var testFunction = expected ? shouldBeTrue : shouldBeFalse;
+    testFunction("window.internals.nodeNeedsStyleRecalc(document.querySelectorAll(\"target\")[0])");
+    testFunction("window.internals.nodeNeedsStyleRecalc(document.querySelectorAll(\"target\")[1])");
+}
+
+function addClass(name) {
+    var allTargets = document.querySelectorAll("target");
+    allTargets[0].classList.add(name);
+    allTargets[1].classList.add(name);
+}
+
+function checkStyle(expectedColor) {
+    var allTargets = document.querySelectorAll("target");
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).color', expectedColor);
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).color', expectedColor);
+}
+// Force a layout to ensure we don't have dirty styles.
+var offsetTop = document.documentElement.offsetTop;
+shouldNeedStyleRecalc(false);
+
+addClass('NotThere');
+shouldNeedStyleRecalc(false);
+
+addClass('Case1');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(0, 1, 2)');
+shouldNeedStyleRecalc(false);
+
+addClass('case1');
+shouldNeedStyleRecalc(false);
+
+addClass('Case2');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(3, 4, 5)');
+shouldNeedStyleRecalc(false);
+
+addClass('case2');
+shouldNeedStyleRecalc(false);
+
+addClass('Case3');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(6, 7, 8)');
+shouldNeedStyleRecalc(false);
+
+addClass('case3');
+shouldNeedStyleRecalc(false);
+
+addClass('Case4');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(9, 10, 11)');
+shouldNeedStyleRecalc(false);
+
+addClass('case4');
+shouldNeedStyleRecalc(false);
+</script>
+<script src=""
+</html>

Added: trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-1-expected.html (0 => 175018)


--- trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-1-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-1-expected.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <style type="text/css">
+            div {
+                background-color: red;
+                border: 1px solid black;
+                width: 100px;
+                height: 100px;
+            }
+
+            div + div {
+                background-color: green;
+            }
+        </style>
+    </head>
+    <body>
+        <p>If the test pass, there should be one red block followed by 3 green blocks.</p>
+        <div style="background-color: red; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-1.html (0 => 175018)


--- trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-1.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-1.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <style type="text/css">
+            div {
+                background-color: red;
+                border: 1px solid black;
+                width: 100px;
+                height: 100px;
+            }
+
+            div + div {
+                background-color: green;
+            }
+        </style>
+    </head>
+    <body>
+        <p>If the test pass, there should be one red block followed by 3 green blocks.</p>
+        <div></div>
+        <div></div>
+        <div></div>
+        <div></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-2-expected.html (0 => 175018)


--- trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-2-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-2-expected.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <style type="text/css">
+            div {
+                background-color: red;
+                border: 1px solid black;
+                width: 100px;
+                height: 100px;
+            }
+
+            div + div {
+                background-color: green;
+            }
+        </style>
+    </head>
+    <body>
+        <p>If the test pass, there should be one red block followed by 3 green blocks.</p>
+        <div style="background-color: red; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-2.html (0 => 175018)


--- trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-2.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-2.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <style type="text/css">
+            div {
+                background-color: red;
+                border: 1px solid black;
+                width: 100px;
+                height: 100px;
+            }
+
+            :matches(div + div) {
+                background-color: green;
+            }
+        </style>
+    </head>
+    <body>
+        <p>If the test pass, there should be one red block followed by 3 green blocks.</p>
+        <div></div>
+        <div></div>
+        <div></div>
+        <div></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-3-expected.html (0 => 175018)


--- trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-3-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-3-expected.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <style type="text/css">
+            div {
+                background-color: red;
+                border: 1px solid black;
+                width: 100px;
+                height: 100px;
+            }
+
+            div + div {
+                background-color: green;
+            }
+        </style>
+    </head>
+    <body>
+        <p>If the test pass, there should be one red block followed by 3 green blocks.</p>
+        <div style="background-color: red; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+        <div style="background-color: green; border: 1px solid black; width: 100px; height: 100px;"></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-3.html (0 => 175018)


--- trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-3.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/direct-adjacent-style-sharing-3.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <style type="text/css">
+            div {
+                background-color: red;
+                border: 1px solid black;
+                width: 100px;
+                height: 100px;
+            }
+
+            :matches(:matches(div + div)) {
+                background-color: green;
+            }
+        </style>
+    </head>
+    <body>
+        <p>If the test pass, there should be one red block followed by 3 green blocks.</p>
+        <div></div>
+        <div></div>
+        <div></div>
+        <div></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/css/id-style-invalidation-optimization-expected.txt (0 => 175018)


--- trunk/LayoutTests/fast/css/id-style-invalidation-optimization-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/css/id-style-invalidation-optimization-expected.txt	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,37 @@
+Test that we do not invalidate the style of an element if we are changing an #id that is not used by the stylesheet.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is true
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is true
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[0]) is false
+PASS window.internals.nodeNeedsStyleRecalc(document.querySelectorAll("target")[1]) is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/css/id-style-invalidation-optimization.html (0 => 175018)


--- trunk/LayoutTests/fast/css/id-style-invalidation-optimization.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/id-style-invalidation-optimization.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+    color: black;
+}
+target#Case1 {
+    color: rgb(0, 1, 2);
+}
+target:matches(foo, #Case2, bar) {
+    color: rgb(3, 4, 5);
+}
+target:matches(foo1, :matches(foo2, #Case3, bar1), bar2) {
+    color: rgb(6, 7, 8);
+}
+target:matches(foo1, :matches(foo2, :matches(foo3, #Case4, bar1), bar2), bar3) {
+    color: rgb(9, 10, 11);
+}
+</style>
+</head>
+<body>
+    <div>
+        <!-- With renderer -->
+        <target></target>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <target></target>
+    </div>
+</body>
+<script>
+
+description('Test that we do not invalidate the style of an element if we are changing an #id that is not used by the stylesheet.');
+
+function shouldNeedStyleRecalc(expected) {
+    var testFunction = expected ? shouldBeTrue : shouldBeFalse;
+    testFunction("window.internals.nodeNeedsStyleRecalc(document.querySelectorAll(\"target\")[0])");
+    testFunction("window.internals.nodeNeedsStyleRecalc(document.querySelectorAll(\"target\")[1])");
+}
+
+function setId(name) {
+    var allTargets = document.querySelectorAll("target");
+    allTargets[0].id = name;
+    allTargets[1].id = name;
+}
+
+function checkStyle(expectedColor) {
+    var allTargets = document.querySelectorAll("target");
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).color', expectedColor);
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).color', expectedColor);
+}
+// Force a layout to ensure we don't have dirty styles.
+var offsetTop = document.documentElement.offsetTop;
+shouldNeedStyleRecalc(false);
+
+setId('NotThere');
+shouldNeedStyleRecalc(false);
+
+setId('Case1');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(0, 1, 2)');
+shouldNeedStyleRecalc(false);
+
+setId('Case2');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(3, 4, 5)');
+shouldNeedStyleRecalc(false);
+
+setId('Case3');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(6, 7, 8)');
+shouldNeedStyleRecalc(false);
+
+setId('Case4');
+shouldNeedStyleRecalc(true);
+checkStyle('rgb(9, 10, 11)');
+shouldNeedStyleRecalc(false);
+</script>
+<script src=""
+</html>

Added: trunk/LayoutTests/fast/selectors/class-style-update-expected.txt (0 => 175018)


--- trunk/LayoutTests/fast/selectors/class-style-update-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/class-style-update-expected.txt	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,51 @@
+Test that the style of elements is invalidated correctly when class changes can affect its style. Elements are only invalidate if the class changed affects the style. It is important to account nested selector lists.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/selectors/class-style-update-with-not-expected.txt (0 => 175018)


--- trunk/LayoutTests/fast/selectors/class-style-update-with-not-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/class-style-update-with-not-expected.txt	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,41 @@
+Test that the style of elements is invalidated correctly when a class changes inside :not() can affect its style. Elements are only invalidate if the class changed affects the style. It is important to account nested selector lists.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(12, 13, 14)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(12, 13, 14)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+FAIL getComputedStyle(document.querySelectorAll("target")[0]).color should be rgb(3, 4, 5). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[1]).color should be rgb(3, 4, 5). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[0]).color should be rgb(6, 7, 8). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[1]).color should be rgb(6, 7, 8). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[0]).color should be rgb(9, 10, 11). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[1]).color should be rgb(9, 10, 11). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[0]).color should be rgb(12, 13, 14). Was rgb(0, 1, 2).
+FAIL getComputedStyle(document.querySelectorAll("target")[1]).color should be rgb(12, 13, 14). Was rgb(0, 1, 2).
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/selectors/class-style-update-with-not.html (0 => 175018)


--- trunk/LayoutTests/fast/selectors/class-style-update-with-not.html	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/class-style-update-with-not.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+    color: black;
+}
+target:not(.Case1) {
+    color: rgb(0, 1, 2);
+}
+target:not(foo, .Case2, bar) {
+    color: rgb(3, 4, 5);
+}
+target:not(foo1, :matches(foo2, .Case3, bar1), bar2) {
+    color: rgb(6, 7, 8);
+}
+target:not(foo1, :not(foo2, .Case4, bar1), bar2) {
+    color: rgb(9, 10, 11);
+}
+target:not(foo1, :matches(foo2, :matches(foo3, .Case5, bar1), bar2), bar3) {
+    color: rgb(12, 13, 14);
+}
+</style>
+</head>
+<body>
+    <div>
+        <!-- With renderer -->
+        <target class="Case1 Case2 Case3 Case5"></target>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <target class="Case1 Case2 Case3 Case5"></target>
+    </div>
+</body>
+<script>
+
+description('Test that the style of elements is invalidated correctly when a class changes inside :not() can affect its style. Elements are only invalidate if the class changed affects the style. It is important to account nested selector lists.');
+
+function addClass(name) {
+    var allTargets = document.querySelectorAll("target");
+    allTargets[0].classList.add(name);
+    allTargets[1].classList.add(name);
+}
+
+function removeClass(name) {
+    var allTargets = document.querySelectorAll("target");
+    allTargets[0].classList.remove(name);
+    allTargets[1].classList.remove(name);
+}
+
+function checkStyle(expectedColor) {
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).color', expectedColor);
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).color', expectedColor);
+}
+
+// Force a layout to ensure we don't have dirty styles.
+var offsetTop = document.documentElement.offsetTop;
+
+checkStyle('rgb(0, 0, 0)');
+
+// Change the classes one by one.
+removeClass('Case1');
+checkStyle('rgb(0, 1, 2)');
+addClass('Case1');
+checkStyle('rgb(0, 0, 0)');
+
+removeClass('Case2');
+checkStyle('rgb(3, 4, 5)');
+addClass('Case2');
+checkStyle('rgb(0, 0, 0)');
+
+removeClass('Case3');
+checkStyle('rgb(6, 7, 8)');
+addClass('Case3');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case4');
+checkStyle('rgb(9, 10, 11)');
+removeClass('Case4');
+checkStyle('rgb(0, 0, 0)');
+
+removeClass('Case5');
+checkStyle('rgb(12, 13, 14)');
+addClass('Case5');
+checkStyle('rgb(0, 0, 0)');
+
+
+// Remove the classes one after the other.
+removeClass('Case1');
+checkStyle('rgb(0, 1, 2)');
+removeClass('Case2');
+checkStyle('rgb(3, 4, 5)');
+removeClass('Case3');
+checkStyle('rgb(6, 7, 8)');
+addClass('Case4');
+checkStyle('rgb(9, 10, 11)');
+removeClass('Case5');
+checkStyle('rgb(12, 13, 14)');
+</script>
+<script src=""
+</html>

Added: trunk/LayoutTests/fast/selectors/class-style-update-with-nth-child-of-expected.txt (0 => 175018)


--- trunk/LayoutTests/fast/selectors/class-style-update-with-nth-child-of-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/class-style-update-with-nth-child-of-expected.txt	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,51 @@
+Test that the style of elements is invalidated correctly when a class changes inside :nth-child(An+B of selectorList) can affect its style. Elements are only invalidate if the class changed affects the style. It is important to account nested selector lists.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 1, 2)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(3, 4, 5)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(6, 7, 8)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(9, 10, 11)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(9, 10, 11)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/selectors/class-style-update-with-nth-child-of.html (0 => 175018)


--- trunk/LayoutTests/fast/selectors/class-style-update-with-nth-child-of.html	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/class-style-update-with-nth-child-of.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+    color: black;
+}
+target:nth-child(2 of .Case1) {
+    color: rgb(0, 1, 2);
+}
+target:nth-child(2 of foo, .Case2, bar) {
+    color: rgb(3, 4, 5);
+}
+target:nth-child(2 of foo1, :matches(foo2, .Case3, bar1), bar2) {
+    color: rgb(6, 7, 8);
+}
+target:nth-child(2 of foo1, :nth-child(n of foo2, .Case4, bar1), bar2) {
+    color: rgb(9, 10, 11);
+}
+target:nth-child(2 of foo1, :matches(foo2, :nth-child(n of foo3, .Case5, bar1), bar2), bar3) {
+    color: rgb(12, 13, 14);
+}
+</style>
+</head>
+<body>
+    <div>
+        <!-- With renderer -->
+        <padding></padding>
+        <target></target>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <padding></padding>
+        <target></target>
+    </div>
+</body>
+<script>
+
+description('Test that the style of elements is invalidated correctly when a class changes inside :nth-child(An+B of selectorList) can affect its style. Elements are only invalidate if the class changed affects the style. It is important to account nested selector lists.');
+
+function addClass(name) {
+    var allTargets = document.querySelectorAll("target, padding");
+    for (var i = 0; i < allTargets.length; ++i)
+        allTargets[i].classList.add(name);
+}
+
+function removeClass(name) {
+    var allTargets = document.querySelectorAll(":matches(target, padding)");
+    for (var i = 0; i < allTargets.length; ++i)
+        allTargets[i].classList.remove(name);
+}
+
+function checkStyle(expectedColor) {
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).color', expectedColor);
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).color', expectedColor);
+}
+
+// Force a layout to ensure we don't have dirty styles.
+var offsetTop = document.documentElement.offsetTop;
+
+checkStyle('rgb(0, 0, 0)');
+
+// Add the classes one by one.
+addClass('Case1');
+checkStyle('rgb(0, 1, 2)');
+removeClass('Case1');
+checkStyle('rgb(0, 0, 0)');
+addClass('case1');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case1');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case2');
+checkStyle('rgb(3, 4, 5)');
+removeClass('Case2');
+checkStyle('rgb(0, 0, 0)');
+addClass('case2');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case2');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case3');
+checkStyle('rgb(6, 7, 8)');
+removeClass('Case3');
+checkStyle('rgb(0, 0, 0)');
+addClass('case3');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case3');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case4');
+checkStyle('rgb(9, 10, 11)');
+removeClass('Case4');
+checkStyle('rgb(0, 0, 0)');
+addClass('case4');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case4');
+checkStyle('rgb(0, 0, 0)');
+
+// Add the classes one after the other.
+addClass('Case1');
+checkStyle('rgb(0, 1, 2)');
+addClass('Case2');
+checkStyle('rgb(3, 4, 5)');
+addClass('Case3');
+checkStyle('rgb(6, 7, 8)');
+addClass('Case4');
+checkStyle('rgb(9, 10, 11)');
+</script>
+<script src=""
+</html>

Added: trunk/LayoutTests/fast/selectors/class-style-update.html (0 => 175018)


--- trunk/LayoutTests/fast/selectors/class-style-update.html	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/class-style-update.html	2014-10-22 03:28:29 UTC (rev 175018)
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+    color: black;
+}
+target.Case1 {
+    color: rgb(0, 1, 2);
+}
+target:matches(foo, .Case2, bar) {
+    color: rgb(3, 4, 5);
+}
+target:matches(foo1, :matches(foo2, .Case3, bar1), bar2) {
+    color: rgb(6, 7, 8);
+}
+target:matches(foo1, :matches(foo2, :matches(foo3, .Case4, bar1), bar2), bar3) {
+    color: rgb(9, 10, 11);
+}
+</style>
+</head>
+<body>
+    <div>
+        <!-- With renderer -->
+        <target></target>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <target></target>
+    </div>
+</body>
+<script>
+
+description('Test that the style of elements is invalidated correctly when class changes can affect its style. Elements are only invalidate if the class changed affects the style. It is important to account nested selector lists.');
+
+function addClass(name) {
+    var allTargets = document.querySelectorAll("target");
+    allTargets[0].classList.add(name);
+    allTargets[1].classList.add(name);
+}
+
+function removeClass(name) {
+    var allTargets = document.querySelectorAll("target");
+    allTargets[0].classList.remove(name);
+    allTargets[1].classList.remove(name);
+}
+
+function checkStyle(expectedColor) {
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).color', expectedColor);
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).color', expectedColor);
+}
+
+// Force a layout to ensure we don't have dirty styles.
+var offsetTop = document.documentElement.offsetTop;
+
+checkStyle('rgb(0, 0, 0)');
+
+// Add the classes one by one.
+addClass('Case1');
+checkStyle('rgb(0, 1, 2)');
+removeClass('Case1');
+checkStyle('rgb(0, 0, 0)');
+addClass('case1');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case1');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case2');
+checkStyle('rgb(3, 4, 5)');
+removeClass('Case2');
+checkStyle('rgb(0, 0, 0)');
+addClass('case2');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case2');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case3');
+checkStyle('rgb(6, 7, 8)');
+removeClass('Case3');
+checkStyle('rgb(0, 0, 0)');
+addClass('case3');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case3');
+checkStyle('rgb(0, 0, 0)');
+
+addClass('Case4');
+checkStyle('rgb(9, 10, 11)');
+removeClass('Case4');
+checkStyle('rgb(0, 0, 0)');
+addClass('case4');
+checkStyle('rgb(0, 0, 0)');
+removeClass('Case4');
+checkStyle('rgb(0, 0, 0)');
+
+// Add the classes one after the other.
+addClass('Case1');
+checkStyle('rgb(0, 1, 2)');
+addClass('Case2');
+checkStyle('rgb(3, 4, 5)');
+addClass('Case3');
+checkStyle('rgb(6, 7, 8)');
+addClass('Case4');
+checkStyle('rgb(9, 10, 11)');
+</script>
+<script src=""
+</html>

Modified: trunk/Source/WebCore/ChangeLog (175017 => 175018)


--- trunk/Source/WebCore/ChangeLog	2014-10-22 02:56:54 UTC (rev 175017)
+++ trunk/Source/WebCore/ChangeLog	2014-10-22 03:28:29 UTC (rev 175018)
@@ -1,3 +1,35 @@
+2014-10-21  Benjamin Poulain  <[email protected]>
+
+        CSS Rule features are ignored for nested CSS Selector lists
+        https://bugs.webkit.org/show_bug.cgi?id=137908
+
+        Reviewed by Andreas Kling.
+
+        When Rule feature sets were collected, any selector list nested inside an other
+        selector list was ignored when collecting properties of the CSS Selector.
+
+        As a result, style was not invalidated properly when any property listed in
+        the nested selectors.
+
+        This patch fixes the issue by make RuleFeatureSet::collectFeaturesFromSelector()
+        recursive, evaluating every chain of every selector lists.
+
+        Tests: fast/css/class-style-invalidation-optimization.html
+               fast/css/direct-adjacent-style-sharing-1.html
+               fast/css/direct-adjacent-style-sharing-2.html
+               fast/css/direct-adjacent-style-sharing-3.html
+               fast/css/id-style-invalidation-optimization.html
+               fast/selectors/class-style-update-with-not.html
+               fast/selectors/class-style-update-with-nth-child-of.html
+               fast/selectors/class-style-update.html
+
+        * css/RuleFeature.cpp:
+        (WebCore::recursivelyCollectFeaturesFromSelector):
+        (WebCore::RuleFeatureSet::collectFeaturesFromSelector):
+        * css/RuleFeature.h:
+        * css/RuleSet.cpp:
+        (WebCore::collectFeaturesFromRuleData):
+
 2014-10-21  Tim Horton  <[email protected]>
 
         Try to fix the iOS build after r175013.

Modified: trunk/Source/WebCore/css/RuleFeature.cpp (175017 => 175018)


--- trunk/Source/WebCore/css/RuleFeature.cpp	2014-10-22 02:56:54 UTC (rev 175017)
+++ trunk/Source/WebCore/css/RuleFeature.cpp	2014-10-22 03:28:29 UTC (rev 175018)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll ([email protected])
  *           (C) 2004-2005 Allan Sandfeld Jensen ([email protected])
  * Copyright (C) 2006, 2007 Nicholas Shanks ([email protected])
- * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2012, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2007 Alexey Proskuryakov <[email protected]>
  * Copyright (C) 2007, 2008 Eric Seidel <[email protected]>
  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
@@ -30,36 +30,59 @@
 #include "RuleFeature.h"
 
 #include "CSSSelector.h"
+#include "CSSSelectorList.h"
 
 namespace WebCore {
 
-void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector* selector)
+static void recursivelyCollectFeaturesFromSelector(RuleFeatureSet& features, const CSSSelector& firstSelector, bool& hasSiblingSelector)
 {
-    if (selector->match() == CSSSelector::Id)
-        idsInRules.add(selector->value().impl());
-    else if (selector->match() == CSSSelector::Class)
-        classesInRules.add(selector->value().impl());
-    else if (selector->isAttributeSelector()) {
-        attributeCanonicalLocalNamesInRules.add(selector->attributeCanonicalLocalName().impl());
-        attributeLocalNamesInRules.add(selector->attribute().localName().impl());
-    } else if (selector->match() == CSSSelector::PseudoElement) {
-        switch (selector->pseudoElementType()) {
-        case CSSSelector::PseudoElementFirstLine:
-            usesFirstLineRules = true;
-            break;
-        case CSSSelector::PseudoElementFirstLetter:
-            usesFirstLetterRules = true;
-            break;
-        case CSSSelector::PseudoElementBefore:
-        case CSSSelector::PseudoElementAfter:
-            usesBeforeAfterRules = true;
-            break;
-        default:
-            break;
+    const CSSSelector* selector = &firstSelector;
+    do {
+        if (selector->match() == CSSSelector::Id)
+            features.idsInRules.add(selector->value().impl());
+        else if (selector->match() == CSSSelector::Class)
+            features.classesInRules.add(selector->value().impl());
+        else if (selector->isAttributeSelector()) {
+            features.attributeCanonicalLocalNamesInRules.add(selector->attributeCanonicalLocalName().impl());
+            features.attributeLocalNamesInRules.add(selector->attribute().localName().impl());
+        } else if (selector->match() == CSSSelector::PseudoElement) {
+            switch (selector->pseudoElementType()) {
+            case CSSSelector::PseudoElementFirstLine:
+                features.usesFirstLineRules = true;
+                break;
+            case CSSSelector::PseudoElementFirstLetter:
+                features.usesFirstLetterRules = true;
+                break;
+            case CSSSelector::PseudoElementBefore:
+            case CSSSelector::PseudoElementAfter:
+                features.usesBeforeAfterRules = true;
+                break;
+            default:
+                break;
+            }
         }
-    }
+
+        if (!hasSiblingSelector && selector->isSiblingSelector())
+            hasSiblingSelector = true;
+
+        if (const CSSSelectorList* selectorList = selector->selectorList()) {
+            for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
+                if (!hasSiblingSelector && selector->isSiblingSelector())
+                    hasSiblingSelector = true;
+                recursivelyCollectFeaturesFromSelector(features, *subSelector, hasSiblingSelector);
+            }
+        }
+
+        selector = selector->tagHistory();
+    } while (selector);
 }
 
+void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& firstSelector, bool& hasSiblingSelector)
+{
+    hasSiblingSelector = false;
+    recursivelyCollectFeaturesFromSelector(*this, firstSelector, hasSiblingSelector);
+}
+
 void RuleFeatureSet::add(const RuleFeatureSet& other)
 {
     idsInRules.add(other.idsInRules.begin(), other.idsInRules.end());

Modified: trunk/Source/WebCore/css/RuleFeature.h (175017 => 175018)


--- trunk/Source/WebCore/css/RuleFeature.h	2014-10-22 02:56:54 UTC (rev 175017)
+++ trunk/Source/WebCore/css/RuleFeature.h	2014-10-22 03:28:29 UTC (rev 175018)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 1999 Lars Knoll ([email protected])
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2011, 2014 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -54,7 +54,7 @@
     void add(const RuleFeatureSet&);
     void clear();
 
-    void collectFeaturesFromSelector(const CSSSelector*);
+    void collectFeaturesFromSelector(const CSSSelector&, bool& hasSiblingSelector);
 
     HashSet<AtomicStringImpl*> idsInRules;
     HashSet<AtomicStringImpl*> classesInRules;

Modified: trunk/Source/WebCore/css/RuleSet.cpp (175017 => 175018)


--- trunk/Source/WebCore/css/RuleSet.cpp	2014-10-22 02:56:54 UTC (rev 175017)
+++ trunk/Source/WebCore/css/RuleSet.cpp	2014-10-22 03:28:29 UTC (rev 175018)
@@ -166,20 +166,10 @@
 
 static void collectFeaturesFromRuleData(RuleFeatureSet& features, const RuleData& ruleData)
 {
-    bool foundSiblingSelector = false;
-    for (const CSSSelector* selector = ruleData.selector(); selector; selector = selector->tagHistory()) {
-        features.collectFeaturesFromSelector(selector);
-        
-        if (const CSSSelectorList* selectorList = selector->selectorList()) {
-            for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
-                if (!foundSiblingSelector && selector->isSiblingSelector())
-                    foundSiblingSelector = true;
-                features.collectFeaturesFromSelector(subSelector);
-            }
-        } else if (!foundSiblingSelector && selector->isSiblingSelector())
-            foundSiblingSelector = true;
-    }
-    if (foundSiblingSelector)
+    bool hasSiblingSelector;
+    features.collectFeaturesFromSelector(*ruleData.selector(), hasSiblingSelector);
+
+    if (hasSiblingSelector)
         features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
     if (ruleData.containsUncommonAttributeSelector())
         features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to