Change the exact attribute matching to be ASCII case-insensitive
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 15 Mar 2015 19:43:18 +0000 (19:43 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 15 Mar 2015 19:43:18 +0000 (19:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142609

Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-03-15
Reviewed by Darin Adler.

Source/WebCore:

In CSS, testing attribute values should be ASCII case-insensitive,
previously we were using full unicode case conversion.

Test: fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html

* css/CSSParser.cpp:
(WebCore::CSSParser::parseKeyframeSelector):
The CSS parser has its own fast version for ASCII case insensitive.
This code was using the general equalIgnoringASCIICase() which was causing name conflicts,
change that to the normal CSS parser version.

* css/SelectorCheckerTestFunctions.h:
(WebCore::equalIgnoringASCIICase): Deleted.
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeValueExactMatching):

Source/WTF:

Add support for ASCII case insensitive comparisons to all the string
classes.

The new file StringCommon.h has the common algorithm to avoid repeating
the same code with different types.

* WTF.vcxproj/WTF.vcxproj:
* WTF.vcxproj/WTF.vcxproj.filters:
* wtf/ASCIICType.h:
(WTF::toASCIILower):
* wtf/CMakeLists.txt:
* wtf/text/AtomicString.h:
(WTF::equalIgnoringASCIICase):
* wtf/text/StringCommon.h: Added.
(WTF::equalIgnoringASCIICase):
(WTF::equalIgnoringASCIICaseCommon):
* wtf/text/StringImpl.cpp:
(WTF::equalIgnoringASCIICase):
(WTF::equalIgnoringASCIICaseNonNull):
(WTF::StringImpl::utf8Impl):
(WTF::StringImpl::defaultWritingDirection): Deleted.
(WTF::StringImpl::adopt): Deleted.
(WTF::StringImpl::sizeInBytes): Deleted.
(WTF::putUTF8Triple): Deleted.
(WTF::StringImpl::utf8): Deleted.
* wtf/text/StringImpl.h:
(WTF::StringImpl::isSubString): Deleted.
(WTF::find): Deleted.

Tools:

* TestWebKitAPI/Tests/WTF/StringImpl.cpp:
* TestWebKitAPI/Tests/WTF/StringView.cpp:

LayoutTests:

* fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive-expected.txt: Added.
* fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html: Added.

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/WTF.vcxproj/WTF.vcxproj
Source/WTF/WTF.vcxproj/WTF.vcxproj.filters
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/ASCIICType.h
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/text/AtomicString.h
Source/WTF/wtf/text/StringCommon.h [new file with mode: 0644]
Source/WTF/wtf/text/StringImpl.cpp
Source/WTF/wtf/text/StringImpl.h
Source/WTF/wtf/text/StringView.h
Source/WTF/wtf/text/WTFString.h
Source/WebCore/ChangeLog
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/SelectorCheckerTestFunctions.h
Source/WebCore/cssjit/SelectorCompiler.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp
Tools/TestWebKitAPI/Tests/WTF/StringView.cpp

index ad460e3..6ecc71a 100644 (file)
@@ -1,3 +1,13 @@
+2015-03-15  Benjamin Poulain  <bpoulain@apple.com>
+
+        Change the exact attribute matching to be ASCII case-insensitive
+        https://bugs.webkit.org/show_bug.cgi?id=142609
+
+        Reviewed by Darin Adler.
+
+        * fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive-expected.txt: Added.
+        * fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html: Added.
+
 2015-03-15  Brent Fulgham  <bfulgham@apple.com>
 
         [Win] Document more debug assertions for later review.
diff --git a/LayoutTests/fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive-expected.txt b/LayoutTests/fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive-expected.txt
new file mode 100644 (file)
index 0000000..6fb5f8a
--- /dev/null
@@ -0,0 +1,213 @@
+When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior for exact value matching (e.g. [a="b"])
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing "[data-attribute=WebKit-É]"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-É]')[0].id is "target1"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=webkit-É]"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-É]')[0].id is "target2"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=WEBKIT-É]"
+PASS document.querySelectorAll('#test-root [data-attribute=WEBKIT-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute=WEBKIT-É]')[0].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=WebKit-é]"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-é]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-é]')[0].id is "target4"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=webkit-é]"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-é]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-é]')[0].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+Testing "[data-attribute=WebKit-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-É i]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=webkit-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-É i]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=WEBKIT-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute=WEBKIT-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute=WEBKIT-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute=WEBKIT-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute=WEBKIT-É i]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[data-attribute=WebKit-é i]"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-é i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-é i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute=WebKit-é i]')[1].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+Testing "[data-attribute=webkit-é i]"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-é i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-é i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute=webkit-é i]')[1].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+Testing "[multiple=WEBKIT-é]"
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[multiple=WebKit-é]"
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[multiple=Webkit-é]"
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[multiple=webkit-É]"
+PASS document.querySelectorAll('#test-root [multiple=webkit-É]').length is 2
+PASS document.querySelectorAll('#test-root [multiple=webkit-É]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple=webkit-É]')[1].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+Testing "[multiple=webKit-É]"
+PASS document.querySelectorAll('#test-root [multiple=webKit-É]').length is 2
+PASS document.querySelectorAll('#test-root [multiple=webKit-É]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple=webKit-É]')[1].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+Testing "[multiple=WEBKIT-é i]"
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple=WEBKIT-é i]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[multiple=WebKit-é i]"
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple=WebKit-é i]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[multiple=Webkit-é i]"
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple=Webkit-é i]')[2].id is "target3"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(255, 0, 0)"
+
+Testing "[multiple=webkit-É i]"
+PASS document.querySelectorAll('#test-root [multiple=webkit-É i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple=webkit-É i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple=webkit-É i]')[1].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+Testing "[multiple=webKit-É i]"
+PASS document.querySelectorAll('#test-root [multiple=webKit-É i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple=webKit-É i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple=webKit-É i]')[1].id is "target5"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[0]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[1]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[2]).backgroundColor is "rgb(255, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[3]).backgroundColor is "rgb(10, 100, 200)"
+PASS getComputedStyle(document.querySelectorAll("#test-root *")[4]).backgroundColor is "rgb(10, 100, 200)"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html b/LayoutTests/fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html
new file mode 100644 (file)
index 0000000..b260045
--- /dev/null
@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+#test-root * {
+    background-color: red;
+}
+</style>
+<style id="style">
+</style>
+</head>
+<body>
+    <div style="display:none" id="test-root">
+        <div data-attribute="WebKit-É" multiple="WEBKIT-é" id="target1"></div>
+        <div data-attribute="webkit-É" multiple="WebKit-é" id="target2"></div>
+        <div data-attribute="WEBKIT-É" multiple="Webkit-é" id="target3"></div>
+        <div data-attribute="WebKit-é" multiple="webkit-É" id="target4"></div>
+        <div data-attribute="webkit-é" multiple="webKit-É" id="target5"></div>
+    </div>
+</body>
+<script>
+description('When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior for exact value matching (e.g. [a="b"])');
+
+function testQuerySelector(selector, expectedIds) {
+    shouldBe("document.querySelectorAll('" + selector + "').length", '' + expectedIds.length);
+    for (var i = 0; i < expectedIds.length; ++i)
+        shouldBeEqualToString("document.querySelectorAll('" + selector + "')[" + i + "].id", 'target' + expectedIds[i]);
+}
+
+function testStyling(selector, expectedIds) {
+    var stylingElement = document.getElementById("style");
+    stylingElement.innerHTML = '' + selector + ' { background-color: rgb(10, 100, 200); }';
+
+    var allTestCases = document.querySelectorAll("#test-root *");
+    for (var i = 0; i < allTestCases.length; ++i) {
+        var expectMatch = expectedIds.indexOf(parseInt(allTestCases[i].id.replace('target', ''))) >= 0;
+        shouldBeEqualToString('getComputedStyle(document.querySelectorAll("#test-root *")[' + i + ']).backgroundColor', expectMatch ? 'rgb(10, 100, 200)' : 'rgb(255, 0, 0)');
+    }
+
+    stylingElement.innerHTML = '';
+}
+
+function testSelector(selector, expectedIds) {
+    debug("Testing \"" + selector + "\"");
+    testQuerySelector("#test-root " + selector, expectedIds);
+    testStyling("#test-root " + selector, expectedIds);
+    debug("");
+}
+
+var testCases = [
+    // Regular attribute matching is case sensitive.
+    ['[data-attribute=WebKit-É]', [1]],
+    ['[data-attribute=webkit-É]', [2]],
+    ['[data-attribute=WEBKIT-É]', [3]],
+    ['[data-attribute=WebKit-é]', [4]],
+    ['[data-attribute=webkit-é]', [5]],
+
+    // Same selectors with the case-insensitivie flag.
+    ['[data-attribute=WebKit-É i]', [1, 2, 3]],
+    ['[data-attribute=webkit-É i]', [1, 2, 3]],
+    ['[data-attribute=WEBKIT-É i]', [1, 2, 3]],
+    ['[data-attribute=WebKit-é i]', [4, 5]],
+    ['[data-attribute=webkit-é i]', [4, 5]],
+
+    // "multiple" is one of those weird legacy exception: it is always case insensitive in HTML.
+    ['[multiple=WEBKIT-é]', [1, 2, 3]],
+    ['[multiple=WebKit-é]', [1, 2, 3]],
+    ['[multiple=Webkit-é]', [1, 2, 3]],
+    ['[multiple=webkit-É]', [4, 5]],
+    ['[multiple=webKit-É]', [4, 5]],
+    ['[multiple=WEBKIT-é i]', [1, 2, 3]],
+    ['[multiple=WebKit-é i]', [1, 2, 3]],
+    ['[multiple=Webkit-é i]', [1, 2, 3]],
+    ['[multiple=webkit-É i]', [4, 5]],
+    ['[multiple=webKit-É i]', [4, 5]],
+];
+
+for (var testCase of testCases) {
+    testSelector(testCase[0], testCase[1]);
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
index 0818360..a45615b 100644 (file)
@@ -1,3 +1,39 @@
+2015-03-15  Benjamin Poulain  <bpoulain@apple.com>
+
+        Change the exact attribute matching to be ASCII case-insensitive
+        https://bugs.webkit.org/show_bug.cgi?id=142609
+
+        Reviewed by Darin Adler.
+
+        Add support for ASCII case insensitive comparisons to all the string
+        classes.
+
+        The new file StringCommon.h has the common algorithm to avoid repeating
+        the same code with different types.
+
+        * WTF.vcxproj/WTF.vcxproj:
+        * WTF.vcxproj/WTF.vcxproj.filters:
+        * wtf/ASCIICType.h:
+        (WTF::toASCIILower):
+        * wtf/CMakeLists.txt:
+        * wtf/text/AtomicString.h:
+        (WTF::equalIgnoringASCIICase):
+        * wtf/text/StringCommon.h: Added.
+        (WTF::equalIgnoringASCIICase):
+        (WTF::equalIgnoringASCIICaseCommon):
+        * wtf/text/StringImpl.cpp:
+        (WTF::equalIgnoringASCIICase):
+        (WTF::equalIgnoringASCIICaseNonNull):
+        (WTF::StringImpl::utf8Impl):
+        (WTF::StringImpl::defaultWritingDirection): Deleted.
+        (WTF::StringImpl::adopt): Deleted.
+        (WTF::StringImpl::sizeInBytes): Deleted.
+        (WTF::putUTF8Triple): Deleted.
+        (WTF::StringImpl::utf8): Deleted.
+        * wtf/text/StringImpl.h:
+        (WTF::StringImpl::isSubString): Deleted.
+        (WTF::find): Deleted.
+
 2015-03-14  Michael Saboff  <msaboff@apple.com>
 
         Disable Yarr JIT for ARMv7k
index f57cea6..d5eb555 100644 (file)
     <ClInclude Include="..\wtf\text\LChar.h" />
     <ClInclude Include="..\wtf\text\StringBuffer.h" />
     <ClInclude Include="..\wtf\text\StringBuilder.h" />
+    <ClInclude Include="..\wtf\text\StringCommon.h" />
     <ClInclude Include="..\wtf\text\StringConcatenate.h" />
     <ClInclude Include="..\wtf\text\StringHash.h" />
     <ClInclude Include="..\wtf\text\StringImpl.h" />
index 2493e5a..3b1dbcc 100644 (file)
     <ClInclude Include="..\wtf\text\StringBuilder.h">
       <Filter>text</Filter>
     </ClInclude>
+    <ClInclude Include="..\wtf\text\StringCommon.h">
+      <Filter>text</Filter>
+    </ClInclude>
     <ClInclude Include="..\wtf\text\StringConcatenate.h">
       <Filter>text</Filter>
     </ClInclude>
index 744b70a..a6ee1e8 100644 (file)
@@ -73,6 +73,7 @@
                2CDED0EF18115C38004DBA70 /* RunLoopCF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CDED0EE18115C38004DBA70 /* RunLoopCF.cpp */; };
                2CDED0F318115C85004DBA70 /* RunLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CDED0F118115C85004DBA70 /* RunLoop.cpp */; };
                2CDED0F418115C85004DBA70 /* RunLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CDED0F218115C85004DBA70 /* RunLoop.h */; };
