Title: [177333] trunk
Revision
177333
Author
[email protected]
Date
2014-12-15 21:56:33 -0800 (Mon, 15 Dec 2014)

Log Message

Extend :lang()'s selector checker to handle ranges with '*' properly and perform matching within the ASCII range
https://bugs.webkit.org/show_bug.cgi?id=139340

Patch by Dhi Aurrahman <[email protected]> on 2014-12-15
Reviewed by Benjamin Poulain.

Source/WebCore:

Asterisk is considered as a valid subtag of a language range to express wildcard matching
in :lang()'s extended filtering procedure. The matching rules introduced by language
range with '*' is outlined in [1].

The matching of subtags is performed case-insensitively within the ASCII range[2].

[1] www.ietf.org/rfc/rfc4647.txt
[2] http://dev.w3.org/csswg/selectors4/#the-lang-pseudo

Test: fast/selectors/lang-equal-ignoring-case.html
      fast/selectors/lang-valid-extended-filtering.html

* css/SelectorCheckerTestFunctions.h:
(WebCore::equalIgnoringCaseWithinASCIIRange): Handle matching case-insensitively within the ASCII range.
(WebCore::containslanguageSubtagMatchingRange):
(WebCore::matchesLangPseudoClass):

LayoutTests:

* fast/selectors/lang-equal-ignoring-ascii-case-expected.txt: Added.
* fast/selectors/lang-equal-ignoring-ascii-case.html: Added.
* fast/selectors/lang-extended-filtering-expected.txt: Updated.
* fast/selectors/lang-extended-filtering.html: Updated.
* fast/selectors/lang-valid-extended-filtering-expected.txt: Added.
* fast/selectors/lang-valid-extended-filtering.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (177332 => 177333)


--- trunk/LayoutTests/ChangeLog	2014-12-16 04:51:22 UTC (rev 177332)
+++ trunk/LayoutTests/ChangeLog	2014-12-16 05:56:33 UTC (rev 177333)
@@ -1,3 +1,17 @@
+2014-12-15  Dhi Aurrahman  <[email protected]>
+
+        Extend :lang()'s selector checker to handle ranges with '*' properly and perform matching within the ASCII range
+        https://bugs.webkit.org/show_bug.cgi?id=139340
+
+        Reviewed by Benjamin Poulain.
+
+        * fast/selectors/lang-equal-ignoring-ascii-case-expected.txt: Added.
+        * fast/selectors/lang-equal-ignoring-ascii-case.html: Added.
+        * fast/selectors/lang-extended-filtering-expected.txt: Updated.
+        * fast/selectors/lang-extended-filtering.html: Updated.
+        * fast/selectors/lang-valid-extended-filtering-expected.txt: Added.
+        * fast/selectors/lang-valid-extended-filtering.html: Added.
+
 2014-12-15  Said Abou-Hallawa  <[email protected]>
 
         Import Mozilla test suite for SVG.

Added: trunk/LayoutTests/fast/selectors/lang-equal-ignoring-ascii-case-expected.txt (0 => 177333)


--- trunk/LayoutTests/fast/selectors/lang-equal-ignoring-ascii-case-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/lang-equal-ignoring-ascii-case-expected.txt	2014-12-16 05:56:33 UTC (rev 177333)
@@ -0,0 +1,27 @@
+Verify :lang()'s equal ignoring ascii case
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelector(':lang(Aà)') is document.getElementById('a')
+PASS document.querySelector(':lang(aÀ)') is null
+PASS document.querySelector(':lang(AÀ)') is null
+PASS document.querySelector(':lang(Cč)') is document.getElementById('c')
+PASS document.querySelector(':lang(cČ)') is null
+PASS document.querySelector(':lang(CČ)') is null
+PASS document.querySelector(':lang(eê)') is document.getElementById('e')
+PASS document.querySelector(':lang(eÊ)') is null
+PASS document.querySelector(':lang(EÊ)') is null
+PASS document.querySelector(':lang(Iį)') is document.getElementById('i')
+PASS document.querySelector(':lang(iÄ®)') is null
+PASS document.querySelector(':lang(IÄ®)') is null
+PASS document.querySelector(':lang(uû)') is document.getElementById('u')
+PASS document.querySelector(':lang(uÛ)') is null
+PASS document.querySelector(':lang(UÛ)') is null
+PASS document.querySelector(':lang(oø)') is document.getElementById('o')
+PASS document.querySelector(':lang(oØ)') is null
+PASS document.querySelector(':lang(OØ)') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/selectors/lang-equal-ignoring-ascii-case.html (0 => 177333)


