Title: [176084] trunk
Revision
176084
Author
[email protected]
Date
2014-11-13 13:00:33 -0800 (Thu, 13 Nov 2014)

Log Message

Implement the matching for :nth-last-child(An+B of selector-list)
https://bugs.webkit.org/show_bug.cgi?id=138650

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

Source/WebCore:

Matching is trivial based on the existing CSS4 infrastructure.

The tricky part is style invalidation. In the cases of the old :nth-last-child()
and :nth-last-of-type(), we were using the flag ChildrenAffectedByBackwardPositionalRules.

The problem with ChildrenAffectedByBackwardPositionalRules is that it only invalidate
subtrees when nodes are being added or removed. It is a valuable optimization by itself
since tree updates are less common than property updates.

For the case of property updates, I have added the new flag "ChildrenAffectedByPropertyBasedBackwardPositionalRules".
It pretty much work the same way but invalidates subtree on style recalc.

Tests: fast/css/nth-child-and-nth-last-child.html
       fast/css/nth-last-child-of-classname.html
       fast/css/nth-last-child-of-complex-selector.html
       fast/css/nth-last-child-of-compound-selector.html
       fast/css/nth-last-child-of-style-sharing-1.html
       fast/css/nth-last-child-of-style-sharing-2.html
       fast/css/nth-last-child-of-style-update-optimization.html
       fast/css/nth-last-child-of-tagname.html
       fast/selectors/nth-last-child-of-class-style-update.html

* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne):
* dom/Element.cpp:
(WebCore::Element::setChildrenAffectedByPropertyBasedBackwardPositionalRules):
(WebCore::Element::hasFlagsSetDuringStylingOfChildren):
(WebCore::Element::rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules):
* dom/Element.h:
(WebCore::Element::childrenAffectedByPropertyBasedBackwardPositionalRules):
* dom/ElementRareData.h:
(WebCore::ElementRareData::childrenAffectedByPropertyBasedBackwardPositionalRules):
(WebCore::ElementRareData::setChildrenAffectedByPropertyBasedBackwardPositionalRules):
(WebCore::ElementRareData::ElementRareData):
(WebCore::ElementRareData::resetDynamicRestyleObservations):
* dom/Node.cpp:
(WebCore::Node::updateAncestorsForStyleRecalc):
(WebCore::Node::setNeedsStyleRecalc):
(WebCore::markAncestorsWithChildNeedsStyleRecalc): Deleted.
* dom/Node.h:
Unlike the other marking steps, ChildrenAffectedByPropertyBasedBackwardPositionalRules can only really
affect one level (because we don't have a parent-selectors). It is quite easy to mark large
trees with ChildrenAffectedByPropertyBasedBackwardPositionalRules by using a selector list that is a bit
too generic. When that happen, we must be careful not invalidating everything, just the affected subtree.

LayoutTests:

* fast/selectors/nth-last-child-of-class-style-update-expected.txt: Added.
* fast/selectors/nth-last-child-of-class-style-update.html: Added.
Verify simple cases of style update.

* fast/css/nth-last-child-of-style-update-optimization-expected.txt: Added.
* fast/css/nth-last-child-of-style-update-optimization.html: Added.
Verify that style invalidation stays reasonable.

* fast/css/nth-last-child-of-style-sharing-1-expected.html: Added.
* fast/css/nth-last-child-of-style-sharing-1.html: Added.
* fast/css/nth-last-child-of-style-sharing-2-expected.html: Added.
* fast/css/nth-last-child-of-style-sharing-2.html: Added.
Check that style sharing is disabled for elements matching :nth-last-child().

* fast/css/nth-child-and-nth-last-child-expected.html: Added.
* fast/css/nth-child-and-nth-last-child.html: Added.
Test uniqueness testing by combining :nth-child() and :nth-last-child().

* fast/css/nth-last-child-of-classname-expected.html: Added.
* fast/css/nth-last-child-of-classname.html: Added.
* fast/css/nth-last-child-of-complex-selector-expected.html: Added.
* fast/css/nth-last-child-of-complex-selector.html: Added.
* fast/css/nth-last-child-of-compound-selector-expected.html: Added.
* fast/css/nth-last-child-of-compound-selector.html: Added.
* fast/css/nth-last-child-of-tagname-expected.html: Added.
* fast/css/nth-last-child-of-tagname.html: Added.
Test matching, those are the "last-child" counterpart of the existing
"nth-child-of" tests.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (176083 => 176084)


--- trunk/LayoutTests/ChangeLog	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/LayoutTests/ChangeLog	2014-11-13 21:00:33 UTC (rev 176084)
@@ -1,3 +1,39 @@
+2014-11-13  Benjamin Poulain  <[email protected]>
+
+        Implement the matching for :nth-last-child(An+B of selector-list)
+        https://bugs.webkit.org/show_bug.cgi?id=138650
+
+        Reviewed by Andreas Kling.
+
+        * fast/selectors/nth-last-child-of-class-style-update-expected.txt: Added.
+        * fast/selectors/nth-last-child-of-class-style-update.html: Added.
+        Verify simple cases of style update.
+
+        * fast/css/nth-last-child-of-style-update-optimization-expected.txt: Added.
+        * fast/css/nth-last-child-of-style-update-optimization.html: Added.
+        Verify that style invalidation stays reasonable.
+
+        * fast/css/nth-last-child-of-style-sharing-1-expected.html: Added.
+        * fast/css/nth-last-child-of-style-sharing-1.html: Added.
+        * fast/css/nth-last-child-of-style-sharing-2-expected.html: Added.
+        * fast/css/nth-last-child-of-style-sharing-2.html: Added.
+        Check that style sharing is disabled for elements matching :nth-last-child().
+
+        * fast/css/nth-child-and-nth-last-child-expected.html: Added.
+        * fast/css/nth-child-and-nth-last-child.html: Added.
+        Test uniqueness testing by combining :nth-child() and :nth-last-child().
+
+        * fast/css/nth-last-child-of-classname-expected.html: Added.
+        * fast/css/nth-last-child-of-classname.html: Added.
+        * fast/css/nth-last-child-of-complex-selector-expected.html: Added.
+        * fast/css/nth-last-child-of-complex-selector.html: Added.
+        * fast/css/nth-last-child-of-compound-selector-expected.html: Added.
+        * fast/css/nth-last-child-of-compound-selector.html: Added.
+        * fast/css/nth-last-child-of-tagname-expected.html: Added.
+        * fast/css/nth-last-child-of-tagname.html: Added.
+        Test matching, those are the "last-child" counterpart of the existing
+        "nth-child-of" tests.
+
 2014-11-12  Jon Honeycutt  <[email protected]>
 
         REGRESSION (r172826): Password field placeholder text is missing if

Added: trunk/LayoutTests/fast/css/nth-child-and-nth-last-child-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-child-and-nth-last-child-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-child-and-nth-last-child-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    target {
+        background-color: red;
+    }
+
+    #success1, #success2, #success3 {
+        background-color: green;
+    }
+</style>
+</head>
+<body>
+    <p>Test the matching of an element only if it is the only child matching the selector list. If the test succeed, every instance of the text "Success" should be on green background.</p>
+    <div>
+        <div></div>
+        <target>Foo</target>
+        <div></div>
+        <target>Bar</target>
+        <div></div>
+    </div>
+    <div>
+        <div></div>
+        <div></div>
+        <target id="success1">Success</target>
+        <div></div>
+    </div>
+    <div>
+        <div></div>
+        <target>Foo</target>
+        <div></div>
+        <target>Bar</target>
+        <div></div>
+    </div>
+    <div>
+        <div></div>
+        <div></div>
+        <target id="success2">Success</target>
+        <div></div>
+    </div>
+    <div>
+        <div></div>
+        <target>Foo</target>
+        <div></div>
+        <target>Bar</target>
+        <div></div>
+    </div>
+    <div>
+        <div></div>
+        <div></div>
+        <target id="success3">Success</target>
+        <div></div>
+    </div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/LayoutTests/fast/css/nth-child-and-nth-last-child.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-child-and-nth-last-child.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-child-and-nth-last-child.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    target1 {
+        background-color: red;
+    }
+
+    :nth-child(1 of target1):nth-last-child(1 of target1) {
+        background-color: green;
+    }
+
+    .target {
+        background-color: red;
+    }
+
+    :nth-child(1 of .target):nth-last-child(1 of .target) {
+        background-color: green;
+    }
+
+    [data-target] {
+        background-color: red;
+    }
+
+    :nth-child(1 of [data-target]):nth-last-child(1 of [data-target]) {
+        background-color: green;
+    }
+</style>
+</head>
+<body>
+    <p>Test the matching of an element only if it is the only child matching the selector list. If the test succeed, every instance of the text "Success" should be on green background.</p>
+    <div>
+        <div></div>
+        <target1>Foo</target1>
+        <div></div>
+        <target1>Bar</target1>
+        <div></div>
+    </div>
+    <div>
+        <div></div>
+        <div></div>
+        <target1>Success</target1>
+        <div></div>
+    </div>
+    <div>
+        <div class="nottarget"></div>
+        <target class="target">Foo</target>
+        <div class="nottarget"></div>
+        <target class="target">Bar</target>
+        <div class="nottarget"></div>
+    </div>
+    <div>
+        <div class="nottarget"></div>
+        <div class="nottarget"></div>
+        <target class="target">Success</target>
+        <div class="nottarget"></div>
+    </div>
+    <div>
+        <div data-not-target></div>
+        <target data-target>Foo</target>
+        <div data-not-target></div>
+        <target data-target>Bar</target>
+        <div data-not-target></div>
+    </div>
+    <div>
+        <div data-not-target></div>
+        <div data-not-target></div>
+        <target data-target>Success</target>
+        <div data-not-target></div>
+    </div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/LayoutTests/fast/css/nth-last-child-of-classname-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-classname-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-classname-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+    <p>White</p>
+    <p>White</p>
+    <p style="background-color: lime">Green!</p>
+    <p>White</p>
+    <p>White</p>
+    <p>White</p>
+    <p>White</p>
+    <p style="background-color: lime">Green!</p>
+    <p>White</p>
+    <p>White</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-classname.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-classname.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-classname.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<head>
+<style>
+p:nth-last-child(even of .webkit, .fast) {
+    background-color: lime;
+}
+</style>
+</head>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+    <p>White</p>
+    <p>White</p>
+    <p class="webkit">Green!</p>
+    <p>White</p>
+    <p>White</p>
+    <p class="webkit">White</p>
+    <p>White</p>
+    <p class="fast">Green!</p>
+    <p>White</p>
+    <p class="webkit">White</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-complex-selector-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-complex-selector-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-complex-selector-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+    <p style="background-color: lime">Green</p>
+    <p>White</p>
+    <p>White</p>
+    <p>White</p>
+    <p style="background-color: lime">Green</p>
+    <p>White</p>
+    <p>White</p>
+    <p>White</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-complex-selector.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-complex-selector.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-complex-selector.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+<head>
+<style>
+p:nth-last-child(4n of html:root>body>p+p:not(empty), :not(*), p:not(p), span, .notthere) {
+    background-color: lime;
+}
+</style>
+</head>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+    <p>Green</p>
+    <p>White</p>
+    <p>White</p>
+    <p>White</p>
+    <p>Green</p>
+    <p>White</p>
+    <p>White</p>
+    <p>White</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-compound-selector-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-compound-selector-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-compound-selector-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+
+    <p>White!</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p style="background-color: lime">Green</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p>White!</p>
+    <p></p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-compound-selector.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-compound-selector.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-compound-selector.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+<head>
+<style>
+p:nth-last-child(3n+1 of p.foobar:not([class=foobar])) {
+    background-color: lime;
+}
+</style>
+</head>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+
+    <p>White!</p>
+    <p class="foo foobar baz">White!</p>
+    <p class="foobar">White!</p>
+    <p class="foo foobar">White!</p>
+    <p class="foobar">White!</p>
+    <p class="foobar foo">Green</p>
+    <p>White!</p>
+    <p class="foo foobar">White!</p>
+    <p>White!</p>
+    <p class="foobar foo">White!</p>
+    <p class="foobar">White!</p>
+    <p class="foobar foo"></p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-1-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-1-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-1-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+<body>
+    <p>Test that style-sharing does not ignore cases matching :nth-last-child(An+B of selector list).</p>
+    <p style="background-color: lime">Target</p>
+    <p>Target</p>
+    <p>Target</p>
+    <p style="background-color: lime">Target</p>
+    <p>Target</p>
+    <p>Target</p>
+    <p style="background-color: lime">Target</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-1.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-1.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-1.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<head>
+<style>
+:nth-last-child(3n+1 of .target) {
+    background-color: lime;
+}
+</style>
+</head>
+<body>
+    <p>Test that style-sharing does not ignore cases matching :nth-last-child(An+B of selector list).</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-2-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-2-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-2-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+    <p>Test that style-sharing does not ignore cases matching :nth-last-child(An+B of selector list).</p>
+    <p>Target</p>
+    <p>Target</p>
+    <p style="background-color: lime">Target</p>
+    <p>Target</p>
+    <p>Target</p>
+    <p style="background-color: lime">Target</p>
+    <p>Target</p>
+    <p>Target</p>
+    <p style="background-color: lime">Target</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-2.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-2.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-style-sharing-2.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+<head>
+<style>
+:nth-last-child(3n+1 of .target) {
+    background-color: lime;
+}
+</style>
+</head>
+<body>
+    <p>Test that style-sharing does not ignore cases matching :nth-last-child(An+B of selector list).</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+    <p class="target">Target</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-style-update-optimization-expected.txt (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-style-update-optimization-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-style-update-optimization-expected.txt	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,9 @@
+Verify that style invalidation of elements affected by :nth-last-child() no not cause an invalidation storm.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/css/nth-last-child-of-style-update-optimization.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-style-update-optimization.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-style-update-optimization.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+    background-color: rgb(0, 0, 0);
+}
+:nth-last-child(n of foo, bar, *, .target) {
+    background-color: rgb(255, 255, 255);
+}
+:root {
+    background-color: rgb(255, 255, 255);
+}
+</style>
+</head>
+<body>
+    <div class="test-case">
+        <parent id="parent1">
+            <target></target>
+            <target id="target1"></target>
+        </parent>
+    </div>
+    <div class="test-case" style="display:none;">
+        <parent id="parent2">
+            <target></target>
+            <target id="target2"></target>
+        </parent>
+    </div>
+</body>
+<script>
+description('Verify that style invalidation of elements affected by :nth-last-child() no not cause an invalidation storm.');
+
+// Force a layout.
+document.documentElement.offsetTop;
+
+// Nothing should need a style recalc.
+var allContent = document.querySelectorAll("*");
+for (var i = 0; i < allContent.length; ++i) {
+    if (internals.nodeNeedsStyleRecalc(allContent[i])) {
+        testFailed("Invalid initial state, node " + i + " has dirty style.");
+        break;
+    }
+}
+
+// Change the class of target1, this could change the order of rules and need to cause a style recalc.
+var target1 = document.getElementById('target1');
+target1.className = 'target';
+
+var allContent = document.querySelectorAll("*");
+for (var i = 0; i < allContent.length; ++i) {
+    var node = allContent[i];
+    if (internals.nodeNeedsStyleRecalc(node)) {
+        if (node !== target1 && node !== target1.parentNode) {
+            testFailed("Invalidating the style of target1 should never invalidate more than target1 and its parent.");
+            break;
+        }
+    }
+}
+
+document.documentElement.offsetTop;
+
+// Change the class of target1, this could change the order of rules and need to cause a style recalc.
+var target2 = document.getElementById('target2');
+target2.className = 'target';
+
+var allContent = document.querySelectorAll("*");
+for (var i = 0; i < allContent.length; ++i) {
+    var node = allContent[i];
+    if (internals.nodeNeedsStyleRecalc(node)) {
+        if (node !== target2 && node !== target2.parentNode) {
+            testFailed("Invalidating the style of target1 should never invalidate more than target1 and its parent.");
+            break;
+        }
+    }
+}
+</script>
+<script src=""
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-tagname-expected.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-tagname-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-tagname-expected.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+    <p>White!</p>
+    <webkit>White!</webkit>
+    <p>White!</p>
+    <webkit style="background-color: lime">Green</webkit>
+    <webkit>White!</webkit>
+    <p>White!</p>
+    <p>White!</p>
+    <fast style="background-color: lime">Green</fast>
+    <p>White!</p>
+    <p>White!</p>
+</body>
+</html>

