LibWeb: Implement Node.lookupNamespaceURI()

This method takes a prefix and returns the namespace URI associated
with it on the given node, or null if no namespace is found.
This commit is contained in:
Tim Ledbetter 2024-07-14 22:50:38 +01:00 committed by Alexander Kalenik
parent d106b6eae2
commit 27d429a85f
Notes: sideshowbarker 2024-07-18 23:46:13 +09:00
5 changed files with 166 additions and 1 deletions

View File

@ -0,0 +1,20 @@
documentFragment.lookupNamespaceURI(null): null
documentFragment.lookupNamespaceURI(""): null
documentFragment.lookupNamespaceURI("foo"): null
documentFragment.lookupNamespaceURI("xml"): null
documentFragment.lookupNamespaceURI("xmlns"): null
docType.lookupNamespaceURI(null): null
docType.lookupNamespaceURI(""): null
docType.lookupNamespaceURI("foo"): null
docType.lookupNamespaceURI("xml"): null
docType.lookupNamespaceURI("xmlns"): null
element.lookupNamespaceURI(null): null
element.lookupNamespaceURI(""): null
element.lookupNamespaceURI("foo"): null
element.lookupNamespaceURI("xml"): http://www.w3.org/XML/1998/namespace
element.lookupNamespaceURI("xmlns"): http://www.w3.org/2000/xmlns/
After setting element attribute xmlns:bar='exampleNamespaceURI'
element.lookupNamespaceURI(null): null
element.lookupNamespaceURI(""): null
element.lookupNamespaceURI("foo"): null
element.lookupNamespaceURI("bar"): exampleNamespaceURI

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
function testLookupNamespaceURI(node, prefix, name) {
let prefixName = prefix;
if (prefixName !== null)
prefixName = `"${prefix}"`;
println(`${name}.lookupNamespaceURI(${prefixName}): ${node.lookupNamespaceURI(prefix)}`);
}
test(() => {
const documentFragment = document.createDocumentFragment();
testLookupNamespaceURI(documentFragment, null, "documentFragment");
testLookupNamespaceURI(documentFragment, "", "documentFragment");
testLookupNamespaceURI(documentFragment, "foo", "documentFragment");
testLookupNamespaceURI(documentFragment, "xml", "documentFragment");
testLookupNamespaceURI(documentFragment, "xmlns", "documentFragment");
const docType = document.doctype;
testLookupNamespaceURI(docType, null, "docType");
testLookupNamespaceURI(docType, "", "docType");
testLookupNamespaceURI(docType, "foo", "docType");
testLookupNamespaceURI(docType, "xml", "docType");
testLookupNamespaceURI(docType, "xmlns", "docType");
const element = document.createElementNS("namespace", "prefix:element");
element.setAttribute("bar", "value");
testLookupNamespaceURI(element, null, "element");
testLookupNamespaceURI(element, "", "element");
testLookupNamespaceURI(element, "foo", "element");
testLookupNamespaceURI(element, "xml", "element");
testLookupNamespaceURI(element, "xmlns", "element");
const XMLNS_NS = "http://www.w3.org/2000/xmlns/";
element.setAttributeNS(XMLNS_NS, 'xmlns:bar', 'exampleNamespaceURI');
println("After setting element attribute xmlns:bar='exampleNamespaceURI'");
testLookupNamespaceURI(element, null, "element");
testLookupNamespaceURI(element, "", "element");
testLookupNamespaceURI(element, "foo", "element");
testLookupNamespaceURI(element, "bar", "element");
});
</script>

View File