--- trunk/LayoutTests/fast/selectors/lang-equal-ignoring-ascii-case.html	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/lang-equal-ignoring-ascii-case.html	2014-12-16 05:56:33 UTC (rev 177333)
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+    <div id="a" lang="aà"></div>
+    <div id="c" lang="cč"></div>
+    <div id="e" lang="eê"></div>
+    <div id="i" lang="iį"></div>
+    <div id="u" lang="uû"></div>
+    <div id="o" lang="oø"></div>
+<script>
+description('Verify :lang()\'s equal ignoring ascii case');
+
+shouldBe("document.querySelector(':lang(Aà)')", "document.getElementById('a')");
+shouldBeNull("document.querySelector(':lang(aÀ)')");
+shouldBeNull("document.querySelector(':lang(AÀ)')");
+
+shouldBe("document.querySelector(':lang(Cč)')", "document.getElementById('c')");
+shouldBeNull("document.querySelector(':lang(cČ)')");
+shouldBeNull("document.querySelector(':lang(CČ)')");
+
+shouldBe("document.querySelector(':lang(eê)')", "document.getElementById('e')");
+shouldBeNull("document.querySelector(':lang(eÊ)')");
+shouldBeNull("document.querySelector(':lang(EÊ)')");
+
+shouldBe("document.querySelector(':lang(Iį)')", "document.getElementById('i')");
+shouldBeNull("document.querySelector(':lang(iĮ)')");
+shouldBeNull("document.querySelector(':lang(IĮ)')");
+
+shouldBe("document.querySelector(':lang(uû)')", "document.getElementById('u')");
+shouldBeNull("document.querySelector(':lang(uÛ)')");
+shouldBeNull("document.querySelector(':lang(UÛ)')");
+
+shouldBe("document.querySelector(':lang(oø)')", "document.getElementById('o')");
+shouldBeNull("document.querySelector(':lang(oØ)')");
+shouldBeNull("document.querySelector(':lang(OØ)')");
+</script>
+<script src=""
+</body>
+</html>
\ No newline at end of file

Modified: trunk/LayoutTests/fast/selectors/lang-extended-filtering-expected.txt (177332 => 177333)


--- trunk/LayoutTests/fast/selectors/lang-extended-filtering-expected.txt	2014-12-16 04:51:22 UTC (rev 177332)
+++ trunk/LayoutTests/fast/selectors/lang-extended-filtering-expected.txt	2014-12-16 05:56:33 UTC (rev 177333)
@@ -1,4 +1,4 @@
-Verify selector specifying extended filetring of :lang() pseudo class
+Verify selector specifying extended filtering of :lang() pseudo class
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
@@ -79,9 +79,73 @@
 PASS document.querySelectorAll(":lang(tic-tac-toe)").length is 1
 PASS document.querySelectorAll(":lang(tic-toe-tac)").length is 0
 
+PASS document.querySelectorAll(":lang(\\*)").length is 35
+PASS document.querySelectorAll(":lang(*-CH)").length is 5
+
+PASS document.querySelectorAll(":lang(a)").length is 2
+PASS document.querySelectorAll(":lang(a-\\*)").length is 1
+PASS document.querySelectorAll(":lang(*-fr-\\*)").length is 2
+PASS document.querySelectorAll(":lang(*-\\*-lang)").length is 2
+PASS document.querySelectorAll(":lang(*-\\*-aa-bb-cc-dd)").length is 3
+PASS document.querySelectorAll(":lang(*-\\*-\\*-bb-cc-dd)").length is 3
+PASS document.querySelectorAll(":lang(*-\\*-\\*-\\*-cc-dd)").length is 3
+PASS document.querySelectorAll(":lang(*-\\*-\\*-\\*-\\*-dd)").length is 3
+PASS document.querySelectorAll(":lang(*-\\*-\\*-\\*-\\*-\\*)").length is 3
+PASS document.querySelectorAll(":lang(*-\\*-aa-\\*-cc-\\*)").length is 3
+PASS document.querySelectorAll(":lang(*-\\*-\\*-bb-\\*)").length is 3
+PASS document.querySelectorAll(":lang(fooÉ)").length is 1
+PASS document.querySelectorAll(":lang(fOOÉ)").length is 1
+PASS document.querySelectorAll(":lang(FoOÉ)").length is 1
+PASS document.querySelectorAll(":lang(FOOÉ)").length is 1
+PASS document.querySelectorAll(":lang(fooé)").length is 0
+PASS document.querySelectorAll(":lang(FOOé)").length is 0
+PASS document.querySelectorAll(":lang(foöÉ-bÁr)").length is 1
+PASS document.querySelectorAll(":lang(FoöÉ-bÁr)").length is 1
+PASS document.querySelectorAll(":lang(FOöÉ-bÁr)").length is 1
+PASS document.querySelectorAll(":lang(FOòÉ-bÁr)").length is 0
+PASS document.querySelectorAll(":lang(FOòę-bÁr)").length is 0
+PASS document.querySelectorAll(":lang(foöÉ-\\*)").length is 1
+PASS document.querySelectorAll(":lang(\\*-bÁr)").length is 1
+PASS document.querySelectorAll(":lang(\\*-BÁr)").length is 1
+PASS document.querySelectorAll(":lang(\\*-BÁR)").length is 1
+PASS document.querySelectorAll(":lang(\\*-Bár)").length is 0
+PASS document.querySelectorAll(":lang(\\*-báR)").length is 0
+PASS document.querySelectorAll(":lang(\\*-BáR)").length is 0
+
+PASS document.querySelectorAll(":lang(FOöÉ-BÁr)").length is 1
+PASS document.querySelectorAll(":lang(FOöÉ-BÁ)").length is 0
+PASS document.querySelectorAll(":lang(FOöÉ-B)").length is 0
+PASS document.querySelectorAll(":lang(f-BÁr)").length is 0
+PASS document.querySelectorAll(":lang(fO-BÁr)").length is 0
+PASS document.querySelectorAll(":lang(fOö-BÁr)").length is 0
+PASS document.querySelectorAll(":lang(FOöÉ-BÁr1)").length is 0
+PASS document.querySelectorAll(":lang(FOöÉ-BÁr12)").length is 0
+PASS document.querySelectorAll(":lang(FOöÉ-BÁr123)").length is 0
+PASS document.querySelectorAll(":lang(F-Xe)").length is 0
+PASS document.querySelectorAll(":lang(Fr-Xe)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xe)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xen)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xeno)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xenom)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xenomo)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xenomor)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xenomorp)").length is 0
+PASS document.querySelectorAll(":lang(fr-Xènömòrph)").length is 0
+PASS document.querySelectorAll(":lang(FR-XENOMORPH)").length is 1
+PASS document.querySelectorAll(":lang(foöÉbÁr)").length is 1
+PASS document.querySelectorAll(":lang(foöÉbÁ)").length is 0
+PASS document.querySelectorAll(":lang(foöÉb)").length is 0
+PASS document.querySelectorAll(":lang(foöÉ)").length is 1
+PASS document.querySelectorAll(":lang(foö)").length is 0
+PASS document.querySelectorAll(":lang(fo)").length is 0
+PASS document.querySelectorAll(":lang(f)").length is 0
+
+PASS document.querySelectorAll(":lang(*)").length threw exception Error: SyntaxError: DOM Exception 12.
+PASS document.querySelectorAll(":lang(*foöÉ)").length threw exception Error: SyntaxError: DOM Exception 12.
 PASS document.querySelectorAll(":lang(--en--)").length threw exception Error: SyntaxError: DOM Exception 12.
 PASS document.querySelectorAll(":lang(---en---)").length threw exception Error: SyntaxError: DOM Exception 12.
 PASS document.querySelectorAll(":lang(en us- de- fr-).length") threw exception Error: SyntaxError: DOM Exception 12.
