Start fixing the handling of Element's attributes when they contain non-ASCII characters
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Jan 2015 02:05:10 +0000 (02:05 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Jan 2015 02:05:10 +0000 (02:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141016

Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-01-28
Reviewed by Ryosuke Niwa.

Source/WebCore:

Attribute handling does not work properly when the attribute name contains non-ASCII character.

The HTML parser tokenize those names as ASCII lowercase. Some of the code is CSS and Element use
unicode lowercase for the names. This breaks all the APIs as soon as a name contains a character
that is non-ASCII and uppercase since some APIs change it, other don't.

This patch is a first step toward fixing this mess, it only address the simple cases.
The HTML spec says the names should be compared ASCII case-insensitive, to I spread that behavior
to places that were using unicode.

Tests: fast/css/attribute-ascii-case-insensitive-html.html
       fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml
       fast/css/attribute-ascii-case-insensitive-xhtml.xhtml
       fast/css/attribute-ascii-case-insensitive-xml-in-html.html
       fast/dom/Element/attribute-ascii-case-insensitive-1.html
       fast/dom/Element/attribute-ascii-case-insensitive-2.html
       fast/selectors/attribute-ascii-case-insensitive-style-update.html
       fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html
       fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html

* css/CSSSelector.cpp:
(WebCore::CSSSelector::setAttribute):
* dom/Element.cpp:
(WebCore::Element::synchronizeAttribute):
(WebCore::Element::setAttribute):
(WebCore::Element::removeAttribute):
(WebCore::Element::hasAttribute):
* dom/ElementData.cpp:
(WebCore::ElementData::findAttributeIndexByNameSlowCase):
* dom/ElementData.h:
(WebCore::ElementData::findAttributeIndexByName):

LayoutTests:

Start some basic testing.

Some tests are failing due to the more complicated case being handled incorrectly, this will
be fixed in follow ups.

* fast/css/attribute-ascii-case-insensitive-html-expected.html: Added.
* fast/css/attribute-ascii-case-insensitive-html.html: Added.
* fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml: Added.
* fast/css/attribute-ascii-case-insensitive-xhtml.xhtml: Added.
* fast/css/attribute-ascii-case-insensitive-xml-in-html-expected.html: Added.
* fast/css/attribute-ascii-case-insensitive-xml-in-html.html: Added.
* fast/dom/Element/attribute-ascii-case-insensitive-1-expected.txt: Added.
* fast/dom/Element/attribute-ascii-case-insensitive-1.html: Added.
* fast/dom/Element/attribute-ascii-case-insensitive-2-expected.txt: Added.
* fast/dom/Element/attribute-ascii-case-insensitive-2.html: Added.
* fast/selectors/attribute-ascii-case-insensitive-style-update-expected.txt: Added.
* fast/selectors/attribute-ascii-case-insensitive-style-update.html: Added.
* fast/selectors/element-matches-attribute-ascii-case-insensitive-html-expected.txt: Added.
* fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html: Added.
* fast/selectors/querySelector-attribute-ascii-case-insensitive-html-expected.txt: Added.
* fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html: Added.

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/attribute-ascii-case-insensitive-html-expected.html [new file with mode: 0644]
LayoutTests/fast/css/attribute-ascii-case-insensitive-html.html [new file with mode: 0644]
LayoutTests/fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml [new file with mode: 0644]
LayoutTests/fast/css/attribute-ascii-case-insensitive-xhtml.xhtml [new file with mode: 0644]
LayoutTests/fast/css/attribute-ascii-case-insensitive-xml-in-html-expected.html [new file with mode: 0644]
LayoutTests/fast/css/attribute-ascii-case-insensitive-xml-in-html.html [new file with mode: 0644]
LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-1-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-1.html [new file with mode: 0644]
LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-2-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-2.html [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-ascii-case-insensitive-style-update-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/attribute-ascii-case-insensitive-style-update.html [new file with mode: 0644]
LayoutTests/fast/selectors/element-matches-attribute-ascii-case-insensitive-html-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html [new file with mode: 0644]
LayoutTests/fast/selectors/querySelector-attribute-ascii-case-insensitive-html-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/CSSSelector.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/ElementData.cpp
Source/WebCore/dom/ElementData.h

index 0ffebe4..149708e 100644 (file)
@@ -1,3 +1,32 @@
+2015-01-28  Benjamin Poulain  <bpoulain@apple.com>
+
+        Start fixing the handling of Element's attributes when they contain non-ASCII characters
+        https://bugs.webkit.org/show_bug.cgi?id=141016
+
+        Reviewed by Ryosuke Niwa.
+
+        Start some basic testing.
+
+        Some tests are failing due to the more complicated case being handled incorrectly, this will
+        be fixed in follow ups.
+
+        * fast/css/attribute-ascii-case-insensitive-html-expected.html: Added.
+        * fast/css/attribute-ascii-case-insensitive-html.html: Added.
+        * fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml: Added.
+        * fast/css/attribute-ascii-case-insensitive-xhtml.xhtml: Added.
+        * fast/css/attribute-ascii-case-insensitive-xml-in-html-expected.html: Added.
+        * fast/css/attribute-ascii-case-insensitive-xml-in-html.html: Added.
+        * fast/dom/Element/attribute-ascii-case-insensitive-1-expected.txt: Added.
+        * fast/dom/Element/attribute-ascii-case-insensitive-1.html: Added.
+        * fast/dom/Element/attribute-ascii-case-insensitive-2-expected.txt: Added.
+        * fast/dom/Element/attribute-ascii-case-insensitive-2.html: Added.
+        * fast/selectors/attribute-ascii-case-insensitive-style-update-expected.txt: Added.
+        * fast/selectors/attribute-ascii-case-insensitive-style-update.html: Added.
+        * fast/selectors/element-matches-attribute-ascii-case-insensitive-html-expected.txt: Added.
+        * fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html: Added.
+        * fast/selectors/querySelector-attribute-ascii-case-insensitive-html-expected.txt: Added.
+        * fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html: Added.
+
 2015-01-28  Brent Fulgham  <bfulgham@apple.com>
 
         [Win] Actually delete the file!
diff --git a/LayoutTests/fast/css/attribute-ascii-case-insensitive-html-expected.html b/LayoutTests/fast/css/attribute-ascii-case-insensitive-html-expected.html
new file mode 100644 (file)
index 0000000..79606aa
--- /dev/null
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+    target {
+        display: block;
+        height: 20px;
+        min-width: 20px;
+        float: left;
+        margin: 2px;
+    }
+</style>
+</head>
+<body>
+    <p>In HTML, attribute name matching is case-insensitive but only in the ASCII range.</p>
+    <p>If the test succeed, each block should be styled as described.</p>
+    <target style="background-color: green; color: blue; border: 2px dashed red;">Green background, blue text, dashed red border.</target>
+    <target style="background-color: green; color: blue; border: 2px dashed red;">Green background, blue text, dashed red border.</target>
+    <target style="background-color: green; color: blue; border: 2px dashed red;">Green background, blue text, dashed red border.</target>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/attribute-ascii-case-insensitive-html.html b/LayoutTests/fast/css/attribute-ascii-case-insensitive-html.html
new file mode 100644 (file)
index 0000000..7c982c2
--- /dev/null
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+    target {
+        display: block;
+        height: 20px;
+        min-width: 20px;
+        float: left;
+        margin: 2px;
+    }
+    target:matches([data-Foo], [data-Æøå], [data-Foo-Æøå]) {
+        background-color: green;
+    }
+    target:matches([data-FOO], [data-ÆØÅ], [data-FOO-ÆØÅ]) {
+        color: blue;
+    }
+    target:matches([data-foo], [data-æøå], [data-foo-æøå]) {
+        border: 2px dashed red;
+    }
+</style>
+</head>
+<body>
+    <p data-Foo data-ÆØÅ data-foo-æøå>In HTML, attribute name matching is case-insensitive but only in the ASCII range.</p>
+    <p data-foo data-Foo-Æøå data-Æøå>If the test succeed, each block should be styled as described.</p>
+    <target data-Foo>Green background, blue text, dashed red border.</target>
+    <target data-FOO>Green background, blue text, dashed red border.</target>
+    <target data-foo>Green background, blue text, dashed red border.</target>
+    <target data-Æøå>Green background color.</target>
+    <target data-ÆØÅ>Blue text.</target>
+    <target data-æøå>Dashed red border.</target>
+    <target data-Foo-Æøå>Green background color.</target>
+    <target data-FOO-ÆØÅ>Blue text.</target>
+    <target data-foo-æøå>Dashed red border.</target>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml b/LayoutTests/fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml
new file mode 100644 (file)
index 0000000..31fc47b
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta charset="utf-8" />
+<style>
+    target {
+        display: block;
+        height: 20px;
+        min-width: 20px;
+        float: left;
+        margin: 2px;
+    }
+</style>
+</head>
+<body>
+    <p>In XHTML, attribute name matching is always case-sensitive.</p>
+    <p>If the test succeed, each block should be styled as described.</p>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/attribute-ascii-case-insensitive-xhtml.xhtml b/LayoutTests/fast/css/attribute-ascii-case-insensitive-xhtml.xhtml
new file mode 100644 (file)
index 0000000..34f226c
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta charset="utf-8" />
+<style>
+    target {
+        display: block;
+        height: 20px;
+        min-width: 20px;
+        float: left;
+        margin: 2px;
+    }
+    target:matches([data-Foo], [data-Æøå], [data-Foo-Æøå]) {
+        background-color: green;
+    }
+    target:matches([data-FOO], [data-ÆØÅ], [data-FOO-ÆØÅ]) {
+        color: blue;
+    }
+    target:matches([data-foo], [data-æøå], [data-foo-æøå]) {
+        border: 2px dashed red;
+    }
+</style>
+</head>
+<body>
+    <p data-Foo="" data-ÆØÅ="" data-foo-æøå="">In XHTML, attribute name matching is always case-sensitive.</p>
+    <p data-foo="" data-Foo-Æøå="" data-Æøå="">If the test succeed, each block should be styled as described.</p>
+    <target data-Foo="">Green background color.</target>
+    <target data-FOO="">Blue text.</target>
+    <target data-foo="">Dashed red border.</target>
+    <target data-Æøå="">Green background color.</target>
+    <target data-ÆØÅ="">Blue text.</target>
+    <target data-æøå="">Dashed red border.</target>
+    <target data-Foo-Æøå="">Green background color.</target>
+    <target data-FOO-ÆØÅ="">Blue text.</target>
+    <target data-foo-æøå="">Dashed red border.</target>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/attribute-ascii-case-insensitive-xml-in-html-expected.html b/LayoutTests/fast/css/attribute-ascii-case-insensitive-xml-in-html-expected.html
new file mode 100644 (file)
index 0000000..9b0c4d2
--- /dev/null
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+    target, target1, target2, target3 {
+        display: block;
+        height: 20px;
+        min-width: 20px;
+        float: left;
+        margin: 2px;
+    }
+    target1 {
+        background-color: green;
+    }
+    target2 {
+        color: blue;
+    }
+    target3 {
+        border: 2px dashed red;
+    }
+</style>
+</head>
+<body>
+    <p>In HTML, attribute name matching is case-insensitive but only in the ASCII range. In XML, attributes are always case-sensitive. When matching attributes on a document mixing XML and HTML, each type should handle the case correctly.</p>
+    <p>If the test succeed, each block should be styled as described.</p>
+    <target style="background-color: green; color: blue; border: 2px dashed red;">Green background, blue text, dashed red border.</target>
+    <target style="background-color: green; color: blue; border: 2px dashed red;">Green background, blue text, dashed red border.</target>
+    <target style="background-color: green; color: blue; border: 2px dashed red;">Green background, blue text, dashed red border.</target>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+    <target style="background-color: green;">Green background color.</target>
+    <target style="color: blue;">Blue text.</target>
+    <target style="border: 2px dashed red;">Dashed red border.</target>
+    <xml-container id="xml-container"></xml-container>
+</body>
+<script>
+    var xmlDocument = new DOMParser().parseFromString('<xml xmlns="https://www.webkit.org/awesome"><target1>Green background color.</target1><target2>Blue text.</target2><target3>Dashed red border.</target3><target1>Green background color.</target1><target2>Blue text.</target2><target3>Dashed red border.</target3></xml>', 'text/xml');
+    var container = document.getElementById("xml-container");
+    container.appendChild(document.importNode(xmlDocument.documentElement, true));
+</script>
+</html>
diff --git a/LayoutTests/fast/css/attribute-ascii-case-insensitive-xml-in-html.html b/LayoutTests/fast/css/attribute-ascii-case-insensitive-xml-in-html.html
new file mode 100644 (file)
index 0000000..634f4b6
--- /dev/null
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+    target {
+        display: block;
+        height: 20px;
+        min-width: 20px;
+        float: left;
+        margin: 2px;
+    }
+    target:matches([data-Foo], [data-Æøå], [data-Foo-Æøå]) {
+        background-color: green;
+    }
+    target:matches([data-FOO], [data-ÆØÅ], [data-FOO-ÆØÅ]) {
+        color: blue;
+    }
+    target:matches([data-foo], [data-æøå], [data-foo-æøå]) {
+        border: 2px dashed red;
+    }
+</style>
+</head>
+<body>
+    <p data-Foo data-ÆØÅ data-foo-æøå>In HTML, attribute name matching is case-insensitive but only in the ASCII range. In XML, attributes are always case-sensitive. When matching attributes on a document mixing XML and HTML, each type should handle the case correctly.</p>
+    <p data-foo data-Foo-Æøå data-Æøå>If the test succeed, each block should be styled as described.</p>
+    <target data-Foo>Green background, blue text, dashed red border.</target>
+    <target data-FOO>Green background, blue text, dashed red border.</target>
+    <target data-foo>Green background, blue text, dashed red border.</target>
+    <target data-Æøå>Green background color.</target>
+    <target data-ÆØÅ>Blue text.</target>
+    <target data-æøå>Dashed red border.</target>
+    <target data-Foo-Æøå>Green background color.</target>
+    <target data-FOO-ÆØÅ>Blue text.</target>
+    <target data-foo-æøå>Dashed red border.</target>
+    <xml-container id="xml-container"></xml-container>
+</body>
+<script>
+    var xmlDocument = new DOMParser().parseFromString('<xml xmlns="https://www.webkit.org/awesome"><target data-Foo="">Green background color.</target><target data-FOO="">Blue text.</target><target data-foo="">Dashed red border.</target><target data-Æøå="">Green background color.</target><target data-ÆØÅ="">Blue text.</target><target data-æøå="">Dashed red border.</target></xml>', 'text/xml');
+    var container = document.getElementById("xml-container");
+    container.appendChild(document.importNode(xmlDocument.documentElement, true));
+</script>
+</html>
diff --git a/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-1-expected.txt b/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-1-expected.txt
new file mode 100644 (file)
index 0000000..bd17e8c
--- /dev/null
@@ -0,0 +1,315 @@
+In HTML, attributes should be ASCII case-insensitive. This test mostly checks Element.hasAttribute() with different source for setting the attribute.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing data-æøå
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+
+Testing DATA-æøå
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+
+Testing Data-æøå
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-æøå") is true
+PASS document.getElementById("target").getAttribute("Data-æøå") is "WebKit!"
+
+Testing data-Æøå
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+
+Testing DATA-Æøå
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+
+Testing Data-Æøå
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+FAIL document.getElementById("target").getAttribute("data-ÆØÅ") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-Æøå") is true
+PASS document.getElementById("target").getAttribute("DATA-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-Æøå") is true
+PASS document.getElementById("target").getAttribute("Data-Æøå") is "WebKit!"
+
+Testing data-ÆØÅ
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+
+Testing DATA-ÆØÅ
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+
+Testing Data-ÆØÅ
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+FAIL document.getElementById("target").getAttribute("data-Æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+FAIL document.getElementById("target").getAttribute("data-æøå") should be null (of type object). Was WebKit! (of type string).
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("DATA-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("DATA-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("Data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("Data-ÆØÅ") is "WebKit!"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-1.html b/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-1.html
new file mode 100644 (file)
index 0000000..5e2f814
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+    <div id="test-container" style="display: none;"></div>
+</body>
+<script>
+description('In HTML, attributes should be ASCII case-insensitive. This test mostly checks Element.hasAttribute() with different source for setting the attribute.');
+
+var testContainer = document.getElementById('test-container');
+
+function testAttributeOnTarget(equivalentNames, distinctNames) {
+    for (var equivalentName of equivalentNames) {
+        shouldBeTrue('document.getElementById("target").hasAttribute("' + equivalentName + '")');
+        shouldBeEqualToString('document.getElementById("target").getAttribute("' + equivalentName + '")', 'WebKit!');
+    }
+
+    for (var distinctName of distinctNames) {
+        shouldBeFalse('document.getElementById("target").hasAttribute("' + distinctName + '")');
+        shouldBe('document.getElementById("target").getAttribute("' + distinctName + '")', 'null');
+    }
+
+    var target = document.getElementById("target");
+    for (var distinctName of distinctNames) {
+        target.removeAttribute(distinctName);
+    }
+
+    for (var equivalentName of equivalentNames) {
+        shouldBeTrue('document.getElementById("target").hasAttribute("' + equivalentName + '")');
+        shouldBeEqualToString('document.getElementById("target").getAttribute("' + equivalentName + '")', 'WebKit!');
+    }
+}
+
+function testParsedAttribute(attribute, equivalentNames, distinctNames) {
+    testContainer.innerHTML = '<div ' + attribute + '="WebKit!" id="target"></div>';
+    testAttributeOnTarget(equivalentNames, distinctNames);
+    testContainer.innerHTML = '';
+}
+
+function testAttributeFromDOMApis(attribute, equivalentNames, distinctNames) {
+    var newElement = document.createElement('div');
+    newElement.setAttribute('id', 'target');
+    newElement.setAttribute(attribute, "WebKit!");
+    testContainer.appendChild(newElement);
+    testAttributeOnTarget(equivalentNames, distinctNames);
+    testContainer.innerHTML = '';
+}
+
+function testAttribute(equivalentNames, distinctNames) {
+    for (var testCase of equivalentNames) {
+        debug("Testing " + testCase);
+        testParsedAttribute(testCase, equivalentNames, distinctNames);
+        testAttributeFromDOMApis(testCase, equivalentNames, distinctNames);
+        debug("");
+    }
+}
+
+testAttribute(['data-æøå', 'DATA-æøå', 'Data-æøå'], ['data-ÆØÅ', 'data-Æøå']);
+testAttribute(['data-Æøå', 'DATA-Æøå', 'Data-Æøå'], ['data-ÆØÅ', 'data-æøå']);
+testAttribute(['data-ÆØÅ', 'DATA-ÆØÅ', 'Data-ÆØÅ'], ['data-Æøå', 'data-æøå']);
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-2-expected.txt b/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-2-expected.txt
new file mode 100644 (file)
index 0000000..7b4a72a
--- /dev/null
@@ -0,0 +1,70 @@
+In HTML, attributes should be ASCII case-insensitive. This test mostly checks Element.removeAttribute() with different source for setting the attribute.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.getElementById("target").hasAttribute("data-ÆøÅ") is true
+PASS document.getElementById("target").hasAttribute("data-ÆøÅ") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æØÅ") is true
+PASS document.getElementById("target").getAttribute("data-æØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æØÅ") is true
+PASS document.getElementById("target").hasAttribute("data-æØÅ") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+PASS document.getElementById("target").hasAttribute("data-ÆøÅ") is true
+PASS document.getElementById("target").hasAttribute("data-ÆøÅ") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æØÅ") is true
+PASS document.getElementById("target").getAttribute("data-æØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æØÅ") is true
+PASS document.getElementById("target").hasAttribute("data-æØÅ") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").getAttribute("data-ÆØÅ") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is true
+PASS document.getElementById("target").hasAttribute("data-ÆØÅ") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").getAttribute("data-æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-æøå") is true
+PASS document.getElementById("target").hasAttribute("data-æøå") is false
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").getAttribute("data-Æøå") is "WebKit!"
+PASS document.getElementById("target").hasAttribute("data-Æøå") is true
+PASS document.getElementById("target").hasAttribute("data-Æøå") is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-2.html b/LayoutTests/fast/dom/Element/attribute-ascii-case-insensitive-2.html
new file mode 100644 (file)
index 0000000..715cb06
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+    <div id="test-container" style="display: none;"></div>
+</body>
+<script>
+description('In HTML, attributes should be ASCII case-insensitive. This test mostly checks Element.removeAttribute() with different source for setting the attribute.');
+
+var testContainer = document.getElementById('test-container');
+
+function testAttributeOnTarget(distinctNames) {
+    var names = distinctNames.slice(0);
+    while (names.length) {
+        var nameToRemove = names.pop();
+        shouldBeTrue('document.getElementById("target").hasAttribute("' + nameToRemove + '")');
+        document.getElementById("target").removeAttribute(nameToRemove);
+        shouldBeFalse('document.getElementById("target").hasAttribute("' + nameToRemove + '")');
+
+        for (var name of names) {
+            shouldBeTrue('document.getElementById("target").hasAttribute("' + name + '")');
+            shouldBeEqualToString('document.getElementById("target").getAttribute("' + name + '")', 'WebKit!');
+        }
+    }
+}
+
+function testParsedAttribute(distinctNames) {
+    var testCaseString = '<div id="target" ';
+    for (var name of distinctNames) {
+        testCaseString += ' ' + name + '="WebKit!"';
+    }
+    testCaseString += '</div>';
+
+    testContainer.innerHTML = testCaseString;
+    testAttributeOnTarget(distinctNames);
+    testContainer.innerHTML = '';
+}
+
+function testAttributeFromDOMApis(distinctNames) {
+    var newElement = document.createElement('div');
+    newElement.setAttribute('id', 'target');
+    for (var name of distinctNames) {
+        newElement.setAttribute(name, "WebKit!");
+    }
+    testContainer.appendChild(newElement);
+    testAttributeOnTarget(distinctNames);
+    testContainer.innerHTML = '';
+}
+
+function testAttributes(distinctNames) {
+    testParsedAttribute(distinctNames);
+    testAttributeFromDOMApis(distinctNames);
+    debug("");
+}
+
+testAttributes(['data-Æøå', 'data-æøå', 'data-ÆØÅ', 'data-æØÅ', 'data-ÆøÅ']);
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/attribute-ascii-case-insensitive-style-update-expected.txt b/LayoutTests/fast/selectors/attribute-ascii-case-insensitive-style-update-expected.txt
new file mode 100644 (file)
index 0000000..844f362
--- /dev/null
@@ -0,0 +1,121 @@
+Test the basic cases of style update for attribute selectors for HTML.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initial state does not match anything, there are no attributes on the targets.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "data-æøå", the background-color should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Removing "data-æøå", the background-color should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "DATA-æøå", the background-color should match because the document is HTML and has case-insensitive attribute matching.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Removing "DATA-æøå", the background-color should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "DATA-æøå", the background-color should match because the document is HTML and has case-insensitive attribute matching.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "data-ÆØÅ", the fill-opacity should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Removing "data-ÆØÅ", the fill-opacity should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "dAta-ÆØÅ", the fill-opacity should match because the document is HTML and has case-insensitive attribute matching.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Removing "dAta-ÆØÅ", the fill-opacity should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "1"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "Data-ÆØÅ", the fill-opacity should match because the document is HTML and has case-insensitive attribute matching.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "data-Æøå", the color should match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(4, 5, 6)"
+Removing "data-Æøå", the color should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "Data-Æøå", the color should match because the document is HTML and has case-insensitive attribute matching.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(4, 5, 6)"
+Removing "Data-Æøå", the color should no longer match.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(0, 0, 0)"
+Adding "DATA-Æøå", the color should match because the document is HTML and has case-insensitive attribute matching.
+PASS getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity is "0.5"
+PASS getComputedStyle(document.querySelectorAll("target")[0]).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.querySelectorAll("target")[1]).color is "rgb(4, 5, 6)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/attribute-ascii-case-insensitive-style-update.html b/LayoutTests/fast/selectors/attribute-ascii-case-insensitive-style-update.html
new file mode 100644 (file)
index 0000000..f4b6fb6
--- /dev/null
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+* {
+    background-color: white;
+    color: black;
+    fill-opacity: 1;
+}
+[data-æøå] {
+    background-color: rgb(1, 2, 3);
+}
+[data-ÆØÅ] {
+    fill-opacity: 0.5;
+}
+[data-Æøå] {
+    color: rgb(4, 5, 6);
+}
+</style>
+</head>
+<body>
+    <div>
+        <!-- With renderer -->
+        <target></target>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <target></target>
+    </div>
+</body>
+<script>
+description('Test the basic cases of style update for attribute selectors for HTML.');
+
+var noMatch = 0;
+var matchFirst = 1;
+var matchSecond = 1 << 1;
+var matchThird = 1 << 2;
+
+function testColor(mask) {
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).backgroundColor', (mask & matchFirst) ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).backgroundColor', (mask & matchFirst) ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).fillOpacity', (mask & matchSecond) ? '0.5' : '1');
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).fillOpacity', (mask & matchSecond) ? '0.5' : '1');
+
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[0]).color', (mask & matchThird) ? 'rgb(4, 5, 6)' : 'rgb(0, 0, 0)');
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("target")[1]).color', (mask & matchThird) ? 'rgb(4, 5, 6)' : 'rgb(0, 0, 0)');
+}
+
+function setAttribute(attribute, value) {
+    var allTargets = document.querySelectorAll("target");
+    for (var i = 0; i < allTargets.length; ++i)
+        allTargets[i].setAttribute(attribute, value);
+}
+
+function removeAttribute(attribute) {
+    var allTargets = document.querySelectorAll("target");
+    for (var i = 0; i < allTargets.length; ++i)
+        allTargets[i].removeAttribute(attribute);
+}
+
+debug("Initial state does not match anything, there are no attributes on the targets.");
+testColor(noMatch);
+
+debug("Adding \"data-æøå\", the background-color should match.");
+setAttribute("data-æøå");
+testColor(matchFirst);
+
+debug("Removing \"data-æøå\", the background-color should no longer match.");
+removeAttribute("data-æøå");
+testColor(noMatch);
+
+debug("Adding \"DATA-æøå\", the background-color should match because the document is HTML and has case-insensitive attribute matching.");
+setAttribute("DATA-æøå");
+testColor(matchFirst);
+
+debug("Removing \"DATA-æøå\", the background-color should no longer match.");
+removeAttribute("DATA-æøå");
+testColor(noMatch);
+
+debug("Adding \"DATA-æøå\", the background-color should match because the document is HTML and has case-insensitive attribute matching.");
+setAttribute("DATA-æøå");
+testColor(matchFirst);
+
+
+debug("Adding \"data-ÆØÅ\", the fill-opacity should match.");
+setAttribute("data-ÆØÅ");
+testColor(matchFirst | matchSecond);
+
+debug("Removing \"data-ÆØÅ\", the fill-opacity should no longer match.");
+removeAttribute("data-ÆØÅ");
+testColor(matchFirst);
+
+debug("Adding \"dAta-ÆØÅ\", the fill-opacity should match because the document is HTML and has case-insensitive attribute matching.");
+setAttribute("dAta-ÆØÅ");
+testColor(matchFirst | matchSecond);
+
+debug("Removing \"dAta-ÆØÅ\", the fill-opacity should no longer match.");
+removeAttribute("dAta-ÆØÅ");
+testColor(matchFirst);
+
+debug("Adding \"Data-ÆØÅ\", the fill-opacity should match because the document is HTML and has case-insensitive attribute matching.");
+setAttribute("Data-ÆØÅ");
+testColor(matchFirst | matchSecond);
+
+
+debug("Adding \"data-Æøå\", the color should match.");
+setAttribute("data-Æøå");
+testColor(matchFirst | matchSecond | matchThird);
+
+debug("Removing \"data-Æøå\", the color should no longer match.");
+removeAttribute("data-Æøå");
+testColor(matchFirst | matchSecond);
+
+debug("Adding \"Data-Æøå\", the color should match because the document is HTML and has case-insensitive attribute matching.");
+setAttribute("Data-Æøå");
+testColor(matchFirst | matchSecond | matchThird);
+
+debug("Removing \"Data-Æøå\", the color should no longer match.");
+removeAttribute("Data-Æøå");
+testColor(matchFirst | matchSecond);
+
+debug("Adding \"DATA-Æøå\", the color should match because the document is HTML and has case-insensitive attribute matching.");
+setAttribute("DATA-Æøå");
+testColor(matchFirst | matchSecond | matchThird);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/element-matches-attribute-ascii-case-insensitive-html-expected.txt b/LayoutTests/fast/selectors/element-matches-attribute-ascii-case-insensitive-html-expected.txt
new file mode 100644 (file)
index 0000000..cd016d0
--- /dev/null
@@ -0,0 +1,105 @@
+Attribute matching is ASCII case-insensitive in HTML.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll('[id^=target]')[0].matches('[data-æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[1].matches('[data-æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[data-æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[data-æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[data-Æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[data-Æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[2].matches('[data-Æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[data-Æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[data-ÆØÅ]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[data-ÆØÅ]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[data-ÆØÅ]') is true
+PASS document.querySelectorAll('[id^=target]')[3].matches('[data-ÆØÅ]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[0].matches('[Data-æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[1].matches('[Data-æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[Data-æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[Data-æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[Data-Æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[Data-Æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[2].matches('[Data-Æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[Data-Æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[Data-ÆØÅ="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[Data-ÆØÅ="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[Data-ÆØÅ="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[3].matches('[Data-ÆØÅ="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[0].matches('[DATA-æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[1].matches('[DATA-æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[DATA-æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[DATA-æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[DATA-Æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[DATA-Æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[2].matches('[DATA-Æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[DATA-Æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[DATA-ÆØÅ^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[DATA-ÆØÅ^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[DATA-ÆØÅ^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[3].matches('[DATA-ÆØÅ^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches('[DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches('[DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches('[DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches('[DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [data-æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [data-æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [data-æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [data-æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [data-Æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [data-Æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [data-Æøå]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [data-Æøå]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [data-ÆØÅ]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [data-ÆØÅ]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [data-ÆØÅ]') is true
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [data-ÆØÅ]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [data-æØå]') is false
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]') is false
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [DATA-æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [DATA-æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [DATA-æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [DATA-æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]') is true
+PASS document.querySelectorAll('[id^=target]')[0].matches(':root >> :matches(body, html) >> [DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[1].matches(':root >> :matches(body, html) >> [DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[2].matches(':root >> :matches(body, html) >> [DATA-æØå^="Web"]') is false
+PASS document.querySelectorAll('[id^=target]')[3].matches(':root >> :matches(body, html) >> [DATA-æØå^="Web"]') is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html b/LayoutTests/fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html
new file mode 100644 (file)
index 0000000..afc9d4d
--- /dev/null
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<div style="display:none">
+    <div id="target1" data-æøå="WebKit!"></div>
+    <div id="target2" data-Æøå="WebKit!"></div>
+    <div id="target3" data-ÆØÅ="WebKit!"></div>
+    <div id="target4" data-æøå="WebKit!" data-Æøå="WebKit!" data-ÆØÅ="WebKit!"></div>
+</div>
+</body>
+<script>
+description('Attribute matching is ASCII case-insensitive in HTML.');
+
+function testSelector(selector, expected) {
+    var allTargets = document.querySelectorAll('[id^=target]');
+    for (var i = 0; i < allTargets.length; ++i) {
+        if (expected.indexOf(i + 1) >= 0)
+            shouldBeTrue('document.querySelectorAll(\'[id^=target]\')[' + i + '].matches(\'' + selector + '\')');
+        else
+            shouldBeFalse('document.querySelectorAll(\'[id^=target]\')[' + i + '].matches(\'' + selector + '\')');
+    }
+}
+
+// Simple selectors.
+testSelector('[data-æøå]', [1, 4]);
+testSelector('[data-Æøå]', [2, 4]);
+testSelector('[data-ÆØÅ]', [3, 4]);
+testSelector('[data-æØå]', []);
+
+testSelector('[Data-æøå="WebKit!"]', [1, 4]);
+testSelector('[Data-Æøå="WebKit!"]', [2, 4]);
+testSelector('[Data-ÆØÅ="WebKit!"]', [3, 4]);
+testSelector('[Data-æØå="WebKit!"]', []);
+
+testSelector('[DATA-æøå^="Web"]', [1, 4]);
+testSelector('[DATA-Æøå^="Web"]', [2, 4]);
+testSelector('[DATA-ÆØÅ^="Web"]', [3, 4]);
+testSelector('[DATA-æØå^="Web"]', []);
+
+// Complex selectors.
+testSelector(':root >> :matches(body, html) >> [data-æøå]', [1, 4]);
+testSelector(':root >> :matches(body, html) >> [data-Æøå]', [2, 4]);
+testSelector(':root >> :matches(body, html) >> [data-ÆØÅ]', [3, 4]);
+testSelector(':root >> :matches(body, html) >> [data-æØå]', []);
+
+testSelector(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]', [1, 4]);
+testSelector(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]', [2, 4]);
+testSelector(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]', [3, 4]);
+testSelector(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]', []);
+
+testSelector(':root >> :matches(body, html) >> [DATA-æøå^="Web"]', [1, 4]);
+testSelector(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]', [2, 4]);
+testSelector(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]', [3, 4]);
+testSelector(':root >> :matches(body, html) >> [DATA-æØå^="Web"]', []);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/querySelector-attribute-ascii-case-insensitive-html-expected.txt b/LayoutTests/fast/selectors/querySelector-attribute-ascii-case-insensitive-html-expected.txt
new file mode 100644 (file)
index 0000000..b3569f7
--- /dev/null
@@ -0,0 +1,69 @@
+Attribute matching is ASCII case-insensitive in HTML.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll('[data-æøå]').length is 2
+PASS document.querySelectorAll('[data-æøå]')[0].id is "target1"
+PASS document.querySelectorAll('[data-æøå]')[1].id is "target4"
+PASS document.querySelectorAll('[data-Æøå]').length is 2
+PASS document.querySelectorAll('[data-Æøå]')[0].id is "target2"
+PASS document.querySelectorAll('[data-Æøå]')[1].id is "target4"
+PASS document.querySelectorAll('[data-ÆØÅ]').length is 2
+PASS document.querySelectorAll('[data-ÆØÅ]')[0].id is "target3"
+PASS document.querySelectorAll('[data-ÆØÅ]')[1].id is "target4"
+PASS document.querySelectorAll('[data-æØå]').length is 0
+PASS document.querySelectorAll('[Data-æøå="WebKit!"]').length is 2
+PASS document.querySelectorAll('[Data-æøå="WebKit!"]')[0].id is "target1"
+PASS document.querySelectorAll('[Data-æøå="WebKit!"]')[1].id is "target4"
+PASS document.querySelectorAll('[Data-Æøå="WebKit!"]').length is 2
+PASS document.querySelectorAll('[Data-Æøå="WebKit!"]')[0].id is "target2"
+PASS document.querySelectorAll('[Data-Æøå="WebKit!"]')[1].id is "target4"
+PASS document.querySelectorAll('[Data-ÆØÅ="WebKit!"]').length is 2
+PASS document.querySelectorAll('[Data-ÆØÅ="WebKit!"]')[0].id is "target3"
+PASS document.querySelectorAll('[Data-ÆØÅ="WebKit!"]')[1].id is "target4"
+PASS document.querySelectorAll('[Data-æØå="WebKit!"]').length is 0
+PASS document.querySelectorAll('[DATA-æøå^="Web"]').length is 2
+PASS document.querySelectorAll('[DATA-æøå^="Web"]')[0].id is "target1"
+PASS document.querySelectorAll('[DATA-æøå^="Web"]')[1].id is "target4"
+PASS document.querySelectorAll('[DATA-Æøå^="Web"]').length is 2
+PASS document.querySelectorAll('[DATA-Æøå^="Web"]')[0].id is "target2"
+PASS document.querySelectorAll('[DATA-Æøå^="Web"]')[1].id is "target4"
+PASS document.querySelectorAll('[DATA-ÆØÅ^="Web"]').length is 2
+PASS document.querySelectorAll('[DATA-ÆØÅ^="Web"]')[0].id is "target3"
+PASS document.querySelectorAll('[DATA-ÆØÅ^="Web"]')[1].id is "target4"
+PASS document.querySelectorAll('[DATA-æØå^="Web"]').length is 0
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-æøå]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-æøå]')[0].id is "target1"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-æøå]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-Æøå]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-Æøå]')[0].id is "target2"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-Æøå]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-ÆØÅ]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-ÆØÅ]')[0].id is "target3"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-ÆØÅ]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [data-æØå]').length is 0
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]')[0].id is "target1"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]')[0].id is "target2"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]')[0].id is "target3"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]').length is 0
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-æøå^="Web"]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-æøå^="Web"]')[0].id is "target1"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-æøå^="Web"]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]')[0].id is "target2"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]').length is 2
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]')[0].id is "target3"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]')[1].id is "target4"
+PASS document.querySelectorAll(':root >> :matches(body, html) >> [DATA-æØå^="Web"]').length is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html b/LayoutTests/fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html
new file mode 100644 (file)
index 0000000..b91c6e0
--- /dev/null
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<div style="display:none">
+    <div id="target1" data-æøå="WebKit!"></div>
+    <div id="target2" data-Æøå="WebKit!"></div>
+    <div id="target3" data-ÆØÅ="WebKit!"></div>
+    <div id="target4" data-æøå="WebKit!" data-Æøå="WebKit!" data-ÆØÅ="WebKit!"></div>
+</div>
+</body>
+<script>
+description('Attribute matching is ASCII case-insensitive in HTML.');
+
+function testSelector(selector, expected) {
+    shouldBe('document.querySelectorAll(\'' + selector + '\').length', '' + expected.length);
+    for (var i = 0; i < expected.length; ++i) {
+        shouldBeEqualToString('document.querySelectorAll(\'' + selector + '\')[' + i + '].id', 'target' + expected[i]);
+    }
+}
+
+// Simple selectors.
+testSelector('[data-æøå]', [1, 4]);
+testSelector('[data-Æøå]', [2, 4]);
+testSelector('[data-ÆØÅ]', [3, 4]);
+testSelector('[data-æØå]', []);
+
+testSelector('[Data-æøå="WebKit!"]', [1, 4]);
+testSelector('[Data-Æøå="WebKit!"]', [2, 4]);
+testSelector('[Data-ÆØÅ="WebKit!"]', [3, 4]);
+testSelector('[Data-æØå="WebKit!"]', []);
+
+testSelector('[DATA-æøå^="Web"]', [1, 4]);
+testSelector('[DATA-Æøå^="Web"]', [2, 4]);
+testSelector('[DATA-ÆØÅ^="Web"]', [3, 4]);
+testSelector('[DATA-æØå^="Web"]', []);
+
+// Complex selectors.
+testSelector(':root >> :matches(body, html) >> [data-æøå]', [1, 4]);
+testSelector(':root >> :matches(body, html) >> [data-Æøå]', [2, 4]);
+testSelector(':root >> :matches(body, html) >> [data-ÆØÅ]', [3, 4]);
+testSelector(':root >> :matches(body, html) >> [data-æØå]', []);
+
+testSelector(':root >> :matches(body, html) >> [Data-æøå="WebKit!"]', [1, 4]);
+testSelector(':root >> :matches(body, html) >> [Data-Æøå="WebKit!"]', [2, 4]);
+testSelector(':root >> :matches(body, html) >> [Data-ÆØÅ="WebKit!"]', [3, 4]);
+testSelector(':root >> :matches(body, html) >> [Data-æØå="WebKit!"]', []);
+
+testSelector(':root >> :matches(body, html) >> [DATA-æøå^="Web"]', [1, 4]);
+testSelector(':root >> :matches(body, html) >> [DATA-Æøå^="Web"]', [2, 4]);
+testSelector(':root >> :matches(body, html) >> [DATA-ÆØÅ^="Web"]', [3, 4]);
+testSelector(':root >> :matches(body, html) >> [DATA-æØå^="Web"]', []);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
index b0e5f3f..da45866 100644 (file)
@@ -1,3 +1,42 @@
+2015-01-28  Benjamin Poulain  <bpoulain@apple.com>
+
+        Start fixing the handling of Element's attributes when they contain non-ASCII characters
+        https://bugs.webkit.org/show_bug.cgi?id=141016
+
+        Reviewed by Ryosuke Niwa.
+
+        Attribute handling does not work properly when the attribute name contains non-ASCII character.
+
+        The HTML parser tokenize those names as ASCII lowercase. Some of the code is CSS and Element use
+        unicode lowercase for the names. This breaks all the APIs as soon as a name contains a character
+        that is non-ASCII and uppercase since some APIs change it, other don't.
+
+        This patch is a first step toward fixing this mess, it only address the simple cases.
+        The HTML spec says the names should be compared ASCII case-insensitive, to I spread that behavior
+        to places that were using unicode.
+
+        Tests: fast/css/attribute-ascii-case-insensitive-html.html
+               fast/css/attribute-ascii-case-insensitive-xhtml-expected.xhtml
+               fast/css/attribute-ascii-case-insensitive-xhtml.xhtml
+               fast/css/attribute-ascii-case-insensitive-xml-in-html.html
+               fast/dom/Element/attribute-ascii-case-insensitive-1.html
+               fast/dom/Element/attribute-ascii-case-insensitive-2.html
+               fast/selectors/attribute-ascii-case-insensitive-style-update.html
+               fast/selectors/element-matches-attribute-ascii-case-insensitive-html.html
+               fast/selectors/querySelector-attribute-ascii-case-insensitive-html.html
+
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::setAttribute):
+        * dom/Element.cpp:
+        (WebCore::Element::synchronizeAttribute):
+        (WebCore::Element::setAttribute):
+        (WebCore::Element::removeAttribute):
+        (WebCore::Element::hasAttribute):
+        * dom/ElementData.cpp:
+        (WebCore::ElementData::findAttributeIndexByNameSlowCase):
+        * dom/ElementData.h:
+        (WebCore::ElementData::findAttributeIndexByName):
+
 2015-01-28  Zalan Bujtas  <zalan@apple.com>
 
         Simple line layout: Drop uncommitted/committed terms from LineState.
index f2d5c82..ed77735 100644 (file)
@@ -732,7 +732,7 @@ void CSSSelector::setAttribute(const QualifiedName& value, bool isCaseInsensitiv
 {
     createRareData();
     m_data.m_rareData->m_attribute = value;
-    m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().lower() : value.localName();
+    m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().convertToASCIILowercase() : value.localName();
 }
 
 void CSSSelector::setArgument(const AtomicString& value)
index 703f821..2920692 100644 (file)
@@ -419,6 +419,7 @@ ALWAYS_INLINE void Element::synchronizeAttribute(const AtomicString& localName)
     // e.g when called from DOM API.
     if (!elementData())
         return;
+    // FIXME: this should be comparing in the ASCII range.
     if (elementData()->styleAttributeIsDirty() && equalPossiblyIgnoringCase(localName, styleAttr.localName(), shouldIgnoreAttributeCase(*this))) {
         ASSERT_WITH_SECURITY_IMPLICATION(isStyledElement());
         static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
@@ -973,7 +974,7 @@ void Element::setAttribute(const AtomicString& localName, const AtomicString& va
     }
 
     synchronizeAttribute(localName);
-    const AtomicString& caseAdjustedLocalName = shouldIgnoreAttributeCase(*this) ? localName.lower() : localName;
+    const AtomicString& caseAdjustedLocalName = shouldIgnoreAttributeCase(*this) ? localName.convertToASCIILowercase() : localName;
 
     unsigned index = elementData() ? elementData()->findAttributeIndexByName(caseAdjustedLocalName, false) : ElementData::attributeNotFound;
     const QualifiedName& qName = index != ElementData::attributeNotFound ? attributeAt(index).name() : QualifiedName(nullAtom, caseAdjustedLocalName, nullAtom);
@@ -1838,7 +1839,7 @@ bool Element::removeAttribute(const AtomicString& name)
     if (!elementData())
         return false;
 
-    AtomicString localName = shouldIgnoreAttributeCase(*this) ? name.lower() : name;
+    AtomicString localName = shouldIgnoreAttributeCase(*this) ? name.convertToASCIILowercase() : name;
     unsigned index = elementData()->findAttributeIndexByName(localName, false);
     if (index == ElementData::attributeNotFound) {
         if (UNLIKELY(localName == styleAttr) && elementData()->styleAttributeIsDirty() && is<StyledElement>(*this))
@@ -1883,7 +1884,7 @@ bool Element::hasAttribute(const AtomicString& localName) const
     if (!elementData())
         return false;
     synchronizeAttribute(localName);
-    return elementData()->findAttributeByName(shouldIgnoreAttributeCase(*this) ? localName.lower() : localName, false);
+    return elementData()->findAttributeByName(shouldIgnoreAttributeCase(*this) ? localName.convertToASCIILowercase() : localName, false);
 }
 
 bool Element::hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const
index fad031b..5ab9353 100644 (file)
@@ -180,6 +180,7 @@ bool ElementData::isEquivalent(const ElementData* other) const
 
 unsigned ElementData::findAttributeIndexByNameSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const
 {
+    // FIXME: this should use ASCII case-insensitive, not unicode case-insensitive.
     // Continue to checking case-insensitively and/or full namespaced names if necessary:
     const Attribute* attributes = attributeBase();
     unsigned length = this->length();
index 07100bc..dae0fc9 100644 (file)
@@ -288,7 +288,7 @@ ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const AtomicString&
 {
     const Attribute* attributes = attributeBase();
     bool doSlowCheck = shouldIgnoreAttributeCase;
-    const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? name.lower() : name;
+    const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? name.convertToASCIILowercase() : name;
 
     // Optimize for the case where the attribute exists and its name exactly matches.
     for (unsigned i = 0, count = length(); i < count; ++i) {