1#![allow(dead_code)]
3
4extern crate alloc;
5use alloc::collections::BTreeMap;
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum MutationType {
12 ChildList,
13 Attributes,
14 CharacterData,
15}
16
17impl MutationType {
18 pub fn as_str(&self) -> &'static str {
19 match self {
20 MutationType::ChildList => "childList",
21 MutationType::Attributes => "attributes",
22 MutationType::CharacterData => "characterData",
23 }
24 }
25
26 pub fn parse(s: &str) -> Option<Self> {
27 match s {
28 "childList" => Some(MutationType::ChildList),
29 "attributes" => Some(MutationType::Attributes),
30 "characterData" => Some(MutationType::CharacterData),
31 _ => None,
32 }
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Default)]
38pub struct MutationObserverOptions {
39 pub child_list: bool,
40 pub attributes: bool,
41 pub character_data: bool,
42 pub subtree: bool,
43 pub attribute_old_value: bool,
44 pub character_data_old_value: bool,
45 pub attribute_filter: Option<Vec<String>>,
46}
47
48impl MutationObserverOptions {
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn normalize(
59 mut self,
60 attributes_specified: bool,
61 character_data_specified: bool,
62 ) -> Result<Self, &'static str> {
63 if !attributes_specified && (self.attribute_old_value || self.attribute_filter.is_some()) {
64 self.attributes = true;
65 }
66 if !character_data_specified && self.character_data_old_value {
67 self.character_data = true;
68 }
69
70 if !self.child_list && !self.attributes && !self.character_data {
71 return Err("TypeError: At least one of childList, attributes, or characterData must be true");
72 }
73 if self.attribute_old_value && !self.attributes {
74 return Err("TypeError: attributeOldValue is true but attributes is false");
75 }
76 if self.attribute_filter.is_some() && !self.attributes {
77 return Err("TypeError: attributeFilter is present but attributes is false");
78 }
79 if self.character_data_old_value && !self.character_data {
80 return Err("TypeError: characterDataOldValue is true but characterData is false");
81 }
82 Ok(self)
83 }
84
85 pub fn matches_attribute(&self, attr_name: &str) -> bool {
87 if !self.attributes {
88 return false;
89 }
90 if let Some(ref filter) = self.attribute_filter {
91 filter.iter().any(|f| f == attr_name)
92 } else {
93 true
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct MutationRecord {
101 pub type_: MutationType,
102 pub target_idx: usize,
103 pub added_nodes: Vec<usize>,
104 pub removed_nodes: Vec<usize>,
105 pub previous_sibling: Option<usize>,
106 pub next_sibling: Option<usize>,
107 pub attribute_name: Option<String>,
108 pub attribute_namespace: Option<String>,
109 pub old_value: Option<String>,
110}
111
112impl MutationRecord {
113 pub fn child_list(target_idx: usize, added: Vec<usize>, removed: Vec<usize>) -> Self {
114 Self {
115 type_: MutationType::ChildList,
116 target_idx,
117 added_nodes: added,
118 removed_nodes: removed,
119 previous_sibling: None,
120 next_sibling: None,
121 attribute_name: None,
122 attribute_namespace: None,
123 old_value: None,
124 }
125 }
126
127 pub fn attribute(target_idx: usize, attr_name: &str, old_val: Option<String>) -> Self {
128 Self {
129 type_: MutationType::Attributes,
130 target_idx,
131 added_nodes: Vec::new(),
132 removed_nodes: Vec::new(),
133 previous_sibling: None,
134 next_sibling: None,
135 attribute_name: Some(String::from(attr_name)),
136 attribute_namespace: None,
137 old_value: old_val,
138 }
139 }
140
141 pub fn character_data(target_idx: usize, old_val: Option<String>) -> Self {
142 Self {
143 type_: MutationType::CharacterData,
144 target_idx,
145 added_nodes: Vec::new(),
146 removed_nodes: Vec::new(),
147 previous_sibling: None,
148 next_sibling: None,
149 attribute_name: None,
150 attribute_namespace: None,
151 old_value: old_val,
152 }
153 }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct RegisteredObserver {
159 pub observer_id: u32,
160 pub target_idx: usize,
161 pub options: MutationObserverOptions,
162}
163
164#[derive(Debug, Default, Clone)]
166pub struct MutationObserverRegistry {
167 pub next_observer_id: u32,
168 pub registrations: Vec<RegisteredObserver>,
169 pub records: Vec<(u32, MutationRecord)>,
170}
171
172impl MutationObserverRegistry {
173 pub fn new() -> Self {
174 Self {
175 next_observer_id: 1,
176 registrations: Vec::new(),
177 records: Vec::new(),
178 }
179 }
180
181 pub fn allocate_id(&mut self) -> u32 {
182 let id = self.next_observer_id;
183 self.next_observer_id = self.next_observer_id.wrapping_add(1).max(1);
184 id
185 }
186
187 pub fn register(&mut self, observer_id: u32, target_idx: usize, options: MutationObserverOptions) {
189 if let Some(pos) = self
190 .registrations
191 .iter()
192 .position(|r| r.observer_id == observer_id && r.target_idx == target_idx)
193 {
194 self.registrations[pos].options = options;
195 } else {
196 self.registrations.push(RegisteredObserver {
197 observer_id,
198 target_idx,
199 options,
200 });
201 }
202 }
203
204 pub fn disconnect(&mut self, observer_id: u32) {
206 self.registrations.retain(|r| r.observer_id != observer_id);
207 self.records.retain(|(id, _)| *id != observer_id);
208 }
209
210 pub fn take_records(&mut self, observer_id: u32) -> Vec<MutationRecord> {
212 let mut matched = Vec::new();
213 let mut rest = Vec::new();
214 for (id, r) in core::mem::take(&mut self.records) {
215 if id == observer_id {
216 matched.push(r);
217 } else {
218 rest.push((id, r));
219 }
220 }
221 self.records = rest;
222 matched
223 }
224
225 pub fn notify_child_list<F>(
227 &mut self,
228 parent_idx: usize,
229 added: Vec<usize>,
230 removed: Vec<usize>,
231 is_ancestor: F,
232 ) where
233 F: Fn(usize, usize) -> bool,
234 {
235 for reg in &self.registrations {
236 let watches = reg.options.child_list
237 && (reg.target_idx == parent_idx
238 || (reg.options.subtree && is_ancestor(reg.target_idx, parent_idx)));
239 if watches {
240 self.records.push((
241 reg.observer_id,
242 MutationRecord::child_list(parent_idx, added.clone(), removed.clone()),
243 ));
244 }
245 }
246 }
247
248 pub fn notify_attribute<F>(
250 &mut self,
251 target_idx: usize,
252 attr_name: &str,
253 old_val: Option<&str>,
254 is_ancestor: F,
255 ) where
256 F: Fn(usize, usize) -> bool,
257 {
258 for reg in &self.registrations {
259 let watches = reg.options.matches_attribute(attr_name)
260 && (reg.target_idx == target_idx
261 || (reg.options.subtree && is_ancestor(reg.target_idx, target_idx)));
262 if watches {
263 let recorded_old_val = if reg.options.attribute_old_value {
264 Some(old_val.unwrap_or("").to_string())
265 } else {
266 None
267 };
268 self.records.push((
269 reg.observer_id,
270 MutationRecord::attribute(target_idx, attr_name, recorded_old_val),
271 ));
272 }
273 }
274 }
275
276 pub fn notify_character_data<F>(
278 &mut self,
279 target_idx: usize,
280 old_val: Option<&str>,
281 is_ancestor: F,
282 ) where
283 F: Fn(usize, usize) -> bool,
284 {
285 for reg in &self.registrations {
286 let watches = reg.options.character_data
287 && (reg.target_idx == target_idx
288 || (reg.options.subtree && is_ancestor(reg.target_idx, target_idx)));
289 if watches {
290 let recorded_old_val = if reg.options.character_data_old_value {
291 Some(old_val.unwrap_or("").to_string())
292 } else {
293 None
294 };
295 self.records.push((
296 reg.observer_id,
297 MutationRecord::character_data(target_idx, recorded_old_val),
298 ));
299 }
300 }
301 }
302
303 pub fn drain_all_grouped(&mut self) -> BTreeMap<u32, Vec<MutationRecord>> {
305 let mut map: BTreeMap<u32, Vec<MutationRecord>> = BTreeMap::new();
306 for (id, rec) in core::mem::take(&mut self.records) {
307 map.entry(id).or_default().push(rec);
308 }
309 map
310 }
311}