diff --git a/Tests/LibWeb/Text/expected/DOM/Node-lookupNamespaceURI.txt b/Tests/LibWeb/Text/expected/DOM/Node-lookupNamespaceURI.txt
new file mode 100644
index 00000000000..d186fdc0459
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/DOM/Node-lookupNamespaceURI.txt
@@ -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
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/input/DOM/Node-lookupNamespaceURI.html b/Tests/LibWeb/Text/input/DOM/Node-lookupNamespaceURI.html
new file mode 100644
index 00000000000..6513f5587b7
--- /dev/null
+++ b/Tests/LibWeb/Text/input/DOM/Node-lookupNamespaceURI.html
@@ -0,0 +1,43 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp
index 25e329e80e2..fbfb089decd 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Node.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -43,6 +44,7 @@
#include
#include
#include
+#include
#include
#include
@@ -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 Node::locate_a_namespace(Optional const& prefix) const
+{
+ // To locate a namespace for a node using prefix, switch on the interface node implements:
+
+ // Element
+ if (is(*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(*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(*this)) {
+ // 1. If its document element is null, then return null.
+ auto* document_element = verify_cast(*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(*this) || is(*this)) {
+ // Return null.
+ return {};
+ }
+
+ // Attr
+ if (is(*this)) {
+ // 1. If its element is null, then return null.
+ auto* element = verify_cast(*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 Node::lookup_namespace_uri(Optional 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
{
diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h
index 14fd18c2ee0..1efa4f5cc77 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.h
+++ b/Userland/Libraries/LibWeb/DOM/Node.h
@@ -695,6 +695,9 @@ public:
ErrorOr accessible_name(Document const&) const;
ErrorOr accessible_description(Document const&) const;
+ Optional locate_a_namespace(Optional const& prefix) const;
+ Optional lookup_namespace_uri(Optional prefix) const;
+
protected:
Node(JS::Realm&, Document&, NodeType);
Node(Document&, NodeType);
diff --git a/Userland/Libraries/LibWeb/DOM/Node.idl b/Userland/Libraries/LibWeb/DOM/Node.idl
index d6c81d5356a..0b6e88f95e6 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.idl
+++ b/Userland/Libraries/LibWeb/DOM/Node.idl
@@ -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);