@ -25,6 +25,7 @@
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/LiveNodeList.h>
#include <LibWeb/DOM/MutationType.h>
#include <LibWeb/DOM/NamedNodeMap.h>
#include <LibWeb/DOM/Node.h>
#include <LibWeb/DOM/NodeIterator.h>
#include <LibWeb/DOM/ProcessingInstruction.h>
@ -43,6 +44,7 @@
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Namespace.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/Painting/PaintableBox.h>
@ -1634,6 +1636,103 @@ bool Node::is_equal_node(Node const* other_node) const
return true;
}
// https://dom.spec.whatwg.org/#locate-a-namespace
Optional<String> Node::locate_a_namespace(Optional<String> const& prefix) const
{
// To locate a namespace for a node using prefix, switch on the interface node implements:
// Element
if (is<Element>(*this)) {
// 1. If prefix is "xml", then return the XML namespace.
if (prefix == "xml")
return Web::Namespace::XML.to_string();
// 2. If prefix is "xmlns", then return the XMLNS namespace.
if (prefix == "xmlns")
return Web::Namespace::XMLNS.to_string();
// 3. If its namespace is non-null and its namespace prefix is prefix, then return namespace.
auto& element = verify_cast<Element>(*this);
if (element.namespace_uri().has_value() && element.prefix() == prefix)
return element.namespace_uri()->to_string();
// 4. If it has an attribute whose namespace is the XMLNS namespace, namespace prefix is "xmlns", and local name is prefix,
// or if prefix is null and it has an attribute whose namespace is the XMLNS namespace, namespace prefix is null,
// and local name is "xmlns", then return its value if it is not the empty string, and null otherwise.
if (auto* attributes = element.attributes()) {
for (size_t i = 0; i < attributes->length(); ++i) {
auto& attr = *attributes->item(i);
if (attr.namespace_uri() == Web::Namespace::XMLNS) {
if ((attr.prefix() == "xmlns" && attr.local_name() == prefix) || (!prefix.has_value() && !attr.prefix().has_value() && attr.local_name() == "xmlns")) {
auto value = attr.value();
if (!value.is_empty())
return value;
return {};
}
}
}
}
// 5. If its parent element is null, then return null.
auto* parent_element = element.parent_element();
if (!element.parent_element())
return {};
// 6. Return the result of running locate a namespace on its parent element using prefix.
return parent_element->locate_a_namespace(prefix);
}
// Document
if (is<Document>(*this)) {
// 1. If its document element is null, then return null.
auto* document_element = verify_cast<Document>(*this).document_element();
if (!document_element)
return {};
// 2. Return the result of running locate a namespace on its document element using prefix.
return document_element->locate_a_namespace(prefix);
}
// DocumentType
// DocumentFragment
if (is<DocumentType>(*this) || is<DocumentFragment>(*this)) {
// Return null.
return {};
}
// Attr
if (is<Attr>(*this)) {
// 1. If its element is null, then return null.
auto* element = verify_cast<Attr>(*this).owner_element();
if (!element)
return {};
// 2. Return the result of running locate a namespace on its element using prefix.
return element->locate_a_namespace(prefix);
}
// Otherwise
// 1. If its parent element is null, then return null.
auto* parent_element = this->parent_element();
if (!parent_element)
return {};
// 2. Return the result of running locate a namespace on its parent element using prefix.
return parent_element->locate_a_namespace(prefix);
}
// https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
Optional<String> Node::lookup_namespace_uri(Optional<String> prefix) const
{
// 1. If prefix is the empty string, then set it to null.
if (prefix.has_value() && prefix->is_empty())
prefix = {};
// 2. Return the result of running locate a namespace for this using prefix.
return locate_a_namespace(prefix);
}
// https://dom.spec.whatwg.org/#in-a-document-tree
bool Node::in_a_document_tree() const
{

View File

@ -695,6 +695,9 @@ public:
ErrorOr<String> accessible_name(Document const&) const;
ErrorOr<String> accessible_description(Document const&) const;
Optional<String> locate_a_namespace(Optional<String> const& prefix) const;
Optional<String> lookup_namespace_uri(Optional<String> prefix) const;
protected:
Node(JS::Realm&, Document&, NodeType);
Node(Document&, NodeType);

View File

@ -55,7 +55,7 @@ interface Node : EventTarget {
boolean contains(Node? other);
[FIXME] DOMString? lookupPrefix(DOMString? namespace);
[FIXME] DOMString? lookupNamespaceURI(DOMString? prefix);
DOMString? lookupNamespaceURI(DOMString? prefix);
[FIXME] boolean isDefaultNamespace(DOMString? namespace);
[ImplementedAs=pre_insert, CEReactions] Node insertBefore(Node node, Node? child);