CSS: fix the case-insensitive matching of the attribute selectors Begin, End and...
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Mar 2015 06:51:44 +0000 (06:51 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Mar 2015 06:51:44 +0000 (06:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142715

Reviewed by Brent Fulgham.

Source/WebCore:

Fix attribute matching with:
-Begin: [a^=b].
-End: [a$=b].
-Hyphen: [a|=b].

Tests: fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html
       fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html
       fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html

* css/SelectorChecker.cpp:
(WebCore::attributeValueMatches):
I forgot to change CSSSelector::Exact in my last patch.
The tests could not catch that since we use the CSS JIT almost everywhere.

* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::attributeValueBeginsWith):
(WebCore::SelectorCompiler::attributeValueEndsWith):
(WebCore::SelectorCompiler::attributeValueMatchHyphenRule):

Source/WTF:

Add the necessary infrastructure to test startsWith() and endsWith() with
ASCII case-insentive comparisons.

* wtf/text/AtomicString.h:
(WTF::AtomicString::startsWith):
(WTF::AtomicString::startsWithIgnoringASCIICase):
(WTF::AtomicString::endsWith):
(WTF::AtomicString::endsWithIgnoringASCIICase):

* wtf/text/StringCommon.h:
(WTF::loadUnaligned):
(WTF::equal):
I moved the low level equal() code from StringImpl to StringCommon
since it is used by both StringImpl and StringView.

(WTF::equalCommon):
(WTF::equalIgnoringASCIICaseCommon):
Ideally we should drop the "Common" part of the name but StringView
wants this inline for some reason. I prefered keeping the current behavior
since I don't know how StringView's matching performance was evaluated.

(WTF::startsWith):
(WTF::startsWithIgnoringASCIICase):
(WTF::endsWith):
(WTF::endsWithIgnoringASCIICase):
Make all that code shared between StringView and Stringimpl.

* wtf/text/StringImpl.cpp:
(WTF::StringImpl::startsWith):
(WTF::StringImpl::startsWithIgnoringASCIICase):
(WTF::StringImpl::endsWith):
(WTF::StringImpl::endsWithIgnoringASCIICase):
(WTF::equal):
(WTF::stringImplContentEqual): Deleted.
* wtf/text/StringImpl.h:
(WTF::loadUnaligned): Deleted.
(WTF::equal): Deleted.

* wtf/text/StringView.cpp:
(WTF::StringView::startsWith):
(WTF::StringView::startsWithIgnoringASCIICase):
(WTF::StringView::endsWith):
(WTF::StringView::endsWithIgnoringASCIICase):
* wtf/text/StringView.h:
Since those are new, we can safely make them out-of-line and
evaluate the inlining impact as needed.

* wtf/text/WTFString.h:
(WTF::String::startsWithIgnoringASCIICase):
(WTF::String::endsWith):
(WTF::String::endsWithIgnoringASCIICase):

Tools:

* TestWebKitAPI/Tests/WTF/StringImpl.cpp:
(TestWebKitAPI::TEST):

LayoutTests:

* fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
* fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html: Added.
* fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt: Added.
* fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html: Added.
* fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
* fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html: Added.

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/text/AtomicString.h
Source/WTF/wtf/text/StringCommon.h
Source/WTF/wtf/text/StringImpl.cpp
Source/WTF/wtf/text/StringImpl.h
Source/WTF/wtf/text/StringView.cpp
Source/WTF/wtf/text/StringView.h
Source/WTF/wtf/text/WTFString.h
Source/WebCore/ChangeLog
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/cssjit/SelectorCompiler.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp

index c8b4fad..cd22e50 100644 (file)
@@ -1,3 +1,17 @@
+2015-03-15  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
+        https://bugs.webkit.org/show_bug.cgi?id=142715
+
+        Reviewed by Brent Fulgham.
+
+        * fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
+        * fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html: Added.
+        * fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt: Added.
+        * fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html: Added.
+        * fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
+        * fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html: Added.
+
 2015-03-15  Brent Fulgham  <bfulgham@apple.com>
 
         Scroll snap points are not supported on iframe content
diff --git a/LayoutTests/fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt b/LayoutTests/fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt
new file mode 100644 (file)
index 0000000..6ad0737
--- /dev/null
@@ -0,0 +1,417 @@
+When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior when matching the end of the values (e.g. [a$="b"])
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing "[data-attribute$=Web-É-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-É-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-É-Kit]')[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$=É-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=É-Kit]')[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$=web-É-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-É-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=web-É-kit]')[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$=É-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=É-kit]')[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$=WEB-É-KIT]"
+PASS document.querySelectorAll('#test-root [data-attribute$=WEB-É-KIT]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=WEB-É-KIT]')[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$=É-KIT]"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-KIT]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=É-KIT]')[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$=Web-é-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-é-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-é-Kit]')[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$=é-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=é-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=é-Kit]')[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$=web-é-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-é-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=web-é-kit]')[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$=é-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute$=é-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute$=é-kit]')[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$=Web-É-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-É-Kit i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-É-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-É-Kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-É-Kit 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$=É-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-Kit i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute$=É-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-Kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-Kit 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$=web-É-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-É-kit i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute$=web-É-kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-É-kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-É-kit 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$=É-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-kit i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute$=É-kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-kit 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$=WEB-É-KIT i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=WEB-É-KIT i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute$=WEB-É-KIT i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute$=WEB-É-KIT i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute$=WEB-É-KIT 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$=É-KIT i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-KIT i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute$=É-KIT i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-KIT i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute$=É-KIT 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$=Web-é-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-é-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-é-Kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute$=Web-é-Kit 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$=é-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=é-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute$=é-Kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute$=é-Kit 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$=web-é-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-é-kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute$=web-é-kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute$=web-é-kit 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$=é-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute$=é-kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute$=é-kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute$=é-kit 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$=WEB-é-KIT]"
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT]')[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$=é-KIT]"
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT]')[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$=Web-é-Kit]"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit]')[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$=é-Kit]"
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit]')[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$=Web-é-kit]"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit]')[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$=é-kit]"
+PASS document.querySelectorAll('#test-root [multiple$=é-kit]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=é-kit]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=é-kit]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=é-kit]')[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$=web-É-kit]"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-kit]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=web-É-kit]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-kit]')[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$=É-kit]"
+PASS document.querySelectorAll('#test-root [multiple$=É-kit]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=É-kit]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=É-kit]')[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$=web-É-Kit]"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-Kit]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=web-É-Kit]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-Kit]')[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$=É-Kit]"
+PASS document.querySelectorAll('#test-root [multiple$=É-Kit]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=É-Kit]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=É-Kit]')[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$=WEB-é-KIT i]"
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=WEB-é-KIT 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$=é-KIT i]"
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=é-KIT 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$=Web-é-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-Kit 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$=é-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=é-Kit 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$=Web-é-kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=Web-é-kit 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$=é-kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=é-kit i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple$=é-kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple$=é-kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple$=é-kit 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$=web-É-kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-kit i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=web-É-kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-kit 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$=É-kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=É-kit i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=É-kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=É-kit 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$=web-É-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=web-É-Kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=web-É-Kit 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$=É-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple$=É-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple$=É-Kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple$=É-Kit 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-endswith-value-matching-is-ascii-case-insensitive.html b/LayoutTests/fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html
new file mode 100644 (file)
index 0000000..dd5cebf
--- /dev/null
@@ -0,0 +1,122 @@
+<!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="Web-É-Kit" multiple="WEB-é-KIT" id="target1"></div>
+        <div data-attribute="web-É-kit" multiple="Web-é-Kit" id="target2"></div>
+        <div data-attribute="WEB-É-KIT" multiple="Web-é-kit" id="target3"></div>
+        <div data-attribute="Web-é-Kit" multiple="web-É-kit" id="target4"></div>
+        <div data-attribute="web-é-kit" multiple="web-É-Kit" id="target5"></div>
+    </div>
+</body>
+<script>
+description('When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior when matching the end of the values (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$=Web-É-Kit]', [1]],
+    ['[data-attribute$=É-Kit]', [1]],
+
+    ['[data-attribute$=web-É-kit]', [2]],
+    ['[data-attribute$=É-kit]', [2]],
+
+    ['[data-attribute$=WEB-É-KIT]', [3]],
+    ['[data-attribute$=É-KIT]', [3]],
+
+    ['[data-attribute$=Web-é-Kit]', [4]],
+    ['[data-attribute$=é-Kit]', [4]],
+
+    ['[data-attribute$=web-é-kit]', [5]],
+    ['[data-attribute$=é-kit]', [5]],
+
+    // Same selectors with the case-insensitive flag.
+    ['[data-attribute$=Web-É-Kit i]', [1, 2, 3]],
+    ['[data-attribute$=É-Kit i]', [1, 2, 3]],
+
+    ['[data-attribute$=web-É-kit i]', [1, 2, 3]],
+    ['[data-attribute$=É-kit i]', [1, 2, 3]],
+
+    ['[data-attribute$=WEB-É-KIT i]', [1, 2, 3]],
+    ['[data-attribute$=É-KIT i]', [1, 2, 3]],
+
+    ['[data-attribute$=Web-é-Kit i]', [4, 5]],
+    ['[data-attribute$=é-Kit i]', [4, 5]],
+
+    ['[data-attribute$=web-é-kit i]', [4, 5]],
+    ['[data-attribute$=é-kit i]', [4, 5]],
+
+    // "multiple" is one of those weird legacy exception: it is always case insensitive in HTML.
+    ['[multiple$=WEB-é-KIT]', [1, 2, 3]],
+    ['[multiple$=é-KIT]', [1, 2, 3]],
+
+    ['[multiple$=Web-é-Kit]', [1, 2, 3]],
+    ['[multiple$=é-Kit]', [1, 2, 3]],
+
+    ['[multiple$=Web-é-kit]', [1, 2, 3]],
+    ['[multiple$=é-kit]', [1, 2, 3]],
+
+    ['[multiple$=web-É-kit]', [4, 5]],
+    ['[multiple$=É-kit]', [4, 5]],
+
+    ['[multiple$=web-É-Kit]', [4, 5]],
+    ['[multiple$=É-Kit]', [4, 5]],
+
+    ['[multiple$=WEB-é-KIT i]', [1, 2, 3]],
+    ['[multiple$=é-KIT i]', [1, 2, 3]],
+
+    ['[multiple$=Web-é-Kit i]', [1, 2, 3]],
+    ['[multiple$=é-Kit i]', [1, 2, 3]],
+
+    ['[multiple$=Web-é-kit i]', [1, 2, 3]],
+    ['[multiple$=é-kit i]', [1, 2, 3]],
+
+    ['[multiple$=web-É-kit i]', [4, 5]],
+    ['[multiple$=É-kit i]', [4, 5]],
+
+    ['[multiple$=web-É-Kit i]', [4, 5]],
+    ['[multiple$=É-Kit i]', [4, 5]],
+];
+
+for (var testCase of testCases) {
+    testSelector(testCase[0], testCase[1]);
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt b/LayoutTests/fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt
new file mode 100644 (file)
index 0000000..5e2a6ea
--- /dev/null
@@ -0,0 +1,349 @@
+When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior when doing hyphen matching on the value (e.g. [a|="b"])
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing "[data-attribute|=Web-É-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É-Kit]')[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|=Web-É]"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É]')[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|=web-É]"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=web-É]')[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|=WEB-É-KIT]"
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É-KIT]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É-KIT]')[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|=WEB-É]"
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É]')[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|=Web-é]"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-é]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-é]')[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|=web-é-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é-kit]')[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|=web-é]"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é]')[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|=Web-É-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É-Kit i]')[1].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(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|=Web-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-É 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|=web-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute|=web-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-É 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|=WEB-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute|=WEB-É 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|=Web-é i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-é i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-é i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute|=Web-é 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|=web-é-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é-kit i]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é-kit i]')[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|=web-é i]"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute|=web-é 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|=WEB-é-KIT]"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é-KIT]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é-KIT]')[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 "[multiple|=WEB-é]"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é]')[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|=Web-é-Kit]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-Kit]')[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 "[multiple|=Web-é]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]')[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|=Web-é-kit]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-kit]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-kit]')[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 "[multiple|=Web-é]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é]')[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|=web-É-kit]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É-kit]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=web-É-kit]')[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 "[multiple|=web-É]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É]').length is 2
+PASS document.querySelectorAll('#test-root [multiple|=web-É]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple|=web-É]')[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|=web-É-Kit]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=web-É-Kit]')[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 "[multiple|=web-É]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É]').length is 2
+PASS document.querySelectorAll('#test-root [multiple|=web-É]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple|=web-É]')[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|=WEB-é-KIT i]"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é-KIT i]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é-KIT i]')[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 "[multiple|=WEB-é i]"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple|=WEB-é 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|=Web-é-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-Kit i]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-Kit i]')[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 "[multiple|=Web-é i]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple|=Web-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é 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|=Web-é-kit i]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-kit i]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=Web-é-kit i]')[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 "[multiple|=Web-é i]"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple|=Web-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple|=Web-é 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|=web-É-kit i]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É-kit i]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=web-É-kit i]')[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 "[multiple|=web-É i]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple|=web-É i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple|=web-É 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|=web-É-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É-Kit i]').length is 1
+PASS document.querySelectorAll('#test-root [multiple|=web-É-Kit i]')[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 "[multiple|=web-É i]"
+PASS document.querySelectorAll('#test-root [multiple|=web-É i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple|=web-É i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple|=web-É 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-hyphen-value-matching-is-ascii-case-insensitive.html b/LayoutTests/fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html
new file mode 100644 (file)
index 0000000..af46891
--- /dev/null
@@ -0,0 +1,117 @@
+<!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="Web-É-Kit" multiple="WEB-é" id="target1"></div>
+        <div data-attribute="web-É" multiple="Web-é-Kit" id="target2"></div>
+        <div data-attribute="WEB-É-KIT" multiple="Web-é" id="target3"></div>
+        <div data-attribute="Web-é" multiple="web-É-kit" id="target4"></div>
+        <div data-attribute="web-é-kit" multiple="web-É" id="target5"></div>
+    </div>
+</body>
+<script>
+description('When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior when doing hyphen matching on the value (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|=Web-É-Kit]', [1]],
+    ['[data-attribute|=Web-É]', [1]],
+
+    ['[data-attribute|=web-É]', [2]],
+
+    ['[data-attribute|=WEB-É-KIT]', [3]],
+    ['[data-attribute|=WEB-É]', [3]],
+
+    ['[data-attribute|=Web-é]', [4]],
+
+    ['[data-attribute|=web-é-kit]', [5]],
+    ['[data-attribute|=web-é]', [5]],
+
+    // Same selectors with the case-insensitive flag.
+    ['[data-attribute|=Web-É-Kit i]', [1, 3]],
+    ['[data-attribute|=Web-É i]', [1, 2, 3]],
+
+    ['[data-attribute|=web-É i]', [1, 2, 3]],
+
+    ['[data-attribute|=WEB-É i]', [1, 2, 3]],
+
+    ['[data-attribute|=Web-é i]', [4, 5]],
+
+    ['[data-attribute|=web-é-kit i]', [5]],
+    ['[data-attribute|=web-é i]', [4, 5]],
+
+    // "multiple" is one of those weird legacy exception: it is always case insensitive in HTML.
+    ['[multiple|=WEB-é-KIT]', [2]],
+    ['[multiple|=WEB-é]', [1, 2, 3]],
+
+    ['[multiple|=Web-é-Kit]', [2]],
+    ['[multiple|=Web-é]', [1, 2, 3]],
+
+    ['[multiple|=Web-é-kit]', [2]],
+    ['[multiple|=Web-é]', [1, 2, 3]],
+
+    ['[multiple|=web-É-kit]', [4]],
+    ['[multiple|=web-É]', [4, 5]],
+
+    ['[multiple|=web-É-Kit]', [4]],
+    ['[multiple|=web-É]', [4, 5]],
+
+    ['[multiple|=WEB-é-KIT i]', [2]],
+    ['[multiple|=WEB-é i]', [1, 2, 3]],
+
+    ['[multiple|=Web-é-Kit i]', [2]],
+    ['[multiple|=Web-é i]', [1, 2, 3]],
+
+    ['[multiple|=Web-é-kit i]', [2]],
+    ['[multiple|=Web-é i]', [1, 2, 3]],
+
+    ['[multiple|=web-É-kit i]', [4]],
+    ['[multiple|=web-É i]', [4, 5]],
+
+    ['[multiple|=web-É-Kit i]', [4]],
+    ['[multiple|=web-É i]', [4, 5]],
+];
+
+for (var testCase of testCases) {
+    testSelector(testCase[0], testCase[1]);
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt b/LayoutTests/fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt
new file mode 100644 (file)
index 0000000..7921466
--- /dev/null
@@ -0,0 +1,417 @@
+When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior when matching the start of the values (e.g. [a^="b"])
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing "[data-attribute^=Web-É-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É-Kit]')[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^=Web-É]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É]')[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^=web-É-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É-kit]')[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^=web-É]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É]')[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^=WEB-É-KIT]"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É-KIT]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É-KIT]')[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^=WEB-É]"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É]')[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^=Web-é-Kit]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é-Kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é-Kit]')[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^=Web-é]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é]')[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^=web-é-kit]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é-kit]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é-kit]')[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^=web-é]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é]').length is 1
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é]')[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^=Web-É-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É-Kit i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É-Kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É-Kit 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^=Web-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-É 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^=web-É-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É-kit i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É-kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É-kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É-kit 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^=web-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-É 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^=WEB-É-KIT i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É-KIT i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É-KIT i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É-KIT i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É-KIT 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^=WEB-É i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É i]').length is 3
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [data-attribute^=WEB-É 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^=Web-é-Kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é-Kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é-Kit 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^=Web-é i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute^=Web-é 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^=web-é-kit i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é-kit i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é-kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é-kit 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^=web-é i]"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é i]').length is 2
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [data-attribute^=web-é 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^=WEB-é-KIT]"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT]')[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^=WEB-é]"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é]')[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^=Web-é-Kit]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit]')[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^=Web-é]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]')[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^=Web-é-kit]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit]')[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^=Web-é]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é]')[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^=web-É-kit]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-kit]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É-kit]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-kit]')[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^=web-É]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É]')[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^=web-É-Kit]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-Kit]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É-Kit]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-Kit]')[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^=web-É]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É]')[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^=WEB-é-KIT i]"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é-KIT 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^=WEB-é i]"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=WEB-é 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^=Web-é-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-Kit 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^=Web-é i]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é 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^=Web-é-kit i]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é-kit 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^=Web-é i]"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é i]').length is 3
+PASS document.querySelectorAll('#test-root [multiple^=Web-é i]')[0].id is "target1"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é i]')[1].id is "target2"
+PASS document.querySelectorAll('#test-root [multiple^=Web-é 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^=web-É-kit i]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-kit i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É-kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-kit 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^=web-É i]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É 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^=web-É-Kit i]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-Kit i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É-Kit i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É-Kit 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^=web-É i]"
+PASS document.querySelectorAll('#test-root [multiple^=web-É i]').length is 2
+PASS document.querySelectorAll('#test-root [multiple^=web-É i]')[0].id is "target4"
+PASS document.querySelectorAll('#test-root [multiple^=web-É 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-startswith-value-matching-is-ascii-case-insensitive.html b/LayoutTests/fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html
new file mode 100644 (file)
index 0000000..c7f38ec
--- /dev/null
@@ -0,0 +1,122 @@
+<!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="Web-É-Kit" multiple="WEB-é-KIT" id="target1"></div>
+        <div data-attribute="web-É-kit" multiple="Web-é-Kit" id="target2"></div>
+        <div data-attribute="WEB-É-KIT" multiple="Web-é-kit" id="target3"></div>
+        <div data-attribute="Web-é-Kit" multiple="web-É-kit" id="target4"></div>
+        <div data-attribute="web-é-kit" multiple="web-É-Kit" id="target5"></div>
+    </div>
+</body>
+<script>
+description('When matching attributes case insensitively, it should be ASCII case insensitive. This test verifies the behavior when matching the start of the values (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^=Web-É-Kit]', [1]],
+    ['[data-attribute^=Web-É]', [1]],
+
+    ['[data-attribute^=web-É-kit]', [2]],
+    ['[data-attribute^=web-É]', [2]],
+
+    ['[data-attribute^=WEB-É-KIT]', [3]],
+    ['[data-attribute^=WEB-É]', [3]],
+
+    ['[data-attribute^=Web-é-Kit]', [4]],
+    ['[data-attribute^=Web-é]', [4]],
+
+    ['[data-attribute^=web-é-kit]', [5]],
+    ['[data-attribute^=web-é]', [5]],
+
+    // Same selectors with the case-insensitive flag.
+    ['[data-attribute^=Web-É-Kit i]', [1, 2, 3]],
+    ['[data-attribute^=Web-É i]', [1, 2, 3]],
+
+    ['[data-attribute^=web-É-kit i]', [1, 2, 3]],
+    ['[data-attribute^=web-É i]', [1, 2, 3]],
+
+    ['[data-attribute^=WEB-É-KIT i]', [1, 2, 3]],
+    ['[data-attribute^=WEB-É i]', [1, 2, 3]],
+
+    ['[data-attribute^=Web-é-Kit i]', [4, 5]],
+    ['[data-attribute^=Web-é i]', [4, 5]],
+
+    ['[data-attribute^=web-é-kit i]', [4, 5]],
+    ['[data-attribute^=web-é i]', [4, 5]],
+
+    // "multiple" is one of those weird legacy exception: it is always case insensitive in HTML.
+    ['[multiple^=WEB-é-KIT]', [1, 2, 3]],
+    ['[multiple^=WEB-é]', [1, 2, 3]],
+
+    ['[multiple^=Web-é-Kit]', [1, 2, 3]],
+    ['[multiple^=Web-é]', [1, 2, 3]],
+
+    ['[multiple^=Web-é-kit]', [1, 2, 3]],
+    ['[multiple^=Web-é]', [1, 2, 3]],
+
+    ['[multiple^=web-É-kit]', [4, 5]],
+    ['[multiple^=web-É]', [4, 5]],
+
+    ['[multiple^=web-É-Kit]', [4, 5]],
+    ['[multiple^=web-É]', [4, 5]],
+
+    ['[multiple^=WEB-é-KIT i]', [1, 2, 3]],
+    ['[multiple^=WEB-é i]', [1, 2, 3]],
+
+    ['[multiple^=Web-é-Kit i]', [1, 2, 3]],
+    ['[multiple^=Web-é i]', [1, 2, 3]],
+
+    ['[multiple^=Web-é-kit i]', [1, 2, 3]],
+    ['[multiple^=Web-é i]', [1, 2, 3]],
+
+    ['[multiple^=web-É-kit i]', [4, 5]],
+    ['[multiple^=web-É i]', [4, 5]],
+
+    ['[multiple^=web-É-Kit i]', [4, 5]],
+    ['[multiple^=web-É i]', [4, 5]],
+];
+
+for (var testCase of testCases) {
+    testSelector(testCase[0], testCase[1]);
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
index a45615b..bba4296 100644 (file)
@@ -1,3 +1,62 @@
+2015-03-15  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
+        https://bugs.webkit.org/show_bug.cgi?id=142715
+
+        Reviewed by Brent Fulgham.
+
+        Add the necessary infrastructure to test startsWith() and endsWith() with
+        ASCII case-insentive comparisons.
+
+        * wtf/text/AtomicString.h:
+        (WTF::AtomicString::startsWith):
+        (WTF::AtomicString::startsWithIgnoringASCIICase):
+        (WTF::AtomicString::endsWith):
+        (WTF::AtomicString::endsWithIgnoringASCIICase):
+
+        * wtf/text/StringCommon.h:
+        (WTF::loadUnaligned):
+        (WTF::equal):
+        I moved the low level equal() code from StringImpl to StringCommon
+        since it is used by both StringImpl and StringView.
+
+        (WTF::equalCommon):
+        (WTF::equalIgnoringASCIICaseCommon):
+        Ideally we should drop the "Common" part of the name but StringView
+        wants this inline for some reason. I prefered keeping the current behavior
+        since I don't know how StringView's matching performance was evaluated.
+
+        (WTF::startsWith):
+        (WTF::startsWithIgnoringASCIICase):
+        (WTF::endsWith):
+        (WTF::endsWithIgnoringASCIICase):
+        Make all that code shared between StringView and Stringimpl.
+
+        * wtf/text/StringImpl.cpp:
+        (WTF::StringImpl::startsWith):
+        (WTF::StringImpl::startsWithIgnoringASCIICase):
+        (WTF::StringImpl::endsWith):
+        (WTF::StringImpl::endsWithIgnoringASCIICase):
+        (WTF::equal):
+        (WTF::stringImplContentEqual): Deleted.
+        * wtf/text/StringImpl.h:
+        (WTF::loadUnaligned): Deleted.
+        (WTF::equal): Deleted.
+
+        * wtf/text/StringView.cpp:
+        (WTF::StringView::startsWith):
+        (WTF::StringView::startsWithIgnoringASCIICase):
+        (WTF::StringView::endsWith):
+        (WTF::StringView::endsWithIgnoringASCIICase):
+        * wtf/text/StringView.h:
+        Since those are new, we can safely make them out-of-line and
+        evaluate the inlining impact as needed.
+
+        * wtf/text/WTFString.h:
+        (WTF::String::startsWithIgnoringASCIICase):
+        (WTF::String::endsWith):
+        (WTF::String::endsWithIgnoringASCIICase):
+
 2015-03-15  Benjamin Poulain  <bpoulain@apple.com>
 
         Change the exact attribute matching to be ASCII case-insensitive
index c9cdd68..6618848 100644 (file)
@@ -126,7 +126,11 @@ public:
     size_t find(CharacterMatchFunctionPtr matchFunction, unsigned start = 0) const
         { return m_string.find(matchFunction, start); }
 
-    bool startsWith(const String& s, bool caseSensitive = true) const
+    bool startsWith(const String& s) const
+        { return m_string.startsWith(s); }
+    bool startsWithIgnoringASCIICase(const String& s) const
+        { return m_string.startsWithIgnoringASCIICase(s); }
+    bool startsWith(const String& s, bool caseSensitive) const
         { return m_string.startsWith(s, caseSensitive); }
     bool startsWith(UChar character) const
         { return m_string.startsWith(character); }
@@ -134,7 +138,11 @@ public:
     bool startsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const
         { return m_string.startsWith<matchLength>(prefix, caseSensitive); }
 
-    bool endsWith(const String& s, bool caseSensitive = true) const
+    bool endsWith(const String& s) const
+        { return m_string.endsWith(s); }
+    bool endsWithIgnoringASCIICase(const String& s) const
+        { return m_string.endsWithIgnoringASCIICase(s); }
+    bool endsWith(const String& s, bool caseSensitive) const
         { return m_string.endsWith(s, caseSensitive); }
     bool endsWith(UChar character) const
         { return m_string.endsWith(character); }
index 3ac9ff6..80641f5 100644 (file)
 
 namespace WTF {
 
+template<typename T>
+inline T loadUnaligned(const char* s)
+{
+#if COMPILER(CLANG)
+    T tmp;
+    memcpy(&tmp, s, sizeof(T));
+    return tmp;
+#else
+    // This may result in undefined behavior due to unaligned access.
+    return *reinterpret_cast<const T*>(s);
+#endif
+}
+
+// Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe.
+#if (CPU(X86_64) || CPU(ARM64)) && !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
+{
+    unsigned dwordLength = length >> 3;
+
+    const char* a = reinterpret_cast<const char*>(aLChar);
+    const char* b = reinterpret_cast<const char*>(bLChar);
+
+    if (dwordLength) {
+        for (unsigned i = 0; i != dwordLength; ++i) {
+            if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
+                return false;
+
+            a += sizeof(uint64_t);
+            b += sizeof(uint64_t);
+        }
+    }
+
+    if (length & 4) {
+        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+            return false;
+
+        a += sizeof(uint32_t);
+        b += sizeof(uint32_t);
+    }
+
+    if (length & 2) {
+        if (loadUnaligned<uint16_t>(a) != loadUnaligned<uint16_t>(b))
+            return false;
+
+        a += sizeof(uint16_t);
+        b += sizeof(uint16_t);
+    }
+
+    if (length & 1 && (*reinterpret_cast<const LChar*>(a) != *reinterpret_cast<const LChar*>(b)))
+        return false;
+
+    return true;
+}
+
+ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
+{
+    unsigned dwordLength = length >> 2;
+
+    const char* a = reinterpret_cast<const char*>(aUChar);
+    const char* b = reinterpret_cast<const char*>(bUChar);
+
+    if (dwordLength) {
+        for (unsigned i = 0; i != dwordLength; ++i) {
+            if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
+                return false;
+
+            a += sizeof(uint64_t);
+            b += sizeof(uint64_t);
+        }
+    }
+
+    if (length & 2) {
+        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+            return false;
+
+        a += sizeof(uint32_t);
+        b += sizeof(uint32_t);
+    }
+
+    if (length & 1 && (*reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b)))
+        return false;
+
+    return true;
+}
+#elif CPU(X86) && !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
+{
+    const char* a = reinterpret_cast<const char*>(aLChar);
+    const char* b = reinterpret_cast<const char*>(bLChar);
+
+    unsigned wordLength = length >> 2;
+    for (unsigned i = 0; i != wordLength; ++i) {
+        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+            return false;
+        a += sizeof(uint32_t);
+        b += sizeof(uint32_t);
+    }
+
+    length &= 3;
+
+    if (length) {
+        const LChar* aRemainder = reinterpret_cast<const LChar*>(a);
+        const LChar* bRemainder = reinterpret_cast<const LChar*>(b);
+
+        for (unsigned i = 0; i <  length; ++i) {
+            if (aRemainder[i] != bRemainder[i])
+                return false;
+        }
+    }
+
+    return true;
+}
+
+ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
+{
+    const char* a = reinterpret_cast<const char*>(aUChar);
+    const char* b = reinterpret_cast<const char*>(bUChar);
+
+    unsigned wordLength = length >> 1;
+    for (unsigned i = 0; i != wordLength; ++i) {
+        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+            return false;
+        a += sizeof(uint32_t);
+        b += sizeof(uint32_t);
+    }
+
+    if (length & 1 && *reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b))
+        return false;
+
+    return true;
+}
+#elif PLATFORM(IOS) && WTF_ARM_ARCH_AT_LEAST(7) && !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
+{
+    bool isEqual = false;
+    uint32_t aValue;
+    uint32_t bValue;
+    asm("subs   %[length], #4\n"
+        "blo    2f\n"
+
+        "0:\n" // Label 0 = Start of loop over 32 bits.
+        "ldr    %[aValue], [%[a]], #4\n"
+        "ldr    %[bValue], [%[b]], #4\n"
+        "cmp    %[aValue], %[bValue]\n"
+        "bne    66f\n"
+        "subs   %[length], #4\n"
+        "bhs    0b\n"
+
+        // At this point, length can be:
+        // -0: 00000000000000000000000000000000 (0 bytes left)
+        // -1: 11111111111111111111111111111111 (3 bytes left)
+        // -2: 11111111111111111111111111111110 (2 bytes left)
+        // -3: 11111111111111111111111111111101 (1 byte left)
+        // -4: 11111111111111111111111111111100 (length was 0)
+        // The pointers are at the correct position.
+        "2:\n" // Label 2 = End of loop over 32 bits, check for pair of characters.
+        "tst    %[length], #2\n"
+        "beq    1f\n"
+        "ldrh   %[aValue], [%[a]], #2\n"
+        "ldrh   %[bValue], [%[b]], #2\n"
+        "cmp    %[aValue], %[bValue]\n"
+        "bne    66f\n"
+
+        "1:\n" // Label 1 = Check for a single character left.
+        "tst    %[length], #1\n"
+        "beq    42f\n"
+        "ldrb   %[aValue], [%[a]]\n"
+        "ldrb   %[bValue], [%[b]]\n"
+        "cmp    %[aValue], %[bValue]\n"
+        "bne    66f\n"
+
+        "42:\n" // Label 42 = Success.
+        "mov    %[isEqual], #1\n"
+        "66:\n" // Label 66 = End without changing isEqual to 1.
+        : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
+        :
+        :
+        );
+    return isEqual;
+}
+
+ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
+{
+    bool isEqual = false;
+    uint32_t aValue;
+    uint32_t bValue;
+    asm("subs   %[length], #2\n"
+        "blo    1f\n"
+
+        "0:\n" // Label 0 = Start of loop over 32 bits.
+        "ldr    %[aValue], [%[a]], #4\n"
+        "ldr    %[bValue], [%[b]], #4\n"
+        "cmp    %[aValue], %[bValue]\n"
+        "bne    66f\n"
+        "subs   %[length], #2\n"
+        "bhs    0b\n"
+
+        // At this point, length can be:
+        // -0: 00000000000000000000000000000000 (0 bytes left)
+        // -1: 11111111111111111111111111111111 (1 character left, 2 bytes)
+        // -2: 11111111111111111111111111111110 (length was zero)
+        // The pointers are at the correct position.
+        "1:\n" // Label 1 = Check for a single character left.
+        "tst    %[length], #1\n"
+        "beq    42f\n"
+        "ldrh   %[aValue], [%[a]]\n"
+        "ldrh   %[bValue], [%[b]]\n"
+        "cmp    %[aValue], %[bValue]\n"
+        "bne    66f\n"
+
+        "42:\n" // Label 42 = Success.
+        "mov    %[isEqual], #1\n"
+        "66:\n" // Label 66 = End without changing isEqual to 1.
+        : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
+        :
+        :
+        );
+    return isEqual;
+}
+#elif !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) { return !memcmp(a, b, length); }
+ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) { return !memcmp(a, b, length * sizeof(UChar)); }
+#else
+ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+#endif
+
+ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+
+ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); }
+
+template<typename StringClassA, typename StringClassB>
+ALWAYS_INLINE bool equalCommon(const StringClassA& a, const StringClassB& b)
+{
+    unsigned length = a.length();
+    if (length != b.length())
+        return false;
+
+    if (a.is8Bit()) {
+        if (b.is8Bit())
+            return equal(a.characters8(), b.characters8(), length);
+
+        return equal(a.characters8(), b.characters16(), length);
+    }
+
+    if (b.is8Bit())
+        return equal(a.characters16(), b.characters8(), length);
+
+    return equal(a.characters16(), b.characters16(), length);
+}
+
+template<typename StringClassA, typename StringClassB>
+ALWAYS_INLINE bool equalCommon(const StringClassA* a, const StringClassB* b)
+{
+    if (a == b)
+        return true;
+    if (!a || !b)
+        return false;
+    return equal(*a, *b);
+}
+
 template<typename CharacterTypeA, typename CharacterTypeB>
 inline bool equalIgnoringASCIICase(const CharacterTypeA* a, const CharacterTypeB* b, unsigned length)
 {
@@ -41,8 +323,8 @@ inline bool equalIgnoringASCIICase(const CharacterTypeA* a, const CharacterTypeB
     return true;
 }
 
-template<typename StringClass>
-bool equalIgnoringASCIICaseCommon(const StringClass& a, const StringClass& b)
+template<typename StringClassA, typename StringClassB>
+bool equalIgnoringASCIICaseCommon(const StringClassA& a, const StringClassB& b)
 {
     unsigned length = a.length();
     if (length != b.length())
@@ -61,6 +343,80 @@ bool equalIgnoringASCIICaseCommon(const StringClass& a, const StringClass& b)
     return equalIgnoringASCIICase(a.characters16(), b.characters16(), length);
 }
 
+template<typename StringClassA, typename StringClassB>
+bool startsWith(const StringClassA& reference, const StringClassB& prefix)
+{
+    unsigned prefixLength = prefix.length();
+    if (prefixLength > reference.length())
+        return false;
+
+    if (reference.is8Bit()) {
+        if (prefix.is8Bit())
+            return equal(reference.characters8(), prefix.characters8(), prefixLength);
+        return equal(reference.characters8(), prefix.characters16(), prefixLength);
+    }
+    if (prefix.is8Bit())
+        return equal(reference.characters16(), prefix.characters8(), prefixLength);
+    return equal(reference.characters16(), prefix.characters16(), prefixLength);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool startsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& prefix)
+{
+    unsigned prefixLength = prefix.length();
+    if (prefixLength > reference.length())
+        return false;
+
+    if (reference.is8Bit()) {
+        if (prefix.is8Bit())
+            return equalIgnoringASCIICase(reference.characters8(), prefix.characters8(), prefixLength);
+        return equalIgnoringASCIICase(reference.characters8(), prefix.characters16(), prefixLength);
+    }
+    if (prefix.is8Bit())
+        return equalIgnoringASCIICase(reference.characters16(), prefix.characters8(), prefixLength);
+    return equalIgnoringASCIICase(reference.characters16(), prefix.characters16(), prefixLength);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool endsWith(const StringClassA& reference, const StringClassB& suffix)
+{
+    unsigned suffixLength = suffix.length();
+    unsigned referenceLength = reference.length();
+    if (suffixLength > referenceLength)
+        return false;
+
+    unsigned startOffset = referenceLength - suffixLength;
+
+    if (reference.is8Bit()) {
+        if (suffix.is8Bit())
+            return equal(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
+        return equal(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
+    }
+    if (suffix.is8Bit())
+        return equal(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
+    return equal(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool endsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& suffix)
+{
+    unsigned suffixLength = suffix.length();
+    unsigned referenceLength = reference.length();
+    if (suffixLength > referenceLength)
+        return false;
+
+    unsigned startOffset = referenceLength - suffixLength;
+
+    if (reference.is8Bit()) {
+        if (suffix.is8Bit())
+            return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
+        return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
+    }
+    if (suffix.is8Bit())
+        return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
+    return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
+}
+
 }
 
 #endif // StringCommon_h
index 9f593f7..fb23a54 100644 (file)
@@ -1380,18 +1380,25 @@ bool StringImpl::startsWith(const StringImpl* str) const
 {
     if (!str)
         return false;
+    return ::WTF::startsWith(*this, *str);
+}
+
+bool StringImpl::startsWith(const StringImpl& str) const
+{
+    return ::WTF::startsWith(*this, str);
+}
 
-    if (str->length() > length())
+bool StringImpl::startsWithIgnoringASCIICase(const StringImpl* prefix) const
+{
+    if (!prefix)
         return false;
 
-    if (is8Bit()) {
-        if (str->is8Bit())
-            return equal(characters8(), str->characters8(), str->length());
-        return equal(characters8(), str->characters16(), str->length());
-    }
-    if (str->is8Bit())
-        return equal(characters16(), str->characters8(), str->length());
-    return equal(characters16(), str->characters16(), str->length());
+    return ::WTF::startsWithIgnoringASCIICase(*this, *prefix);
+}
+
+bool StringImpl::startsWithIgnoringASCIICase(const StringImpl& prefix) const
+{
+    return ::WTF::startsWithIgnoringASCIICase(*this, prefix);
 }
 
 bool StringImpl::startsWith(UChar character) const
@@ -1412,6 +1419,19 @@ bool StringImpl::hasInfixStartingAt(const StringImpl& matchString, unsigned star
     return equalInner(*this, startOffset, matchString);
 }
 
+bool StringImpl::endsWith(StringImpl* suffix)
+{
+    if (!suffix)
+        return false;
+
+    return ::WTF::endsWith(*this, *suffix);
+}
+
+bool StringImpl::endsWith(StringImpl& suffix)
+{
+    return ::WTF::endsWith(*this, suffix);
+}
+
 bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive)
 {
     ASSERT(matchString);
@@ -1422,6 +1442,19 @@ bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive)
     return false;
 }
 
+bool StringImpl::endsWithIgnoringASCIICase(const StringImpl* suffix) const
+{
+    if (!suffix)
+        return false;
+
+    return ::WTF::endsWithIgnoringASCIICase(*this, *suffix);
+}
+
+bool StringImpl::endsWithIgnoringASCIICase(const StringImpl& suffix) const
+{
+    return ::WTF::endsWithIgnoringASCIICase(*this, suffix);
+}
+
 bool StringImpl::endsWith(UChar character) const
 {
     return m_length && (*this)[m_length - 1] == character;
@@ -1824,34 +1857,9 @@ Ref<StringImpl> StringImpl::replace(StringImpl* pattern, StringImpl* replacement
     return newImpl;
 }
 
-static ALWAYS_INLINE bool stringImplContentEqual(const StringImpl& a, const StringImpl& b)
-{
-    unsigned aLength = a.length();
-    unsigned bLength = b.length();
-    if (aLength != bLength)
-        return false;
-
-    if (a.is8Bit()) {
-        if (b.is8Bit())
-            return equal(a.characters8(), b.characters8(), aLength);
-
-        return equal(a.characters8(), b.characters16(), aLength);
-    }
-
-    if (b.is8Bit())
-        return equal(a.characters16(), b.characters8(), aLength);
-
-    return equal(a.characters16(), b.characters16(), aLength);
-}
-
 bool equal(const StringImpl* a, const StringImpl* b)
 {
-    if (a == b)
-        return true;
-    if (!a || !b)
-        return false;
-
-    return stringImplContentEqual(*a, *b);
+    return equalCommon(a, b);
 }
 
 template <typename CharType>
@@ -1916,10 +1924,7 @@ bool equal(const StringImpl* a, const LChar* b)
 
 bool equal(const StringImpl& a, const StringImpl& b)
 {
-    if (&a == &b)
-        return true;
-
-    return stringImplContentEqual(a, b);
+    return equalCommon(a, b);
 }
 
 bool equalIgnoringCase(const StringImpl* a, const StringImpl* b)
index d074064..8cc0abf 100644 (file)
@@ -669,6 +669,9 @@ public:
     WTF_EXPORT_STRING_API size_t reverseFindIgnoringCase(StringImpl*, unsigned index = UINT_MAX);
 
     WTF_EXPORT_STRING_API bool startsWith(const StringImpl*) const;
+    WTF_EXPORT_STRING_API bool startsWith(const StringImpl&) const;
+    WTF_EXPORT_STRING_API bool startsWithIgnoringASCIICase(const StringImpl*) const;
+    WTF_EXPORT_STRING_API bool startsWithIgnoringASCIICase(const StringImpl&) const;
     bool startsWith(StringImpl* str, bool caseSensitive) { return caseSensitive ? startsWith(str) : (reverseFindIgnoringCase(str, 0) == 0); }
     WTF_EXPORT_STRING_API bool startsWith(UChar) const;
     WTF_EXPORT_STRING_API bool startsWith(const char*, unsigned matchLength, bool caseSensitive) const;
@@ -676,7 +679,11 @@ public:
     bool startsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const { return startsWith(prefix, matchLength - 1, caseSensitive); }
     WTF_EXPORT_STRING_API bool hasInfixStartingAt(const StringImpl&, unsigned startOffset) const;
 
-    WTF_EXPORT_STRING_API bool endsWith(StringImpl*, bool caseSensitive = true);
+    WTF_EXPORT_STRING_API bool endsWith(StringImpl*);
+    WTF_EXPORT_STRING_API bool endsWith(StringImpl&);
+    WTF_EXPORT_STRING_API bool endsWithIgnoringASCIICase(const StringImpl*) const;
+    WTF_EXPORT_STRING_API bool endsWithIgnoringASCIICase(const StringImpl&) const;
+    WTF_EXPORT_STRING_API bool endsWith(StringImpl*, bool caseSensitive);
     WTF_EXPORT_STRING_API bool endsWith(UChar) const;
     WTF_EXPORT_STRING_API bool endsWith(const char*, unsigned matchLength, bool caseSensitive) const;
     template<unsigned matchLength>
@@ -861,258 +868,6 @@ inline bool equal(const LChar* a, StringImpl* b) { return equal(b, a); }
 inline bool equal(const char* a, StringImpl* b) { return equal(b, reinterpret_cast<const LChar*>(a)); }
 WTF_EXPORT_STRING_API bool equal(const StringImpl& a, const StringImpl& b);
 
-template<typename T>
-inline T loadUnaligned(const char* s)
-{
-#if COMPILER(CLANG)
-    T tmp;
-    memcpy(&tmp, s, sizeof(T));
-    return tmp;
-#else
-    // This may result in undefined behavior due to unaligned access.
-    return *reinterpret_cast<const T*>(s);
-#endif
-}
-
-// Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe.
-#if (CPU(X86_64) || CPU(ARM64)) && !ASAN_ENABLED
-ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
-{
-    unsigned dwordLength = length >> 3;
-
-    const char* a = reinterpret_cast<const char*>(aLChar);
-    const char* b = reinterpret_cast<const char*>(bLChar);
-
-    if (dwordLength) {
-        for (unsigned i = 0; i != dwordLength; ++i) {
-            if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
-                return false;
-
-            a += sizeof(uint64_t);
-            b += sizeof(uint64_t);
-        }
-    }
-
-    if (length & 4) {
-        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
-            return false;
-
-        a += sizeof(uint32_t);
-        b += sizeof(uint32_t);
-    }
-
-    if (length & 2) {
-        if (loadUnaligned<uint16_t>(a) != loadUnaligned<uint16_t>(b))
-            return false;
-
-        a += sizeof(uint16_t);
-        b += sizeof(uint16_t);
-    }
-
-    if (length & 1 && (*reinterpret_cast<const LChar*>(a) != *reinterpret_cast<const LChar*>(b)))
-        return false;
-
-    return true;
-}
-
-ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
-{
-    unsigned dwordLength = length >> 2;
-
-    const char* a = reinterpret_cast<const char*>(aUChar);
-    const char* b = reinterpret_cast<const char*>(bUChar);
-
-    if (dwordLength) {
-        for (unsigned i = 0; i != dwordLength; ++i) {
-            if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
-                return false;
-
-            a += sizeof(uint64_t);
-            b += sizeof(uint64_t);
-        }
-    }
-
-    if (length & 2) {
-        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
-            return false;
-
-        a += sizeof(uint32_t);
-        b += sizeof(uint32_t);
-    }
-
-    if (length & 1 && (*reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b)))
-        return false;
-
-    return true;
-}
-#elif CPU(X86) && !ASAN_ENABLED
-ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
-{
-    const char* a = reinterpret_cast<const char*>(aLChar);
-    const char* b = reinterpret_cast<const char*>(bLChar);
-
-    unsigned wordLength = length >> 2;
-    for (unsigned i = 0; i != wordLength; ++i) {
-        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
-            return false;
-        a += sizeof(uint32_t);
-        b += sizeof(uint32_t);
-    }
-
-    length &= 3;
-
-    if (length) {
-        const LChar* aRemainder = reinterpret_cast<const LChar*>(a);
-        const LChar* bRemainder = reinterpret_cast<const LChar*>(b);
-
-        for (unsigned i = 0; i <  length; ++i) {
-            if (aRemainder[i] != bRemainder[i])
-                return false;
-        }
-    }
-
-    return true;
-}
-
-ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
-{
-    const char* a = reinterpret_cast<const char*>(aUChar);
-    const char* b = reinterpret_cast<const char*>(bUChar);
-
-    unsigned wordLength = length >> 1;
-    for (unsigned i = 0; i != wordLength; ++i) {
-        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
-            return false;
-        a += sizeof(uint32_t);
-        b += sizeof(uint32_t);
-    }
-
-    if (length & 1 && *reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b))
-        return false;
-
-    return true;
-}
-#elif PLATFORM(IOS) && WTF_ARM_ARCH_AT_LEAST(7) && !ASAN_ENABLED
-ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
-{
-    bool isEqual = false;
-    uint32_t aValue;
-    uint32_t bValue;
-    asm("subs   %[length], #4\n"
-        "blo    2f\n"
-
-        "0:\n" // Label 0 = Start of loop over 32 bits.
-        "ldr    %[aValue], [%[a]], #4\n"
-        "ldr    %[bValue], [%[b]], #4\n"
-        "cmp    %[aValue], %[bValue]\n"
-        "bne    66f\n"
-        "subs   %[length], #4\n"
-        "bhs    0b\n"
-
-        // At this point, length can be:
-        // -0: 00000000000000000000000000000000 (0 bytes left)
-        // -1: 11111111111111111111111111111111 (3 bytes left)
-        // -2: 11111111111111111111111111111110 (2 bytes left)
-        // -3: 11111111111111111111111111111101 (1 byte left)
-        // -4: 11111111111111111111111111111100 (length was 0)
-        // The pointers are at the correct position.
-        "2:\n" // Label 2 = End of loop over 32 bits, check for pair of characters.
-        "tst    %[length], #2\n"
-        "beq    1f\n"
-        "ldrh   %[aValue], [%[a]], #2\n"
-        "ldrh   %[bValue], [%[b]], #2\n"
-        "cmp    %[aValue], %[bValue]\n"
-        "bne    66f\n"
-
-        "1:\n" // Label 1 = Check for a single character left.
-        "tst    %[length], #1\n"
-        "beq    42f\n"
-        "ldrb   %[aValue], [%[a]]\n"
-        "ldrb   %[bValue], [%[b]]\n"
-        "cmp    %[aValue], %[bValue]\n"
-        "bne    66f\n"
-
-        "42:\n" // Label 42 = Success.
-        "mov    %[isEqual], #1\n"
-        "66:\n" // Label 66 = End without changing isEqual to 1.
-        : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
-        :
-        :
-        );
-    return isEqual;
-}
-
-ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
-{
-    bool isEqual = false;
-    uint32_t aValue;
-    uint32_t bValue;
-    asm("subs   %[length], #2\n"
-        "blo    1f\n"
-
-        "0:\n" // Label 0 = Start of loop over 32 bits.
-        "ldr    %[aValue], [%[a]], #4\n"
-        "ldr    %[bValue], [%[b]], #4\n"
-        "cmp    %[aValue], %[bValue]\n"
-        "bne    66f\n"
-        "subs   %[length], #2\n"
-        "bhs    0b\n"
-
-        // At this point, length can be:
-        // -0: 00000000000000000000000000000000 (0 bytes left)
-        // -1: 11111111111111111111111111111111 (1 character left, 2 bytes)
-        // -2: 11111111111111111111111111111110 (length was zero)
-        // The pointers are at the correct position.
-        "1:\n" // Label 1 = Check for a single character left.
-        "tst    %[length], #1\n"
-        "beq    42f\n"
-        "ldrh   %[aValue], [%[a]]\n"
-        "ldrh   %[bValue], [%[b]]\n"
-        "cmp    %[aValue], %[bValue]\n"
-        "bne    66f\n"
-
-        "42:\n" // Label 42 = Success.
-        "mov    %[isEqual], #1\n"
-        "66:\n" // Label 66 = End without changing isEqual to 1.
-        : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
-        :
-        :
-        );
-    return isEqual;
-}
-#elif !ASAN_ENABLED
-ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) { return !memcmp(a, b, length); }
-ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) { return !memcmp(a, b, length * sizeof(UChar)); }
-#else
-ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
-{
-    for (unsigned i = 0; i < length; ++i) {
-        if (a[i] != b[i])
-            return false;
-    }
-    return true;
-}
-ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
-{
-    for (unsigned i = 0; i < length; ++i) {
-        if (a[i] != b[i])
-            return false;
-    }
-    return true;
-}
-#endif
-
-ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length)
-{
-    for (unsigned i = 0; i < length; ++i) {
-        if (a[i] != b[i])
-            return false;
-    }
-    return true;
-}
-
-ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); }
-
 WTF_EXPORT_STRING_API bool equalIgnoringCase(const StringImpl*, const StringImpl*);
 WTF_EXPORT_STRING_API bool equalIgnoringCase(const StringImpl*, const LChar*);
 inline bool equalIgnoringCase(const LChar* a, const StringImpl* b) { return equalIgnoringCase(b, a); }