+PASS document.querySelectorAll(":lang(-\\* \\*-)").length threw exception Error: SyntaxError: DOM Exception 12.
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/selectors/lang-extended-filtering.html (177332 => 177333)


--- trunk/LayoutTests/fast/selectors/lang-extended-filtering.html	2014-12-16 04:51:22 UTC (rev 177332)
+++ trunk/LayoutTests/fast/selectors/lang-extended-filtering.html	2014-12-16 05:56:33 UTC (rev 177333)
@@ -37,8 +37,26 @@
     <div lang="cocoa-1-bar"></div>
     <div lang="cocoa-a-bar"></div>
 
+    <div lang="a"></div>
+    <div lang="a-fr-lang"></div>
+    <div lang="b-fr-lang"></div>
+
+    <div lang="de-CH"></div>
+    <div lang="it-CH"></div>
+    <div lang="fr-CH"></div>
+    <div lang="rm-CH"></div>
+    <div lang="es-CH"></div>
+
+    <div lang="es1-KK-aa-bb-cc-dd"></div>
+    <div lang="es2-KL-aa-bb-cc-dd"></div>
+    <div lang="es3-KM-aa-bb-cc-dd"></div>
+
+    <div lang="fooÉ"></div>
+    <div lang="foöÉ-bÁr"></div>
+    <div lang="foöÉbÁr"></div>
+
     <script>
-    description('Verify selector specifying extended filetring of :lang() pseudo class');
+    description('Verify selector specifying extended filtering of :lang() pseudo class');
 
     shouldBe('document.querySelectorAll(":lang(en)").length', '3');
     shouldBe('document.querySelectorAll(":lang(en-)").length', '2');
@@ -138,9 +156,87 @@
 
     debug('');
 
+    shouldBe('document.querySelectorAll(":lang(\\\\*)").length', '35');
+    shouldBe('document.querySelectorAll(":lang(*-CH)").length', '5');
+
+    debug('');  
+
+    shouldBe('document.querySelectorAll(":lang(a)").length', '2');
+    shouldBe('document.querySelectorAll(":lang(a-\\\\*)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(*-fr-\\\\*)").length', '2');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-lang)").length', '2');   
+
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-aa-bb-cc-dd)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-\\\\*-bb-cc-dd)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-\\\\*-\\\\*-cc-dd)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-\\\\*-\\\\*-\\\\*-dd)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-\\\\*-\\\\*-\\\\*-\\\\*)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-aa-\\\\*-cc-\\\\*)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(*-\\\\*-\\\\*-bb-\\\\*)").length', '3');
+
+    shouldBe('document.querySelectorAll(":lang(fooÉ)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(fOOÉ)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(FoOÉ)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(FOOÉ)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(fooé)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FOOé)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(foöÉ-bÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(FoöÉ-bÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-bÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(FOòÉ-bÁr)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FOòę-bÁr)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(foöÉ-\\\\*)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(\\\\*-bÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(\\\\*-BÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(\\\\*-BÁR)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(\\\\*-Bár)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(\\\\*-báR)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(\\\\*-BáR)").length', '0');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-BÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-BÁ)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-B)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(f-BÁr)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fO-BÁr)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fOö-BÁr)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-BÁr1)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-BÁr12)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FOöÉ-BÁr123)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(F-Xe)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(Fr-Xe)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xe)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xen)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xeno)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xenom)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xenomo)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xenomor)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xenomorp)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-Xènömòrph)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(FR-XENOMORPH)").length', '1');
+
+    shouldBe('document.querySelectorAll(":lang(foöÉbÁr)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foöÉbÁ)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(foöÉb)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(foöÉ)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foö)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fo)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(f)").length', '0');
+
+    debug('');
+
+    shouldThrow('document.querySelectorAll(":lang(*)").length');
+    shouldThrow('document.querySelectorAll(":lang(*foöÉ)").length');
     shouldThrow('document.querySelectorAll(":lang(--en--)").length');
     shouldThrow('document.querySelectorAll(":lang(---en---)").length');
     shouldThrow('document.querySelectorAll(":lang(en us- de- fr-).length")');
