Add selector checker for :lang pseudo class in Selectors level 4
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Nov 2014 08:57:13 +0000 (08:57 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Nov 2014 08:57:13 +0000 (08:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=138281

Patch by Dhi Aurrahman <diorahman@rockybars.com> on 2014-11-19
Reviewed by Benjamin Poulain.

Source/WebCore:

Add selector checker for :lang pseudo class in Selectors level 4.

The language tags matching is specified in [1,2].

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

Tests: fast/selectors/lang-extended-filtering.html
       fast/selectors/lang-multiple.html

* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne):
* css/SelectorCheckerTestFunctions.h:
(WebCore::containslanguageSubtagMatchingRange):
(WebCore::matchesLangPseudoClass):
(WebCore::matchesLangPseudoClassDeprecated):
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::addPseudoClassType):

LayoutTests:

Add layout tests for :lang selector checker with multiple arguments and
related behaviors on extended filtering.

* fast/selectors/lang-extended-filtering-expected.txt: Added.
* fast/selectors/lang-extended-filtering.html: Added.
* fast/selectors/lang-multiple-expected.txt: Added.
* fast/selectors/lang-multiple.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@176313 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/selectors/lang-extended-filtering-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/lang-extended-filtering.html [new file with mode: 0644]
LayoutTests/fast/selectors/lang-multiple-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/lang-multiple.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/css/SelectorCheckerTestFunctions.h
Source/WebCore/cssjit/SelectorCompiler.cpp

index 121405b..e0ced9d 100644 (file)
@@ -1,3 +1,18 @@
+2014-11-19  Dhi Aurrahman  <diorahman@rockybars.com>
+
+        Add selector checker for :lang pseudo class in Selectors level 4
+        https://bugs.webkit.org/show_bug.cgi?id=138281
+
+        Reviewed by Benjamin Poulain.
+
+        Add layout tests for :lang selector checker with multiple arguments and
+        related behaviors on extended filtering.
+
+        * fast/selectors/lang-extended-filtering-expected.txt: Added.
+        * fast/selectors/lang-extended-filtering.html: Added.
+        * fast/selectors/lang-multiple-expected.txt: Added.
+        * fast/selectors/lang-multiple.html: Added.
+
 2014-11-18  Philippe Normand  <pnormand@igalia.com>
 
         start/stop method for AudioBufferSourceNodes and OscillatorNodes can take no args