+               430B47891AAAAC1A001223DA /* StringCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 430B47871AAAAC1A001223DA /* StringCommon.h */; };
                7CBBA07419BB7FDC00BBF025 /* OSObjectPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CBBA07319BB7FDC00BBF025 /* OSObjectPtr.h */; };
                7CDD7FF8186D291E007433CD /* IteratorAdaptors.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CDD7FF7186D291E007433CD /* IteratorAdaptors.h */; };
                7CDD7FFA186D2A54007433CD /* IteratorRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CDD7FF9186D2A54007433CD /* IteratorRange.h */; };
                2CDED0EE18115C38004DBA70 /* RunLoopCF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RunLoopCF.cpp; sourceTree = "<group>"; };
                2CDED0F118115C85004DBA70 /* RunLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RunLoop.cpp; sourceTree = "<group>"; };
                2CDED0F218115C85004DBA70 /* RunLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RunLoop.h; sourceTree = "<group>"; };
+               430B47871AAAAC1A001223DA /* StringCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringCommon.h; sourceTree = "<group>"; };
                5D247B6214689B8600E78B76 /* libWTF.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libWTF.a; sourceTree = BUILT_PRODUCTS_DIR; };
                5D247B6E14689C4700E78B76 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
                5D247B7014689C4700E78B76 /* DebugRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DebugRelease.xcconfig; sourceTree = "<group>"; };
                                A8A47323151A825B004123FF /* StringBuffer.h */,
                                A8A47324151A825B004123FF /* StringBuilder.cpp */,
                                A8A47325151A825B004123FF /* StringBuilder.h */,
