Reviewed by darin.
authoreric@webkit.org <eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Feb 2008 23:46:58 +0000 (23:46 +0000)
committereric@webkit.org <eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Feb 2008 23:46:58 +0000 (23:46 +0000)
        Make createElementNS and createAttributeNS follow the (vague) DOM Core 2 spec
        by throwing exceptions for more types of invalid qualified names.
        http://bugs.webkit.org/show_bug.cgi?id=16833

        Tests: fast/dom/Document/createAttributeNS-namespace-err.html
               fast/dom/Document/createElementNS-namespace-err.html

        * dom/Document.cpp:
        (WebCore::Document::createElement):
        (WebCore::hasNamespaceError):
        (WebCore::Document::createElementNS):
        (WebCore::Document::createAttributeNS):
        * dom/Document.idl:

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/dom/xhtml/level3/core/documentsetstricterrorchecking02-expected.txt
LayoutTests/fast/dom/Document/createAttributeNS-namespace-err-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Document/createAttributeNS-namespace-err.html [new file with mode: 0644]
LayoutTests/fast/dom/Document/createElementNS-namespace-err-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Document/createElementNS-namespace-err.html [new file with mode: 0644]
LayoutTests/fast/dom/Document/resources/TEMPLATE.html [new file with mode: 0644]
LayoutTests/fast/dom/Document/resources/createAttributeNS-namespace-err.js [new file with mode: 0644]
LayoutTests/fast/dom/Document/resources/createElementNS-namespace-err.js [new file with mode: 0644]
WebCore/ChangeLog
WebCore/dom/Document.cpp
WebCore/dom/Document.idl

index ef5441f..f092999 100644 (file)
@@ -1,3 +1,26 @@
+2008-02-03  Eric Seidel  <eric@webkit.org>
+
+        Reviewed by darin.
+
+        Make createElementNS and createAttributeNS follow the (vague) DOM Core 2 spec
+        by throwing exceptions for more types of invalid qualified names.
+        http://bugs.webkit.org/show_bug.cgi?id=16833
+        
+        These tests were originally from Hixie's Acid3, then I added some, then Josh 
+        from mozilla added more. We pass all of mine and Hixie's.  Some of Josh's
+        still fail (extreme edge cases, such as *which* exception to throw when
+        a QName is a valid XML name but invalid QName, etc.)
+        The "failure" results are still being checked in, perhaps some day we'll "fix" them.
+
+        * dom/xhtml/level3/core/documentsetstricterrorchecking02-expected.txt:
+        * fast/dom/Document/createAttributeNS-namespace-err-expected.txt: Added.
+        * fast/dom/Document/createAttributeNS-namespace-err.html: Added.
+        * fast/dom/Document/createElementNS-namespace-err-expected.txt: Added.
+        * fast/dom/Document/createElementNS-namespace-err.html: Added.
+        * fast/dom/Document/resources/TEMPLATE.html: Added.
+        * fast/dom/Document/resources/createAttributeNS-namespace-err.js: Added.
+        * fast/dom/Document/resources/createElementNS-namespace-err.js: Added.
+
 2008-02-03  Darin Adler  <darin@apple.com>
 
         - updated some more only-child and only-of-type tests to expect success
index 2f233e1..8b5a75d 100644 (file)
@@ -1,3 +1,2 @@
 Test   http://www.w3.org/2001/DOM-Test-Suite/level3/core/documentsetstricterrorchecking02
