diff --git a/Tests/LibWeb/Text/expected/DOM/Node-lookupPrefix.txt b/Tests/LibWeb/Text/expected/DOM/Node-lookupPrefix.txt
new file mode 100644
index 00000000000..c0ae5479cbe
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/DOM/Node-lookupPrefix.txt
@@ -0,0 +1,9 @@
+Prefix for namespace "test" on document: ex
+Prefix for namespace "test" on document fragment: null
+Prefix for namespace "test" on comment node 0: ex
+Prefix null on document: null
+Prefix for absent namespace "no" on document: null
+Prefix for namespace "def" on child: abc
+Prefix null on child: null
+Prefix for namespace "test" (in parent) on child: ex
+Prefix for namespace "def" (from attribute "x") on child: abc
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/input/DOM/Node-lookupPrefix.html b/Tests/LibWeb/Text/input/DOM/Node-lookupPrefix.html
new file mode 100644
index 00000000000..dfdb3d79e46
--- /dev/null
+++ b/Tests/LibWeb/Text/input/DOM/Node-lookupPrefix.html
@@ -0,0 +1,21 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp
index 78ff42c43b3..1688b5765f9 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Element.cpp
@@ -2156,6 +2156,30 @@ void Element::set_prefix(Optional value)
m_qualified_name.set_prefix(move(value));
}
+// https://dom.spec.whatwg.org/#locate-a-namespace-prefix
+Optional Element::locate_a_namespace_prefix(Optional const& namespace_) const
+{
+ // 1. If element’s namespace is namespace and its namespace prefix is non-null, then return its namespace prefix.
+ if (this->namespace_uri() == namespace_ && this->prefix().has_value())
+ return this->prefix()->to_string();
+
+ // 2. If element has an attribute whose namespace prefix is "xmlns" and value is namespace, then return element’s first such attribute’s local name.
+ if (auto* attributes = this->attributes()) {
+ for (size_t i = 0; i < attributes->length(); ++i) {
+ auto& attr = *attributes->item(i);
+ if (attr.prefix() == "xmlns" && attr.value() == namespace_)
+ return attr.local_name().to_string();
+ }
+ }
+
+ // 3. If element’s parent element is not null, then return the result of running locate a namespace prefix on that element using namespace.
+ if (auto* parent = this->parent_element())
+ return parent->locate_a_namespace_prefix(namespace_);
+
+ // 4. Return null
+ return {};
+}
+
void Element::for_each_attribute(Function callback) const
{
for (size_t i = 0; i < m_attributes->length(); ++i)
diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h
index 9a231a65df5..c490fce8c31 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.h
+++ b/Userland/Libraries/LibWeb/DOM/Element.h
@@ -106,6 +106,8 @@ public:
void set_prefix(Optional value);
+ Optional locate_a_namespace_prefix(Optional const& namespace_) const;
+
// NOTE: This is for the JS bindings
Optional const& namespace_uri() const { return m_qualified_name.namespace_(); }
diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp
index b4a3f22b299..db1d643fccc 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Node.cpp
@@ -1736,6 +1736,57 @@ Optional Node::lookup_namespace_uri(Optional prefix) const
return locate_a_namespace(prefix);
}
+// https://dom.spec.whatwg.org/#dom-node-lookupprefix
+Optional Node::lookup_prefix(Optional namespace_) const
+{
+ // 1. If namespace is null or the empty string, then return null.
+ if (!namespace_.has_value() || namespace_->is_empty())
+ return {};
+
+ // 2. Switch on the interface this implements:
+
+ // Element
+ if (is(*this)) {
+ // Return the result of locating a namespace prefix for it using namespace.
+ auto& element = verify_cast(*this);
+ return element.locate_a_namespace_prefix(namespace_);
+ }
+
+ // Document
+ if (is(*this)) {
+ // Return the result of locating a namespace prefix for its document element, if its document element is non-null; otherwise null.
+ auto* document_element = verify_cast(*this).document_element();
+ if (!document_element)
+ return {};
+
+ return document_element->locate_a_namespace_prefix(namespace_);
+ }
+
+ // DocumentType
+ // DocumentFragment
+ if (is(*this) || is(*this))
+ // Return null
+ return {};
+
+ // Attr
+ if (is(*this)) {
+ // Return the result of locating a namespace prefix for its element, if its element is non-null; otherwise null.
+ auto* element = verify_cast(*this).owner_element();
+ if (!element)
+ return {};
+
+ return element->locate_a_namespace_prefix(namespace_);
+ }
+
+ // Otherwise
+ // Return the result of locating a namespace prefix for its parent element, if its parent element is non-null; otherwise null.
+ auto* parent_element = this->parent_element();
+ if (!parent_element)
+ return {};
+
+ return parent_element->locate_a_namespace_prefix(namespace_);
+}
+
// https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
bool Node::is_default_namespace(Optional namespace_) const
{
diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h
index fa5530dff3e..a2e3da96d66 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.h
+++ b/Userland/Libraries/LibWeb/DOM/Node.h
@@ -698,6 +698,7 @@ public:
Optional locate_a_namespace(Optional const& prefix) const;
Optional lookup_namespace_uri(Optional prefix) const;
+ Optional lookup_prefix(Optional namespace_) const;
bool is_default_namespace(Optional namespace_) const;
protected:
diff --git a/Userland/Libraries/LibWeb/DOM/Node.idl b/Userland/Libraries/LibWeb/DOM/Node.idl
index f09c7933963..8bc4c00bdfa 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.idl
+++ b/Userland/Libraries/LibWeb/DOM/Node.idl
@@ -54,7 +54,7 @@ interface Node : EventTarget {
unsigned short compareDocumentPosition(Node? otherNode);
boolean contains(Node? other);
- [FIXME] DOMString? lookupPrefix(DOMString? namespace);
+ DOMString? lookupPrefix(DOMString? namespace);
DOMString? lookupNamespaceURI(DOMString? prefix);
boolean isDefaultNamespace(DOMString? namespace);