+    shouldThrow('document.querySelectorAll(":lang(-\\\\* \\\\*-)").length');
+
     </script>
     <script src=""
 </body>

Added: trunk/LayoutTests/fast/selectors/lang-valid-extended-filtering-expected.txt (0 => 177333)


--- trunk/LayoutTests/fast/selectors/lang-valid-extended-filtering-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/lang-valid-extended-filtering-expected.txt	2014-12-16 05:56:33 UTC (rev 177333)
@@ -0,0 +1,594 @@
+Verify selector specifying extended filtering of :lang() pseudo class
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+- af-ZA
+PASS document.querySelectorAll(':lang(af)').length == 1 is true
+PASS document.querySelectorAll(':lang(af-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(af-ZA)').length == 1 is true
+PASS document.querySelectorAll(':lang(af-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ZA)').length == 1 is true
+
+- ar-AE
+PASS document.querySelectorAll(':lang(ar)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-AE)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-AE)').length == 1 is true
+
+- ar-BH
+PASS document.querySelectorAll(':lang(ar)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-BH)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BH)').length == 1 is true
+
+- ar-YE
+PASS document.querySelectorAll(':lang(ar)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-YE)').length == 1 is true
+PASS document.querySelectorAll(':lang(ar-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-YE)').length == 1 is true
+
+- art-lojban
+PASS document.querySelectorAll(':lang(art)').length == 1 is true
+PASS document.querySelectorAll(':lang(art-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(art-lojban)').length == 1 is true
+PASS document.querySelectorAll(':lang(art-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-lojban)').length == 1 is true
+
+- az-Arab-IR
+PASS document.querySelectorAll(':lang(az)').length == 1 is true
+PASS document.querySelectorAll(':lang(az-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(az-Arab)').length == 1 is true
+PASS document.querySelectorAll(':lang(az-\\*-IR)').length == 1 is true
+PASS document.querySelectorAll(':lang(az-Arab-IR)').length == 1 is true
+PASS document.querySelectorAll(':lang(az-Arab-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Arab-IR)').length == 1 is true
+
+- be-BY
+PASS document.querySelectorAll(':lang(be)').length == 1 is true
+PASS document.querySelectorAll(':lang(be-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(be-BY)').length == 1 is true
+PASS document.querySelectorAll(':lang(be-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BY)').length == 1 is true
+
+- bg-BG
+PASS document.querySelectorAll(':lang(bg)').length == 1 is true
+PASS document.querySelectorAll(':lang(bg-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(bg-BG)').length == 1 is true
+PASS document.querySelectorAll(':lang(bg-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BG)').length == 1 is true
+
+- ca-ES
+PASS document.querySelectorAll(':lang(ca)').length == 1 is true
+PASS document.querySelectorAll(':lang(ca-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ca-ES)').length == 1 is true
+PASS document.querySelectorAll(':lang(ca-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ES)').length == 1 is true
+
+- cs-CZ
+PASS document.querySelectorAll(':lang(cs)').length == 1 is true
+PASS document.querySelectorAll(':lang(cs-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(cs-CZ)').length == 1 is true
+PASS document.querySelectorAll(':lang(cs-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CZ)').length == 1 is true
+
+- cy-GB
+PASS document.querySelectorAll(':lang(cy)').length == 1 is true
+PASS document.querySelectorAll(':lang(cy-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(cy-GB)').length == 1 is true
+PASS document.querySelectorAll(':lang(cy-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-GB)').length == 1 is true
+
+- de-AT
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-AT)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-AT)').length == 1 is true
+
+- de-CH
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH)').length == 1 is true
+
+- de-CH-1996
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH-1996)').length == 1 is true
+
+- de-CH-1997
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-1997)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH-1997)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH-1997)').length == 1 is true
+
+- de-DE
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-DE)').length == 1 is true
+
+- de-DE-1996
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-DE-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-DE-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-DE-1996)').length == 1 is true
+
+- de-Latn-DE
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Latn-DE)').length == 1 is true
+
+- de-Latf-DE
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latf)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latf-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latf-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Latf-DE)').length == 1 is true
+
+- de-Latn-DE-1996
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*-DE-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn-\\*-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn-DE-1996)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-Latn-DE-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Latn-DE-1996)').length == 1 is true
+
+- en-US
+PASS document.querySelectorAll(':lang(en)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-US)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-US)').length == 1 is true
+
+- en-GB
+PASS document.querySelectorAll(':lang(en)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-GB)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-GB)').length == 1 is true
+
+- en-Latn-Brai
+PASS document.querySelectorAll(':lang(en)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-Latn)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-\\*-Brai)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-Latn-Brai)').length == 1 is true
+PASS document.querySelectorAll(':lang(en-Latn-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Latn-Brai)').length == 1 is true
+
+- es-ES
+PASS document.querySelectorAll(':lang(es)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-ES)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ES)').length == 1 is true
+
+- es-ES-valencia
+PASS document.querySelectorAll(':lang(es)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-ES)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-\\*-valencia)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-ES-valencia)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-ES-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ES-valencia)').length == 1 is true
+
+- es-AR
+PASS document.querySelectorAll(':lang(es)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-AR)').length == 1 is true
+PASS document.querySelectorAll(':lang(es-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-AR)').length == 1 is true
+
+- fr-BE
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-BE)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BE)').length == 1 is true
+
+- fr-CA
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-CA)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CA)').length == 1 is true
+
+- fr-CH
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH)').length == 1 is true
+
+- fr-FR
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-FR)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-FR)').length == 1 is true
+
+- fr-LU
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-LU)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-LU)').length == 1 is true
+
+- fr-MC
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-MC)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-MC)').length == 1 is true
+
+- gl-ES
+PASS document.querySelectorAll(':lang(gl)').length == 1 is true
+PASS document.querySelectorAll(':lang(gl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(gl-ES)').length == 1 is true
+PASS document.querySelectorAll(':lang(gl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ES)').length == 1 is true
+
+- gu-IN
+PASS document.querySelectorAll(':lang(gu)').length == 1 is true
+PASS document.querySelectorAll(':lang(gu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(gu-IN)').length == 1 is true
+PASS document.querySelectorAll(':lang(gu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-IN)').length == 1 is true
+
+- hr-BA
+PASS document.querySelectorAll(':lang(hr)').length == 1 is true
+PASS document.querySelectorAll(':lang(hr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(hr-BA)').length == 1 is true
+PASS document.querySelectorAll(':lang(hr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BA)').length == 1 is true
+
+- id-ID
+PASS document.querySelectorAll(':lang(id)').length == 1 is true
+PASS document.querySelectorAll(':lang(id-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(id-ID)').length == 1 is true
+PASS document.querySelectorAll(':lang(id-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ID)').length == 1 is true
+
+- i-klingon
+PASS document.querySelectorAll(':lang(i)').length == 1 is true
+PASS document.querySelectorAll(':lang(i-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(i-klingon)').length == 1 is true
+PASS document.querySelectorAll(':lang(i-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-klingon)').length == 1 is true
+
+- ja-JP
+PASS document.querySelectorAll(':lang(ja)').length == 1 is true
+PASS document.querySelectorAll(':lang(ja-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ja-JP)').length == 1 is true
+PASS document.querySelectorAll(':lang(ja-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-JP)').length == 1 is true
+
+- ko-KR
+PASS document.querySelectorAll(':lang(ko)').length == 1 is true
+PASS document.querySelectorAll(':lang(ko-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ko-KR)').length == 1 is true
+PASS document.querySelectorAll(':lang(ko-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-KR)').length == 1 is true
+
+- lt-LT
+PASS document.querySelectorAll(':lang(lt)').length == 1 is true
+PASS document.querySelectorAll(':lang(lt-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(lt-LT)').length == 1 is true
+PASS document.querySelectorAll(':lang(lt-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-LT)').length == 1 is true
+
+- mi-NZ
+PASS document.querySelectorAll(':lang(mi)').length == 1 is true
+PASS document.querySelectorAll(':lang(mi-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(mi-NZ)').length == 1 is true
+PASS document.querySelectorAll(':lang(mi-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-NZ)').length == 1 is true
+
+- mn-MN
+PASS document.querySelectorAll(':lang(mn)').length == 1 is true
+PASS document.querySelectorAll(':lang(mn-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(mn-MN)').length == 1 is true
+PASS document.querySelectorAll(':lang(mn-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-MN)').length == 1 is true
+
+- nl-BE
+PASS document.querySelectorAll(':lang(nl)').length == 1 is true
+PASS document.querySelectorAll(':lang(nl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(nl-BE)').length == 1 is true
+PASS document.querySelectorAll(':lang(nl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BE)').length == 1 is true
+
+- nl-NL
+PASS document.querySelectorAll(':lang(nl)').length == 1 is true
+PASS document.querySelectorAll(':lang(nl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(nl-NL)').length == 1 is true
+PASS document.querySelectorAll(':lang(nl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-NL)').length == 1 is true
+
+- pl-PL
+PASS document.querySelectorAll(':lang(pl)').length == 1 is true
+PASS document.querySelectorAll(':lang(pl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(pl-PL)').length == 1 is true
+PASS document.querySelectorAll(':lang(pl-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-PL)').length == 1 is true
+
+- qu-BO
+PASS document.querySelectorAll(':lang(qu)').length == 1 is true
+PASS document.querySelectorAll(':lang(qu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(qu-BO)').length == 1 is true
+PASS document.querySelectorAll(':lang(qu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BO)').length == 1 is true
+
+- se-FI
+PASS document.querySelectorAll(':lang(se)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-FI)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-FI)').length == 1 is true
+
+- se-NO
+PASS document.querySelectorAll(':lang(se)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-NO)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-NO)').length == 1 is true
+
+- sgn-BE-FR
+PASS document.querySelectorAll(':lang(sgn)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-BE)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-\\*-FR)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-BE-FR)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-BE-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BE-FR)').length == 1 is true
+
+- sgn-BE-NL
+PASS document.querySelectorAll(':lang(sgn)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-BE)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-\\*-NL)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-BE-NL)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-BE-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-BE-NL)').length == 1 is true
+
+- sgn-CH-DE
+PASS document.querySelectorAll(':lang(sgn)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-\\*-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-CH-DE)').length == 1 is true
+PASS document.querySelectorAll(':lang(sgn-CH-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH-DE)').length == 1 is true
+
+- sr-Latn-RS
+PASS document.querySelectorAll(':lang(sr)').length == 1 is true
+PASS document.querySelectorAll(':lang(sr-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(sr-Latn)').length == 1 is true
+PASS document.querySelectorAll(':lang(sr-\\*-RS)').length == 1 is true
+PASS document.querySelectorAll(':lang(sr-Latn-RS)').length == 1 is true
+PASS document.querySelectorAll(':lang(sr-Latn-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Latn-RS)').length == 1 is true
+
+- sv-SE
+PASS document.querySelectorAll(':lang(sv)').length == 1 is true
+PASS document.querySelectorAll(':lang(sv-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(sv-SE)').length == 1 is true
+PASS document.querySelectorAll(':lang(sv-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-SE)').length == 1 is true
+
+- syr-SY
+PASS document.querySelectorAll(':lang(syr)').length == 1 is true
+PASS document.querySelectorAll(':lang(syr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(syr-SY)').length == 1 is true
+PASS document.querySelectorAll(':lang(syr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-SY)').length == 1 is true
+
+- tt-RU
+PASS document.querySelectorAll(':lang(tt)').length == 1 is true
+PASS document.querySelectorAll(':lang(tt-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(tt-RU)').length == 1 is true
+PASS document.querySelectorAll(':lang(tt-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-RU)').length == 1 is true
+
+- uz-UZ
+PASS document.querySelectorAll(':lang(uz)').length == 1 is true
+PASS document.querySelectorAll(':lang(uz-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(uz-UZ)').length == 1 is true
+PASS document.querySelectorAll(':lang(uz-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-UZ)').length == 1 is true
+
+- vi-VN
+PASS document.querySelectorAll(':lang(vi)').length == 1 is true
+PASS document.querySelectorAll(':lang(vi-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(vi-VN)').length == 1 is true
+PASS document.querySelectorAll(':lang(vi-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-VN)').length == 1 is true
+
+- xh-ZA
+PASS document.querySelectorAll(':lang(xh)').length == 1 is true
+PASS document.querySelectorAll(':lang(xh-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(xh-ZA)').length == 1 is true
+PASS document.querySelectorAll(':lang(xh-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ZA)').length == 1 is true
+
+- yue-Hant-HK
+PASS document.querySelectorAll(':lang(yue)').length == 1 is true
+PASS document.querySelectorAll(':lang(yue-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(yue-Hant)').length == 1 is true
+PASS document.querySelectorAll(':lang(yue-\\*-HK)').length == 1 is true
+PASS document.querySelectorAll(':lang(yue-Hant-HK)').length == 1 is true
+PASS document.querySelectorAll(':lang(yue-Hant-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Hant-HK)').length == 1 is true
+
+- zh-yue-Hant-HK
+PASS document.querySelectorAll(':lang(zh)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-yue)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*-Hant-HK)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-yue-Hant)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-yue-\\*-HK)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-yue-Hant-HK)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-yue-Hant-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-yue-Hant-HK)').length == 1 is true
+
+- zh-CN
+PASS document.querySelectorAll(':lang(zh)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-CN)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CN)').length == 1 is true
+
+- zh-Hant-CN
+PASS document.querySelectorAll(':lang(zh)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hant)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*-CN)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hant-CN)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hant-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Hant-CN)').length == 1 is true
+
+- zh-Hans-CN
+PASS document.querySelectorAll(':lang(zh)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hans)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*-CN)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hans-CN)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hans-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Hans-CN)').length == 1 is true
+
+- zh-Hant
+PASS document.querySelectorAll(':lang(zh)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-Hant)').length == 1 is true
+PASS document.querySelectorAll(':lang(zh-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-Hant)').length == 1 is true
+
+- de-CH
+PASS document.querySelectorAll(':lang(de)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(de-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH)').length == 1 is true
+
+- it-CH
+PASS document.querySelectorAll(':lang(it)').length == 1 is true
+PASS document.querySelectorAll(':lang(it-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(it-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(it-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH)').length == 2 is true
+
+- fr-CH
+PASS document.querySelectorAll(':lang(fr)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(fr-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH)').length == 3 is true
+
+- rm-CH
+PASS document.querySelectorAll(':lang(rm)').length == 1 is true
+PASS document.querySelectorAll(':lang(rm-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(rm-CH)').length == 1 is true
+PASS document.querySelectorAll(':lang(rm-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-CH)').length == 4 is true
+
+- de-CH,it-CH,fr-CH,rm-CH
+PASS document.querySelectorAll(':lang(*-CH)').length == 4 is true
+
+- hi-IN
+PASS document.querySelectorAll(':lang(hi)').length == 1 is true
+PASS document.querySelectorAll(':lang(hi-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(hi-IN)').length == 1 is true
+PASS document.querySelectorAll(':lang(hi-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-IN)').length == 1 is true
+
+- gu-IN
+PASS document.querySelectorAll(':lang(gu)').length == 1 is true
+PASS document.querySelectorAll(':lang(gu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(gu-IN)').length == 1 is true
+PASS document.querySelectorAll(':lang(gu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-IN)').length == 2 is true
+
+- kok-IN
+PASS document.querySelectorAll(':lang(kok)').length == 1 is true
+PASS document.querySelectorAll(':lang(kok-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(kok-IN)').length == 1 is true
+PASS document.querySelectorAll(':lang(kok-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-IN)').length == 3 is true
+
+- hi-IN,gu-IN,kok-IN
+PASS document.querySelectorAll(':lang(*-IN)').length == 3 is true
+
+- xa-ZA
+PASS document.querySelectorAll(':lang(xa)').length == 1 is true
+PASS document.querySelectorAll(':lang(xa-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(xa-ZA)').length == 1 is true
+PASS document.querySelectorAll(':lang(xa-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ZA)').length == 1 is true
+
+- zu-ZA
+PASS document.querySelectorAll(':lang(zu)').length == 1 is true
+PASS document.querySelectorAll(':lang(zu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(zu-ZA)').length == 1 is true
+PASS document.querySelectorAll(':lang(zu-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-ZA)').length == 2 is true
+
+- xa-ZA,zu-ZA
+PASS document.querySelectorAll(':lang(*-ZA)').length == 2 is true
+
+- se-FI
+PASS document.querySelectorAll(':lang(se)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-FI)').length == 1 is true
+PASS document.querySelectorAll(':lang(se-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-FI)').length == 1 is true
+
+- sv-FI
+PASS document.querySelectorAll(':lang(sv)').length == 1 is true
+PASS document.querySelectorAll(':lang(sv-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(sv-FI)').length == 1 is true
+PASS document.querySelectorAll(':lang(sv-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-FI)').length == 2 is true
+
+- se-FI,sv-FI
+PASS document.querySelectorAll(':lang(*-FI)').length == 2 is true
+
+- ur-PK
+PASS document.querySelectorAll(':lang(ur)').length == 1 is true
+PASS document.querySelectorAll(':lang(ur-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(ur-PK)').length == 1 is true
+PASS document.querySelectorAll(':lang(ur-\\*)').length == 1 is true
+PASS document.querySelectorAll(':lang(*-PK)').length == 1 is true
+
+- ur-PK
+PASS document.querySelectorAll(':lang(*-PK)').length == 1 is true
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/selectors/lang-valid-extended-filtering.html (0 => 177333)


