Skip to main content

blitz_dom_api/
character_data.rs

1//! `CharacterData.data`, which `blitz-script` implements as the `nodeValue`
2//! accessor pair shared with `Node`.
3//!
4//! Upstream: `blitz-script/src/dom/node.rs` (`node_value` / `set_node_value`,
5//! registered on the CharacterData prototype as `data`). See MAPPING.md.
6
7use blitz_dom::node::NodeData;
8use blitz_dom::{BaseDocument, NodeId};
9
10use crate::Result;
11
12/// `characterData.data`.
13///
14/// `None` is the DOM's `null`, which is what a node with no character data
15/// returns. A comment reports its contents, as `CharacterData` requires.
16///
17/// That last part was a bug in the fork until this crate was written. The
18/// contents were on the node all along (`NodeData::Comment { contents }`), and
19/// `clone_node` copied them, but the accessor returned `""` and the mutator
20/// dropped writes on the floor, so a script could clone a comment and read
21/// back text the original refused to report. Fixed across all three sites in
22/// one change, `blitz-script` included, so reparenting is still
23/// behaviour-preserving. See MAPPING.md.
24pub fn data(doc: &BaseDocument, node: NodeId) -> Result<Option<String>> {
25    Ok(match doc.get_node(node).map(|node| &node.data) {
26        Some(NodeData::Text(data)) => Some(data.content.clone()),
27        Some(NodeData::Comment { contents }) => Some(contents.clone()),
28        _ => None,
29    })
30}
31
32/// `characterData.data = text`.
33///
34/// Applies to any node the mutator will set text on: text nodes and comments.
35/// Upstream does not restrict it to character data either, and on anything
36/// else it is a no-op rather than an error.
37///
38/// A comment write is inert by design. A comment generates no layout box, so
39/// this schedules no damage and no relayout, which is asserted in
40/// `blitz-dom`'s own `setting_a_comments_data_does_not_dirty_layout`.
41pub fn set_data(doc: &mut BaseDocument, node: NodeId, text: &str) -> Result<()> {
42    doc.mutate().set_node_text(node, text);
43    Ok(())
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::document;
50    use crate::test_support::skeleton;
51
52    #[test]
53    fn data_reads_a_text_node() {
54        let (mut doc, _html, _head, _body) = skeleton();
55        let text = document::create_text_node(&mut doc, "hello").unwrap();
56        assert_eq!(data(&doc, text).unwrap(), Some("hello".to_string()));
57    }
58
59    #[test]
60    fn data_is_null_for_an_element() {
61        let (doc, _html, _head, body) = skeleton();
62        assert_eq!(data(&doc, body).unwrap(), None);
63    }
64
65    #[test]
66    fn data_reads_a_comments_contents() {
67        let (mut doc, _html, _head, _body) = skeleton();
68        let comment = document::create_comment(&mut doc, "note").unwrap();
69        assert_eq!(data(&doc, comment).unwrap(), Some("note".to_string()));
70    }
71
72    #[test]
73    fn set_data_rewrites_the_text_in_place() {
74        let (mut doc, _html, _head, _body) = skeleton();
75        let text = document::create_text_node(&mut doc, "before").unwrap();
76        set_data(&mut doc, text, "after").unwrap();
77        assert_eq!(data(&doc, text).unwrap(), Some("after".to_string()));
78    }
79
80    /// The round trip that would have caught the original disagreement: read,
81    /// write, read back, then clone and read the clone. `clone_node` copied
82    /// comment contents from the start, so the clone leg is what made the
83    /// getter's `""` provably wrong rather than merely undocumented.
84    #[test]
85    fn a_comments_data_round_trips_through_a_write_and_a_clone() {
86        let (mut doc, _html, _head, _body) = skeleton();
87        let comment = document::create_comment(&mut doc, "first").unwrap();
88        assert_eq!(data(&doc, comment).unwrap(), Some("first".to_string()));
89
90        set_data(&mut doc, comment, "second").unwrap();
91        assert_eq!(data(&doc, comment).unwrap(), Some("second".to_string()));
92
93        let copy = crate::node::clone_node(&mut doc, comment, false).unwrap();
94        assert_ne!(copy, comment);
95        assert_eq!(
96            data(&doc, copy).unwrap(),
97            data(&doc, comment).unwrap(),
98            "a comment and its clone must report the same data"
99        );
100        assert_eq!(data(&doc, copy).unwrap(), Some("second".to_string()));
101    }
102}