LibWeb: Update DOMImplementation.createDocumentType() name validation

This now follows the latest specification steps.
This commit is contained in:
Tim Ledbetter 2025-06-19 12:31:03 +01:00 committed by Tim Flynn
commit 16dbb44de2
Notes: github-actions[bot] 2025-06-19 11:57:08 +00:00
6 changed files with 228 additions and 6 deletions

View file

@ -137,14 +137,16 @@ GC::Ref<Document> DOMImplementation::create_html_document(Optional<String> const
} }
// https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype // https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
WebIDL::ExceptionOr<GC::Ref<DocumentType>> DOMImplementation::create_document_type(String const& qualified_name, String const& public_id, String const& system_id) WebIDL::ExceptionOr<GC::Ref<DocumentType>> DOMImplementation::create_document_type(String const& name, String const& public_id, String const& system_id)
{ {
// 1. Validate qualifiedName.
TRY(Document::validate_qualified_name(realm(), qualified_name));
// 2. Return a new doctype, with qualifiedName as its name, publicId as its public ID, and systemId as its system ID, and with its node document set to the associated document of this. // 1. If name is not a valid doctype name, then throw an "InvalidCharacterError" DOMException.
if (!is_valid_doctype_name(name))
return WebIDL::InvalidCharacterError::create(realm(), "Invalid doctype name"_string);
// 2. Return a new doctype, with name as its name, publicId as its public ID, and systemId as its system ID, and with its node document set to the associated document of this.
auto document_type = DocumentType::create(document()); auto document_type = DocumentType::create(document());
document_type->set_name(qualified_name); document_type->set_name(name);
document_type->set_public_id(public_id); document_type->set_public_id(public_id);
document_type->set_system_id(system_id); document_type->set_system_id(system_id);
return document_type; return document_type;

View file

@ -23,7 +23,7 @@ public:
WebIDL::ExceptionOr<GC::Ref<XMLDocument>> create_document(Optional<FlyString> const&, String const&, GC::Ptr<DocumentType>) const; WebIDL::ExceptionOr<GC::Ref<XMLDocument>> create_document(Optional<FlyString> const&, String const&, GC::Ptr<DocumentType>) const;
GC::Ref<Document> create_html_document(Optional<String> const& title) const; GC::Ref<Document> create_html_document(Optional<String> const& title) const;
WebIDL::ExceptionOr<GC::Ref<DocumentType>> create_document_type(String const& qualified_name, String const& public_id, String const& system_id); WebIDL::ExceptionOr<GC::Ref<DocumentType>> create_document_type(String const& name, String const& public_id, String const& system_id);
// https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
bool has_feature() const bool has_feature() const

View file

@ -28,4 +28,12 @@ void DocumentType::initialize(JS::Realm& realm)
Base::initialize(realm); Base::initialize(realm);
} }
// https://dom.spec.whatwg.org/#valid-doctype-name
bool is_valid_doctype_name(String const& name)
{
// A string is a valid doctype name if it does not contain ASCII whitespace, U+0000 NULL, or U+003E (>).
constexpr Array<u32, 7> INVALID_DOCTYPE_CHARACTERS { '\t', '\n', '\f', '\r', ' ', '\0', '>' };
return !name.code_points().contains_any_of(INVALID_DOCTYPE_CHARACTERS);
}
} }

View file

@ -44,6 +44,8 @@ private:
String m_system_id; String m_system_id;
}; };
bool is_valid_doctype_name(String const&);
template<> template<>
inline bool Node::fast_is<DocumentType>() const { return is_document_type(); } inline bool Node::fast_is<DocumentType>() const { return is_document_type(); }

View file