index ad9dad5..ee652fe 100644 (file)
@@ -118,6 +118,26 @@ void StringView::setUnderlyingString(const StringView& string)
     adoptUnderlyingString(underlyingString);
 }
 
+bool StringView::startsWith(const StringView& prefix) const
+{
+    return ::WTF::startsWith(*this, prefix);
+}
+
+bool StringView::startsWithIgnoringASCIICase(const StringView& prefix) const
+{
+    return ::WTF::endsWithIgnoringASCIICase(*this, prefix);
+}
+
+bool StringView::endsWith(const StringView& prefix) const
+{
+    return ::WTF::startsWith(*this, prefix);
+}
+
+bool StringView::endsWithIgnoringASCIICase(const StringView& prefix) const
+{
+    return ::WTF::endsWithIgnoringASCIICase(*this, prefix);
+}
+
 }
 
 #endif
index 5ccb3ad..4be46a5 100644 (file)
@@ -106,6 +106,12 @@ public:
     size_t find(UChar, unsigned start = 0) const;
     bool contains(UChar) const;
 
+    bool startsWith(const StringView&) const;
+    bool startsWithIgnoringASCIICase(const StringView&) const;
+
+    bool endsWith(const StringView&) const;
+    bool endsWithIgnoringASCIICase(const StringView&) const;
+
     int toInt(bool& isValid) const;
     float toFloat(bool& isValid) const;
 
