ladybird/Userland/Libraries/LibWeb/DOM/Attr.cpp
Andreas Kling 80d6330a26 LibWeb: Don't create mutation record node lists if nobody is interested
By deferring allocation of StaticNodeList objects until we know somebody
actually wants the MutationRecord, we avoid a *lot* of allocation work.

This shaves several seconds off of loading https://tc39.es/ecma262/

At least one other engine (WebKit) skips creating mutation records if
nobody is interested, so even if this is observable somehow, we would
at least match the behavior of a major engine.
2023-05-23 06:31:37 +02:00

110 lines
4.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/Attr.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/MutationType.h>
#include <LibWeb/DOM/StaticNodeList.h>
#include <LibWeb/HTML/CustomElements/CustomElementReactionNames.h>
namespace Web::DOM {
WebIDL::ExceptionOr<JS::NonnullGCPtr<Attr>> Attr::create(Document& document, DeprecatedFlyString local_name, DeprecatedString value, Element* owner_element)
{
return MUST_OR_THROW_OOM(document.heap().allocate<Attr>(document.realm(), document, QualifiedName(move(local_name), {}, {}), move(value), owner_element));
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<Attr>> Attr::create(Document& document, QualifiedName qualified_name, DeprecatedString value, Element* owner_element)
{
return MUST_OR_THROW_OOM(document.heap().allocate<Attr>(document.realm(), document, move(qualified_name), move(value), owner_element));
}
JS::NonnullGCPtr<Attr> Attr::clone(Document& document)
{
return *heap().allocate<Attr>(realm(), document, m_qualified_name, m_value, nullptr).release_allocated_value_but_fixme_should_propagate_errors();
}
Attr::Attr(Document& document, QualifiedName qualified_name, DeprecatedString value, Element* owner_element)
: Node(document, NodeType::ATTRIBUTE_NODE)
, m_qualified_name(move(qualified_name))
, m_value(move(value))
, m_owner_element(owner_element)
{
}
JS::ThrowCompletionOr<void> Attr::initialize(JS::Realm& realm)
{
MUST_OR_THROW_OOM(Base::initialize(realm));
set_prototype(&Bindings::ensure_web_prototype<Bindings::AttrPrototype>(realm, "Attr"));
return {};
}
void Attr::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_owner_element.ptr());
}
Element* Attr::owner_element()
{
return m_owner_element.ptr();
}
Element const* Attr::owner_element() const
{
return m_owner_element.ptr();
}
void Attr::set_owner_element(Element* owner_element)
{
m_owner_element = owner_element;
}
// https://dom.spec.whatwg.org/#set-an-existing-attribute-value
void Attr::set_value(DeprecatedString value)
{
// 1. If attributes element is null, then set attributes value to value.
if (!owner_element()) {
m_value = move(value);
return;
}
// 2. Otherwise, change attribute to value.
// https://dom.spec.whatwg.org/#concept-element-attributes-change
// 1. Handle attribute changes for attribute with attributes element, attributes value, and value.
handle_attribute_changes(*owner_element(), m_value, value);
// 2. Set attributes value to value.
m_value = move(value);
}
// https://dom.spec.whatwg.org/#handle-attribute-changes
void Attr::handle_attribute_changes(Element& element, DeprecatedString const& old_value, DeprecatedString const& new_value)
{
// 1. Queue a mutation record of "attributes" for element with attributes local name, attributes namespace, oldValue, « », « », null, and null.
element.queue_mutation_record(MutationType::attributes, local_name(), namespace_uri(), old_value, {}, {}, nullptr, nullptr);
// 2. If element is custom, then enqueue a custom element callback reaction with element, callback name "attributeChangedCallback", and an argument list containing attributes local name, oldValue, newValue, and attributes namespace.
if (element.is_custom()) {
auto& vm = this->vm();
JS::MarkedVector<JS::Value> arguments { vm.heap() };
arguments.append(JS::PrimitiveString::create(vm, local_name()));
arguments.append(old_value.is_null() ? JS::js_null() : JS::PrimitiveString::create(vm, old_value));
arguments.append(new_value.is_null() ? JS::js_null() : JS::PrimitiveString::create(vm, new_value));
arguments.append(namespace_uri().is_null() ? JS::js_null() : JS::PrimitiveString::create(vm, namespace_uri()));
element.enqueue_a_custom_element_callback_reaction(HTML::CustomElementReactionNames::attributeChangedCallback, move(arguments));
}
// FIXME: 3. Run the attribute change steps with element, attributes local name, oldValue, newValue, and attributes namespace.
}
}