script/dom/mutationobserver/
mutationrecord.rs1use dom_struct::dom_struct;
6use html5ever::{LocalName, Namespace};
7use js::context::JSContext;
8use script_bindings::domstring::TracedDOMString;
9use script_bindings::reflector::{Reflector, reflect_dom_object};
10
11use crate::dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecord_Binding::MutationRecordMethods;
12use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
13use crate::dom::bindings::str::DOMString;
14use crate::dom::node::{Node, NodeTraits};
15use crate::dom::nodelist::NodeList;
16
17#[dom_struct]
18pub(crate) struct MutationRecord {
19 reflector_: Reflector,
20 record_type: TracedDOMString,
21 target: Dom<Node>,
22 attribute_name: Option<TracedDOMString>,
23 attribute_namespace: Option<TracedDOMString>,
24 old_value: Option<TracedDOMString>,
25 added_nodes: MutNullableDom<NodeList>,
26 removed_nodes: MutNullableDom<NodeList>,
27 next_sibling: Option<Dom<Node>>,
28 prev_sibling: Option<Dom<Node>>,
29}
30
31impl MutationRecord {
32 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
33 pub(crate) fn attribute_mutated(
34 cx: &mut JSContext,
35 target: &Node,
36 attribute_name: &LocalName,
37 attribute_namespace: Option<&Namespace>,
38 old_value: Option<DOMString>,
39 ) -> DomRoot<MutationRecord> {
40 let record = Box::new(MutationRecord::new_inherited(
41 "attributes",
42 target,
43 Some(TracedDOMString::from(attribute_name.clone()).root()),
44 attribute_namespace.map(|n| TracedDOMString::from(n.clone()).root()),
45 old_value,
46 None,
47 None,
48 None,
49 None,
50 ));
51 reflect_dom_object(cx, record, &*target.owner_window())
52 }
53
54 pub(crate) fn character_data_mutated(
55 cx: &mut JSContext,
56 target: &Node,
57 old_value: Option<DOMString>,
58 ) -> DomRoot<MutationRecord> {
59 reflect_dom_object(
60 cx,
61 Box::new(MutationRecord::new_inherited(
62 "characterData",
63 target,
64 None,
65 None,
66 old_value,
67 None,
68 None,
69 None,
70 None,
71 )),
72 &*target.owner_window(),
73 )
74 }
75
76 pub(crate) fn child_list_mutated(
77 cx: &mut JSContext,
78 target: &Node,
79 added_nodes: Option<&[&Node]>,
80 removed_nodes: Option<&[&Node]>,
81 next_sibling: Option<&Node>,
82 prev_sibling: Option<&Node>,
83 ) -> DomRoot<MutationRecord> {
84 let window = target.owner_window();
85 let added_nodes =
86 added_nodes.map(|list| NodeList::new_simple_list_slice(cx, &window, list));
87 let removed_nodes =
88 removed_nodes.map(|list| NodeList::new_simple_list_slice(cx, &window, list));
89
90 reflect_dom_object(
91 cx,
92 Box::new(MutationRecord::new_inherited(
93 "childList",
94 target,
95 None,
96 None,
97 None,
98 added_nodes.as_deref(),
99 removed_nodes.as_deref(),
100 next_sibling,
101 prev_sibling,
102 )),
103 &*window,
104 )
105 }
106
107 #[expect(clippy::too_many_arguments)]
108 fn new_inherited(
109 record_type: &'static str,
110 target: &Node,
111 attribute_name: Option<DOMString>,
112 attribute_namespace: Option<DOMString>,
113 old_value: Option<DOMString>,
114 added_nodes: Option<&NodeList>,
115 removed_nodes: Option<&NodeList>,
116 next_sibling: Option<&Node>,
117 prev_sibling: Option<&Node>,
118 ) -> MutationRecord {
119 MutationRecord {
120 reflector_: Reflector::new(),
121 record_type: DOMString::from_static(record_type).traced(),
122 target: Dom::from_ref(target),
123 attribute_name: attribute_name.map(|domstring| domstring.traced()),
124 attribute_namespace: attribute_namespace.map(|domstring| domstring.traced()),
125 old_value: old_value.map(|domstring| domstring.traced()),
126 added_nodes: MutNullableDom::new(added_nodes),
127 removed_nodes: MutNullableDom::new(removed_nodes),
128 next_sibling: next_sibling.map(Dom::from_ref),
129 prev_sibling: prev_sibling.map(Dom::from_ref),
130 }
131 }
132}
133
134impl MutationRecordMethods<crate::DomTypeHolder> for MutationRecord {
135 fn Type(&self) -> DOMString {
137 self.record_type.clone().root()
138 }
139
140 fn Target(&self) -> DomRoot<Node> {
142 DomRoot::from_ref(&*self.target)
143 }
144
145 fn GetAttributeName(&self) -> Option<DOMString> {
147 self.attribute_name
148 .clone()
149 .map(|domstring| domstring.root())
150 }
151
152 fn GetAttributeNamespace(&self) -> Option<DOMString> {
154 self.attribute_namespace
155 .clone()
156 .map(|domstring| domstring.root())
157 }
158
159 fn GetOldValue(&self) -> Option<DOMString> {
161 self.old_value.clone().map(|domstring| domstring.root())
162 }
163
164 fn AddedNodes(&self, cx: &mut JSContext) -> DomRoot<NodeList> {
166 self.added_nodes
167 .or_init(|| NodeList::empty(cx, &self.target.owner_window()))
168 }
169
170 fn RemovedNodes(&self, cx: &mut JSContext) -> DomRoot<NodeList> {
172 self.removed_nodes
173 .or_init(|| NodeList::empty(cx, &self.target.owner_window()))
174 }
175
176 fn GetPreviousSibling(&self) -> Option<DomRoot<Node>> {
178 self.prev_sibling
179 .as_ref()
180 .map(|node| DomRoot::from_ref(&**node))
181 }
182
183 fn GetNextSibling(&self) -> Option<DomRoot<Node>> {
185 self.next_sibling
186 .as_ref()
187 .map(|node| DomRoot::from_ref(&**node))
188 }
189}