@@ -469,22 +475,7 @@ template<typename CharacterType, size_t inlineCapacity> void append(Vector<Chara
 
 inline bool equal(StringView a, StringView b)
 {
-    unsigned aLength = a.length();
-    unsigned bLength = b.length();
-    if (aLength != bLength)
-        return false;
-
-    if (a.is8Bit()) {
-        if (b.is8Bit())
-            return equal(a.characters8(), b.characters8(), aLength);
-
-        return equal(a.characters8(), b.characters16(), aLength);
-    }
-
-    if (b.is8Bit())
-        return equal(a.characters16(), b.characters8(), aLength);
-
-    return equal(a.characters16(), b.characters16(), aLength);
+    return equalCommon(a, b);
 }
 
 inline bool equal(StringView a, const LChar* b)
index 609a002..6d7175b 100644 (file)
@@ -263,6 +263,8 @@ public:
 
     bool startsWith(const String& s) const
         { return m_impl ? m_impl->startsWith(s.impl()) : s.isEmpty(); }
+    bool startsWithIgnoringASCIICase(const String& s) const
+        { return m_impl ? m_impl->startsWithIgnoringASCIICase(s.impl()) : s.isEmpty(); }
     bool startsWith(const String& s, bool caseSensitive) const
         { return m_impl ? m_impl->startsWith(s.impl(), caseSensitive) : s.isEmpty(); }
     bool startsWith(UChar character) const
@@ -273,7 +275,11 @@ public:
     bool hasInfixStartingAt(const String& prefix, unsigned startOffset) const
         { return m_impl && prefix.impl() ? m_impl->hasInfixStartingAt(*prefix.impl(), startOffset) : false; }
 
-    bool endsWith(const String& s, bool caseSensitive = true) const
+    bool endsWith(const String& s) const
+        { return m_impl ? m_impl->endsWith(s.impl()) : s.isEmpty(); }
+    bool endsWithIgnoringASCIICase(const String& s) const
+        { return m_impl ? m_impl->endsWithIgnoringASCIICase(s.impl()) : s.isEmpty(); }
+    bool endsWith(const String& s, bool caseSensitive) const
         { return m_impl ? m_impl->endsWith(s.impl(), caseSensitive) : s.isEmpty(); }
     bool endsWith(UChar character) const
         { return m_impl ? m_impl->endsWith(character) : false; }
index 9cd47ba..514860b 100644 (file)
@@ -1,3 +1,29 @@
+2015-03-15  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
+        https://bugs.webkit.org/show_bug.cgi?id=142715
+
+        Reviewed by Brent Fulgham.
+
+        Fix attribute matching with:
+        -Begin: [a^=b].
+        -End: [a$=b].
+        -Hyphen: [a|=b].
+
+        Tests: fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html
+               fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html
+               fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html
+
+        * css/SelectorChecker.cpp:
+        (WebCore::attributeValueMatches):
+        I forgot to change CSSSelector::Exact in my last patch.
+        The tests could not catch that since we use the CSS JIT almost everywhere.
+
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::attributeValueBeginsWith):
+        (WebCore::SelectorCompiler::attributeValueEndsWith):
+        (WebCore::SelectorCompiler::attributeValueMatchHyphenRule):
+
 2015-03-15  Dan Bernstein  <mitz@apple.com>
 
         Fixed the iOS build after r181522.
