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);