Added: trunk/LayoutTests/fast/css/nth-last-child-of-tagname.html (0 => 176084)


--- trunk/LayoutTests/fast/css/nth-last-child-of-tagname.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css/nth-last-child-of-tagname.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<head>
+<style>
+:nth-last-child(odd of webkit, fast) {
+    background-color: lime;
+}
+</style>
+</head>
+<body>
+    <p>This test the styling of paragraphs with :nth-last-child(An+B of selector). If the test succeed, paragraphs that say "green" should have a green background.</p>
+    <p>White!</p>
+    <webkit>White!</webkit>
+    <p>White!</p>
+    <webkit>Green</webkit>
+    <webkit>White!</webkit>
+    <p>White!</p>
+    <p>White!</p>
+    <fast>Green</fast>
+    <p>White!</p>
+    <p>White!</p>
+</body>
+</html>

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


--- trunk/LayoutTests/fast/selectors/nth-last-child-of-class-style-update-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/nth-last-child-of-class-style-update-expected.txt	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,233 @@
+Test style update of :nth-last-child() when the tree structure is modified.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initial state.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Removing the .webkit class from the fifth element, nothing should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Adding it back, we should be back to the original state.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Removing the .webkit class from the third element, nothing should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Adding it back, we should be back to the original state.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Removing the .webkit class from the second element, nothing should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Appending we should have a match again, skipping 2.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Adding the .webkit class back on 2, we should now match 2 elements.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Removing the element we added should put us back in the initial state.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+Appending an element, we should match the first two targets again.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Appending an element <nottarget>, we should now match 1, 2, 3.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Appending an element <nottarget>, we should now match 1, 2, 3, 4.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Appending an element <target>, we should now match 1, 2, 3, 4 and 5.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[12]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[13]).backgroundColor is "rgb(255, 255, 255)"
+Removing one of the <nottarget>, 5 should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[12]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[13]).backgroundColor is "rgb(255, 255, 255)"
+Removing the class from one of the <nottarget>, 4 should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[12]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[13]).backgroundColor is "rgb(255, 255, 255)"
+Removing one of the <notarget>, 3 should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Removing the class from one of the <target>, 2 should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[11]).backgroundColor is "rgb(255, 255, 255)"
+Removing one of the <target>, nothing should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

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