index a3ef519..e18b178 100644 (file)
@@ -420,7 +420,7 @@ static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match
     case CSSSelector::Set:
         break;
     case CSSSelector::Exact:
-        if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
+        if (caseSensitive ? selectorValue != value : !equalIgnoringASCIICase(selectorValue, value))
             return false;
         break;
     case CSSSelector::List:
@@ -450,18 +450,37 @@ static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match
             return false;
         break;
     case CSSSelector::Begin:
-        if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
+        if (selectorValue.isEmpty())
             return false;
+        if (caseSensitive) {
+            if (!value.startsWith(selectorValue))
+                return false;
+        } else {
+            if (!value.startsWithIgnoringASCIICase(selectorValue))
+                return false;
+        }
         break;
     case CSSSelector::End:
-        if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
+        if (selectorValue.isEmpty())
             return false;
+        if (caseSensitive) {
+            if (!value.endsWith(selectorValue))
+                return false;
+        } else {
+            if (!value.endsWithIgnoringASCIICase(selectorValue))
+                return false;
+        }
         break;
     case CSSSelector::Hyphen:
         if (value.length() < selectorValue.length())
             return false;
-        if (!value.startsWith(selectorValue, caseSensitive))
-            return false;
+        if (caseSensitive) {
+            if (!value.startsWith(selectorValue))
+                return false;
+        } else {
+            if (!value.startsWithIgnoringASCIICase(selectorValue))
+                return false;
+        }
         // It they start the same, check for exact match or following '-':
         if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
             return false;
