serval_scripted_dom/
forms.rs1use layout_dom_api::NodeKind;
9
10use crate::{NodeId, ScriptedDom};
11
12impl ScriptedDom {
13 pub fn form_values(&self, root: NodeId) -> Vec<(String, String)> {
18 let mut out = Vec::new();
19 self.collect_form_values(root, &mut out);
20 out
21 }
22
23 fn collect_form_values(&self, id: NodeId, out: &mut Vec<(String, String)>) {
24 let node = self.node(id);
25 if node.kind == NodeKind::Element {
26 if let Some(name) = &node.name {
27 let tag = name.local.as_ref();
28 if matches!(tag, "input" | "select" | "textarea") {
29 let attr = |local: &str| {
30 node.attrs
31 .iter()
32 .find(|(n, _)| n.local.as_ref() == local)
33 .map(|(_, v)| v.as_str())
34 };
35 if let Some(key) = attr("name").or_else(|| attr("id")) {
36 let value = if tag == "textarea" {
37 self.text_content(id)
38 } else {
39 attr("value").unwrap_or("").to_owned()
40 };
41 out.push((key.to_owned(), value));
42 }
43 }
44 }
45 }
46 for &child in &node.children {
47 self.collect_form_values(child, out);
48 }
49 }
50
51 fn text_content(&self, id: NodeId) -> String {
53 let mut out = String::new();
54 self.append_text_content(id, &mut out);
55 out
56 }
57
58 fn append_text_content(&self, id: NodeId, out: &mut String) {
59 let node = self.node(id);
60 if node.kind == NodeKind::Text {
61 if let Some(text) = &node.text {
62 out.push_str(text);
63 }
64 }
65 for &child in &node.children {
66 self.append_text_content(child, out);
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use html5ever::{local_name, ns};
75 use layout_dom_api::{LayoutDomMut, QualName};
76
77 fn tag(local: markup5ever::LocalName) -> QualName {
78 QualName::new(None, ns!(html), local)
79 }
80 fn attr(local: markup5ever::LocalName) -> QualName {
81 QualName::new(None, ns!(), local)
82 }
83
84 #[test]
85 fn collects_input_value_keyed_by_name() {
86 let mut dom = ScriptedDom::new();
87 let form = dom.create_element(tag(local_name!("form")));
88 let input = dom.create_element(tag(local_name!("input")));
89 dom.set_attribute(input, attr(local_name!("name")), "email");
90 dom.set_attribute(input, attr(local_name!("value")), "a@b.com");
91 dom.append_child(form, input);
92 assert_eq!(
93 dom.form_values(form),
94 vec![("email".to_owned(), "a@b.com".to_owned())]
95 );
96 }
97
98 #[test]
99 fn textarea_value_is_its_text_content() {
100 let mut dom = ScriptedDom::new();
101 let ta = dom.create_element(tag(local_name!("textarea")));
102 dom.set_attribute(ta, attr(local_name!("id")), "note");
103 let text = dom.create_text("hello");
104 dom.append_child(ta, text);
105 assert_eq!(
106 dom.form_values(ta),
107 vec![("note".to_owned(), "hello".to_owned())]
108 );
109 }
110
111 #[test]
112 fn unnamed_controls_are_skipped() {
113 let mut dom = ScriptedDom::new();
114 let input = dom.create_element(tag(local_name!("input")));
115 dom.set_attribute(input, attr(local_name!("value")), "x");
116 assert!(dom.form_values(input).is_empty());
117 }
118}