-Status failure
-Message        NAMESPACE_ERR_documentsetstricterrorchecking02: assertTrue failed
+Status Success
diff --git a/LayoutTests/fast/dom/Document/createAttributeNS-namespace-err-expected.txt b/LayoutTests/fast/dom/Document/createAttributeNS-namespace-err-expected.txt
new file mode 100644 (file)
index 0000000..8e8b8a4
--- /dev/null
@@ -0,0 +1,52 @@
+createAttributeNS tests adapted from createElementNS tests attached to webkit bug 16833
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.createAttributeNS().toString() is "[object Attr]"
+PASS document.createAttributeNS("http://www.example.com").toString() is "[object Attr]"
+PASS createAttributeNS(undefined, undefined)
+PASS createAttributeNS(null, undefined)
+PASS createAttributeNS(undefined, null); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, null); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, ""); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("", null); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("", ""); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, "<div>"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, "0div"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, "di v"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, "di<v"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, "-div"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, ".div"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "<div>"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "0div"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "di<v"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "-div"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", ".div"); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS(null, ":div"); threw NAMESPACE_ERR
+PASS createAttributeNS(null, "div:"); threw NAMESPACE_ERR
+PASS createAttributeNS("http://example.com/", ":div"); threw NAMESPACE_ERR
+PASS createAttributeNS("http://example.com/", "div:"); threw NAMESPACE_ERR
+PASS createAttributeNS(null, "d:iv"); threw NAMESPACE_ERR
+FAIL createAttributeNS(null, "a:b:c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+FAIL createAttributeNS("http://example.com/", "a:b:c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+FAIL createAttributeNS(null, "a::c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+FAIL createAttributeNS("http://example.com/", "a::c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "a:0"); valid XML name, not a valid QName; threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "0:a"); 0 at start makes it not a valid XML name; threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "a:_")
+FAIL createAttributeNS("http://example.com/", "a:ெ"); non-ASCII character after colon is CombiningChar, which is NCNameChar but not (Letter | "_") so invalid at start of NCName (but still a valid XML name, hence not INVALID_CHARACTER_ERR); expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "ெ:a"); non-ASCII character after colon is CombiningChar, which is NCNameChar but not (Letter | "_") so invalid at start of NCName (Gecko chooses to throw NAMESPACE_ERR here, but either is valid as this is both an invalid XML name and an invalid QName); threw INVALID_CHARACTER_ERR
+PASS createAttributeNS("http://example.com/", "a:aெ")
+PASS createAttributeNS("http://example.com/", "aெ:a")
+PASS createAttributeNS("http://example.com/", "xml:test"); binding xml prefix wrong; threw NAMESPACE_ERR
+PASS createAttributeNS("http://example.com/", "xmlns:test"); binding xmlns prefix wrong; threw NAMESPACE_ERR
+PASS createAttributeNS("http://www.w3.org/2000/xmlns/", "x:test"); binding namespace namespace to wrong prefix; threw NAMESPACE_ERR
+PASS createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:test")
+PASS createAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:test")
+PASS createAttributeNS("http://www.w3.org/XML/1998/namespace", "x:test")
+PASS createAttributeNS("http://example.com/", "xmlns"); threw NAMESPACE_ERR
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Document/createAttributeNS-namespace-err.html b/LayoutTests/fast/dom/Document/createAttributeNS-namespace-err.html
new file mode 100644 (file)
index 0000000..c0cb2bd
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/createAttributeNS-namespace-err.js"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/Document/createElementNS-namespace-err-expected.txt b/LayoutTests/fast/dom/Document/createElementNS-namespace-err-expected.txt
new file mode 100644 (file)
index 0000000..52c28d8
--- /dev/null
@@ -0,0 +1,51 @@
+createElementNS tests from mozilla, attached to webkit bug 16833
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.createElementNS().toString() is "[object Element]"
+PASS document.createElementNS("http://www.example.com").toString() is "[object Element]"
+PASS createElementNS(undefined, undefined)
+PASS createElementNS(null, undefined)
+PASS createElementNS(undefined, null); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, null); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, ""); threw INVALID_CHARACTER_ERR
+PASS createElementNS("", null); threw INVALID_CHARACTER_ERR
+PASS createElementNS("", ""); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, "<div>"); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, "0div"); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, "di v"); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, "di<v"); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, "-div"); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, ".div"); threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "<div>"); threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "0div"); threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "di<v"); threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "-div"); threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", ".div"); threw INVALID_CHARACTER_ERR
+PASS createElementNS(null, ":div"); threw NAMESPACE_ERR
+PASS createElementNS(null, "div:"); threw NAMESPACE_ERR
+PASS createElementNS("http://example.com/", ":div"); threw NAMESPACE_ERR
+PASS createElementNS("http://example.com/", "div:"); threw NAMESPACE_ERR
+PASS createElementNS(null, "d:iv"); threw NAMESPACE_ERR
+FAIL createElementNS(null, "a:b:c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+FAIL createElementNS("http://example.com/", "a:b:c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+FAIL createElementNS(null, "a::c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+FAIL createElementNS("http://example.com/", "a::c"); valid XML name, invalid QName; expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "a:0"); valid XML name, not a valid QName; threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "0:a"); 0 at start makes it not a valid XML name; threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "a:_")
+FAIL createElementNS("http://example.com/", "a:ெ"); non-ASCII character after colon is CombiningChar, which is NCNameChar but not (Letter | "_") so invalid at start of NCName (but still a valid XML name, hence not INVALID_CHARACTER_ERR); expected NAMESPACE_ERR, threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "ெ:a"); non-ASCII character after colon is CombiningChar, which is NCNameChar but not (Letter | "_") so invalid at start of NCName (Gecko chooses to throw NAMESPACE_ERR here, but either is valid as this is both an invalid XML name and an invalid QName); threw INVALID_CHARACTER_ERR
+PASS createElementNS("http://example.com/", "a:aெ")
+PASS createElementNS("http://example.com/", "aெ:a")
+PASS createElementNS("http://example.com/", "xml:test"); binding xml prefix wrong; threw NAMESPACE_ERR
+PASS createElementNS("http://example.com/", "xmlns:test"); binding xmlns prefix wrong; threw NAMESPACE_ERR
+PASS createElementNS("http://www.w3.org/2000/xmlns/", "x:test"); binding namespace namespace to wrong prefix; threw NAMESPACE_ERR
+PASS createElementNS("http://www.w3.org/2000/xmlns/", "xmlns:test")
+PASS createElementNS("http://www.w3.org/XML/1998/namespace", "xml:test")
+PASS createElementNS("http://www.w3.org/XML/1998/namespace", "x:test")
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Document/createElementNS-namespace-err.html b/LayoutTests/fast/dom/Document/createElementNS-namespace-err.html
new file mode 100644 (file)
index 0000000..e3e944f
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/createElementNS-namespace-err.js"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/Document/resources/TEMPLATE.html b/LayoutTests/fast/dom/Document/resources/TEMPLATE.html
new file mode 100644 (file)
index 0000000..1951c43
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="YOUR_JS_FILE_HERE"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/Document/resources/createAttributeNS-namespace-err.js b/LayoutTests/fast/dom/Document/resources/createAttributeNS-namespace-err.js
new file mode 100644 (file)
index 0000000..e7add52
--- /dev/null
@@ -0,0 +1,135 @@
+description("createAttributeNS tests adapted from createElementNS tests attached to webkit bug 16833");
+
+function assert(c, m)
+{
+    if (!c)
+        testFailed(m);
+    else
+        testPassed(m);
+}
+
+function stringForExceptionCode(c)
+{
+    var exceptionName;
+    switch(c) {
+        case DOMException.INVALID_CHARACTER_ERR:
+            exceptionName = "INVALID_CHARACTER_ERR";
+            break;
+        case DOMException.NAMESPACE_ERR:
+            exceptionName = "NAMESPACE_ERR";
+    }
+    if (exceptionName)
+        return exceptionName; // + "(" + c + ")";
+    return c;
+}
+
+function assertEquals(actual, expect, m)
+{
+    if (actual !== expect) {
+        m += "; expected " + stringForExceptionCode(expect) + ", threw " + stringForExceptionCode(actual);
+        testFailed(m);
+    } else {
+        m += "; threw " + stringForExceptionCode(actual);;
+        testPassed(m);
+    }
+}
+
+var allNSTests = [
+   { args: [undefined, undefined] },
+   { args: [null, undefined] },
+   { args: [undefined, null], code: 5 },
+   { args: [null, null], code: 5 },
+   { args: [null, ""], code: 5 },
+   { args: ["", null], code: 5 },
+   { args: ["", ""], code: 5 },
+   { args: [null, "<div>"], code: 5 },
+   { args: [null, "0div"], code: 5 },
+   { args: [null, "di v"], code: 5 },
+   { args: [null, "di<v"], code: 5 },
+   { args: [null, "-div"], code: 5 },
+   { args: [null, ".div"], code: 5 },
+   { args: ["http://example.com/", "<div>"], code: 5 },
+   { args: ["http://example.com/", "0div"], code: 5 },
+   { args: ["http://example.com/", "di<v"], code: 5 },
+   { args: ["http://example.com/", "-div"], code: 5 },
+   { args: ["http://example.com/", ".div"], code: 5 },
+   { args: [null, ":div"], code: 14 },
+   { args: [null, "div:"], code: 14 },
+   { args: ["http://example.com/", ":div"], code: 14 },
+   { args: ["http://example.com/", "div:"], code: 14 },
+   { args: [null, "d:iv"], code: 14 },
+   { args: [null, "a:b:c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: ["http://example.com/", "a:b:c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: [null, "a::c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: ["http://example.com/", "a::c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: ["http://example.com/", "a:0"], code: 5, message: "valid XML name, not a valid QName" },
+   { args: ["http://example.com/", "0:a"], code: 5, message: "0 at start makes it not a valid XML name" },
+   { args: ["http://example.com/", "a:_"] },
+   { args: ["http://example.com/", "a:\u0BC6"], code: 14,
+     message: "non-ASCII character after colon is CombiningChar, which is " +
+              "NCNameChar but not (Letter | \"_\") so invalid at start of " +
+              "NCName (but still a valid XML name, hence not INVALID_CHARACTER_ERR)" },
+   { args: ["http://example.com/", "\u0BC6:a"], code: 5,
+     message: "non-ASCII character after colon is CombiningChar, which is " +
+              "NCNameChar but not (Letter | \"_\") so invalid at start of " +
+              "NCName (Gecko chooses to throw NAMESPACE_ERR here, but either is valid " +
+              "as this is both an invalid XML name and an invalid QName)" },
+   { args: ["http://example.com/", "a:a\u0BC6"] },
+   { args: ["http://example.com/", "a\u0BC6:a"] },
+   { args: ["http://example.com/", "xml:test"], code: 14, message: "binding xml prefix wrong" },
+   { args: ["http://example.com/", "xmlns:test"], code: 14, message: "binding xmlns prefix wrong" },
+   { args: ["http://www.w3.org/2000/xmlns/", "x:test"], code: 14, message: "binding namespace namespace to wrong prefix" },
+   { args: ["http://www.w3.org/2000/xmlns/", "xmlns:test"] },
+   { args: ["http://www.w3.org/XML/1998/namespace", "xml:test"] },
+   { args: ["http://www.w3.org/XML/1998/namespace", "x:test"] },
+   { args: ["http://example.com/", "xmlns"], code: 14 }, // from the createAttributeNS section
+];
+
+function sourceify(v)
+{
+    switch (typeof v) {
+    case "undefined":
+        return v;
+
+    case "string":
+        return '"' + v.replace('"', '\\"') + '"';
+
+    default:
+        return String(v);
+    }
+}
+
+function runNSTests()
+{
+    var createFunction = document.createAttributeNS;
+    var createFunctionName = "createAttributeNS";
+    var doc = document;
+
+    for (var i = 0, sz = allNSTests.length; i < sz; i++) {
+        var test = allNSTests[i];
+
+        var code = -1;
+        var argStr = sourceify(test.args[0]) + ", " + sourceify(test.args[1]);
+        try {
+            createFunction.apply(doc, test.args);
+            var msg = createFunctionName + "(" + argStr + ")";
+            if ("message" in test)
+                msg += "; " + test.message;
+            assert(!(code in test), msg);
+        } catch (e) {
+            var argStr = sourceify(test.args[0]) + ", " + sourceify(test.args[1]);
+            msg = createFunctionName + "(" + argStr + ")";
+            if ("message" in test)
+                msg += "; " + test.message;
+            assertEquals(e.code, test.code || "expected no exception", msg);
+        }
+    }
+}
+
+// Moz throws a "Not enough arguments" exception in these, we don't:
+shouldBeEqualToString("document.createAttributeNS().toString()", "[object Attr]");
+shouldBeEqualToString("document.createAttributeNS(\"http://www.example.com\").toString()", "[object Attr]");
+
+runNSTests();
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/dom/Document/resources/createElementNS-namespace-err.js b/LayoutTests/fast/dom/Document/resources/createElementNS-namespace-err.js
new file mode 100644 (file)
index 0000000..ce9999e
--- /dev/null
@@ -0,0 +1,134 @@
+description("createElementNS tests from mozilla, attached to webkit bug 16833");
+
+function assert(c, m)
+{
+    if (!c)
+        testFailed(m);
+    else
+        testPassed(m);
+}
+
+function stringForExceptionCode(c)
+{
+    var exceptionName;
+    switch(c) {
+        case DOMException.INVALID_CHARACTER_ERR:
+            exceptionName = "INVALID_CHARACTER_ERR";
+            break;
+        case DOMException.NAMESPACE_ERR:
+            exceptionName = "NAMESPACE_ERR";
+    }
+    if (exceptionName)
+        return exceptionName; // + "(" + c + ")";
+    return c;
+}
+
+function assertEquals(actual, expect, m)
+{
+    if (actual !== expect) {
+        m += "; expected " + stringForExceptionCode(expect) + ", threw " + stringForExceptionCode(actual);
+        testFailed(m);
+    } else {
+        m += "; threw " + stringForExceptionCode(actual);;
+        testPassed(m);
+    }
+}
+
+var allNSTests = [
+   { args: [undefined, undefined] },
+   { args: [null, undefined] },
+   { args: [undefined, null], code: 5 },
+   { args: [null, null], code: 5 },
+   { args: [null, ""], code: 5 },
+   { args: ["", null], code: 5 },
+   { args: ["", ""], code: 5 },
+   { args: [null, "<div>"], code: 5 },
+   { args: [null, "0div"], code: 5 },
+   { args: [null, "di v"], code: 5 },
+   { args: [null, "di<v"], code: 5 },
+   { args: [null, "-div"], code: 5 },
+   { args: [null, ".div"], code: 5 },
+   { args: ["http://example.com/", "<div>"], code: 5 },
+   { args: ["http://example.com/", "0div"], code: 5 },
+   { args: ["http://example.com/", "di<v"], code: 5 },
+   { args: ["http://example.com/", "-div"], code: 5 },
+   { args: ["http://example.com/", ".div"], code: 5 },
+   { args: [null, ":div"], code: 14 },
+   { args: [null, "div:"], code: 14 },
+   { args: ["http://example.com/", ":div"], code: 14 },
+   { args: ["http://example.com/", "div:"], code: 14 },
+   { args: [null, "d:iv"], code: 14 },
+   { args: [null, "a:b:c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: ["http://example.com/", "a:b:c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: [null, "a::c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: ["http://example.com/", "a::c"], code: 14, message: "valid XML name, invalid QName" },
+   { args: ["http://example.com/", "a:0"], code: 5, message: "valid XML name, not a valid QName" },
+   { args: ["http://example.com/", "0:a"], code: 5, message: "0 at start makes it not a valid XML name" },
+   { args: ["http://example.com/", "a:_"] },
+   { args: ["http://example.com/", "a:\u0BC6"], code: 14,
+     message: "non-ASCII character after colon is CombiningChar, which is " +
+              "NCNameChar but not (Letter | \"_\") so invalid at start of " +
+              "NCName (but still a valid XML name, hence not INVALID_CHARACTER_ERR)" },
+   { args: ["http://example.com/", "\u0BC6:a"], code: 5,
+     message: "non-ASCII character after colon is CombiningChar, which is " +
+              "NCNameChar but not (Letter | \"_\") so invalid at start of " +
+              "NCName (Gecko chooses to throw NAMESPACE_ERR here, but either is valid " +
+              "as this is both an invalid XML name and an invalid QName)" },
+   { args: ["http://example.com/", "a:a\u0BC6"] },
+   { args: ["http://example.com/", "a\u0BC6:a"] },
+   { args: ["http://example.com/", "xml:test"], code: 14, message: "binding xml prefix wrong" },
+   { args: ["http://example.com/", "xmlns:test"], code: 14, message: "binding xmlns prefix wrong" },
+   { args: ["http://www.w3.org/2000/xmlns/", "x:test"], code: 14, message: "binding namespace namespace to wrong prefix" },
+   { args: ["http://www.w3.org/2000/xmlns/", "xmlns:test"] },
+   { args: ["http://www.w3.org/XML/1998/namespace", "xml:test"] },
+   { args: ["http://www.w3.org/XML/1998/namespace", "x:test"] },
+];
+
+function sourceify(v)
+{
+    switch (typeof v) {
+    case "undefined":
+        return v;
+
+    case "string":
+        return '"' + v.replace('"', '\\"') + '"';
+
+    default:
+        return String(v);
+    }
+}
+
+function runNSTests()
+{
+    var createFunction = document.createElementNS;
+    var createFunctionName = "createElementNS";
+    var doc = document;
+
+    for (var i = 0, sz = allNSTests.length; i < sz; i++) {
+        var test = allNSTests[i];
+
+        var code = -1;
+        var argStr = sourceify(test.args[0]) + ", " + sourceify(test.args[1]);
+        try {
+            createFunction.apply(doc, test.args);
+            var msg = createFunctionName + "(" + argStr + ")";
+            if ("message" in test)
+                msg += "; " + test.message;
+            assert(!(code in test), msg);
+        } catch (e) {
+            var argStr = sourceify(test.args[0]) + ", " + sourceify(test.args[1]);
+            msg = createFunctionName + "(" + argStr + ")";
+            if ("message" in test)
+                msg += "; " + test.message;
+            assertEquals(e.code, test.code || "expected no exception", msg);
+        }
+    }
+}
+
+// Moz throws a "Not enough arguments" exception in these, we don't:
+shouldBeEqualToString("document.createElementNS().toString()", "[object Element]");
+shouldBeEqualToString("document.createElementNS(\"http://www.example.com\").toString()", "[object Element]");
+
+runNSTests();
+
+var successfullyParsed = true;
index f27edf5..ad0d2ca 100644 (file)
@@ -1,3 +1,21 @@
+2008-02-03  Eric Seidel  <eric@webkit.org>
+
+        Reviewed by darin.
+
+        Make createElementNS and createAttributeNS follow the (vague) DOM Core 2 spec
+        by throwing exceptions for more types of invalid qualified names.
+        http://bugs.webkit.org/show_bug.cgi?id=16833
+
+        Tests: fast/dom/Document/createAttributeNS-namespace-err.html
+               fast/dom/Document/createElementNS-namespace-err.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::createElement):
+        (WebCore::hasNamespaceError):
+        (WebCore::Document::createElementNS):
+        (WebCore::Document::createAttributeNS):
+        * dom/Document.idl:
+
 2008-02-03  Nikolas Zimmermann  <zimmermann@kde.org>
 
         Reviewed by Eric.
index a2e245f..575100e 100644 (file)
 #include "UIEvent.h"
 #include "WheelEvent.h"
 #include "XMLHttpRequest.h"
+#include "XMLNames.h"
 #include "XMLTokenizer.h"
 #include "kjs_binding.h"
 #include "kjs_proxy.h"
@@ -508,7 +509,7 @@ PassRefPtr<Element> Document::createElement(const String &name, ExceptionCode& e
             return 0;
         }
 
-        return HTMLElementFactory::createHTMLElement(AtomicString(name), this, 0, false);
+        return HTMLElementFactory::createHTMLElement(name, this, 0, false);
     } else
         return createElementNS(nullAtom, name, ec);
 }
@@ -699,6 +700,32 @@ PassRefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec)
     return source;
 }
 