diff --git a/LayoutTests/fast/selectors/lang-extended-filtering-expected.txt b/LayoutTests/fast/selectors/lang-extended-filtering-expected.txt
new file mode 100644 (file)
index 0000000..8667455
--- /dev/null
@@ -0,0 +1,88 @@
+Verify selector specifying extended filetring of :lang() pseudo class
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll(":lang(en)").length is 3
+PASS document.querySelectorAll(":lang(en-)").length is 2
+PASS document.querySelectorAll(":lang(en--)").length is 1
+PASS document.querySelectorAll(":lang(en---)").length is 0
+
+PASS document.querySelectorAll(":lang(de-de)").length is 8
+PASS document.querySelectorAll(":lang(de-DE)").length is 8
+PASS document.querySelectorAll(":lang(DE-de)").length is 8
+PASS document.querySelectorAll(":lang(dE-dE)").length is 8
+PASS document.querySelectorAll(":lang(De-De)").length is 8
+PASS document.querySelectorAll(":lang(DE-DE)").length is 8
+PASS document.querySelectorAll(":lang(Latn)").length is 1
+PASS document.querySelectorAll(":lang(Latf)").length is 1
+PASS document.querySelectorAll(":lang(Latn-de)").length is 1
+PASS document.querySelectorAll(":lang(Latf-de)").length is 1
+PASS document.querySelectorAll(":lang(Latn, de-Latn)").length is 3
+PASS document.querySelectorAll(":lang(Latf, de-Latf)").length is 2
+PASS document.querySelectorAll(":lang(Latn, de-Latn)").length is 3
+PASS document.querySelectorAll(":lang(Latf, de-Latf)").length is 2
+PASS document.querySelectorAll(":lang(de-DE--, de-DE--)").length is 0
+
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[0]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[1]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[2]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[3]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[4]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[5]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[6]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[7]).color is "rgb(1, 2, 3)"
+
+PASS getComputedStyle(document.querySelector(":lang(Latn, Latn-de)")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelector(":lang(Latf, Latf-de)")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color is "rgb(0, 0, 0)"
+
+PASS document.querySelectorAll(":lang(-en)").length is 0
+PASS document.querySelectorAll(":lang(-en-)").length is 0
+PASS document.querySelectorAll(":lang(-en--)").length is 0
+PASS document.querySelectorAll(":lang(-en, -en-)").length is 0
+PASS document.querySelectorAll(":lang(-en-, -en--)").length is 0
+
+PASS document.querySelectorAll(":lang(fr-x)").length is 0
+PASS document.querySelectorAll(":lang(fr-xenomorph)").length is 1
+
+PASS document.querySelectorAll(":lang(cocoa-1)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-a)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-bar)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-1, cocoa-bar)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-a, cocoa-bar)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-1, cocoa-a)").length is 0
+
+PASS document.querySelectorAll(":lang(foo)").length is 1
+PASS document.querySelectorAll(":lang(foo-bar)").length is 1
+PASS document.querySelectorAll(":lang(foo--bar)").length is 1
+PASS document.querySelectorAll(":lang(foo-)").length is 1
+PASS document.querySelectorAll(":lang(foo--)").length is 0
+PASS document.querySelectorAll(":lang(foo, foo-)").length is 1
+PASS document.querySelectorAll(":lang(foo-bar, foo--bar)").length is 1
+PASS document.querySelectorAll(":lang(foo-, foo--)").length is 1
+PASS document.querySelectorAll(":lang(foo--, foo---)").length is 0
+PASS document.querySelectorAll(":lang(id)").length is 1
+PASS document.querySelectorAll(":lang(id-)").length is 1
+PASS document.querySelectorAll(":lang(id--)").length is 1
+PASS document.querySelectorAll(":lang(id---Java)").length is 1
+PASS document.querySelectorAll(":lang(id--Java)").length is 1
+PASS document.querySelectorAll(":lang(id-Java)").length is 1
+PASS document.querySelectorAll(":lang(id---)").length is 0
+PASS document.querySelectorAll(":lang(id, id-)").length is 1
+PASS document.querySelectorAll(":lang(id-, id--)").length is 1
+PASS document.querySelectorAll(":lang(id--Java, id---Java)").length is 1
+PASS document.querySelectorAll(":lang(id-Java, id--Java)").length is 1
+PASS document.querySelectorAll(":lang(id---, id----)").length is 0
+
+PASS document.querySelectorAll(":lang(tic-tac-toe)").length is 1
+PASS document.querySelectorAll(":lang(tic-toe-tac)").length is 0
+
+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 successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/lang-extended-filtering.html b/LayoutTests/fast/selectors/lang-extended-filtering.html
new file mode 100644 (file)
index 0000000..f511caa
--- /dev/null
@@ -0,0 +1,147 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+:lang(de-DE, DE-de) {
+    color: rgb(1, 2, 3);
+}
+</style>
+</head>
+<body>
+    <div lang="en"></div>
+    <div lang="en-"></div>
+    <div lang="en--"></div>
+
+    <div lang="de-DE" class="de"></div>
+    <div lang="de-de" class="de"></div>
+    <div lang="de-De" class="de"></div>
+    <div lang="de-dE" class="de"></div>
+    <div lang="de-DE-1996" class="de"></div>
+    <div lang="de-Latn-DE" class="de"></div>
+    <div lang="de-Latf-DE" class="de"></div>
+    <div lang="de-Latn-DE-1996" class="de"></div>
+
+    <div lang="Latn-de"></div>
+    <div lang="Latf-de"></div>
+
+    <div lang="foo--bar"></div>
+    <div lang="id---Java"></div>
+
+    <div lang="tic-tac-tac-toe"></div>
+
+    <div lang="fr-x"></div>
+    <div lang="fr-xenomorph"></div>
+    <div lang="fr-x-xenomorph"></div>
+
+    <div lang="cocoa-1-bar"></div>
+    <div lang="cocoa-a-bar"></div>
+
+    <script>
+    description('Verify selector specifying extended filetring of :lang() pseudo class');
+
+    shouldBe('document.querySelectorAll(":lang(en)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(en-)").length', '2');
+    shouldBe('document.querySelectorAll(":lang(en--)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(en---)").length', '0');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(de-de)").length', '8');
+    shouldBe('document.querySelectorAll(":lang(de-DE)").length', '8');
+    shouldBe('document.querySelectorAll(":lang(DE-de)").length', '8');
+    shouldBe('document.querySelectorAll(":lang(dE-dE)").length', '8');
+    shouldBe('document.querySelectorAll(":lang(De-De)").length', '8');
+    shouldBe('document.querySelectorAll(":lang(DE-DE)").length', '8');
+
+    shouldBe('document.querySelectorAll(":lang(Latn)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(Latf)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(Latn-de)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(Latf-de)").length', '1');
+
+    shouldBe('document.querySelectorAll(":lang(Latn, de-Latn)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(Latf, de-Latf)").length', '2');
+
+    shouldBe('document.querySelectorAll(":lang(Latn, de-Latn)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(Latf, de-Latf)").length', '2');
+
+    shouldBe('document.querySelectorAll(":lang(de-DE--, de-DE--)").length', '0');
+
+    debug('');
+
+    for (var i = 0; i < document.querySelectorAll(":lang(de-DE, DE-de)").length; i++) {
+        shouldBeEqualToString('getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[' + i + ']).color', 'rgb(1, 2, 3)');
+    }
+
+    debug('');
+
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latn, Latn-de)")).color', 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latf, Latf-de)")).color', 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color', 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color', 'rgb(0, 0, 0)');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(-en)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(-en-)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(-en--)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(-en, -en-)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(-en-, -en--)").length', '0');
+
+    debug(''); 
+
+    shouldBe('document.querySelectorAll(":lang(fr-x)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-xenomorph)").length', '1');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(cocoa-1)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(cocoa-a)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(cocoa-bar)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(cocoa-1, cocoa-bar)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(cocoa-a, cocoa-bar)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(cocoa-1, cocoa-a)").length', '0');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(foo)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo-bar)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo--bar)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo-)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo--)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(foo, foo-)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo-bar, foo--bar)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo-, foo--)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo--, foo---)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(id)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id-)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id--)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id---Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id--Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id-Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id---)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(id, id-)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id-, id--)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id--Java, id---Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id-Java, id--Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id---, id----)").length', '0');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(tic-tac-toe)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(tic-toe-tac)").length', '0');
+
+    debug('');
+
+    shouldThrow('document.querySelectorAll(":lang(--en--)").length');
+    shouldThrow('document.querySelectorAll(":lang(---en---)").length');
+    shouldThrow('document.querySelectorAll(":lang(en us- de- fr-).length")');
+    </script>
+    <script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/fast/selectors/lang-multiple-expected.txt b/LayoutTests/fast/selectors/lang-multiple-expected.txt
new file mode 100644 (file)
index 0000000..c93fd01
--- /dev/null
@@ -0,0 +1,71 @@
+Verify selector specifying multiple :lang() pseudo class using comma-separated list arguments.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll(":lang(en, en)").length is 3
+PASS document.querySelectorAll(":lang(en-, en-)").length is 2
+PASS document.querySelectorAll(":lang(en--, en--)").length is 1
+PASS document.querySelectorAll(":lang(en---, en---)").length is 0
+
+PASS document.querySelectorAll(":lang(de-DE, DE-de)").length is 8
+PASS document.querySelectorAll(":lang(Latn, de-Latn)").length is 3
+PASS document.querySelectorAll(":lang(Latf, de-Latf)").length is 2
+PASS document.querySelectorAll(":lang(de-DE--, de-DE--)").length is 0
+
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[0]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[1]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[2]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[3]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[4]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[5]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[6]).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[7]).color is "rgb(1, 2, 3)"
+
+PASS getComputedStyle(document.querySelector(":lang(Latn, Latn-de)")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelector(":lang(Latf, Latf-de)")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color is "rgb(0, 0, 0)"
+PASS document.querySelectorAll(":lang(ab)").length is 1
+PASS document.querySelectorAll(":lang(cd)").length is 1
+PASS document.querySelectorAll(":lang(ef)").length is 1
+PASS document.querySelectorAll(":lang(ab, cd)").length is 2
+PASS document.querySelectorAll(":lang(ab, cd, ef)").length is 3
+PASS getComputedStyle(document.querySelectorAll(":lang(ab, cd)")[1]).color is "rgb(3, 2, 1)"
+PASS getComputedStyle(document.querySelectorAll(":lang(ab, cd, ef)")[2]).color is "rgb(3, 2, 1)"
+
+PASS document.querySelectorAll(":lang(-en, -en)").length is 0
+PASS document.querySelectorAll(":lang(-en-, -en-)").length is 0
+PASS document.querySelectorAll(":lang(-en, -en-)").length is 0
+PASS document.querySelectorAll(":lang(-en-, -en--)").length is 0
+
+PASS document.querySelectorAll(":lang(fr-x, fr-x)").length is 0
+PASS document.querySelectorAll(":lang(fr-xenomorph, fr-xenomorph)").length is 1
+
+PASS document.querySelectorAll(":lang(cocoa-1, cocoa-1)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-a, cocoa-a)").length is 0
+PASS document.querySelectorAll(":lang(cocoa-bar, cocoa-bar)").length is 0
+
+PASS document.querySelectorAll(":lang(foo, foo)").length is 1
+PASS document.querySelectorAll(":lang(foo-bar, foo-bar)").length is 1
+PASS document.querySelectorAll(":lang(foo--bar, foo--bar)").length is 1
+PASS document.querySelectorAll(":lang(foo-, foo-)").length is 1
+PASS document.querySelectorAll(":lang(foo--, foo--)").length is 0
+PASS document.querySelectorAll(":lang(id, id)").length is 1
+PASS document.querySelectorAll(":lang(id-, id-)").length is 1
+PASS document.querySelectorAll(":lang(id--, id--)").length is 1
+PASS document.querySelectorAll(":lang(id---Java, id---Java)").length is 1
+PASS document.querySelectorAll(":lang(id--Java, id--Java)").length is 1
+PASS document.querySelectorAll(":lang(id-Java, id-Java)").length is 1
+PASS document.querySelectorAll(":lang(id---, id---)").length is 0
+
+PASS document.querySelectorAll(":lang(tic-tac-toe, tic-tac)").length is 1
+PASS document.querySelectorAll(":lang(tic-toe-tac, tic-toe-tac-tac)").length is 0
+
+PASS document.querySelectorAll(":lang(--en--, --en--)").length threw exception Error: SyntaxError: DOM Exception 12.
+PASS document.querySelectorAll(":lang(---en---, ---en---)").length threw exception Error: SyntaxError: DOM Exception 12.
+PASS document.querySelectorAll(":lang(en us- de- fr-, en us- de- fr-).length") threw exception Error: SyntaxError: DOM Exception 12.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/lang-multiple.html b/LayoutTests/fast/selectors/lang-multiple.html
new file mode 100644 (file)
index 0000000..7571127
--- /dev/null
@@ -0,0 +1,134 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+:lang(de-DE, DE-de) {
+    color: rgb(1, 2, 3);
+}
+:lang(ab, cd, ef) {
+    color: rgb(3, 2, 1);
+}
+</style>
+</head>
+<body>
+    <div lang="de-DE" class="de"></div>
+    <div lang="de-de" class="de"></div>
+    <div lang="de-De" class="de"></div>
+    <div lang="de-dE" class="de"></div>
+    <div lang="de-DE-1996" class="de"></div>
+    <div lang="de-Latn-DE" class="de"></div>
+    <div lang="de-Latf-DE" class="de"></div>
+    <div lang="de-Latn-DE-1996" class="de"></div>
+
+    <div lang="Latn-de"></div>
+    <div lang="Latf-de"></div>
+
+    <div lang="en"></div>
+    <div lang="en-"></div>
+    <div lang="en--"></div>
+
+    <div lang="foo--bar"></div>
+    <div lang="id---Java"></div>
+
+    <div lang="tic-tac-tac-toe"></div>
+
+    <div lang="fr-x"></div>
+    <div lang="fr-xenomorph"></div>
+    <div lang="fr-x-xenomorph"></div>
+
+    <div lang="cocoa-1-bar"></div>
+    <div lang="cocoa-a-bar"></div>
+
+    <div lang="ab"></div>
+    <div lang="cd"></div>
+    <div lang="ef"></div>
+
+    <script>
+    description('Verify selector specifying multiple :lang() pseudo class using comma-separated list arguments.');
+
+    shouldBe('document.querySelectorAll(":lang(en, en)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(en-, en-)").length', '2');
+    shouldBe('document.querySelectorAll(":lang(en--, en--)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(en---, en---)").length', '0');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(de-DE, DE-de)").length', '8');
+    shouldBe('document.querySelectorAll(":lang(Latn, de-Latn)").length', '3');
+    shouldBe('document.querySelectorAll(":lang(Latf, de-Latf)").length', '2');
+
+    shouldBe('document.querySelectorAll(":lang(de-DE--, de-DE--)").length', '0');
+
+    debug('');
+
+    for (var i = 0; i < document.querySelectorAll(":lang(de-DE, DE-de)").length; i++) {
+        shouldBeEqualToString('getComputedStyle(document.querySelectorAll(":lang(de-DE, DE-de)")[' + i + ']).color', 'rgb(1, 2, 3)');
+    }
+
+    debug('');
+
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latn, Latn-de)")).color', 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latf, Latf-de)")).color', 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color', 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelector(":lang(Latf, Latn)")).color', 'rgb(0, 0, 0)');
+
+    shouldBe('document.querySelectorAll(":lang(ab)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(cd)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(ef)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(ab, cd)").length', '2');
+    shouldBe('document.querySelectorAll(":lang(ab, cd, ef)").length', '3');
+
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll(":lang(ab, cd)")[1]).color', 'rgb(3, 2, 1)');
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll(":lang(ab, cd, ef)")[2]).color', 'rgb(3, 2, 1)');
+
+    debug('');
+    
+    shouldBe('document.querySelectorAll(":lang(-en, -en)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(-en-, -en-)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(-en, -en-)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(-en-, -en--)").length', '0');
+
+    debug(''); 
+
+    shouldBe('document.querySelectorAll(":lang(fr-x, fr-x)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(fr-xenomorph, fr-xenomorph)").length', '1');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(cocoa-1, cocoa-1)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(cocoa-a, cocoa-a)").length', '0');
+    shouldBe('document.querySelectorAll(":lang(cocoa-bar, cocoa-bar)").length', '0');
+
+    debug('');
+
+    shouldBe('document.querySelectorAll(":lang(foo, foo)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo-bar, foo-bar)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo--bar, foo--bar)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo-, foo-)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(foo--, foo--)").length', '0');
+
+    shouldBe('document.querySelectorAll(":lang(id, id)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id-, id-)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id--, id--)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id---Java, id---Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id--Java, id--Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id-Java, id-Java)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(id---, id---)").length', '0');
+
+    debug(''); 
+
+    shouldBe('document.querySelectorAll(":lang(tic-tac-toe, tic-tac)").length', '1');
+    shouldBe('document.querySelectorAll(":lang(tic-toe-tac, tic-toe-tac-tac)").length', '0');
+
+    debug('')
+
+    shouldThrow('document.querySelectorAll(":lang(--en--, --en--)").length');
+    shouldThrow('document.querySelectorAll(":lang(---en---, ---en---)").length');
+    shouldThrow('document.querySelectorAll(":lang(en us- de- fr-, en us- de- fr-).length")');
+
+    </script>
+
+    <script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
index e3c0f91..de26bde 100644 (file)
@@ -1,3 +1,29 @@
+2014-11-19  Dhi Aurrahman  <diorahman@rockybars.com>
+
+        Add selector checker for :lang pseudo class in Selectors level 4
+        https://bugs.webkit.org/show_bug.cgi?id=138281
+
+        Reviewed by Benjamin Poulain.
+
+        Add selector checker for :lang pseudo class in Selectors level 4.
+
+        The language tags matching is specified in [1,2].
+
+        [1] http://www.ietf.org/rfc/rfc4647.txt
+        [2] http://dev.w3.org/csswg/selectors4/#the-lang-pseudo
+
+        Tests: fast/selectors/lang-extended-filtering.html
+               fast/selectors/lang-multiple.html
+
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOne):
+        * css/SelectorCheckerTestFunctions.h:
+        (WebCore::containslanguageSubtagMatchingRange):
+        (WebCore::matchesLangPseudoClass):
+        (WebCore::matchesLangPseudoClassDeprecated):
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::addPseudoClassType):
+
 2014-11-18  Philippe Normand  <pnormand@igalia.com>
 
         start/stop method for AudioBufferSourceNodes and OscillatorNodes can take no args