--- trunk/LayoutTests/fast/selectors/lang-valid-extended-filtering.html	                        (rev 0)
+++ trunk/LayoutTests/fast/selectors/lang-valid-extended-filtering.html	2014-12-16 05:56:33 UTC (rev 177333)
@@ -0,0 +1,156 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<style id="style-container">
+</style>
+</head>
+<body>
+<div id="target"></div>
+<div id="target1"></div>
+<div id="target2"></div>
+<div id="target3"></div>
+<div id="target4"></div>
+</body>
+<script>
+description('Verify selector specifying extended filtering of :lang() pseudo class');
+
+function testValidLanguageSelectorOnSingleElement(language, length) {
+    var range = language;
+    var rangeSubtags = language.split('-');
+    var target = document.querySelector('#target').setAttribute('lang', language);
+    var currentRange = rangeSubtags[0];
+    var suffixRange = '';
+
+    length = length || 1;
+    debug('- ' + language);
+
+    for (var i = 0; i < rangeSubtags.length; i++){
+        shouldBeTrue("document.querySelectorAll(':lang(" + currentRange + ")').length == 1");
+        currentRange += '-' + rangeSubtags[i + 1];
+
+        var asteriskRange = rangeSubtags[0];
+        for (var j = 1; j < rangeSubtags.length; j++) {
+            if (i == j || i == 0) {
+                asteriskRange += '-\\\\*';
+            } else {
+                asteriskRange += '-' + rangeSubtags[j];  
+            }
+        }
+        shouldBeTrue("document.querySelectorAll(':lang(" + asteriskRange + ")').length == 1");
+        if (i > 0)
+            suffixRange += '-' + rangeSubtags[i];  
+    }
+    shouldBeTrue("document.querySelectorAll(':lang(*" + suffixRange + ")').length == " + length);
+    document.querySelector('#target').removeAttribute('lang');
+
+    debug('');
+}
+
+function testValidLanguageSelectorOnMultipleElementsWithFirstAsterisk(languages) {
+    for (var i = 0; i < languages.length; i++) {
+        var language = languages[i];
+        testValidLanguageSelectorOnSingleElement(language, i + 1);
+        document.querySelector('#target' + (i + 1)).setAttribute('lang', language);
+    }
+
+    debug('- ' + languages);
+    for (var i = 0; i < languages.length; i++) {
+        var language = languages[i];
+        var ranges = language.split('-');
+        var range = '*';
+        for (var j = 1; j < ranges.length; j++) {
+            range += '-' + ranges[j];
+        }
+    }
+    shouldBeTrue("document.querySelectorAll(':lang(" + range + ")').length == " + languages.length);
+    debug('');
+}
+
+var validLanguagesOnSingleElement = [
+    "af-ZA",
+    "ar-AE",
+    "ar-BH",
+    "ar-YE",
+    "art-lojban",
+    "az-Arab-IR",
+    "be-BY",
+    "bg-BG",
+    "ca-ES",
+    "cs-CZ",
+    "cy-GB",
+    "de-AT",
+    "de-CH",
+    "de-CH-1996",
+    "de-CH-1997",
+    "de-DE",
+    "de-DE-1996",
+    "de-Latn-DE",
+    "de-Latf-DE",
+    "de-Latn-DE-1996",
+    "en-US",
+    "en-GB",
+    "en-Latn-Brai",
+    "es-ES",
+    "es-ES-valencia",
+    "es-AR",
+    "fr-BE",
+    "fr-CA",
+    "fr-CH",
+    "fr-FR",
+    "fr-LU",
+    "fr-MC",
+    "gl-ES",
+    "gu-IN",
+    "hr-BA",
+    "id-ID",
+    "i-klingon",
+    "ja-JP",
+    "ko-KR",
+    "lt-LT",
+    "mi-NZ",
+    "mn-MN",
+    "nl-BE",
+    "nl-NL",
+    "pl-PL",
+    "qu-BO",
+    "se-FI",
+    "se-NO",
+    "sgn-BE-FR",
+    "sgn-BE-NL",
+    "sgn-CH-DE",
+    "sr-Latn-RS",
+    "sv-SE",
+    "syr-SY",
+    "tt-RU",
+    "uz-UZ",
+    "vi-VN",
+    "xh-ZA",
+    "yue-Hant-HK",
+    "zh-yue-Hant-HK",
+    "zh-CN",
+    "zh-Hant-CN",
+    "zh-Hans-CN",
+    "zh-Hant"
+];
+
+var validLanguagesOnMultipleElements = [
+    ["de-CH", "it-CH", "fr-CH", "rm-CH"],
+    ["hi-IN", "gu-IN", "kok-IN"],
+    ["xa-ZA", "zu-ZA"],
+    ["se-FI", "sv-FI"],
+    ["ur-PK"],
+];
+
+for (var i = 0; i < validLanguagesOnSingleElement.length; ++i) {
+    var lang = validLanguagesOnSingleElement[i];
+    testValidLanguageSelectorOnSingleElement(lang);
+}
+
+for (var i = 0; i < validLanguagesOnMultipleElements.length; ++i) {
+    var lang = validLanguagesOnMultipleElements[i];
+    testValidLanguageSelectorOnMultipleElementsWithFirstAsterisk(lang);
+}
+</script>
+<script src=""
+</html>

