CSS Rule features are ignored for nested CSS Selector lists
https://bugs.webkit.org/show_bug.cgi?id=137908
Patch by Benjamin Poulain <bpoulain@apple.com> 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.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@175018 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/fast/selectors/class-style-update-with-not.html b/LayoutTests/fast/selectors/class-style-update-with-not.html
new file mode 100644
index 0000000..451e666
--- /dev/null
+++ b/LayoutTests/fast/selectors/class-style-update-with-not.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<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="../../resources/js-test-post.js"></script>
+</html>