1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:07:46 +00:00

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.
This commit is contained in:
Luke Wilde 2021-09-22 18:06:19 +01:00 committed by Andreas Kling
parent 5c7dca3526
commit d47e431d54
8 changed files with 70 additions and 24 deletions

View file

@ -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) 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) { return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {

View file

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

View file

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

View file

@ -272,17 +272,6 @@ bool Element::is_active() const
return document().active_element() == this; 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) 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) { return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {

View file

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

View file

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

View file

@ -9,6 +9,7 @@
#include <LibWeb/DOM/HTMLCollection.h> #include <LibWeb/DOM/HTMLCollection.h>
#include <LibWeb/DOM/ParentNode.h> #include <LibWeb/DOM/ParentNode.h>
#include <LibWeb/Dump.h> #include <LibWeb/Dump.h>
#include <LibWeb/Namespace.h>
namespace Web::DOM { 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 roots 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;
});
}
} }

View file

@ -27,6 +27,9 @@ public:
NonnullRefPtr<HTMLCollection> children(); 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: protected:
ParentNode(Document& document, NodeType type) ParentNode(Document& document, NodeType type)
: Node(document, type) : Node(document, type)