+                               430B47871AAAAC1A001223DA /* StringCommon.h */,
                                A8A47326151A825B004123FF /* StringConcatenate.h */,
                                A8A47327151A825B004123FF /* StringHash.h */,
                                A8A47328151A825B004123FF /* StringImpl.cpp */,
                                83FBA93219DF459700F30ADB /* TypeCasts.h in Headers */,
                                1A6EB1E0187D0BD30030126F /* StringView.h in Headers */,
                                A8A47433151A825B004123FF /* TemporaryChange.h in Headers */,
+                               430B47891AAAAC1A001223DA /* StringCommon.h in Headers */,
                                A8A47444151A825B004123FF /* TextPosition.h in Headers */,
                                A8A47447151A825B004123FF /* ThreadFunctionInvocation.h in Headers */,
                                A8A47449151A825B004123FF /* ThreadIdentifierDataPthreads.h in Headers */,
index 20ed3ea..089d73f 100644 (file)
@@ -30,6 +30,7 @@
 #define WTF_ASCIICType_h
 
 #include <wtf/Assertions.h>
+#include <wtf/text/LChar.h>
 
 // The behavior of many of the functions in the <ctype.h> header is dependent
 // on the current locale. But in the WebKit project, all uses of those functions
 
 namespace WTF {
 
+const unsigned char asciiCaseFoldTable[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
 template<typename CharType> inline bool isASCII(CharType c)
 {
     return !(c & ~0x7F);
@@ -116,6 +136,18 @@ template<typename CharType> inline CharType toASCIILower(CharType c)
     return c | ((c >= 'A' && c <= 'Z') << 5);
 }
 
+template<>
+inline char toASCIILower(char c)
+{
+    return static_cast<char>(asciiCaseFoldTable[static_cast<unsigned char>(c)]);
+}
+
+template<>
+inline LChar toASCIILower(LChar c)
+{
+    return asciiCaseFoldTable[c];
+}
+
 template<typename CharType> inline CharType toASCIILowerUnchecked(CharType character)
 {
     // This function can be used for comparing any input character
index 0ed53bc..79e059f 100644 (file)
@@ -129,6 +129,7 @@ set(WTF_HEADERS
     text/IntegerToStringConversion.h
     text/LChar.h
     text/StringBuffer.h
+    text/StringCommon.h
     text/StringHash.h
     text/StringImpl.h
     text/StringView.h
index bae9d36..c9cdd68 100644 (file)
@@ -247,6 +247,10 @@ inline bool equalIgnoringCase(const LChar* a, const AtomicString& b) { return eq
 inline bool equalIgnoringCase(const char* a, const AtomicString& b) { return equalIgnoringCase(reinterpret_cast<const LChar*>(a), b.impl()); }
 inline bool equalIgnoringCase(const String& a, const AtomicString& b) { return equalIgnoringCase(a.impl(), b.impl()); }
 
+inline bool equalIgnoringASCIICase(const AtomicString& a, const AtomicString& b) { return equalIgnoringASCIICase(a.impl(), b.impl()); }
+inline bool equalIgnoringASCIICase(const AtomicString& a, const String& b) { return equalIgnoringASCIICase(a.impl(), b.impl()); }
+inline bool equalIgnoringASCIICase(const String& a, const AtomicString& b) { return equalIgnoringASCIICase(a.impl(), b.impl()); }
+
 // Define external global variables for the commonly used atomic strings.
 // These are only usable from the main thread.
 #ifndef ATOMICSTRING_HIDE_GLOBALS
diff --git a/Source/WTF/wtf/text/StringCommon.h b/Source/WTF/wtf/text/StringCommon.h
new file mode 100644 (file)
index 0000000..3ac9ff6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef StringCommon_h
+#define StringCommon_h
+
+#include <unicode/uchar.h>
+#include <wtf/ASCIICType.h>
+
+namespace WTF {
+
+template<typename CharacterTypeA, typename CharacterTypeB>
+inline bool equalIgnoringASCIICase(const CharacterTypeA* a, const CharacterTypeB* b, unsigned length)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if (toASCIILower(a[i]) != toASCIILower(b[i]))
+            return false;
+    }
+    return true;
+}
+
+template<typename StringClass>
+bool equalIgnoringASCIICaseCommon(const StringClass& a, const StringClass& b)
+{
+    unsigned length = a.length();
+    if (length != b.length())
+        return false;
+
+    if (a.is8Bit()) {
+        if (b.is8Bit())
+            return equalIgnoringASCIICase(a.characters8(), b.characters8(), length);
+
+        return equalIgnoringASCIICase(a.characters8(), b.characters16(), length);
+    }
+
+    if (b.is8Bit())
+        return equalIgnoringASCIICase(a.characters16(), b.characters8(), length);
+
+    return equalIgnoringASCIICase(a.characters16(), b.characters16(), length);
+}
+
+}
+
+#endif // StringCommon_h
index 0b7749a..9f593f7 100644 (file)
@@ -2018,6 +2018,27 @@ bool equalIgnoringNullity(StringImpl* a, StringImpl* b)
     return equal(a, b);
 }
 
+bool equalIgnoringASCIICase(const StringImpl& a, const StringImpl& b)
+{
+    return equalIgnoringASCIICaseCommon(a, b);
+}
+
+bool equalIgnoringASCIICase(const StringImpl* a, const StringImpl*b)
+{
+    if (a == b)
+        return true;
+    if (!a || !b)
+        return false;
+    return equalIgnoringASCIICaseCommon(*a, *b);
+}
+
+bool equalIgnoringASCIICaseNonNull(const StringImpl* a, const StringImpl* b)
+{
+    ASSERT(a);
+    ASSERT(b);
+    return equalIgnoringASCIICaseCommon(*a, *b);
+}
+
 UCharDirection StringImpl::defaultWritingDirection(bool* hasStrongDirectionality)
 {
     for (unsigned i = 0; i < m_length; ++i) {
index 667223e..d074064 100644 (file)
@@ -1132,6 +1132,10 @@ WTF_EXPORT_STRING_API bool equalIgnoringCaseNonNull(const StringImpl*, const Str
 WTF_EXPORT_STRING_API bool equalIgnoringNullity(StringImpl*, StringImpl*);
 WTF_EXPORT_STRING_API bool equalIgnoringNullity(const UChar*, size_t length, StringImpl*);
 
+WTF_EXPORT_STRING_API bool equalIgnoringASCIICase(const StringImpl&, const StringImpl&);
+WTF_EXPORT_STRING_API bool equalIgnoringASCIICase(const StringImpl*, const StringImpl*);
+WTF_EXPORT_STRING_API bool equalIgnoringASCIICaseNonNull(const StringImpl*, const StringImpl*);
+
 template<typename CharacterType>
 inline size_t find(const CharacterType* characters, unsigned length, CharacterType matchCharacter, unsigned index = 0)
 {
@@ -1374,6 +1378,7 @@ template<> struct DefaultHash<RefPtr<StringImpl>> {
 
 using WTF::StringImpl;
 using WTF::equal;
+using WTF::equalIgnoringASCIICase;
 using WTF::TextCaseSensitivity;
 using WTF::TextCaseSensitive;
 using WTF::TextCaseInsensitive;
index 6f94bf8..5ccb3ad 100644 (file)
@@ -31,6 +31,7 @@
 #include <wtf/RetainPtr.h>
 #include <wtf/Vector.h>
 #include <wtf/text/LChar.h>
+#include <wtf/text/StringCommon.h>
 
 // FIXME: Enabling the StringView lifetime checking causes the MSVC build to fail. Figure out why.
 // FIXME: Enable StringView lifetime checking once the underlying assertions have been fixed.
@@ -505,14 +506,7 @@ inline bool equal(StringView a, const char* b)
 
 inline bool equalIgnoringASCIICase(StringView a, StringView b)
 {
-    unsigned aLength = a.length();
-    if (aLength != b.length()) 
-        return false;
-    for (size_t i = 0; i < aLength; ++i) {
-        if (toASCIILower(a[i]) != toASCIILower(b[i]))
-            return false;
-    }
-    return true;
+    return equalIgnoringASCIICaseCommon(a, b);
 }
 
 class StringView::CodePoints {
index 84b605f..609a002 100644 (file)
@@ -492,6 +492,8 @@ inline bool equalIgnoringCase(const String& a, const char* b) { return equalIgno
 inline bool equalIgnoringCase(const LChar* a, const String& b) { return equalIgnoringCase(a, b.impl()); }
 inline bool equalIgnoringCase(const char* a, const String& b) { return equalIgnoringCase(reinterpret_cast<const LChar*>(a), b.impl()); }
 
+inline bool equalIgnoringASCIICase(const String& a, const String& b) { return equalIgnoringASCIICase(a.impl(), b.impl()); }
+
 inline bool equalPossiblyIgnoringCase(const String& a, const String& b, bool ignoreCase) 
 {
     return ignoreCase ? equalIgnoringCase(a, b) : (a == b);
index af94782..b3f2467 100644 (file)
@@ -1,3 +1,26 @@
+2015-03-15  Benjamin Poulain  <bpoulain@apple.com>
+
+        Change the exact attribute matching to be ASCII case-insensitive
+        https://bugs.webkit.org/show_bug.cgi?id=142609
+
+        Reviewed by Darin Adler.
+
+        In CSS, testing attribute values should be ASCII case-insensitive,
+        previously we were using full unicode case conversion.
+
+        Test: fast/selectors/attribute-exact-value-match-is-ascii-case-insensitive.html
+
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::parseKeyframeSelector):
+        The CSS parser has its own fast version for ASCII case insensitive.
+        This code was using the general equalIgnoringASCIICase() which was causing name conflicts,
+        change that to the normal CSS parser version.
+
+        * css/SelectorCheckerTestFunctions.h:
+        (WebCore::equalIgnoringASCIICase): Deleted.
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeValueExactMatching):
+
 2015-03-15  Brent Fulgham  <bfulgham@apple.com>
 
         scroll snap points do not properly account for zoomed pages
index d68a734..84bc151 100644 (file)
@@ -4774,9 +4774,9 @@ Vector<double> CSSParser::parseKeyframeSelector(const String& selector) {
         String cur = strings[i].stripWhiteSpace();
 
         // For now the syntax MUST be 'xxx%' or 'from' or 'to', where xxx is a legal floating point number
-        if (equalIgnoringASCIICase(cur, "from"))
+        if (equalIgnoringCase(cur, "from"))
             key = 0;
-        else if (equalIgnoringASCIICase(cur, "to"))
+        else if (equalIgnoringCase(cur, "to"))
             key = 1;
         else if (cur.endsWith('%')) {
             double k = cur.substring(0, cur.length() - 1).toDouble();
index 4d5fa37..ad7a1c9 100644 (file)
@@ -120,17 +120,6 @@ ALWAYS_INLINE bool isWindowInactive(const Element* element)
     return !element->document().page()->focusController().isActive();
 }
 
-ALWAYS_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;
-}
-
 ALWAYS_INLINE bool containslanguageSubtagMatchingRange(StringView language, StringView range, unsigned languageLength, unsigned& position)
 {
     unsigned languageSubtagsStartIndex = position;
index e705e98..d51c75a 100644 (file)
@@ -2934,7 +2934,7 @@ void SelectorCodeGenerator::generateElementAttributeValueExactMatching(Assembler
         m_assembler.loadPtr(Assembler::Address(currentAttributeAddress, Attribute::valueMemoryOffset()), valueStringImpl);
 
         FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
-        functionCall.setFunctionAddress(WTF::equalIgnoringCaseNonNull);
+        functionCall.setFunctionAddress(WTF::equalIgnoringASCIICaseNonNull);
         functionCall.setTwoArguments(valueStringImpl, expectedValueRegister);
         failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
 
@@ -2947,7 +2947,7 @@ void SelectorCodeGenerator::generateElementAttributeValueExactMatching(Assembler
 
         Assembler::Jump skipCaseInsensitiveComparison = m_assembler.branchPtr(Assembler::Equal, valueStringImpl, expectedValueRegister);
         FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
-        functionCall.setFunctionAddress(WTF::equalIgnoringCaseNonNull);
+        functionCall.setFunctionAddress(WTF::equalIgnoringASCIICaseNonNull);
         functionCall.setTwoArguments(valueStringImpl, expectedValueRegister);
         failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
         skipCaseInsensitiveComparison.link(&m_assembler);
index f4f8653..8ccbb3c 100644 (file)
@@ -1,3 +1,13 @@
+2015-03-15  Benjamin Poulain  <bpoulain@apple.com>
+
+        Change the exact attribute matching to be ASCII case-insensitive
+        https://bugs.webkit.org/show_bug.cgi?id=142609
+
+        Reviewed by Darin Adler.
+
+        * TestWebKitAPI/Tests/WTF/StringImpl.cpp:
+        * TestWebKitAPI/Tests/WTF/StringView.cpp:
+
 2015-03-13  Youenn Fablet  <youenn.fablet@crf.canon.fr>
 
         WebKit test infrastructure should automate the process of cloning W3C test suite and importing tests from it
index c2a332a..995d0d4 100644 (file)
@@ -99,4 +99,69 @@ TEST(WTF, StringImplReplaceWithLiteral)
     ASSERT_TRUE(equal(testStringImpl.get(), "r555sum555"));
 }
 
+TEST(WTF, StringImplEqualIgnoringASCIICaseBasic)
+{
+    RefPtr<StringImpl> a = StringImpl::createFromLiteral("aBcDeFG");
+    RefPtr<StringImpl> b = StringImpl::createFromLiteral("ABCDEFG");
+    RefPtr<StringImpl> c = StringImpl::createFromLiteral("abcdefg");
+    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    RefPtr<StringImpl> shorter = StringImpl::createFromLiteral("abcdef");
+
+    // Identity.
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), a.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(b.get(), b.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(c.get(), c.get()));
+
+    // Transitivity.
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), b.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(b.get(), c.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), c.get()));
+
+    // Negative cases.
+    ASSERT_FALSE(equalIgnoringASCIICase(a.get(), empty.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(b.get(), empty.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(c.get(), empty.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(a.get(), shorter.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(b.get(), shorter.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(c.get(), shorter.get()));
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseWithNull)
+{
+    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+    ASSERT_FALSE(equalIgnoringASCIICase(nullptr, reference.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(reference.get(), nullptr));
+    ASSERT_TRUE(equalIgnoringASCIICase(nullptr, nullptr));
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseWithEmpty)
+{
+    RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), b.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(b.get(), a.get()));
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseWithLatin1Characters)
+{
+    RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>("aBcéeFG"));
+    RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>("ABCÉEFG"));
+    RefPtr<StringImpl> c = StringImpl::create(reinterpret_cast<const LChar*>("ABCéEFG"));
+    RefPtr<StringImpl> d = StringImpl::create(reinterpret_cast<const LChar*>("abcéefg"));
+
+    // Identity.
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), a.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(b.get(), b.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(c.get(), c.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(d.get(), d.get()));
+
+    // All combination.
+    ASSERT_FALSE(equalIgnoringASCIICase(a.get(), b.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), c.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(a.get(), d.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(b.get(), c.get()));
+    ASSERT_FALSE(equalIgnoringASCIICase(b.get(), d.get()));
+    ASSERT_TRUE(equalIgnoringASCIICase(c.get(), d.get()));
+}
+
 } // namespace TestWebKitAPI
index dc55446..4d7d96c 100644 (file)
@@ -142,4 +142,77 @@ TEST(WTF, StringViewIterators)
     EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'}));
 }
 
+TEST(WTF, StringViewEqualIgnoringASCIICaseBasic)
+{
+    RefPtr<StringImpl> a = StringImpl::createFromLiteral("aBcDeFG");
+    RefPtr<StringImpl> b = StringImpl::createFromLiteral("ABCDEFG");
+    RefPtr<StringImpl> c = StringImpl::createFromLiteral("abcdefg");
+    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    RefPtr<StringImpl> shorter = StringImpl::createFromLiteral("abcdef");
+
+    StringView stringViewA(*a.get());
+    StringView stringViewB(*b.get());
+    StringView stringViewC(*c.get());
+    StringView emptyStringView(*empty.get());
+    StringView shorterStringView(*shorter.get());
+
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC));
+
+    // Identity.
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewA));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewB));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewC));
+
+    // Transitivity.
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewC));
+
+    // Negative cases.
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, emptyStringView));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, emptyStringView));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, emptyStringView));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, shorterStringView));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, shorterStringView));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, shorterStringView));
+}
+
+TEST(WTF, StringViewEqualIgnoringASCIICaseWithEmpty)
+{
+    RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    StringView stringViewA(*a.get());
+    StringView stringViewB(*b.get());
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewA));
+}
+
+TEST(WTF, StringViewEqualIgnoringASCIICaseWithLatin1Characters)
+{
+    RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>("aBcéeFG"));
+    RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>("ABCÉEFG"));
+    RefPtr<StringImpl> c = StringImpl::create(reinterpret_cast<const LChar*>("ABCéEFG"));
+    RefPtr<StringImpl> d = StringImpl::create(reinterpret_cast<const LChar*>("abcéefg"));
+    StringView stringViewA(*a.get());
+    StringView stringViewB(*b.get());
+    StringView stringViewC(*c.get());
+    StringView stringViewD(*d.get());
+
+    // Identity.
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewA));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewB));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewC));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewD, stringViewD));
+
+    // All combination.
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, stringViewB));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewC));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewD));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, stringViewC));
+    ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, stringViewD));
+    ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewD));
+}
+
 } // namespace TestWebKitAPI