mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-24 21:45:20 +00:00
LibWeb: Create Element::attributes (NamedNodeMap) lazily
Many elements have zero attributes, and they don't need a NamedNodeMap until someone asks for it.
This commit is contained in:
parent
5085b0dd66
commit
94cc4adefd
Notes:
github-actions[bot]
2025-04-20 16:44:29 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/94cc4adefd6 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4408
3 changed files with 53 additions and 16 deletions
|
@ -99,8 +99,6 @@ void Element::initialize(JS::Realm& realm)
|
|||
{
|
||||
Base::initialize(realm);
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(Element);
|
||||
|
||||
m_attributes = NamedNodeMap::create(*this);
|
||||
}
|
||||
|
||||
void Element::visit_edges(Cell::Visitor& visitor)
|
||||
|
@ -134,6 +132,8 @@ void Element::visit_edges(Cell::Visitor& visitor)
|
|||
Optional<String> Element::get_attribute(FlyString const& name) const
|
||||
{
|
||||
// 1. Let attr be the result of getting an attribute given qualifiedName and this.
|
||||
if (!m_attributes)
|
||||
return {};
|
||||
auto const* attribute = m_attributes->get_attribute(name);
|
||||
|
||||
// 2. If attr is null, return null.
|
||||
|
@ -148,6 +148,8 @@ Optional<String> Element::get_attribute(FlyString const& name) const
|
|||
Optional<String> Element::get_attribute_ns(Optional<FlyString> const& namespace_, FlyString const& name) const
|
||||
{
|
||||
// 1. Let attr be the result of getting an attribute given namespace, localName, and this.
|
||||
if (!m_attributes)
|
||||
return {};
|
||||
auto const* attribute = m_attributes->get_attribute_ns(namespace_, name);
|
||||
|
||||
// 2. If attr is null, return null.
|
||||
|
@ -162,6 +164,8 @@ Optional<String> Element::get_attribute_ns(Optional<FlyString> const& namespace_
|
|||
String Element::get_attribute_value(FlyString const& local_name, Optional<FlyString> const& namespace_) const
|
||||
{
|
||||
// 1. Let attr be the result of getting an attribute given namespace, localName, and element.
|
||||
if (!m_attributes)
|
||||
return {};
|
||||
auto const* attribute = m_attributes->get_attribute_ns(namespace_, local_name);
|
||||
|
||||
// 2. If attr is null, then return the empty string.
|
||||
|
@ -176,6 +180,8 @@ String Element::get_attribute_value(FlyString const& local_name, Optional<FlyStr
|
|||
GC::Ptr<Attr> Element::get_attribute_node(FlyString const& name) const
|
||||
{
|
||||
// The getAttributeNode(qualifiedName) method steps are to return the result of getting an attribute given qualifiedName and this.
|
||||
if (!m_attributes)
|
||||
return {};
|
||||
return m_attributes->get_attribute(name);
|
||||
}
|
||||
|
||||
|
@ -183,6 +189,8 @@ GC::Ptr<Attr> Element::get_attribute_node(FlyString const& name) const
|
|||
GC::Ptr<Attr> Element::get_attribute_node_ns(Optional<FlyString> const& namespace_, FlyString const& name) const
|
||||
{
|
||||
// The getAttributeNodeNS(namespace, localName) method steps are to return the result of getting an attribute given namespace, localName, and this.
|
||||
if (!m_attributes)
|
||||
return {};
|
||||
return m_attributes->get_attribute_ns(namespace_, name);
|
||||
}
|
||||
|
||||
|
@ -197,7 +205,7 @@ WebIDL::ExceptionOr<void> Element::set_attribute(FlyString const& name, String c
|
|||
bool insert_as_lowercase = namespace_uri() == Namespace::HTML && document().document_type() == Document::Type::HTML;
|
||||
|
||||
// 3. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
|
||||
auto* attribute = m_attributes->get_attribute(name);
|
||||
auto* attribute = attributes()->get_attribute(name);
|
||||
|
||||
// 4. If attribute is null, create an attribute whose local name is qualifiedName, value is value, and node document
|
||||
// is this’s node document, then append this attribute to this, and then return.
|
||||
|
@ -280,20 +288,20 @@ WebIDL::ExceptionOr<void> Element::set_attribute_ns(Optional<FlyString> const& n
|
|||
// https://dom.spec.whatwg.org/#concept-element-attributes-append
|
||||
void Element::append_attribute(FlyString const& name, String const& value)
|
||||
{
|
||||
m_attributes->append_attribute(Attr::create(document(), name, value));
|
||||
attributes()->append_attribute(Attr::create(document(), name, value));
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-element-attributes-append
|
||||
void Element::append_attribute(Attr& attribute)
|
||||
{
|
||||
m_attributes->append_attribute(attribute);
|
||||
attributes()->append_attribute(attribute);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-element-attributes-set-value
|
||||
void Element::set_attribute_value(FlyString const& local_name, String const& value, Optional<FlyString> const& prefix, Optional<FlyString> const& namespace_)
|
||||
{
|
||||
// 1. Let attribute be the result of getting an attribute given namespace, localName, and element.
|
||||
auto* attribute = m_attributes->get_attribute_ns(namespace_, local_name);
|
||||
auto* attribute = attributes()->get_attribute_ns(namespace_, local_name);
|
||||
|
||||
// 2. If attribute is null, create an attribute whose namespace is namespace, namespace prefix is prefix, local name
|
||||
// is localName, value is value, and node document is element’s node document, then append this attribute to element,
|
||||
|
@ -315,20 +323,22 @@ void Element::set_attribute_value(FlyString const& local_name, String const& val
|
|||
WebIDL::ExceptionOr<GC::Ptr<Attr>> Element::set_attribute_node(Attr& attr)
|
||||
{
|
||||
// The setAttributeNode(attr) and setAttributeNodeNS(attr) methods steps are to return the result of setting an attribute given attr and this.
|
||||
return m_attributes->set_attribute(attr);
|
||||
return attributes()->set_attribute(attr);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-setattributenodens
|
||||
WebIDL::ExceptionOr<GC::Ptr<Attr>> Element::set_attribute_node_ns(Attr& attr)
|
||||
{
|
||||
// The setAttributeNode(attr) and setAttributeNodeNS(attr) methods steps are to return the result of setting an attribute given attr and this.
|
||||
return m_attributes->set_attribute(attr);
|
||||
return attributes()->set_attribute(attr);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-removeattribute
|
||||
void Element::remove_attribute(FlyString const& name)
|
||||
{
|
||||
// The removeAttribute(qualifiedName) method steps are to remove an attribute given qualifiedName and this, and then return undefined.
|
||||
if (!m_attributes)
|
||||
return;
|
||||
m_attributes->remove_attribute(name);
|
||||
}
|
||||
|
||||
|
@ -336,24 +346,31 @@ void Element::remove_attribute(FlyString const& name)
|
|||
void Element::remove_attribute_ns(Optional<FlyString> const& namespace_, FlyString const& name)
|
||||
{
|
||||
// The removeAttributeNS(namespace, localName) method steps are to remove an attribute given namespace, localName, and this, and then return undefined.
|
||||
if (!m_attributes)
|
||||
return;
|
||||
m_attributes->remove_attribute_ns(namespace_, name);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-removeattributenode
|
||||
WebIDL::ExceptionOr<GC::Ref<Attr>> Element::remove_attribute_node(GC::Ref<Attr> attr)
|
||||
{
|
||||
return m_attributes->remove_attribute_node(attr);
|
||||
return attributes()->remove_attribute_node(attr);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-hasattribute
|
||||
bool Element::has_attribute(FlyString const& name) const
|
||||
{
|
||||
if (!m_attributes)
|
||||
return false;
|
||||
return m_attributes->get_attribute(name) != nullptr;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-hasattributens
|
||||
bool Element::has_attribute_ns(Optional<FlyString> const& namespace_, FlyString const& name) const
|
||||
{
|
||||
if (!m_attributes)
|
||||
return false;
|
||||
|
||||
// 1. If namespace is the empty string, then set it to null.
|
||||
// 2. Return true if this has an attribute whose namespace is namespace and local name is localName; otherwise false.
|
||||
if (namespace_ == FlyString {})
|
||||
|
@ -373,7 +390,7 @@ WebIDL::ExceptionOr<bool> Element::toggle_attribute(FlyString const& name, Optio
|
|||
bool insert_as_lowercase = namespace_uri() == Namespace::HTML && document().document_type() == Document::Type::HTML;
|
||||
|
||||
// 3. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
|
||||
auto* attribute = m_attributes->get_attribute(name);
|
||||
auto* attribute = attributes()->get_attribute(name);
|
||||
|
||||
// 4. If attribute is null, then:
|
||||
if (!attribute) {
|
||||
|
@ -404,6 +421,8 @@ WebIDL::ExceptionOr<bool> Element::toggle_attribute(FlyString const& name, Optio
|
|||
Vector<String> Element::get_attribute_names() const
|
||||
{
|
||||
// The getAttributeNames() method steps are to return the qualified names of the attributes in this’s attribute list, in order; otherwise a new list.
|
||||
if (!m_attributes)
|
||||
return {};
|
||||
Vector<String> names;
|
||||
for (size_t i = 0; i < m_attributes->length(); ++i) {
|
||||
auto const* attribute = m_attributes->item(i);
|
||||
|
@ -2481,7 +2500,8 @@ JS::ThrowCompletionOr<void> Element::upgrade_element(GC::Ref<HTML::CustomElement
|
|||
|
||||
// 4. For each attribute in element's attribute list, in order, enqueue a custom element callback reaction with element, callback name "attributeChangedCallback",
|
||||
// and « attribute's local name, null, attribute's value, attribute's namespace ».
|
||||
for (size_t attribute_index = 0; attribute_index < m_attributes->length(); ++attribute_index) {
|
||||
size_t attribute_count = m_attributes ? m_attributes->length() : 0;
|
||||
for (size_t attribute_index = 0; attribute_index < attribute_count; ++attribute_index) {
|
||||
auto const* attribute = m_attributes->item(attribute_index);
|
||||
VERIFY(attribute);
|
||||
|
||||
|
@ -2617,7 +2637,7 @@ Optional<String> Element::locate_a_namespace_prefix(Optional<String> const& name
|
|||
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()) {
|
||||
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_)
|
||||
|
@ -2635,6 +2655,8 @@ Optional<String> Element::locate_a_namespace_prefix(Optional<String> const& name
|
|||
|
||||
void Element::for_each_attribute(Function<void(Attr const&)> callback) const
|
||||
{
|
||||
if (!m_attributes)
|
||||
return;
|
||||
for (size_t i = 0; i < m_attributes->length(); ++i)
|
||||
callback(*m_attributes->item(i));
|
||||
}
|
||||
|
@ -2658,12 +2680,12 @@ GC::Ptr<Layout::NodeWithStyle const> Element::layout_node() const
|
|||
|
||||
bool Element::has_attributes() const
|
||||
{
|
||||
return !m_attributes->is_empty();
|
||||
return m_attributes && !m_attributes->is_empty();
|
||||
}
|
||||
|
||||
size_t Element::attribute_list_size() const
|
||||
{
|
||||
return m_attributes->length();
|
||||
return m_attributes ? m_attributes->length() : 0;
|
||||
}
|
||||
|
||||
GC::Ptr<CSS::CascadedProperties> Element::cascaded_properties(Optional<CSS::PseudoElement> pseudo_element) const
|
||||
|
@ -3819,4 +3841,16 @@ bool Element::has_pointer_capture(WebIDL::Long pointer_id)
|
|||
return false;
|
||||
}
|
||||
|
||||
GC::Ptr<NamedNodeMap> Element::attributes()
|
||||
{
|
||||
if (!m_attributes)
|
||||
m_attributes = NamedNodeMap::create(*this);
|
||||
return m_attributes;
|
||||
}
|
||||
|
||||
GC::Ptr<NamedNodeMap const> Element::attributes() const
|
||||
{
|
||||
return const_cast<Element&>(*this).attributes();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -154,7 +154,10 @@ public:
|
|||
|
||||
WebIDL::ExceptionOr<bool> toggle_attribute(FlyString const& name, Optional<bool> force);
|
||||
size_t attribute_list_size() const;
|
||||
NamedNodeMap const* attributes() const { return m_attributes.ptr(); }
|
||||
|
||||
GC::Ptr<NamedNodeMap const> attributes() const;
|
||||
GC::Ptr<NamedNodeMap> attributes();
|
||||
|
||||
Vector<String> get_attribute_names() const;
|
||||
|
||||
GC::Ptr<Attr> get_attribute_node(FlyString const& name) const;
|
||||
|
|
|
@ -2035,7 +2035,7 @@ Optional<String> Node::locate_a_namespace(Optional<String> const& prefix) const
|
|||
// 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()) {
|
||||
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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue