/src/serenity/Userland/Libraries/LibWeb/DOM/Attr.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org> |
3 | | * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibWeb/Bindings/AttrPrototype.h> |
9 | | #include <LibWeb/Bindings/Intrinsics.h> |
10 | | #include <LibWeb/DOM/Attr.h> |
11 | | #include <LibWeb/DOM/Document.h> |
12 | | #include <LibWeb/DOM/Element.h> |
13 | | #include <LibWeb/DOM/MutationType.h> |
14 | | #include <LibWeb/DOM/StaticNodeList.h> |
15 | | #include <LibWeb/HTML/CustomElements/CustomElementReactionNames.h> |
16 | | |
17 | | namespace Web::DOM { |
18 | | |
19 | | JS_DEFINE_ALLOCATOR(Attr); |
20 | | |
21 | | JS::NonnullGCPtr<Attr> Attr::create(Document& document, FlyString local_name, String value, Element* owner_element) |
22 | 0 | { |
23 | 0 | return document.heap().allocate<Attr>(document.realm(), document, QualifiedName(move(local_name), Optional<FlyString> {}, Optional<FlyString> {}), move(value), owner_element); |
24 | 0 | } |
25 | | |
26 | | JS::NonnullGCPtr<Attr> Attr::create(Document& document, QualifiedName qualified_name, String value, Element* owner_element) |
27 | 0 | { |
28 | 0 | return document.heap().allocate<Attr>(document.realm(), document, move(qualified_name), move(value), owner_element); |
29 | 0 | } |
30 | | |
31 | | JS::NonnullGCPtr<Attr> Attr::clone(Document& document) |
32 | 0 | { |
33 | 0 | return *heap().allocate<Attr>(realm(), document, m_qualified_name, m_value, nullptr); |
34 | 0 | } |
35 | | |
36 | | Attr::Attr(Document& document, QualifiedName qualified_name, String value, Element* owner_element) |
37 | 0 | : Node(document, NodeType::ATTRIBUTE_NODE) |
38 | 0 | , m_qualified_name(move(qualified_name)) |
39 | 0 | , m_lowercase_name(MUST(String(m_qualified_name.as_string()).to_lowercase())) |
40 | 0 | , m_value(move(value)) |
41 | 0 | , m_owner_element(owner_element) |
42 | 0 | { |
43 | 0 | } |
44 | | |
45 | | void Attr::initialize(JS::Realm& realm) |
46 | 0 | { |
47 | 0 | Base::initialize(realm); |
48 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(Attr); |
49 | 0 | } |
50 | | |
51 | | void Attr::visit_edges(Cell::Visitor& visitor) |
52 | 0 | { |
53 | 0 | Base::visit_edges(visitor); |
54 | 0 | visitor.visit(m_owner_element); |
55 | 0 | } |
56 | | |
57 | | Element* Attr::owner_element() |
58 | 0 | { |
59 | 0 | return m_owner_element.ptr(); |
60 | 0 | } |
61 | | |
62 | | Element const* Attr::owner_element() const |
63 | 0 | { |
64 | 0 | return m_owner_element.ptr(); |
65 | 0 | } |
66 | | |
67 | | void Attr::set_owner_element(Element* owner_element) |
68 | 0 | { |
69 | 0 | m_owner_element = owner_element; |
70 | 0 | } |
71 | | |
72 | | // https://dom.spec.whatwg.org/#set-an-existing-attribute-value |
73 | | void Attr::set_value(String value) |
74 | 0 | { |
75 | | // 1. If attribute’s element is null, then set attribute’s value to value. |
76 | 0 | if (!owner_element()) { |
77 | 0 | m_value = move(value); |
78 | 0 | } |
79 | | // 2. Otherwise, change attribute to value. |
80 | 0 | else { |
81 | 0 | change_attribute(move(value)); |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | // https://dom.spec.whatwg.org/#concept-element-attributes-change |
86 | | void Attr::change_attribute(String value) |
87 | 0 | { |
88 | | // 1. Let oldValue be attribute’s value. |
89 | 0 | auto old_value = move(m_value); |
90 | | |
91 | | // 2. Set attribute’s value to value. |
92 | 0 | m_value = move(value); |
93 | | |
94 | | // 3. Handle attribute changes for attribute with attribute’s element, oldValue, and value. |
95 | 0 | handle_attribute_changes(*owner_element(), old_value, m_value); |
96 | 0 | } |
97 | | |
98 | | // https://dom.spec.whatwg.org/#handle-attribute-changes |
99 | | void Attr::handle_attribute_changes(Element& element, Optional<String> const& old_value, Optional<String> const& new_value) |
100 | 0 | { |
101 | | // 1. Queue a mutation record of "attributes" for element with attribute’s local name, attribute’s namespace, oldValue, « », « », null, and null. |
102 | 0 | element.queue_mutation_record(MutationType::attributes, local_name(), namespace_uri(), old_value, {}, {}, nullptr, nullptr); |
103 | | |
104 | | // 2. If element is custom, then enqueue a custom element callback reaction with element, callback name "attributeChangedCallback", and an argument list containing attribute’s local name, oldValue, newValue, and attribute’s namespace. |
105 | 0 | if (element.is_custom()) { |
106 | 0 | auto& vm = this->vm(); |
107 | |
|
108 | 0 | JS::MarkedVector<JS::Value> arguments { vm.heap() }; |
109 | 0 | arguments.append(JS::PrimitiveString::create(vm, local_name())); |
110 | 0 | arguments.append(!old_value.has_value() ? JS::js_null() : JS::PrimitiveString::create(vm, old_value.value())); |
111 | 0 | arguments.append(!new_value.has_value() ? JS::js_null() : JS::PrimitiveString::create(vm, new_value.value())); |
112 | 0 | arguments.append(!namespace_uri().has_value() ? JS::js_null() : JS::PrimitiveString::create(vm, namespace_uri().value())); |
113 | |
|
114 | 0 | element.enqueue_a_custom_element_callback_reaction(HTML::CustomElementReactionNames::attributeChangedCallback, move(arguments)); |
115 | 0 | } |
116 | | |
117 | | // 3. Run the attribute change steps with element, attribute’s local name, oldValue, newValue, and attribute’s namespace. |
118 | 0 | element.run_attribute_change_steps(local_name(), old_value, new_value, namespace_uri()); |
119 | 0 | } |
120 | | |
121 | | } |