--- trunk/LayoutTests/fast/selectors/nth-last-child-of-class-style-update.html	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/nth-last-child-of-class-style-update.html	2014-11-13 21:00:33 UTC (rev 176084)
@@ -0,0 +1,149 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<style>
+target {
+    background-color: white;
+}
+target:nth-last-child(n+5 of .webkit) {
+    background-color: rgb(1, 2, 3);
+}
+</style>
+</head>
+<body>
+    <div id="with-renderer">
+        <target class="element_1 webkit"></target>
+        <target class="element_2 webkit"></target>
+        <target class="element_3 webkit"></target>
+        <target class="element_4 webkit"></target>
+        <target class="element_5 webkit"></target>
+    </div>
+    <div id="without-renderer" style="display:none;">
+        <target class="element_1 webkit"></target>
+        <target class="element_2 webkit"></target>
+        <target class="element_3 webkit"></target>
+        <target class="element_4 webkit"></target>
+        <target class="element_5 webkit"></target>
+    </div>
+</body>
+<script>
+description('Test style update of :nth-last-child() when the tree structure is modified.');
+
+function testColor(classesThatShouldMatch) {
+    var allTargets = document.querySelectorAll("target");
+    for (var i = 0; i < allTargets.length; ++i) {
+        var expectMath = classesThatShouldMatch.indexOf(allTargets[i].classList[0]) != -1;
+        shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[' + i + ']).backgroundColor', expectMath ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    }
+}
+
+function appendElement(tagName, className)
+{
+    var newElement = document.createElement(tagName);
+    newElement.className = className + ' webkit';
+
+    var withRenderer = document.getElementById("with-renderer");
+    withRenderer.appendChild(newElement);
+
+    var withoutRenderer = document.getElementById("without-renderer");
+    withoutRenderer.appendChild(newElement.cloneNode());
+}
+
+function removeWebKitClassFromElementsOfClass(className)
+{
+    var allElementsToUpdate = document.querySelectorAll('.' + className);
+    for (var i = 0; i < allElementsToUpdate.length; ++i)
+        allElementsToUpdate[i].classList.remove("webkit");
+}
+
+function addWebKitClassToElementsOfClass(className)
+{
+    var allElementsToUpdate = document.querySelectorAll('.' + className);
+    for (var i = 0; i < allElementsToUpdate.length; ++i)
+        allElementsToUpdate[i].classList.add("webkit");
+}
+
+
+function removeElementsOfClass(className)
+{
+    var allElementsToRemove = document.querySelectorAll('.' + className);
+    for (var i = 0; i < allElementsToRemove.length; ++i)
+        allElementsToRemove[i].parentElement.removeChild(allElementsToRemove[i]);
+}
+
+debug("Initial state.");
+testColor(["element_1"]);
+
+debug("Removing the .webkit class from the fifth element, nothing should match.");
+removeWebKitClassFromElementsOfClass("element_5");
+testColor([]);
+
+debug("Adding it back, we should be back to the original state.");
+addWebKitClassToElementsOfClass("element_5");
+testColor(["element_1"]);
+
+debug("Removing the .webkit class from the third element, nothing should match.");
+removeWebKitClassFromElementsOfClass("element_3");
+testColor([]);
+
+debug("Adding it back, we should be back to the original state.");
+addWebKitClassToElementsOfClass("element_3");
+testColor(["element_1"]);
+
+debug("Removing the .webkit class from the second element, nothing should match.");
+removeWebKitClassFromElementsOfClass("element_2");
+testColor([]);
+
+debug("Appending we should have a match again, skipping 2.");
+appendElement("target", "element_6")
+testColor(["element_1"]);
+
+debug("Adding the .webkit class back on 2, we should now match 2 elements.");
+addWebKitClassToElementsOfClass("element_2");
+testColor(["element_1", "element_2"]);
+
+debug("Removing the element we added should put us back in the initial state.");
+removeElementsOfClass("element_6");
+testColor(["element_1"]);
+
+debug("Appending an element, we should match the first two targets again.");
+appendElement("target", "element_6")
+testColor(["element_1", "element_2"]);
+
+// Using nottarget is interesting because the ':nth-last-child()' part is not matched for those elements.
+debug("Appending an element &lt;nottarget&gt;, we should now match 1, 2, 3.");
+appendElement("nottarget", "element_-1")
+testColor(["element_1", "element_2", "element_3"]);
+
+debug("Appending an element &lt;nottarget&gt;, we should now match 1, 2, 3, 4.");
+appendElement("nottarget", "element_-2")
+testColor(["element_1", "element_2", "element_3", "element_4"]);
+
+debug("Appending an element &lt;target&gt;, we should now match 1, 2, 3, 4 and 5.");
+appendElement("target", "element_-3")
+testColor(["element_1", "element_2", "element_3", "element_4", "element_5"]);
+
+debug("Removing one of the &lt;nottarget&gt;, 5 should no longer match.");
+removeElementsOfClass("element_-2");
+testColor(["element_1", "element_2", "element_3", "element_4"]);
+
+debug("Removing the class from one of the &lt;nottarget&gt;, 4 should no longer match.");
+removeWebKitClassFromElementsOfClass("element_-1");
+testColor(["element_1", "element_2", "element_3"]);
+
+debug("Removing one of the &lt;notarget&gt;, 3 should no longer match.");
+removeElementsOfClass("element_-3");
+testColor(["element_1", "element_2"]);
+
+debug("Removing the class from one of the &lt;target&gt;, 2 should no longer match.");
+removeWebKitClassFromElementsOfClass("element_3");
+testColor(["element_1"]);
+
+debug("Removing one of the &lt;target&gt;, nothing should match.");
+removeElementsOfClass("element_4");
+testColor([]);
+
+</script>
+<script src=""
+</html>

