Diff
Modified: trunk/LayoutTests/ChangeLog (176306 => 176307)
--- trunk/LayoutTests/ChangeLog 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/LayoutTests/ChangeLog 2014-11-19 05:12:53 UTC (rev 176307)
@@ -1,3 +1,31 @@
+2014-11-18 Benjamin Poulain <[email protected]>
+
+ Add the initial implementation of dynamic specificity for :matches()
+ https://bugs.webkit.org/show_bug.cgi?id=138822
+
+ Reviewed by Andreas Kling.
+
+ * fast/css/matches-specificity-1-expected.html: Added.
+ * fast/css/matches-specificity-1.html: Added.
+ * fast/css/matches-specificity-2-expected.html: Added.
+ * fast/css/matches-specificity-2.html: Added.
+ * fast/css/matches-specificity-3-expected.html: Added.
+ * fast/css/matches-specificity-3.html: Added.
+ * fast/css/matches-specificity-4-expected.html: Added.
+ * fast/css/matches-specificity-4.html: Added.
+ * fast/css/matches-specificity-5-expected.html: Added.
+ * fast/css/matches-specificity-5.html: Added.
+ * fast/css/matches-specificity-6-expected.html: Added.
+ * fast/css/matches-specificity-6.html: Added.
+ * fast/css/matches-specificity-7-expected.html: Added.
+ * fast/css/matches-specificity-7.html: Added.
+ * fast/css/matches-specificity-8-expected.html: Added.
+ * fast/css/matches-specificity-8.html: Added.
+ * fast/css/matches-specificity-9-expected.html: Added.
+ * fast/css/matches-specificity-9.html: Added.
+ * fast/css/matches-specificity-10-expected.html: Added.
+ * fast/css/matches-specificity-10.html: Added.
+
2014-11-18 Chris Dumez <[email protected]>
Crash when setting 'z-index' / 'flex-shrink' CSS properties to a calculated value
Added: trunk/LayoutTests/fast/css/matches-specificity-1-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-1-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-1-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ * {
+ background-color: white;
+ color: black;
+ }
+</style>
+</head>
+<body>
+ <p>This test the specificity of :matches() with static specificity. The test passes if the text "target" is displayed white on green background.</p>
+ <target style="background-color: green; color: white;">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-1.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-1.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-1.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ /* The following 3 rules should all have the same specificity when matching <target>. They should be be applied in order. */
+ foo, bar, target {
+ background-color: red;
+ color: red;
+ }
+ :matches(foo, bar, target) {
+ background-color: green;
+ color: blue;
+ }
+ foo, bar, target {
+ color: white;
+ }
+ * {
+ background-color: white;
+ color: black;
+ }
+</style>
+</head>
+<body>
+ <p>This test the specificity of :matches() with static specificity. The test passes if the text "target" is displayed white on green background.</p>
+ <target>Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-10-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-10-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-10-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,84 @@
+<!doctype html>
+<html>
+<body>
+ <p>This test the dynamic specificity of :matches() with a simple descendant backtracking case. The test pass if each block has style matching its description. None of the result should have a border.</p>
+
+ <foo id="b">
+ <foo id="c">
+ <e>
+ <padding>
+ <b>
+ <d>
+ <e>
+ <padding>
+ <aBlock>
+ <c>
+ <foo id="f">
+ <padding>
+ <target style="color: white; background-color: green;">White text on green background.</target>
+ </padding>
+ </foo>
+ </c>
+ </aBlock>
+ </padding>
+ </e>
+ </d>
+ </b>
+ <padding>
+ </e>
+ </foo>
+ </foo>
+
+ <foo id="b">
+ <d>
+ <e>
+ <padding>
+ <b>
+ <d>
+ <e>
+ <padding>
+ <aBlock>
+ <c>
+ <foo id="f">
+ <padding>
+ <target style="color: white; background-color: purple;">White text on purple background.</target>
+ </padding>
+ </foo>
+ </c>
+ </aBlock>
+ </padding>
+ </e>
+ </d>
+ </b>
+ <padding>
+ </e>
+ </d>
+ </foo>
+
+ <aBlock>
+ <d>
+ <e>
+ <padding>
+ <b>
+ <d>
+ <e>
+ <padding>
+ <a>
+ <c>
+ <foo id="f">
+ <padding>
+ <target style="color: green; background-color: purple;">Green text on purple background.</target>
+ </padding>
+ </foo>
+ </c>
+ </a>
+ </padding>
+ </e>
+ </d>
+ </b>
+ <padding>
+ </e>
+ </d>
+ </aBlock>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-10.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-10.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-10.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,129 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ /* Specificity (2, 0, 2). */
+ #f#f > padding > target {
+ background-color: purple;
+ }
+
+ /* Specificity (1, 0, 2). */
+ #f > padding > target {
+ background-color: red;
+ color: green;
+ }
+
+ /* Specificity (0, 0, 5) */
+ padding padding foo padding target {
+ color: green;
+ }
+
+ /* Specificity (0, 0, 4) */
+ padding padding padding target {
+ border: 5px blue solid;
+ }
+
+ :matches(aBlock, #b) > :matches(#c, d) > :matches(e, #f) target {
+ background-color: green;
+ color: white;
+ border: none;
+ }
+</style>
+</head>
+<body>
+ <p>This test the dynamic specificity of :matches() with a simple descendant backtracking case. The test pass if each block has style matching its description. None of the result should have a border.</p>
+
+ <!--
+ This case should match ":matches(a, #b) > :matches(#c, d) > matches(e, #f) target"
+ like "#a > #c > e target"
+ with the specificity (2, 0, 2).
+ -->
+ <foo id="b">
+ <foo id="c">
+ <e> <!-- Success case from here. -->
+ <padding>
+ <b> <!-- Fail here as we need <aBlock> or #b -->
+ <d>
+ <e>
+ <padding>
+ <aBlock>
+ <c> <!-- Fail here as we need <d> or #c -->
+ <foo id="f">
+ <padding>
+ <target>White text on green background.</target>
+ </padding>
+ </foo>
+ </c>
+ </aBlock>
+ </padding>
+ </e>
+ </d>
+ </b>
+ <padding>
+ </e>
+ </foo>
+ </foo>
+
+ <!--
+ This case should match ":matches(a, #b) > :matches(#c, d) > matches(e, #f) target"
+ like "#a > d > e target"
+ with the specificity (1, 0, 3).
+ -->
+ <foo id="b">
+ <d>
+ <e> <!-- Success case from here. -->
+ <padding>
+ <b> <!-- Fail here as we need <aBlock> or #b -->
+ <d>
+ <e>
+ <padding>
+ <aBlock>
+ <c> <!-- Fail here as we need <d> or #c -->
+ <foo id="f">
+ <padding>
+ <target>White text on purple background.</target>
+ </padding>
+ </foo>
+ </c>
+ </aBlock>
+ </padding>
+ </e>
+ </d>
+ </b>
+ <padding>
+ </e>
+ </d>
+ </foo>
+
+ <!--
+ This case should match ":matches(a, #b) > :matches(#c, d) > matches(e, #f) target"
+ like "a > d > e target"
+ with the specificity (0, 0, 4).
+ -->
+ <aBlock>
+ <d>
+ <e> <!-- Success case from here. -->
+ <padding>
+ <b> <!-- Fail here as we need <aBlock> or #b -->
+ <d>
+ <e>
+ <padding>
+ <a>
+ <c> <!-- Fail here as we need <d> or #c -->
+ <foo id="f">
+ <padding>
+ <target>Green text on purple background.</target>
+ </padding>
+ </foo>
+ </c>
+ </a>
+ </padding>
+ </e>
+ </d>
+ </b>
+ <padding>
+ </e>
+ </d>
+ </aBlock>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-2-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-2-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-2-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ * {
+ border: none;
+ }
+</style>
+</head>
+<body>
+ <p>This test the specificity of :matches() with a dynamic specificity of zero. The test passes if the text "target" is displayed white on green background.</p>
+ <target style="background-color: green; color: white;">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-2.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-2.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-2.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ * {
+ background-color: blue;
+ color: black;
+ border: 5px solid black;
+ }
+ html, body, p {
+ background-color: white;
+ color: black;
+ }
+ target {
+ background-color: green;
+ }
+
+ /* The only dynamic selector that matches is "*". Every rules except the first one (* selector) should override the style defined here.
+ Background-color is defined by the two tag selectors above.
+ Color is defined below by the star rule. It has the same specificity but a higher position.
+ Border overrides the black border defined in the first rule. */
+ :matches(#foo, .bar, target.notthere, *, foo>bar) {
+ background-color: red;
+ color: blue;
+ border: none;
+ }
+ * {
+ color: white;
+ }
+</style>
+</head>
+<body>
+ <p>This test the specificity of :matches() with a dynamic specificity of zero. The test passes if the text "target" is displayed white on green background.</p>
+ <target>Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-3-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-3-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-3-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ body > * {
+ display: block;
+ }
+ html, body, p {
+ background-color: white;
+ color: black;
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the dynamic specificity of matches changing the order in which rules are applied depending on the element. Each element should have the colors described in the text and no border.</p>
+ <nottarget style="color: purple; background-color: red;">Purple text on red background.</nottarget>
+ <target style="color: white; background-color: green;">White text on green background.</target>
+ <nottarget style="color: purple; background-color: red;">Purple text on red background.</nottarget>
+ <target style="color: purple; background-color: red;">Purple text on red background.</target>
+ <nottarget style="color: purple; background-color: blue;">Purple text on blue background.</nottarget>
+ <target style="color: purple; background-color: blue;">Purple text on blue background.</target>
+ <nottarget style="color: purple; background-color: red;">Purple text on red background.</nottarget>
+ <target style="color: purple; background-color: red;">Purple text on red background.</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-3.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-3.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-3.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,46 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ * {
+ background-color: blue;
+ color: blue;
+ border: 5px solid black;
+ }
+
+ /* The following :matches() gets a different class of specificity. Depending on what
+ matches, the later rules may override this or not. */
+ :matches(.classB, target, #classA, *) {
+ background-color: red;
+ color: purple;
+ border: none;
+ }
+
+ target {
+ background-color: green;
+ color: white;
+ }
+ .classB {
+ background-color: blue;
+ }
+ body > * {
+ display: block;
+ }
+ html, body, p {
+ background-color: white;
+ color: black;
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the dynamic specificity of matches changing the order in which rules are applied depending on the element. Each element should have the colors described in the text and no border.</p>
+ <nottarget>Purple text on red background.</nottarget>
+ <target>White text on green background.</target>
+ <nottarget id="classA">Purple text on red background.</nottarget>
+ <target id="classA">Purple text on red background.</target>
+ <nottarget class="classB">Purple text on blue background.</nottarget>
+ <target class="classB">Purple text on blue background.</target>
+ <nottarget id="classA" class="classB">Purple text on red background.</nottarget>
+ <target id="classA" class="classB">Purple text on red background.</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-4-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-4-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-4-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<body>
+ <p>This test the dynamic specificity of :matches() with a complex selector. The text succeed if the text "Target" shows up in white on green background.</p>
+ <container>
+ <target style="background-color: green; color: white;">Target</target>
+ </container>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-4.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-4.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-4.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,44 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ /* The position of :matches() should override this rule. */
+ container > target {
+ border: 5px solid black;
+ color: blue;
+ }
+
+ /* A class rule should supersede the following :matches() because it has higher specificity. */
+ .target {
+ color: white;
+ }
+
+ /* The only subselector that matches is "container > target". It should override the simple "target"
+ selector but be superseded by anything larger than two tagnames. */
+ :matches(foo.bar, bar.foo, container > target, foo#bar, bar#foo) {
+ background-color: green;
+ color: purple;
+ border: 25px solid red;
+ }
+
+ /* Those two rules only have score one tagname, they cannot take precedence over :matches(). */
+ target {
+ background-color: red;
+ }
+ container > * {
+ color: black;
+ }
+
+ /* This rule should have the same specificity as the :matches() but higher position. */
+ container > target {
+ border: none;
+ }
+</style>
+</head>
+<body>
+ <p>This test the dynamic specificity of :matches() with a complex selector. The text succeed if the text "Target" shows up in white on green background.</p>
+ <container>
+ <target class="target">Target</target>
+ </container>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-5-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-5-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-5-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ target::before {
+ content: "Target";
+ background-color: green;
+ color: white;
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the specificity of the rules inside :matches() applying to the pseudo element ::before. The test succeed if the text "Target" is displayed in white on a green background.</p>
+ <target></target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-5.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-5.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-5.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ .target::before {
+ content: "Target";
+ }
+
+ /* This rule has two .target, making it higher specificity than the rules below. */
+ .target:matches(::before, target::before, .target::before, ::before, target::before) {
+ background-color: green;
+ }
+
+ /* This rule has a tagname and a class name, making it higher specificity than ".target:matches(::before)". */
+ target:matches(::before, target::before, .target::before, ::before, target::before) {
+ color: white;
+ }
+
+ .target:matches(::before) {
+ background-color: red;
+ color: blue;
+ border: none;
+ }
+
+ /* This rule only has (0, 0, 3) and it superseded by the rule above. */
+ target:matches(::before, target::before, ::before) {
+ border: 25px solid purple;
+ }
+
+ /* This rule has lower specificity than all the rules above and its property never applies. */
+ :matches(#target::before, target::before) {
+ content: "Not Target";
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the specificity of the rules inside :matches() applying to the pseudo element ::before. The test succeed if the text "Target" is displayed in white on a green background.</p>
+ <target class="target"></target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-6-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-6-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-6-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ target::before {
+ content: "Before ";
+ background-color: green;
+ color: white;
+ }
+ target {
+ background-color: green;
+ color: white;
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the dynamic specificity of rules mixing pseudo element and regular element matching. If the text succeed, the text should be "Before Target" in white over a green background and no borders.</p>
+ <target>Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-6.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-6.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-6.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ .target::before {
+ content: "Before ";
+ }
+
+ target:matches(.target::before, #target::before, #target) {
+ border: none;
+ }
+
+ target:matches(.target::before, #target::before, .target) {
+ color: white;
+ }
+
+ target:matches(.target::before, #target::before, target) {
+ background-color: green;
+ }
+
+ target:matches(.target::before, target::before, *) {
+ background-color: red;
+ color: blue;
+ border: 25px solid red;
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the dynamic specificity of rules mixing pseudo element and regular element matching. If the text succeed, the text should be "Before Target" in white over a green background and no borders.</p>
+ <target id="target" class="target">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-7-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-7-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-7-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,7 @@
+<!doctype html>
+<html>
+<body>
+ <p>This test checks the dynamic specificity of :matches() with compound selectors. The test succeed is the text "Target" appears on in white over a green background.</p>
+ <target style="background-color: green; color: white; border: none;">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-7.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-7.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-7.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ /* The most specific selector is target#target.target with a specificity of (1, 1, 1) */
+ :matches(target, target.target, #target, .target, target#target.target, #target.target, target#target) {
+ background-color: green;
+ }
+
+ /* This selector has a specificity of (1, 1, 0) but the selector below has a higher position. */
+ #target.target:matches(*) {
+ background-color: red;
+ color: blue;
+ }
+
+ /* The most specific selector is #target.target with a specificity of (1, 1, 0) */
+ :matches(target, target.target, #target, .target, #target.target, target#target) {
+ background-color: red;
+ color: white;
+ }
+
+ /* This selector has a specificity of (1, 0, 1) but the selector below has a higher position. */
+ target#target:not(:not(*, matches(*, *))) {
+ background-color: red;
+ color: blue;
+ border: 25px solid purple;
+ }
+
+ /* The most specific selector is target#target with a specificity of (1, 0, 1) */
+ :matches(target, target.target, #target, .target, target#target) {
+ background-color: red;
+ color: blue;
+ border: none;
+ }
+</style>
+</head>
+<body>
+ <p>This test checks the dynamic specificity of :matches() with compound selectors. The test succeed is the text "Target" appears on in white over a green background.</p>
+ <target id="target" class="target">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-8-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-8-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-8-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,7 @@
+<!doctype html>
+<html>
+<body>
+ <p>This test the specificity of simple nested :matches() selectors. The test succeed is the text "Target" appears on in white over a green background.</p>
+ <target style="background-color: green; color: white; border: none;">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-8.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-8.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-8.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ /* The most specific selector is target#target.target with a specificity of (1, 1, 1) */
+ :matches(foo, :matches(target, :matches(target.target, :matches(#target, .target, target#target.target), #target.target), target#target), bar) {
+ background-color: green;
+ }
+
+ /* This selector has a specificity of (1, 1, 0) but the selector below has a higher position. */
+ #target.target:matches(*) {
+ background-color: red;
+ color: blue;
+ }
+
+ /* The most specific selector is #target.target with a specificity of (1, 1, 0) */
+ :matches(target, :matches(target.target, :matches(#target, .target), #target.target), target#target) {
+ background-color: red;
+ color: white;
+ }
+
+ /* This selector has a specificity of (1, 0, 1) but the selector below has a higher position. */
+ target#target:not(:not(*, matches(*, *))) {
+ background-color: red;
+ color: blue;
+ border: 25px solid purple;
+ }
+
+ /* The most specific selector is target#target with a specificity of (1, 0, 1) */
+ :matches(target, :matches(target.target, #target, .target), target#target) {
+ background-color: red;
+ color: blue;
+ border: none;
+ }
+</style>
+</head>
+<body>
+ <p>This test the specificity of simple nested :matches() selectors. The test succeed is the text "Target" appears on in white over a green background.</p>
+ <target id="target" class="target">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-9-expected.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-9-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-9-expected.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,7 @@
+<!doctype html>
+<html>
+<body>
+ <p>This test the specificity when chaining multiple :matches() selectors. The test succeed is the text "Target" appears on in white over a green background.</p>
+ <target style="background-color: green; color: white; border: none;">Target</target>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/matches-specificity-9.html (0 => 176307)
--- trunk/LayoutTests/fast/css/matches-specificity-9.html (rev 0)
+++ trunk/LayoutTests/fast/css/matches-specificity-9.html 2014-11-19 05:12:53 UTC (rev 176307)
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+<head>
+<style>
+ /* Specificity of (1, 1, 1) */
+ :matches(.target):matches(#target):matches(target) {
+ background-color: green;
+ }
+
+ /* This selector has a specificity of (1, 1, 0) but the selector below has a higher position. */
+ #target.target:matches(*) {
+ background-color: red;
+ color: blue;
+ }
+
+ /* Specificity of (1, 1, 0) */
+ :matches(#target):matches(.target) {
+ background-color: red;
+ color: white;
+ }
+
+ /* This selector has a specificity of (1, 0, 1) but the selector below has a higher position. */
+ target#target:not(:not(*, matches(*, *))) {
+ background-color: red;
+ color: blue;
+ border: 25px solid purple;
+ }
+
+ /* Specificity of (1, 0, 1) */
+ :matches(target):matches(#target) {
+ background-color: red;
+ color: blue;
+ border: none;
+ }
+</style>
+</head>
+<body>
+ <p>This test the specificity when chaining multiple :matches() selectors. The test succeed is the text "Target" appears on in white over a green background.</p>
+ <target id="target" class="target">Target</target>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (176306 => 176307)
--- trunk/Source/WebCore/ChangeLog 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/Source/WebCore/ChangeLog 2014-11-19 05:12:53 UTC (rev 176307)
@@ -1,3 +1,62 @@
+2014-11-18 Benjamin Poulain <[email protected]>
+
+ Add the initial implementation of dynamic specificity for :matches()
+ https://bugs.webkit.org/show_bug.cgi?id=138822
+
+ Reviewed by Andreas Kling.
+
+ Previously we completely ignored the selector list of :matches() when computing
+ the specificity of the selector.
+
+ The spec (http://dev.w3.org/csswg/selectors4/#specificity) says:
+ "The specificity of a :matches() pseudo-class, however, is the specificity
+ of the most specific complex selector that matched the given element."
+
+ This patch does just that.
+
+ In the CSS JIT, we only consider specificities that can be computed statically
+ for now, this should be extended later.
+ When the specificity is dynamic, we fall back to SelectorChecker. In that case,
+ we execute every selector of the selector list and we keep the maximum value
+ as the specificity for the whole :matches().
+
+ Tests: fast/css/matches-specificity-1.html
+ fast/css/matches-specificity-2.html
+ fast/css/matches-specificity-3.html
+ fast/css/matches-specificity-4.html
+ fast/css/matches-specificity-5.html
+ fast/css/matches-specificity-6.html
+ fast/css/matches-specificity-7.html
+ fast/css/matches-specificity-8.html
+ fast/css/matches-specificity-9.html
+ fast/css/matches-specificity-10.html
+
+ * css/CSSSelector.cpp:
+ (WebCore::selectorSpecificity):
+ (WebCore::maxSpecificity):
+ (WebCore::CSSSelector::specificity):
+ (WebCore::simpleSelectorSpecificityInternal):
+ (WebCore::CSSSelector::simpleSelectorSpecificity):
+ :matches() itself should not have any specificity. The specificity of its components
+ is computed in SelectorChecker.
+
+ Since :matches() is no longer accounted as a class B specificity, I had to finish
+ the specificity of :not() or some test breaks.
+
+ * css/CSSSelector.h:
+ * css/PageRuleCollector.cpp:
+ (WebCore::comparePageRules):
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::checkOne):
+ We can no longer shortcut the execution when context.pseudoElementEffective is false.
+ There is no guarantee that a following selector wouldn't match with a higher specificity.
+
+ * cssjit/SelectorCompiler.cpp:
+ (WebCore::SelectorCompiler::addPseudoClassType):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
+ (WebCore::SelectorCompiler::constructFragments):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateSelectorChecker):
+
2014-11-18 David Kilzer <[email protected]>
FeatureDefines.xcconfig: Switch from using PLATFORM_NAME to SDK selectors
Modified: trunk/Source/WebCore/css/CSSSelector.cpp (176306 => 176307)
--- trunk/Source/WebCore/css/CSSSelector.cpp 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/Source/WebCore/css/CSSSelector.cpp 2014-11-19 05:12:53 UTC (rev 176307)
@@ -59,37 +59,55 @@
m_hasRareData = true;
}
+static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity);
+
+static unsigned selectorSpecificity(const CSSSelector& firstSimpleSelector, bool isComputingMaximumSpecificity)
+{
+ unsigned total = simpleSelectorSpecificityInternal(firstSimpleSelector, isComputingMaximumSpecificity);
+
+ for (const CSSSelector* selector = firstSimpleSelector.tagHistory(); selector; selector = selector->tagHistory())
+ total = CSSSelector::addSpecificities(total, simpleSelectorSpecificityInternal(*selector, isComputingMaximumSpecificity));
+ return total;
+}
+
+static unsigned maxSpecificity(const CSSSelectorList& selectorList)
+{
+ unsigned maxSpecificity = 0;
+ for (const CSSSelector* subSelector = selectorList.first(); subSelector; subSelector = CSSSelectorList::next(subSelector))
+ maxSpecificity = std::max(maxSpecificity, selectorSpecificity(*subSelector, true));
+ return maxSpecificity;
+}
+
unsigned CSSSelector::specificity() const
{
if (isForPage())
return specificityForPage() & maxValueMask;
- unsigned total = simpleSelectorSpecificity();
-
- for (const CSSSelector* selector = this->tagHistory(); selector; selector = selector->tagHistory())
- total = addSpecificities(total, selector->simpleSelectorSpecificity());
- return total;
+ return selectorSpecificity(*this, false);
}
-unsigned CSSSelector::simpleSelectorSpecificity() const
+static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity)
{
- ASSERT_WITH_MESSAGE(!isForPage(), "At the time of this writing, page selectors are not treated as real selectors that are matched. The value computed here only account for real selectors.");
+ ASSERT_WITH_MESSAGE(!simpleSelector.isForPage(), "At the time of this writing, page selectors are not treated as real selectors that are matched. The value computed here only account for real selectors.");
- switch (match()) {
- case Id:
+ switch (simpleSelector.match()) {
+ case CSSSelector::Id:
return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
- case PagePseudoClass:
+ case CSSSelector::PagePseudoClass:
break;
- case PseudoClass:
+ case CSSSelector::PseudoClass:
#if ENABLE(CSS_SELECTORS_LEVEL4)
- if (pseudoClassType() == PseudoClassNot) {
- ASSERT_WITH_MESSAGE(selectorList() && selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
+ if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassMatches) {
+ ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :matches().");
+ if (!isComputingMaximumSpecificity)
+ return 0;
+ return maxSpecificity(*simpleSelector.selectorList());
+ }
- unsigned maxSpecificity = 0;
- for (const CSSSelector* subSelector = selectorList()->first(); subSelector; subSelector = CSSSelectorList::next(subSelector))
- maxSpecificity = std::max(maxSpecificity, subSelector->specificity());
- return maxSpecificity;
+ if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassNot) {
+ ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
+ return maxSpecificity(*simpleSelector.selectorList());
}
FALLTHROUGH;
#else
@@ -97,26 +115,31 @@
return selectorList()->first()->simpleSelectorSpecificity();
FALLTHROUGH;
#endif
- case Exact:
- case Class:
- case Set:
- case List:
- case Hyphen:
- case Contain:
- case Begin:
- case End:
+ case CSSSelector::Exact:
+ case CSSSelector::Class:
+ case CSSSelector::Set:
+ case CSSSelector::List:
+ case CSSSelector::Hyphen:
+ case CSSSelector::Contain:
+ case CSSSelector::Begin:
+ case CSSSelector::End:
return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
- case Tag:
- return (tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
- case PseudoElement:
+ case CSSSelector::Tag:
+ return (simpleSelector.tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
+ case CSSSelector::PseudoElement:
return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
- case Unknown:
+ case CSSSelector::Unknown:
return 0;
}
ASSERT_NOT_REACHED();
return 0;
}
+unsigned CSSSelector::simpleSelectorSpecificity() const
+{
+ return simpleSelectorSpecificityInternal(*this, false);
+}
+
unsigned CSSSelector::addSpecificities(unsigned a, unsigned b)
{
unsigned total = a;
Modified: trunk/Source/WebCore/css/CSSSelector.h (176306 => 176307)
--- trunk/Source/WebCore/css/CSSSelector.h 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/Source/WebCore/css/CSSSelector.h 2014-11-19 05:12:53 UTC (rev 176307)
@@ -59,6 +59,7 @@
static const unsigned elementMask = 0xff;
unsigned specificity() const;
+ unsigned specificityForPage() const;
unsigned simpleSelectorSpecificity() const;
static unsigned addSpecificities(unsigned, unsigned);
@@ -312,7 +313,6 @@
unsigned m_tagIsForNamespaceRule : 1;
unsigned simpleSelectorSpecificityForPage() const;
- unsigned specificityForPage() const;
// Hide.
CSSSelector& operator=(const CSSSelector&);
Modified: trunk/Source/WebCore/css/PageRuleCollector.cpp (176306 => 176307)
--- trunk/Source/WebCore/css/PageRuleCollector.cpp 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/Source/WebCore/css/PageRuleCollector.cpp 2014-11-19 05:12:53 UTC (rev 176307)
@@ -37,7 +37,7 @@
static inline bool comparePageRules(const StyleRulePage* r1, const StyleRulePage* r2)
{
- return r1->selector()->specificity() < r2->selector()->specificity();
+ return r1->selector()->specificityForPage() < r2->selector()->specificityForPage();
}
bool PageRuleCollector::isLeftPage(int pageIndex) const
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (176306 => 176307)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2014-11-19 05:12:53 UTC (rev 176307)
@@ -726,6 +726,8 @@
case CSSSelector::PseudoClassMatches:
{
bool hasMatchedAnything = false;
+ unsigned maxSpecificity = 0;
+
MatchType localMatchType = MatchType::VirtualPseudoElementOnly;
for (const CSSSelector* subselector = selector->selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
CheckingContextWithStatus subcontext(context);
@@ -737,12 +739,7 @@
unsigned localSpecificity = 0;
MatchResult result = matchRecursively(subcontext, localDynamicPseudoIdSet, localSpecificity);
if (result.match == Match::SelectorMatches) {
- if (!context.pseudoElementEffective) {
- // When pseudo elements are not effective in this fragment (e.g. it's not righmost fragment),
- // it's not necessary to check all selectors to collect pseudo element ids.
- ASSERT(!localDynamicPseudoIdSet);
- return true;
- }
+ maxSpecificity = std::max(maxSpecificity, localSpecificity);
if (result.matchType == MatchType::Element)
localMatchType = MatchType::Element;
@@ -751,8 +748,10 @@
hasMatchedAnything = true;
}
}
- if (hasMatchedAnything)
+ if (hasMatchedAnything) {
matchType = localMatchType;
+ specificity = CSSSelector::addSpecificities(specificity, maxSpecificity);
+ }
return hasMatchedAnything;
}
Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (176306 => 176307)
--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2014-11-19 05:08:41 UTC (rev 176306)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2014-11-19 05:12:53 UTC (rev 176307)
@@ -218,6 +218,7 @@
public:
unsigned registerRequirements = std::numeric_limits<unsigned>::max();
unsigned stackRequirements = std::numeric_limits<unsigned>::max();
+ unsigned staticSpecificity = 0;
bool clobberElementAddressRegister = true;
};
@@ -357,7 +358,9 @@
StackAllocator::StackReference m_lastVisitedElement;
StackAllocator::StackReference m_startElement;
+#if CSS_SELECTOR_JIT_DEBUGGING
const CSSSelector* m_originalSelector;
+#endif
};
const Assembler::RegisterID SelectorCodeGenerator::returnRegister = JSC::GPRInfo::returnValueGPR;
@@ -462,7 +465,7 @@
return FunctionType::CannotMatchAnything;
}
-static inline FunctionType addPseudoClassType(const CSSSelector& selector, SelectorFragment& fragment, SelectorContext selectorContext, FragmentsLevel fragmentLevel, FragmentPositionInRootFragments positionInRootFragments, bool visitedMatchEnabled, VisitedMode& visitedMode, PseudoElementMatchingBehavior pseudoElementMatchingBehavior)
+static inline FunctionType addPseudoClassType(const CSSSelector& selector, SelectorFragment& fragment, unsigned& internalSpecificity, SelectorContext selectorContext, FragmentsLevel fragmentLevel, FragmentPositionInRootFragments positionInRootFragments, bool visitedMatchEnabled, VisitedMode& visitedMode, PseudoElementMatchingBehavior pseudoElementMatchingBehavior)
{
CSSSelector::PseudoClassType type = selector.pseudoClassType();
switch (type) {
@@ -759,6 +762,8 @@
SelectorList matchesList;
const CSSSelectorList* selectorList = selector.selectorList();
FunctionType functionType = FunctionType::SimpleSelectorChecker;
+ unsigned firstFragmentListSpecificity = 0;
+ bool firstFragmentListSpecificitySet = false;
for (const CSSSelector* subselector = selectorList->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
SelectorFragmentList selectorFragments;
VisitedMode ignoreVisitedMode = VisitedMode::None;
@@ -776,6 +781,15 @@
if (selectorFragments.first().pseudoElementSelector)
return FunctionType::CannotCompile;
+ if (firstFragmentListSpecificitySet) {
+ // The CSS JIT does not handle dynamic specificity yet.
+ if (selectorContext == SelectorContext::RuleCollector && selectorFragments.staticSpecificity != firstFragmentListSpecificity)
+ return FunctionType::CannotCompile;
+ } else {
+ firstFragmentListSpecificitySet = true;
+ firstFragmentListSpecificity = selectorFragments.staticSpecificity;
+ }
+
functionType = mostRestrictiveFunctionType(functionType, localFunctionType);
matchesList.append(selectorFragments);
}
@@ -784,6 +798,8 @@
if (matchesList.isEmpty())
return FunctionType::CannotMatchAnything;
+ internalSpecificity = firstFragmentListSpecificity;
+
fragment.matchesFilters.append(matchesList);
return functionType;
@@ -805,7 +821,9 @@
, m_functionType(FunctionType::SimpleSelectorChecker)
, m_visitedMode(VisitedMode::None)
, m_descendantBacktrackingStartInUse(false)
+#if CSS_SELECTOR_JIT_DEBUGGING
, m_originalSelector(rootSelector)
+#endif
{
#if CSS_SELECTOR_JIT_DEBUGGING
dataLogF("Compiling \"%s\"\n", m_originalSelector->selectorText().utf8().data());
@@ -835,7 +853,10 @@
SelectorFragment fragment;
FragmentRelation relationToPreviousFragment = FragmentRelation::Rightmost;
FunctionType functionType = FunctionType::SimpleSelectorChecker;
+ unsigned specificity = 0;
for (const CSSSelector* selector = rootSelector; selector; selector = selector->tagHistory()) {
+ specificity = CSSSelector::addSpecificities(specificity, selector->simpleSelectorSpecificity());
+
CSSSelector::Relation relation = selector->relation();
// A selector is invalid if something follows a pseudo-element.
@@ -874,8 +895,11 @@
subPosition = FragmentPositionInRootFragments::NotRightmost;
if (fragment.pseudoElementSelector && isScrollbarPseudoElement(fragment.pseudoElementSelector->pseudoElementType()))
functionType = mostRestrictiveFunctionType(functionType, addScrollbarPseudoClassType(*selector, fragment));
- else
- functionType = mostRestrictiveFunctionType(functionType, addPseudoClassType(*selector, fragment, selectorContext, fragmentLevel, subPosition, visitedMatchEnabled, visitedMode, pseudoElementMatchingBehavior));
+ else {
+ unsigned internalSpecificity = 0;
+ functionType = mostRestrictiveFunctionType(functionType, addPseudoClassType(*selector, fragment, internalSpecificity, selectorContext, fragmentLevel, subPosition, visitedMatchEnabled, visitedMode, pseudoElementMatchingBehavior));
+ specificity = CSSSelector::addSpecificities(specificity, internalSpecificity);
+ }
if (!pseudoClassOnlyMatchesLinksInQuirksMode(*selector))
fragment._onlyMatchesLinksInQuirksMode_ = false;
if (functionType == FunctionType::CannotCompile || functionType == FunctionType::CannotMatchAnything)
@@ -975,6 +999,9 @@
selectorFragments.append(fragment);
fragment = SelectorFragment();
}
+
+ selectorFragments.staticSpecificity = specificity;
+
return functionType;
}
@@ -1617,7 +1644,7 @@
}
if (m_selectorContext == SelectorContext::RuleCollector) {
- unsigned specificity = m_originalSelector->specificity();
+ unsigned specificity = m_selectorFragments.staticSpecificity;
if (m_functionType == FunctionType::SelectorCheckerWithCheckingContext)
m_assembler.store32(Assembler::TrustedImm32(specificity), JSC::GPRInfo::argumentGPR2);
else