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