Prechádzať zdrojové kódy

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.
Tim Ledbetter 11 mesiacov pred
rodič
commit
27d429a85f

+ 20 - 0
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

+ 43 - 0
Tests/LibWeb/Text/input/DOM/Node-lookupNamespaceURI.html

@@ -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>

+ 99 - 0
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -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
 {

+ 3 - 0
Userland/Libraries/LibWeb/DOM/Node.h

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

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