Skip to main content

atmos/os_lib/
mutation_observer.rs

1// src/os_lib/mutation_observer.rs - W3C DOM MutationObserver Pure Logic
2#![allow(dead_code)]
3
4extern crate alloc;
5use alloc::collections::BTreeMap;
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8
9/// MutationRecord の種別
10#[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/// MutationObserver.observe() で指定される監視オプション
37#[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    /// W3C 仕様準拠のオプション正規化・バリデーション:
54    /// - attributes が未指定でも attributeOldValue / attributeFilter があれば attributes = true
55    /// - characterData が未指定でも characterDataOldValue があれば characterData = true
56    /// - childList, attributes, characterData のいずれも true でなければエラー
57    /// - 矛盾したオプション組み合わせの検査
58    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    /// 属性名が監視条件に合致するか判定
86    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/// 変更通知レコード
99#[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/// 登録エントリ
157#[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/// MutationObserver 管理コア状態機械
165#[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    /// W3C 仕様: 同一 observer_id かつ同一 target_idx の登録は既存を更新、別 target_idx は追加
188    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    /// disconnect(): 当該 observer_id の全登録と保留レコードを破棄
205    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    /// takeRecords(): 当該 observer_id の保留レコードを取り出してクリア
211    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    /// childList 変化を通知
226    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    /// 属性変化を通知
249    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    /// characterData 変化を通知
277    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    /// 全レコードを一括ドレイン(observer_id ごとにグループ化)
304    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}