+static bool hasNamespaceError(const QualifiedName& qName)
+{
+    static const AtomicString xmlnsNamespaceURI = "http://www.w3.org/2000/xmlns/";
+    static const AtomicString xmlns = "xmlns";
+    static const AtomicString xml = "xml";
+
+    // These checks are from DOM Core Level 2, createElementNS
+    // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS
+    if (qName.prefix() == emptyAtom) // createElementNS(null, ":div")
+        return true;
+    if (qName.localName().isEmpty()) // createElementNS(null, ""), createElementNS(null, null), createElementNS()
+        return true;
+    if (!qName.prefix().isEmpty() && qName.namespaceURI().isNull()) // createElementNS(null, "html:div")
+        return true;
+    if (qName.prefix() == xml && qName.namespaceURI() != XMLNames::xmlNamespaceURI) // createElementNS("http://www.example.com", "xml:lang")
+        return true;
+
+    // Required by DOM Level 3 Core and unspecified by DOM Level 2 Core:
+    // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
+    // createElementNS("http://www.w3.org/2000/xmlns/", "foo:bar"), createElementNS(null, "xmlns:bar")
+    if ((qName.prefix() == xmlns && qName.namespaceURI() != xmlnsNamespaceURI) || (qName.prefix() != xmlns && qName.namespaceURI() == xmlnsNamespaceURI))
+        return true;
+
+    return false;
+}
+
 // FIXME: This should really be in a possible ElementFactory class
 PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool createdByParser, ExceptionCode& ec)
 {
@@ -715,17 +742,22 @@ PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool cre
     if (!e)
         e = new Element(qName, document());
     
+    // FIXME: The element factories should be fixed to not ignore qName.prefix()
+    // Instead they should pass the entire qName into element creation so we don't
+    // need to manually set the prefix after creation.
+    // Then this code can become ASSERT(qName == e.qname());
+    // and Document::createElement can stop taking ExceptionCode& as well.
     if (e && !qName.prefix().isNull()) {
         ec = 0;
         e->setPrefix(qName.prefix(), ec);
         if (ec)
             return 0;
-    }    
+    }
     
     return e.release();
 }
 