index eb3653b..deddff5 100644 (file)
@@ -967,13 +967,13 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context, PseudoI
             {
 #if ENABLE(CSS_SELECTORS_LEVEL4)
                 ASSERT(selector->argumentList() && !selector->argumentList()->isEmpty());
-                const AtomicString& argument = selector->argumentList()->first();
+                return matchesLangPseudoClass(element, *selector->argumentList());
 #else
-                const AtomicString& argument = selector->argument();
-#endif                
+                const AtomicString& argument = selector->argument();      
                 if (argument.isNull())
                     return false;
-                return matchesLangPseudoClass(element, argument.impl());
+                return matchesLangPseudoClassDeprecated(element, argument.impl());
+#endif                          
             }
 #if ENABLE(FULLSCREEN_API)
         case CSSSelector::PseudoClassFullScreen:
index 7f9d808..c74311e 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Dhi Aurrahman <diorahman@rockybars.com>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -118,8 +119,72 @@ ALWAYS_INLINE bool isWindowInactive(const Element* element)
 {
     return !element->document().page()->focusController().isActive();
 }
+
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+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)
+            return false;
+        if (equalIgnoringCase(range, currentLanguageSubtag)) {
+            position = languageSubtagIndex + 1;
+            return true;
+        }
+    }
+    return false;
+}
+
+inline bool matchesLangPseudoClass(const Element* element, const Vector<AtomicString>& ranges)
+{
+    ASSERT(element);
+
+    AtomicString language;
+#if ENABLE(VIDEO_TRACK)
+    if (is<WebVTTElement>(*element))
+        language = downcast<WebVTTElement>(*element).language();
+    else
+#endif
+        language = element->computeInheritedLanguage();
+
+    if (language.isNull())
+        return false;
+
+    // Implement basic and extended filterings of given language tags 
+    // as specified in www.ietf.org/rfc/rfc4647.txt.
+    Vector<String> rangeSubtags;
+    Vector<String> languageSubtags;
+
+    language.string().split('-', true, languageSubtags);
+
+    for (const AtomicString& range : ranges) {
+        if (equalIgnoringCase(language, range) && !language.contains('-'))
+            return true;
+
+        range.string().split('-', true, rangeSubtags);
+
+        if (rangeSubtags.size() > languageSubtags.size()) 
+            continue;
+
+        if (!equalIgnoringCase(rangeSubtags.first(), languageSubtags.first())) 
+            continue;
+
+        size_t lastMatchedLanguageSubtagIndex = 1;
+        bool matchedRange = true;
+        for (size_t rangeSubtagIndex = 1; rangeSubtagIndex < rangeSubtags.size(); ++rangeSubtagIndex) {
+            if (!containslanguageSubtagMatchingRange(languageSubtags, rangeSubtags[rangeSubtagIndex], lastMatchedLanguageSubtagIndex)) {
+                matchedRange = false;
+                break;
+            }
+        }
+        if (matchedRange)
+            return true;
+    }
+    return false;
+}
+#endif
     