Modified: trunk/Source/WebCore/ChangeLog (176083 => 176084)


--- trunk/Source/WebCore/ChangeLog	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/ChangeLog	2014-11-13 21:00:33 UTC (rev 176084)
@@ -1,3 +1,55 @@
+2014-11-13  Benjamin Poulain  <[email protected]>
+
+        Implement the matching for :nth-last-child(An+B of selector-list)
+        https://bugs.webkit.org/show_bug.cgi?id=138650
+
+        Reviewed by Andreas Kling.
+
+        Matching is trivial based on the existing CSS4 infrastructure.
+
+        The tricky part is style invalidation. In the cases of the old :nth-last-child()
+        and :nth-last-of-type(), we were using the flag ChildrenAffectedByBackwardPositionalRules.
+
+        The problem with ChildrenAffectedByBackwardPositionalRules is that it only invalidate
+        subtrees when nodes are being added or removed. It is a valuable optimization by itself
+        since tree updates are less common than property updates.
+
+        For the case of property updates, I have added the new flag "ChildrenAffectedByPropertyBasedBackwardPositionalRules".
+        It pretty much work the same way but invalidates subtree on style recalc.
+
+        Tests: fast/css/nth-child-and-nth-last-child.html
+               fast/css/nth-last-child-of-classname.html
+               fast/css/nth-last-child-of-complex-selector.html
+               fast/css/nth-last-child-of-compound-selector.html
+               fast/css/nth-last-child-of-style-sharing-1.html
+               fast/css/nth-last-child-of-style-sharing-2.html
+               fast/css/nth-last-child-of-style-update-optimization.html
+               fast/css/nth-last-child-of-tagname.html
+               fast/selectors/nth-last-child-of-class-style-update.html
+
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOne):
+        * dom/Element.cpp:
+        (WebCore::Element::setChildrenAffectedByPropertyBasedBackwardPositionalRules):
+        (WebCore::Element::hasFlagsSetDuringStylingOfChildren):
+        (WebCore::Element::rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules):
+        * dom/Element.h:
+        (WebCore::Element::childrenAffectedByPropertyBasedBackwardPositionalRules):
+        * dom/ElementRareData.h:
+        (WebCore::ElementRareData::childrenAffectedByPropertyBasedBackwardPositionalRules):
+        (WebCore::ElementRareData::setChildrenAffectedByPropertyBasedBackwardPositionalRules):
+        (WebCore::ElementRareData::ElementRareData):
+        (WebCore::ElementRareData::resetDynamicRestyleObservations):
+        * dom/Node.cpp:
+        (WebCore::Node::updateAncestorsForStyleRecalc):
+        (WebCore::Node::setNeedsStyleRecalc):
+        (WebCore::markAncestorsWithChildNeedsStyleRecalc): Deleted.
+        * dom/Node.h:
+        Unlike the other marking steps, ChildrenAffectedByPropertyBasedBackwardPositionalRules can only really
+        affect one level (because we don't have a parent-selectors). It is quite easy to mark large
+        trees with ChildrenAffectedByPropertyBasedBackwardPositionalRules by using a selector list that is a bit
+        too generic. When that happen, we must be careful not invalidating everything, just the affected subtree.
+
 2014-11-12  Jon Honeycutt  <[email protected]>
 
         REGRESSION (r172826): Password field placeholder text is missing if

Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (176083 => 176084)


--- trunk/Source/WebCore/css/SelectorChecker.cpp	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp	2014-11-13 21:00:33 UTC (rev 176084)
@@ -778,11 +778,36 @@
             if (!selector->parseNth())
                 break;
             if (Element* parentElement = element->parentElement()) {
+                if (!parentElement->isFinishedParsingChildren())
+                    return false;
+
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+                if (const CSSSelectorList* selectorList = selector->selectorList()) {
+                    if (!matchSelectorList(context, *element, *selectorList))
+                        return false;
+
+                    if (context.resolvingMode == Mode::ResolvingStyle) {
+                        parentElement->setChildrenAffectedByPropertyBasedBackwardPositionalRules();
+                        parentElement->setChildrenAffectedByBackwardPositionalRules();
+                    }
+                } else
+#endif
                 if (context.resolvingMode == Mode::ResolvingStyle)
                     parentElement->setChildrenAffectedByBackwardPositionalRules();
-                if (!parentElement->isFinishedParsingChildren())
-                    return false;
-                int count = 1 + countElementsAfter(element);
+
+                int count = 1;
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+                if (const CSSSelectorList* selectorList = selector->selectorList()) {
+                    for (Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling)) {
+                        if (matchSelectorList(context, *sibling, *selectorList))
+                            ++count;
+                    }
+                } else
+#endif
+                {
+                    count += countElementsAfter(element);
+                }
+
                 if (selector->matchNth(count))
                     return true;
             }

