Skip to main content

script/dom/mutationobserver/
mutationrecord.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-type>
136    fn Type(&self) -> DOMString {
137        self.record_type.clone().root()
138    }
139
140    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-target>
141    fn Target(&self) -> DomRoot<Node> {
142        DomRoot::from_ref(&*self.target)
143    }
144
145    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-attributename>
146    fn GetAttributeName(&self) -> Option<DOMString> {
147        self.attribute_name
148            .clone()
149            .map(|domstring| domstring.root())
150    }
151
152    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-attributenamespace>
153    fn GetAttributeNamespace(&self) -> Option<DOMString> {
154        self.attribute_namespace
155            .clone()
156            .map(|domstring| domstring.root())
157    }
158
159    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-oldvalue>
160    fn GetOldValue(&self) -> Option<DOMString> {
161        self.old_value.clone().map(|domstring| domstring.root())
162    }
163
164    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-addednodes>
165    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    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-removednodes>
171    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    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling>
177    fn GetPreviousSibling(&self) -> Option<DomRoot<Node>> {
178        self.prev_sibling
179            .as_ref()
180            .map(|node| DomRoot::from_ref(&**node))
181    }
182
183    /// <https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling>
184    fn GetNextSibling(&self) -> Option<DomRoot<Node>> {
185        self.next_sibling
186            .as_ref()
187            .map(|node| DomRoot::from_ref(&**node))
188    }
189}