-inline bool matchesLangPseudoClass(const Element* element, AtomicStringImpl* filter)
+inline bool matchesLangPseudoClassDeprecated(const Element* element, AtomicStringImpl* filter)
 {
     AtomicString value;
 #if ENABLE(VIDEO_TRACK)
index d112280..2961eaa 100644 (file)
@@ -734,11 +734,10 @@ static inline FunctionType addPseudoClassType(const CSSSelector& selector, Selec
     case CSSSelector::PseudoClassLang:
         {
 #if ENABLE(CSS_SELECTORS_LEVEL4)
-            ASSERT(selector.argumentList() && !selector.argumentList()->isEmpty());
-            const AtomicString& argument = selector.argumentList()->first();
+            return FunctionType::CannotCompile;
 #else
             const AtomicString& argument = selector.argument();
-#endif
+
             if (argument.isEmpty())
                 return FunctionType::CannotMatchAnything;
 
@@ -754,6 +753,7 @@ static inline FunctionType addPseudoClassType(const CSSSelector& selector, Selec
                     return FunctionType::CannotMatchAnything;
             }
             return FunctionType::SimpleSelectorChecker;
+#endif
         }
 
 #if ENABLE(CSS_SELECTORS_LEVEL4)
@@ -3146,7 +3146,7 @@ void SelectorCodeGenerator::generateElementIsInLanguage(Assembler::JumpList& fai
 
     Assembler::RegisterID elementAddress = elementAddressRegister;
     FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
-    functionCall.setFunctionAddress(matchesLangPseudoClass);
+    functionCall.setFunctionAddress(matchesLangPseudoClassDeprecated);
     functionCall.setTwoArguments(elementAddress, langFilterRegister);
     failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
 }