Modified: trunk/Source/WebCore/dom/Element.cpp (176083 => 176084)


--- trunk/Source/WebCore/dom/Element.cpp	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/dom/Element.cpp	2014-11-13 21:00:33 UTC (rev 176084)
@@ -2126,6 +2126,11 @@
     ensureElementRareData().setChildrenAffectedByBackwardPositionalRules(true);
 }
 
+void Element::setChildrenAffectedByPropertyBasedBackwardPositionalRules()
+{
+    ensureElementRareData().setChildrenAffectedByPropertyBasedBackwardPositionalRules(true);
+}
+
 void Element::setChildIndex(unsigned index)
 {
     ElementRareData& rareData = ensureElementRareData();
@@ -2143,7 +2148,8 @@
         return false;
     return rareDataChildrenAffectedByActive()
         || rareDataChildrenAffectedByDrag()
-        || rareDataChildrenAffectedByBackwardPositionalRules();
+        || rareDataChildrenAffectedByBackwardPositionalRules()
+        || rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules();
 }
 
 bool Element::rareDataStyleAffectedByEmpty() const
@@ -2170,6 +2176,12 @@
     return elementRareData()->childrenAffectedByBackwardPositionalRules();
 }
 
+bool Element::rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules() const
+{
+    ASSERT(hasRareData());
+    return elementRareData()->childrenAffectedByPropertyBasedBackwardPositionalRules();
+}
+
 unsigned Element::rareDataChildIndex() const
 {
     ASSERT(hasRareData());

Modified: trunk/Source/WebCore/dom/Element.h (176083 => 176084)


--- trunk/Source/WebCore/dom/Element.h	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/dom/Element.h	2014-11-13 21:00:33 UTC (rev 176084)
@@ -3,7 +3,7 @@
  *           (C) 1999 Antti Koivisto ([email protected])
  *           (C) 2001 Peter Kelly ([email protected])
  *           (C) 2001 Dirk Mueller ([email protected])
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 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
@@ -351,6 +351,7 @@
     bool childrenAffectedByFirstChildRules() const { return getFlag(ChildrenAffectedByFirstChildRulesFlag); }
     bool childrenAffectedByLastChildRules() const { return getFlag(ChildrenAffectedByLastChildRulesFlag); }
     bool childrenAffectedByBackwardPositionalRules() const { return hasRareData() && rareDataChildrenAffectedByBackwardPositionalRules(); }
+    bool childrenAffectedByPropertyBasedBackwardPositionalRules() const { return hasRareData() && rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules(); }
     bool affectsNextSiblingElementStyle() const { return getFlag(AffectsNextSiblingElementStyle); }
     unsigned childIndex() const { return hasRareData() ? rareDataChildIndex() : 0; }
 
@@ -363,6 +364,7 @@
     void setChildrenAffectedByFirstChildRules() { setFlag(ChildrenAffectedByFirstChildRulesFlag); }
     void setChildrenAffectedByLastChildRules() { setFlag(ChildrenAffectedByLastChildRulesFlag); }
     void setChildrenAffectedByBackwardPositionalRules();
+    void setChildrenAffectedByPropertyBasedBackwardPositionalRules();
     void setAffectsNextSiblingElementStyle() { setFlag(AffectsNextSiblingElementStyle); }
     void setStyleIsAffectedByPreviousSibling() { setFlag(StyleIsAffectedByPreviousSibling); }
     void setChildIndex(unsigned);
@@ -633,6 +635,7 @@
     bool rareDataChildrenAffectedByDrag() const;
     bool rareDataChildrenAffectedByLastChildRules() const;
     bool rareDataChildrenAffectedByBackwardPositionalRules() const;
+    bool rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules() const;
     unsigned rareDataChildIndex() const;
 
     SpellcheckAttributeState spellcheckAttributeState() const;

Modified: trunk/Source/WebCore/dom/ElementRareData.h (176083 => 176084)


--- trunk/Source/WebCore/dom/ElementRareData.h	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/dom/ElementRareData.h	2014-11-13 21:00:33 UTC (rev 176084)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2008 David Smith <[email protected]>
  *
  * This library is free software; you can redistribute it and/or
@@ -76,6 +76,8 @@
     void setChildrenAffectedByLastChildRules(bool value) { m_childrenAffectedByLastChildRules = value; }
     bool childrenAffectedByBackwardPositionalRules() const { return m_childrenAffectedByBackwardPositionalRules; }
     void setChildrenAffectedByBackwardPositionalRules(bool value) { m_childrenAffectedByBackwardPositionalRules = value; }
+    bool childrenAffectedByPropertyBasedBackwardPositionalRules() const { return m_childrenAffectedByPropertyBasedBackwardPositionalRules; }
+    void setChildrenAffectedByPropertyBasedBackwardPositionalRules(bool value) { m_childrenAffectedByPropertyBasedBackwardPositionalRules = value; }
 
     unsigned childIndex() const { return m_childIndex; }
     void setChildIndex(unsigned index) { m_childIndex = index; }
@@ -130,6 +132,7 @@
     // *-child-of-type, we will just give up and re-evaluate whenever children change at all.
     unsigned m_childrenAffectedByLastChildRules : 1;
     unsigned m_childrenAffectedByBackwardPositionalRules : 1;
+    unsigned m_childrenAffectedByPropertyBasedBackwardPositionalRules : 1;
 
     RegionOversetState m_regionOversetState;
 
@@ -169,6 +172,7 @@
     , m_childrenAffectedByDrag(false)
     , m_childrenAffectedByLastChildRules(false)
     , m_childrenAffectedByBackwardPositionalRules(false)
+    , m_childrenAffectedByPropertyBasedBackwardPositionalRules(false)
     , m_regionOversetState(RegionUndefined)
     , m_minimumSizeForResizing(defaultMinimumSizeForResizing())
 {
@@ -206,6 +210,7 @@
     setChildrenAffectedByDrag(false);
     setChildrenAffectedByLastChildRules(false);
     setChildrenAffectedByBackwardPositionalRules(false);
+    setChildrenAffectedByPropertyBasedBackwardPositionalRules(false);
 }
 
 } // namespace