index d51c75a..3fd0c49 100644 (file)
@@ -2803,10 +2803,12 @@ enum CaseSensitivity {
 template<CaseSensitivity caseSensitivity>
 static bool attributeValueBeginsWith(const Attribute* attribute, AtomicStringImpl* expectedString)
 {
+    ASSERT(expectedString);
+
     AtomicStringImpl& valueImpl = *attribute->value().impl();
     if (caseSensitivity == CaseSensitive)
-        return valueImpl.startsWith(expectedString);
-    return valueImpl.startsWith(expectedString, false);
+        return valueImpl.startsWith(*expectedString);
+    return valueImpl.startsWithIgnoringASCIICase(*expectedString);
 }
 
 template<CaseSensitivity caseSensitivity>
@@ -2821,24 +2823,28 @@ static bool attributeValueContains(const Attribute* attribute, AtomicStringImpl*
 template<CaseSensitivity caseSensitivity>
 static bool attributeValueEndsWith(const Attribute* attribute, AtomicStringImpl* expectedString)
 {
+    ASSERT(expectedString);
+
     AtomicStringImpl& valueImpl = *attribute->value().impl();
     if (caseSensitivity == CaseSensitive)
-        return valueImpl.endsWith(expectedString);
-    return valueImpl.endsWith(expectedString, false);
+        return valueImpl.endsWith(*expectedString);
+    return valueImpl.endsWithIgnoringASCIICase(*expectedString);
 }
 
 template<CaseSensitivity caseSensitivity>
 static bool attributeValueMatchHyphenRule(const Attribute* attribute, AtomicStringImpl* expectedString)
 {
+    ASSERT(expectedString);
+
     AtomicStringImpl& valueImpl = *attribute->value().impl();
     if (valueImpl.length() < expectedString->length())
         return false;
 
     bool valueStartsWithExpectedString;
     if (caseSensitivity == CaseSensitive)
-        valueStartsWithExpectedString = valueImpl.startsWith(expectedString);
+        valueStartsWithExpectedString = valueImpl.startsWith(*expectedString);
     else
-        valueStartsWithExpectedString = valueImpl.startsWith(expectedString, false);
+        valueStartsWithExpectedString = valueImpl.startsWithIgnoringASCIICase(*expectedString);
 
     if (!valueStartsWithExpectedString)
         return false;
index 8ccbb3c..32bf330 100644 (file)
@@ -1,3 +1,13 @@
+2015-03-15  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
+        https://bugs.webkit.org/show_bug.cgi?id=142715
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/Tests/WTF/StringImpl.cpp:
+        (TestWebKitAPI::TEST):
+
 2015-03-15  Benjamin Poulain  <bpoulain@apple.com>
 
         Change the exact attribute matching to be ASCII case-insensitive
index 995d0d4..f358e07 100644 (file)
@@ -164,4 +164,152 @@ TEST(WTF, StringImplEqualIgnoringASCIICaseWithLatin1Characters)
     ASSERT_TRUE(equalIgnoringASCIICase(c.get(), d.get()));
 }
 
+TEST(WTF, StringImplStartsWithIgnoringASCIICaseBasic)
+{
+    RefPtr<StringImpl> reference = StringImpl::create(reinterpret_cast<const LChar*>("aBcéX"));
+    RefPtr<StringImpl> referenceEquivalent = StringImpl::create(reinterpret_cast<const LChar*>("AbCéx"));
+
+    // Identity.
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(reference.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*reference.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(referenceEquivalent.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*referenceEquivalent.get()));
+    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(reference.get()));
+    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*reference.get()));
+    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(referenceEquivalent.get()));
+    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*referenceEquivalent.get()));
+
+    // Proper prefixes.
+    RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a");
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aLower.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aLower.get()));
+    RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("A");
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aUpper.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aUpper.get()));
+
+    RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("abc");
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcLower.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcLower.get()));
+    RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("ABC");
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcUpper.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcUpper.get()));
+
+    RefPtr<StringImpl> abcAccentLower = StringImpl::create(reinterpret_cast<const LChar*>("abcé"));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentLower.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentLower.get()));
+    RefPtr<StringImpl> abcAccentUpper = StringImpl::create(reinterpret_cast<const LChar*>("ABCé"));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentUpper.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentUpper.get()));
+
+    // Negative cases.
+    RefPtr<StringImpl> differentFirstChar = StringImpl::create(reinterpret_cast<const LChar*>("bBcéX"));
+    RefPtr<StringImpl> differentFirstCharProperPrefix = StringImpl::create(reinterpret_cast<const LChar*>("CBcé"));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstChar.get()));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstChar.get()));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstCharProperPrefix.get()));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstCharProperPrefix.get()));
+
+    RefPtr<StringImpl> uppercaseAccent = StringImpl::create(reinterpret_cast<const LChar*>("aBcÉX"));
+    RefPtr<StringImpl> uppercaseAccentProperPrefix = StringImpl::create(reinterpret_cast<const LChar*>("aBcÉX"));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccent.get()));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccent.get()));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccentProperPrefix.get()));
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccentProperPrefix.get()));
+}
+
+TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithNull)
+{
+    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(nullptr));
+
+    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    ASSERT_FALSE(empty->startsWithIgnoringASCIICase(nullptr));
+}
+
+TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithEmpty)
+{
+    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(empty.get()));
+    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*empty.get()));
+    ASSERT_TRUE(empty->startsWithIgnoringASCIICase(empty.get()));
+    ASSERT_TRUE(empty->startsWithIgnoringASCIICase(*empty.get()));
+    ASSERT_FALSE(empty->startsWithIgnoringASCIICase(reference.get()));
+    ASSERT_FALSE(empty->startsWithIgnoringASCIICase(*reference.get()));
+}
+
+TEST(WTF, StringImplEndsWithIgnoringASCIICaseBasic)
+{
+    RefPtr<StringImpl> reference = StringImpl::create(reinterpret_cast<const LChar*>("XÉCbA"));
+    RefPtr<StringImpl> referenceEquivalent = StringImpl::create(reinterpret_cast<const LChar*>("xÉcBa"));
+
+    // Identity.
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(reference.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*reference.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(referenceEquivalent.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*referenceEquivalent.get()));
+    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(reference.get()));
+    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*reference.get()));
+    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(referenceEquivalent.get()));
+    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*referenceEquivalent.get()));
+
+    // Proper suffixes.
+    RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a");
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aLower.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aLower.get()));
+    RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("a");
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aUpper.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aUpper.get()));
+
+    RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("cba");
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcLower.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcLower.get()));
+    RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("CBA");
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcUpper.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcUpper.get()));
+
+    RefPtr<StringImpl> abcAccentLower = StringImpl::create(reinterpret_cast<const LChar*>("Écba"));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentLower.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentLower.get()));
+    RefPtr<StringImpl> abcAccentUpper = StringImpl::create(reinterpret_cast<const LChar*>("ÉCBA"));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentUpper.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentUpper.get()));
+
+    // Negative cases.
+    RefPtr<StringImpl> differentLastChar = StringImpl::create(reinterpret_cast<const LChar*>("XÉCbB"));
+    RefPtr<StringImpl> differentLastCharProperSuffix = StringImpl::create(reinterpret_cast<const LChar*>("ÉCbb"));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastChar.get()));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastChar.get()));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastCharProperSuffix.get()));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastCharProperSuffix.get()));
+
+    RefPtr<StringImpl> lowercaseAccent = StringImpl::create(reinterpret_cast<const LChar*>("aBcéX"));
+    RefPtr<StringImpl> loweraseAccentProperSuffix = StringImpl::create(reinterpret_cast<const LChar*>("aBcéX"));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(lowercaseAccent.get()));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*lowercaseAccent.get()));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(loweraseAccentProperSuffix.get()));
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*loweraseAccentProperSuffix.get()));
+}
+
+TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithNull)
+{
+    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(nullptr));
+
+    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    ASSERT_FALSE(empty->endsWithIgnoringASCIICase(nullptr));
+}
+
+TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithEmpty)
+{
+    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(empty.get()));
+    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*empty.get()));
+    ASSERT_TRUE(empty->endsWithIgnoringASCIICase(empty.get()));
+    ASSERT_TRUE(empty->endsWithIgnoringASCIICase(*empty.get()));
+    ASSERT_FALSE(empty->endsWithIgnoringASCIICase(reference.get()));
+    ASSERT_FALSE(empty->endsWithIgnoringASCIICase(*reference.get()));
+}
+
 } // namespace TestWebKitAPI