@ -0,0 +1,87 @@
Harness status: OK
Found 82 tests
82 Pass
Pass DOMImplementation.createDocumentType(qualifiedName, publicId, systemId)
Pass createDocumentType("", "", "") should work
Pass createDocumentType("test:root", "1234", "") should work
Pass createDocumentType("test:root", "1234", "test") should work
Pass createDocumentType("test:root", "test", "") should work
Pass createDocumentType("test:root", "test", "test") should work
Pass createDocumentType("_:_", "", "") should work
Pass createDocumentType("_:h0", "", "") should work
Pass createDocumentType("_:test", "", "") should work
Pass createDocumentType("_:_.", "", "") should work
Pass createDocumentType("_:a-", "", "") should work
Pass createDocumentType("l_:_", "", "") should work
Pass createDocumentType("ns:_0", "", "") should work
Pass createDocumentType("ns:a0", "", "") should work
Pass createDocumentType("ns0:test", "", "") should work
Pass createDocumentType("ns:EEE.", "", "") should work
Pass createDocumentType("ns:_-", "", "") should work
Pass createDocumentType("a.b:c", "", "") should work
Pass createDocumentType("a-b:c.j", "", "") should work
Pass createDocumentType("a-b:c", "", "") should work
Pass createDocumentType("foo", "", "") should work
Pass createDocumentType("1foo", "", "") should work
Pass createDocumentType("foo1", "", "") should work
Pass createDocumentType("f1oo", "", "") should work
Pass createDocumentType("@foo", "", "") should work
Pass createDocumentType("foo@", "", "") should work
Pass createDocumentType("f@oo", "", "") should work
Pass createDocumentType("edi:{", "", "") should work
Pass createDocumentType("edi:}", "", "") should work
Pass createDocumentType("edi:~", "", "") should work
Pass createDocumentType("edi:'", "", "") should work
Pass createDocumentType("edi:!", "", "") should work
Pass createDocumentType("edi:@", "", "") should work
Pass createDocumentType("edi:#", "", "") should work
Pass createDocumentType("edi:$", "", "") should work
Pass createDocumentType("edi:%", "", "") should work
Pass createDocumentType("edi:^", "", "") should work
Pass createDocumentType("edi:&", "", "") should work
Pass createDocumentType("edi:*", "", "") should work
Pass createDocumentType("edi:(", "", "") should work
Pass createDocumentType("edi:)", "", "") should work
Pass createDocumentType("edi:+", "", "") should work
Pass createDocumentType("edi:=", "", "") should work
Pass createDocumentType("edi:[", "", "") should work
Pass createDocumentType("edi:]", "", "") should work
Pass createDocumentType("edi:\\", "", "") should work
Pass createDocumentType("edi:/", "", "") should work
Pass createDocumentType("edi:;", "", "") should work
Pass createDocumentType("edi:`", "", "") should work
Pass createDocumentType("edi:<", "", "") should work
Pass createDocumentType("edi:>", "", "") should throw INVALID_CHARACTER_ERR
Pass createDocumentType("edi:,", "", "") should work
Pass createDocumentType("edi:a ", "", "") should throw INVALID_CHARACTER_ERR
Pass createDocumentType("edi:\"", "", "") should work
Pass createDocumentType("{", "", "") should work
Pass createDocumentType("}", "", "") should work
Pass createDocumentType("'", "", "") should work
Pass createDocumentType("~", "", "") should work
Pass createDocumentType("`", "", "") should work
Pass createDocumentType("@", "", "") should work
Pass createDocumentType("#", "", "") should work
Pass createDocumentType("$", "", "") should work
Pass createDocumentType("%", "", "") should work
Pass createDocumentType("^", "", "") should work
Pass createDocumentType("&", "", "") should work
Pass createDocumentType("*", "", "") should work
Pass createDocumentType("(", "", "") should work
Pass createDocumentType(")", "", "") should work
Pass createDocumentType("f:oo", "", "") should work
Pass createDocumentType(":foo", "", "") should work
Pass createDocumentType("foo:", "", "") should work
Pass createDocumentType("prefix::local", "", "") should work
Pass createDocumentType("foo", "foo", "") should work
Pass createDocumentType("foo", "", "foo") should work
Pass createDocumentType("foo", "f'oo", "") should work
Pass createDocumentType("foo", "", "f'oo") should work
Pass createDocumentType("foo", "f\"oo", "") should work
Pass createDocumentType("foo", "", "f\"oo") should work
Pass createDocumentType("foo", "f'o\"o", "") should work
Pass createDocumentType("foo", "", "f'o\"o") should work
Pass createDocumentType("foo", "foo>", "") should work
Pass createDocumentType("foo", "", "foo>") should work

View file