Modified: trunk/Source/WebCore/dom/Node.cpp (176083 => 176084)


--- trunk/Source/WebCore/dom/Node.cpp	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/dom/Node.cpp	2014-11-13 21:00:33 UTC (rev 176084)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll ([email protected])
  *           (C) 1999 Antti Koivisto ([email protected])
  *           (C) 2001 Dirk Mueller ([email protected])
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
  *
@@ -670,16 +670,21 @@
     deref();
 }
 
-static inline void markAncestorsWithChildNeedsStyleRecalc(Node& node)
+inline void Node::updateAncestorsForStyleRecalc()
 {
-    if (ContainerNode* ancestor = is<PseudoElement>(node) ? downcast<PseudoElement>(node).hostElement() : node.parentOrShadowHostNode()) {
+    if (ContainerNode* ancestor = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentOrShadowHostNode()) {
         ancestor->setDirectChildNeedsStyleRecalc();
 
+        if (is<Element>(*ancestor) && downcast<Element>(*ancestor).childrenAffectedByPropertyBasedBackwardPositionalRules()) {
+            if (ancestor->styleChangeType() < FullStyleChange)
+                ancestor->setStyleChange(FullStyleChange);
+        }
+
         for (; ancestor && !ancestor->childNeedsStyleRecalc(); ancestor = ancestor->parentOrShadowHostNode())
             ancestor->setChildNeedsStyleRecalc();
     }
 
-    Document& document = node.document();
+    Document& document = this->document();
     if (document.childNeedsStyleRecalc())
         document.scheduleStyleRecalc();
 }
@@ -695,7 +700,7 @@
         setStyleChange(changeType);
 
     if (existingChangeType == NoStyleChange || changeType == ReconstructRenderTree)
-        markAncestorsWithChildNeedsStyleRecalc(*this);
+        updateAncestorsForStyleRecalc();
 }
 
 unsigned Node::computeNodeIndex() const

Modified: trunk/Source/WebCore/dom/Node.h (176083 => 176084)


--- trunk/Source/WebCore/dom/Node.h	2014-11-13 20:43:32 UTC (rev 176083)
+++ trunk/Source/WebCore/dom/Node.h	2014-11-13 21:00:33 UTC (rev 176084)
@@ -667,6 +667,7 @@
     void setTreeScope(TreeScope& scope) { m_treeScope = &scope; }
 
     void setStyleChange(StyleChangeType changeType) { m_nodeFlags = (m_nodeFlags & ~StyleChangeMask) | changeType; }
+    void updateAncestorsForStyleRecalc();
 
 private:
     friend class TreeShared<Node>;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to