-PassRefPtr<Element> Document::createElementNS(const String &_namespaceURI, const String &qualifiedName, ExceptionCode& ec)
+PassRefPtr<Element> Document::createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec)
 {
     String prefix, localName;
     if (!parseQualifiedName(qualifiedName, prefix, localName)) {
@@ -733,13 +765,16 @@ PassRefPtr<Element> Document::createElementNS(const String &_namespaceURI, const
         return 0;
     }
 
-    RefPtr<Element> e;
-    QualifiedName qName = QualifiedName(AtomicString(prefix), AtomicString(localName), AtomicString(_namespaceURI));
-    
+    QualifiedName qName(prefix, localName, namespaceURI);
+    if (hasNamespaceError(qName)) {
+        ec = NAMESPACE_ERR;
+        return 0;
+    }
+
     return createElement(qName, false, ec);
 }
 
-Element *Document::getElementById(const AtomicString& elementId) const
+ElementDocument::getElementById(const AtomicString& elementId) const
 {
     if (elementId.length() == 0)
         return 0;
@@ -2677,7 +2712,7 @@ bool Document::isValidName(const String &name)
     return true;
 }
 
-bool Document::parseQualifiedName(const String &qualifiedName, String &prefix, String &localName)
+bool Document::parseQualifiedName(const String& qualifiedName, String& prefix, String& localName)
 {
     unsigned length = qualifiedName.length();
 
@@ -3390,29 +3425,29 @@ Document *Document::topDocument() const
     return doc;
 }
 
-PassRefPtr<Attr> Document::createAttributeNS(const String &namespaceURI, const String &qualifiedName, ExceptionCode& ec)
+PassRefPtr<Attr> Document::createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec)
 {
-    if (qualifiedName.isNull()) {
-        ec = NAMESPACE_ERR;
+    String prefix, localName;
+    if (!parseQualifiedName(qualifiedName, prefix, localName)) {
+        ec = INVALID_CHARACTER_ERR;
         return 0;
     }
 
-    String localName = qualifiedName;
-    String prefix;
-    int colonpos;
-    if ((colonpos = qualifiedName.find(':')) >= 0) {
-        prefix = qualifiedName.substring(0, colonpos);
-        localName = qualifiedName.substring(colonpos + 1);
+    QualifiedName qName(prefix, localName, namespaceURI);
+    if (hasNamespaceError(qName)) {
+        ec = NAMESPACE_ERR;
+        return 0;
     }
 
-    if (!isValidName(localName)) {
-        ec = INVALID_CHARACTER_ERR;
+    // Spec: DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrAttrNS
+    if (qName.localName() == "xmlns" && qName.namespaceURI() != "http://www.w3.org/2000/xmlns/") {
+        ec = NAMESPACE_ERR;
         return 0;
     }
-    
+
     // FIXME: Assume this is a mapped attribute, since createAttribute isn't namespace-aware.  There's no harm to XML
     // documents if we're wrong.
-    return new Attr(0, this, new MappedAttribute(QualifiedName(prefix, localName, namespaceURI), StringImpl::empty()));
+    return new Attr(0, this, new MappedAttribute(qName, StringImpl::empty()));
 }
 
 #if ENABLE(SVG)
index 6de51d7..939f304 100644 (file)
@@ -55,10 +55,10 @@ module core {
                                        in boolean deep)
             raises (DOMException);
         [OldStyleObjC] Element createElementNS(in [ConvertNullToNullString] DOMString namespaceURI,
-                                               in DOMString qualifiedName)
+                                               in [ConvertNullToNullString] DOMString qualifiedName)
             raises (DOMException);
         [OldStyleObjC] Attr createAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI,
-                                              in DOMString qualifiedName)
+                                              in [ConvertNullToNullString] DOMString qualifiedName)
             raises (DOMException);
         [OldStyleObjC] NodeList getElementsByTagNameNS(in [ConvertNullToNullString] DOMString namespaceURI,
                                                        in DOMString localName);