@ -0,0 +1,123 @@
<!doctype html>
<meta charset=utf-8>
<title>DOMImplementation.createDocumentType(qualifiedName, publicId, systemId)</title>
<link rel=help href="https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype">
<link rel=help href="https://dom.spec.whatwg.org/#dom-documenttype-name">
<link rel=help href="https://dom.spec.whatwg.org/#dom-documenttype-publicid">
<link rel=help href="https://dom.spec.whatwg.org/#dom-documenttype-systemid">
<link rel=help href="https://dom.spec.whatwg.org/#dom-node-ownerdocument">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
test(function() {
var tests = [
["", "", "", null],
["test:root", "1234", "", null],
["test:root", "1234", "test", null],
["test:root", "test", "", null],
["test:root", "test", "test", null],
["_:_", "", "", null],
["_:h0", "", "", null],
["_:test", "", "", null],
["_:_.", "", "", null],
["_:a-", "", "", null],
["l_:_", "", "", null],
["ns:_0", "", "", null],
["ns:a0", "", "", null],
["ns0:test", "", "", null],
["ns:EEE.", "", "", null],
["ns:_-", "", "", null],
["a.b:c", "", "", null],
["a-b:c.j", "", "", null],
["a-b:c", "", "", null],
["foo", "", "", null],
["1foo", "", "", null],
["foo1", "", "", null],
["f1oo", "", "", null],
["@foo", "", "", null],
["foo@", "", "", null],
["f@oo", "", "", null],
["edi:{", "", "", null],
["edi:}", "", "", null],
["edi:~", "", "", null],
["edi:'", "", "", null],
["edi:!", "", "", null],
["edi:@", "", "", null],
["edi:#", "", "", null],
["edi:$", "", "", null],
["edi:%", "", "", null],
["edi:^", "", "", null],
["edi:&", "", "", null],
["edi:*", "", "", null],
["edi:(", "", "", null],
["edi:)", "", "", null],
["edi:+", "", "", null],
["edi:=", "", "", null],
["edi:[", "", "", null],
["edi:]", "", "", null],
["edi:\\", "", "", null],
["edi:/", "", "", null],
["edi:;", "", "", null],
["edi:`", "", "", null],
["edi:<", "", "", null],
["edi:>", "", "", "INVALID_CHARACTER_ERR"],
["edi:,", "", "", null],
["edi:a ", "", "", "INVALID_CHARACTER_ERR"],
["edi:\"", "", "", null],
["{", "", "", null],
["}", "", "", null],
["'", "", "", null],
["~", "", "", null],
["`", "", "", null],
["@", "", "", null],
["#", "", "", null],
["$", "", "", null],
["%", "", "", null],
["^", "", "", null],
["&", "", "", null],
["*", "", "", null],
["(", "", "", null],
[")", "", "", null],
["f:oo", "", "", null],
[":foo", "", "", null],
["foo:", "", "", null],
["prefix::local", "", "", null],
["foo", "foo", "", null],
["foo", "", "foo", null],
["foo", "f'oo", "", null],
["foo", "", "f'oo", null],
["foo", 'f"oo', "", null],
["foo", "", 'f"oo', null],
["foo", "f'o\"o", "", null],
["foo", "", "f'o\"o", null],
["foo", "foo>", "", null],
["foo", "", "foo>", null]
]
var doc = document.implementation.createHTMLDocument("title");
var doTest = function(aDocument, aQualifiedName, aPublicId, aSystemId) {
var doctype = aDocument.implementation.createDocumentType(aQualifiedName, aPublicId, aSystemId);
assert_equals(doctype.name, aQualifiedName, "name")
assert_equals(doctype.nodeName, aQualifiedName, "nodeName")
assert_equals(doctype.publicId, aPublicId, "publicId")
assert_equals(doctype.systemId, aSystemId, "systemId")
assert_equals(doctype.ownerDocument, aDocument, "ownerDocument")
assert_equals(doctype.nodeValue, null, "nodeValue")
}
tests.forEach(function(t) {
var qualifiedName = t[0], publicId = t[1], systemId = t[2], expected = t[3]
test(function() {
if (expected) {
assert_throws_dom(expected, function() {
document.implementation.createDocumentType(qualifiedName, publicId, systemId)
})
} else {
doTest(document, qualifiedName, publicId, systemId);
doTest(doc, qualifiedName, publicId, systemId);
}
}, "createDocumentType(" + format_value(qualifiedName) + ", " + format_value(publicId) + ", " + format_value(systemId) + ") should " +
(expected ? "throw " + expected : "work"));
});
})
</script>