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 ad460e325cc0a5fcb87d0fa008c21ce941c0a227..6ecc71a1249a22a6e5829854508321638d9947d2 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 08183600e38b372b3680b39ebdab2810a4e1a27a..a45615b88a843def704f34e1525e683a3f27d710 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 f57cea6d52b466fdbc20a4862a125d5fb4b5d380..d5eb55509e272531d9ed16f465ff14d134f95bbb 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 2493e5ade84437bf8e04d063170820ca3ddfee6b..3b1dbcc8a8902070646d9c968df04d9ac7434f9b 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 744b70ae59575ef0d8596bcc9067949697e7e0fc..a6ee1e8dff75fce57cd53788da9a99cd6d20c76b 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 20ed3eae2e2ca972e4bf0eb5c13839690e6293a6..089d73f392e7756d60d9c5ca83e7482bf4c811ec 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 0ed53bce4de6d1af513e0bd45312fd9367781476..79e059f0fdc7cca6d5540dcb5bebd999cf415fb6 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 bae9d362d2751e5a73ce0b1b7e2a411b01837f33..c9cdd68092ed57e000d252ebbf16a78c24ff1883 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 0b7749af8f92fbe65964dcc83de1cec5873c092f..9f593f76caba3438c6408f1c6ea438f2b4d8d1e4 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 667223e6da75d12b53174410903a3b3a7631a476..d074064f65c426567350327484e55a1987b657dc 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 6f94bf893faef6983bf038e34e8f0023c2005bc9..5ccb3ad7e1328a056c7e76aecbf06314afa5f843 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 84b605f6172fcdfc7be32fd893e877ceac20d489..609a00244a5dca810806ac55c2514a62e2bd80ee 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 af9478270240cef5ff54c490cdae0869e8c71572..b3f2467f6d426b7cfdf405a6ede2c6af6f54ee04 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 d68a7346da20a6cd0601f8258f546c59b9738d3e..84bc151709305a7282c918a5a88775022569ea4b 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 4d5fa3710834f513d45a505d359de4fb9835d11d..ad7a1c92fcbb32d4464754f7ae1791b376721b26 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 e705e982964537d456b9d1c4859e8b99a2bd54c3..d51c75a8d88ca8b443f3adf64b849d70a9403f9f 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 f4f8653aee94544cedd9bda5a9837c05c733776e..8ccbb3c260d801b2a6a1d80c87bdc8a4cf474fee 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 c2a332a1c50a832c92589d64b2b11d6635210f74..995d0d422b1f1b24dca3c4586992514c4862556b 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 dc55446ff846cb8c8d1b0d37904036fcde166af7..4d7d96c718f1796ffc4b70adb6bb4ae92339a804 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