Modified: trunk/Source/WebCore/ChangeLog (177332 => 177333)


--- trunk/Source/WebCore/ChangeLog	2014-12-16 04:51:22 UTC (rev 177332)
+++ trunk/Source/WebCore/ChangeLog	2014-12-16 05:56:33 UTC (rev 177333)
@@ -1,3 +1,27 @@
+2014-12-15  Dhi Aurrahman  <[email protected]>
+
+        Extend :lang()'s selector checker to handle ranges with '*' properly and perform matching within the ASCII range
+        https://bugs.webkit.org/show_bug.cgi?id=139340
+
+        Reviewed by Benjamin Poulain.
+
+        Asterisk is considered as a valid subtag of a language range to express wildcard matching 
+        in :lang()'s extended filtering procedure. The matching rules introduced by language 
+        range with '*' is outlined in [1].
+
+        The matching of subtags is performed case-insensitively within the ASCII range[2].
+
+        [1] www.ietf.org/rfc/rfc4647.txt
+        [2] http://dev.w3.org/csswg/selectors4/#the-lang-pseudo
+
+        Test: fast/selectors/lang-equal-ignoring-case.html
+              fast/selectors/lang-valid-extended-filtering.html
+
+        * css/SelectorCheckerTestFunctions.h:
+        (WebCore::equalIgnoringCaseWithinASCIIRange): Handle matching case-insensitively within the ASCII range.
+        (WebCore::containslanguageSubtagMatchingRange):
+        (WebCore::matchesLangPseudoClass):
+
 2014-12-15  Chris Dumez  <[email protected]>
 
         [iOS] Add feature counting support

