Bladeren bron

LibWeb: Add getElementsByTagNameNS and add support for * in non-NS

This also moves getElementsByTagName to ParentNode to remove the code
duplication between Document and Element. This additionally fixes a bug
where getElementsByTagName did not check if the element was a
descendant, meaning it would also include the context element if the
condition matched.
Luke Wilde 3 jaren geleden
bovenliggende
commit
d47e431d54

+ 0 - 11
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -511,17 +511,6 @@ NonnullRefPtr<HTMLCollection> Document::get_elements_by_name(String const& name)
     });
 }
 
-NonnullRefPtr<HTMLCollection> Document::get_elements_by_tag_name(FlyString const& tag_name)
-{
-    // FIXME: Support "*" for tag_name
-    // https://dom.spec.whatwg.org/#concept-getelementsbytagname
-    return HTMLCollection::create(*this, [tag_name](Element const& element) {
-        if (element.namespace_() == Namespace::HTML)
-            return element.local_name().to_lowercase() == tag_name.to_lowercase();
-        return element.local_name() == tag_name;
-    });
-}
-
 NonnullRefPtr<HTMLCollection> Document::get_elements_by_class_name(FlyString const& class_name)
 {
     return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {

+ 0 - 1
Userland/Libraries/LibWeb/DOM/Document.h

@@ -158,7 +158,6 @@ public:
     void schedule_forced_layout();
 
     NonnullRefPtr<HTMLCollection> get_elements_by_name(String const&);
-    NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
     NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&);
 
     NonnullRefPtr<HTMLCollection> applets();

+ 1 - 0
Userland/Libraries/LibWeb/DOM/Document.idl

@@ -21,6 +21,7 @@ interface Document : Node {
     Element? getElementById(DOMString id);
     HTMLCollection getElementsByName(DOMString name);
     HTMLCollection getElementsByTagName(DOMString tagName);
+    HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
     HTMLCollection getElementsByClassName(DOMString className);
 
     readonly attribute HTMLCollection applets;

+ 0 - 11
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -272,17 +272,6 @@ bool Element::is_active() const
     return document().active_element() == this;
 }
 
-NonnullRefPtr<HTMLCollection> Element::get_elements_by_tag_name(FlyString const& tag_name)
-{
-    // FIXME: Support "*" for tag_name
-    // https://dom.spec.whatwg.org/#concept-getelementsbytagname
-    return HTMLCollection::create(*this, [tag_name](Element const& element) {
-        if (element.namespace_() == Namespace::HTML)
-            return element.local_name().to_lowercase() == tag_name.to_lowercase();
-        return element.local_name() == tag_name;
-    });
-}
-
 NonnullRefPtr<HTMLCollection> Element::get_elements_by_class_name(FlyString const& class_name)
 {
     return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {

+ 0 - 1
Userland/Libraries/LibWeb/DOM/Element.h

@@ -89,7 +89,6 @@ public:
 
     bool is_active() const;
 
-    NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
     NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&);
 
     ShadowRoot* shadow_root() { return m_shadow_root; }

+ 1 - 0
Userland/Libraries/LibWeb/DOM/Element.idl

@@ -11,6 +11,7 @@ interface Element : Node {
     boolean hasAttributes();
 
     HTMLCollection getElementsByTagName(DOMString tagName);
+    HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
     HTMLCollection getElementsByClassName(DOMString className);
 
     // FIXME: This should come from a InnerHTML mixin.

+ 65 - 0
Userland/Libraries/LibWeb/DOM/ParentNode.cpp

@@ -9,6 +9,7 @@
 #include <LibWeb/DOM/HTMLCollection.h>
 #include <LibWeb/DOM/ParentNode.h>
 #include <LibWeb/Dump.h>
+#include <LibWeb/Namespace.h>
 
 namespace Web::DOM {
 
@@ -87,4 +88,68 @@ NonnullRefPtr<HTMLCollection> ParentNode::children()
     });
 }
 
+// https://dom.spec.whatwg.org/#concept-getelementsbytagname
+// NOTE: This method is only exposed on Document and Element, but is in ParentNode to prevent code duplication.
+NonnullRefPtr<HTMLCollection> ParentNode::get_elements_by_tag_name(FlyString const& qualified_name)
+{
+    // 1. If qualifiedName is "*" (U+002A), return a HTMLCollection rooted at root, whose filter matches only descendant elements.
+    if (qualified_name == "*") {
+        return HTMLCollection::create(*this, [this](Element const& element) {
+            return element.is_descendant_of(*this);
+        });
+    }
+
+    // FIXME: 2. Otherwise, if root’s node document is an HTML document, return a HTMLCollection rooted at root, whose filter matches the following descendant elements:
+    //           (It is currently always a HTML document)
+    return HTMLCollection::create(*this, [this, qualified_name](Element const& element) {
+        if (!element.is_descendant_of(*this))
+            return false;
+
+        // - Whose namespace is the HTML namespace and whose qualified name is qualifiedName, in ASCII lowercase.
+        if (element.namespace_() == Namespace::HTML)
+            return element.qualified_name().to_lowercase() == qualified_name.to_lowercase();
+
+        // - Whose namespace is not the HTML namespace and whose qualified name is qualifiedName.
+        return element.qualified_name() == qualified_name;
+    });
+
+    // FIXME: 3. Otherwise, return a HTMLCollection rooted at root, whose filter matches descendant elements whose qualified name is qualifiedName.
+}
+
+// https://dom.spec.whatwg.org/#concept-getelementsbytagnamens
+// NOTE: This method is only exposed on Document and Element, but is in ParentNode to prevent code duplication.
+NonnullRefPtr<HTMLCollection> ParentNode::get_elements_by_tag_name_ns(FlyString const& nullable_namespace, FlyString const& local_name)
+{
+    // 1. If namespace is the empty string, set it to null.
+    String namespace_ = nullable_namespace;
+    if (namespace_.is_empty())
+        namespace_ = {};
+
+    // 2. If both namespace and localName are "*" (U+002A), return a HTMLCollection rooted at root, whose filter matches descendant elements.
+    if (namespace_ == "*" && local_name == "*") {
+        return HTMLCollection::create(*this, [this](Element const& element) {
+            return element.is_descendant_of(*this);
+        });
+    }
+
+    // 3. Otherwise, if namespace is "*" (U+002A), return a HTMLCollection rooted at root, whose filter matches descendant elements whose local name is localName.
+    if (namespace_ == "*") {
+        return HTMLCollection::create(*this, [this, local_name](Element const& element) {
+            return element.is_descendant_of(*this) && element.local_name() == local_name;
+        });
+    }
+
+    // 4. Otherwise, if localName is "*" (U+002A), return a HTMLCollection rooted at root, whose filter matches descendant elements whose namespace is namespace.
+    if (local_name == "*") {
+        return HTMLCollection::create(*this, [this, namespace_](Element const& element) {
+            return element.is_descendant_of(*this) && element.namespace_() == namespace_;
+        });
+    }
+
+    // 5. Otherwise, return a HTMLCollection rooted at root, whose filter matches descendant elements whose namespace is namespace and local name is localName.
+    return HTMLCollection::create(*this, [this, namespace_, local_name](Element const& element) {
+        return element.is_descendant_of(*this) && element.namespace_() == namespace_ && element.local_name() == local_name;
+    });
+}
+
 }

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

@@ -27,6 +27,9 @@ public:
 
     NonnullRefPtr<HTMLCollection> children();
 
+    NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
+    NonnullRefPtr<HTMLCollection> get_elements_by_tag_name_ns(FlyString const&, FlyString const&);
+
 protected:
     ParentNode(Document& document, NodeType type)
         : Node(document, type)