Modified: trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h (177332 => 177333)


--- trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h	2014-12-16 04:51:22 UTC (rev 177332)
+++ trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h	2014-12-16 05:56:33 UTC (rev 177333)
@@ -121,13 +121,24 @@
 }
 
 #if ENABLE(CSS_SELECTORS_LEVEL4)
+inline bool equalIgnoringASCIICase(const String& a, const String& b)
+{
+    if (a.length() != b.length()) 
+        return false;
+    for (size_t i = 0; i < a.length(); ++i) {
+        if (toASCIILower(a[i]) != toASCIILower(b[i]))
+            return false;
+    }
+    return true;
+}
+
 inline bool containslanguageSubtagMatchingRange(const Vector<String>& languageSubtags, const String& range, size_t& position)
 {
     for (size_t languageSubtagIndex = position; languageSubtagIndex < languageSubtags.size(); ++languageSubtagIndex) {
         const String& currentLanguageSubtag = languageSubtags[languageSubtagIndex];
-        if (currentLanguageSubtag.length() == 1)
+        if (currentLanguageSubtag.length() == 1 && range != "*")
             return false;
-        if (equalIgnoringCase(range, currentLanguageSubtag)) {
+        if (equalIgnoringASCIICase(range, currentLanguageSubtag) || range == "*") {
             position = languageSubtagIndex + 1;
             return true;
         }
@@ -158,15 +169,19 @@
     language.string().split('-', true, languageSubtags);
 
     for (const AtomicString& range : ranges) {
-        if (equalIgnoringCase(language, range) && !language.contains('-'))
+        if (range == "*")
             return true;
 
+        if (equalIgnoringASCIICase(language, range) && !language.contains('-'))
+            return true;
+
         range.string().split('-', true, rangeSubtags);
 
         if (rangeSubtags.size() > languageSubtags.size()) 
             continue;
 
-        if (!equalIgnoringCase(rangeSubtags.first(), languageSubtags.first())) 
+        const String& firstRangeSubtag = rangeSubtags.first();
+        if (!equalIgnoringASCIICase(firstRangeSubtag, languageSubtags.first()) && firstRangeSubtag != "*") 
             continue;
 
         size_t lastMatchedLanguageSubtagIndex = 1;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to