script/dom/execcommand/commands/
indent.rs1use std::collections::HashSet;
6
7use js::context::JSContext;
8use script_bindings::codegen::GenericBindings::NodeBinding::NodeMethods;
9use script_bindings::inheritance::Castable;
10
11use crate::dom::bindings::root::DomRoot;
12use crate::dom::document::Document;
13use crate::dom::element::Element;
14use crate::dom::execcommand::contenteditable::node::{
15 NodeOrString, is_allowed_child, wrap_node_list,
16};
17use crate::dom::html::htmllielement::HTMLLIElement;
18use crate::dom::html::htmlolistelement::HTMLOListElement;
19use crate::dom::html::htmlulistelement::HTMLUListElement;
20use crate::dom::node::Node;
21use crate::dom::selection::Selection;
22
23pub(crate) fn indent(cx: &mut JSContext, context_object: &Document, node_list: Vec<DomRoot<Node>>) {
25 if node_list.is_empty() {
27 return;
28 }
29
30 let first_node = node_list
32 .first()
33 .expect("Must have a first node by now.")
34 .clone();
35
36 if let Some(parent) = first_node.GetParentElement() &&
38 (parent.is::<HTMLOListElement>() || parent.is::<HTMLUListElement>())
39 {
40 let tag = parent.local_name();
42
43 wrap_node_list(
47 cx,
48 node_list,
49 |sibling| {
50 sibling
51 .downcast::<Element>()
52 .is_some_and(|sibling| sibling.local_name() == tag)
53 },
54 |cx| {
55 Some(DomRoot::upcast(
56 first_node.owner_doc().create_element(cx, tag),
57 ))
58 },
59 );
60
61 return;
63 }
64
65 let new_parent = wrap_node_list(
70 cx,
71 node_list,
72 |sibling| {
73 sibling
74 .downcast::<Element>()
75 .is_some_and(|sibling| sibling.is_simple_indentation_element())
76 },
77 |cx| {
78 Some(DomRoot::upcast(
79 first_node.owner_doc().create_element(cx, "blockquote"),
80 ))
81 },
82 );
83
84 if let Some(new_parent) = new_parent {
86 new_parent.fix_disallowed_ancestors(cx, context_object);
87 }
88}
89
90pub(crate) fn execute_indent_command(
92 cx: &mut JSContext,
93 document: &Document,
94 selection: &Selection,
95) -> bool {
96 let mut active_range = selection
97 .active_range(cx)
98 .expect("Must always have an active range.");
99 let items: HashSet<DomRoot<Node>> = active_range
101 .start_container()
102 .ancestors()
103 .chain(active_range.end_container().ancestors())
104 .filter(|ancestor| ancestor.is::<HTMLLIElement>())
105 .collect();
106
107 for item in items {
109 item.normalize_sublists(cx);
110 }
111
112 active_range = selection
114 .active_range(cx)
115 .expect("Must always have an active range.");
116
117 let new_range = active_range.block_extend(cx, document);
119
120 let mut node_list: Vec<DomRoot<Node>> = vec![];
122
123 for node in new_range.contained_nodes(cx.no_gc()) {
128 if node.is_editable() &&
129 (is_allowed_child(
130 NodeOrString::Node(node.clone()),
131 NodeOrString::String("div".to_owned()),
132 ) || is_allowed_child(
133 NodeOrString::Node(node.clone()),
134 NodeOrString::String("ol".to_owned()),
135 )) &&
136 node_list
137 .last()
138 .is_none_or(|last| !last.is_ancestor_of(&node))
139 {
140 node_list.push(node.as_rooted());
141 }
142 }
143
144 if let Some(first_visible_member) = node_list.iter().find(|node| node.is_visible(cx.no_gc())) &&
146 first_visible_member.is::<HTMLLIElement>() &&
147 let Some(parent) = first_visible_member.GetParentNode() &&
148 (parent.is::<HTMLOListElement>() || parent.is::<HTMLUListElement>())
149 {
150 let mut sibling = first_visible_member.GetPreviousSibling();
152
153 while let Some(ref some_sibling) = sibling &&
155 some_sibling.is_invisible(cx.no_gc())
156 {
157 sibling = some_sibling.GetPreviousSibling();
158 }
159
160 if let Some(sibling) = sibling &&
162 sibling.is::<HTMLLIElement>()
163 {
164 sibling.normalize_sublists(cx);
165 }
166 }
167
168 let mut node_list_iter = node_list.iter().peekable();
170 while node_list_iter.peek().is_some() {
171 let mut sublist: Vec<DomRoot<Node>> = vec![];
173
174 sublist.push(
176 node_list_iter
177 .next()
178 .expect("Must always have a next item")
179 .clone(),
180 );
181
182 while node_list_iter.peek().is_some_and(|node| {
185 sublist
186 .last()
187 .expect("Must always have last element here.")
188 .GetNextSibling()
189 .is_some_and(|next_sibling| &&next_sibling == node)
190 }) {
191 sublist.push(
192 node_list_iter
193 .next()
194 .expect("Must always have a next item")
195 .clone(),
196 );
197 }
198
199 indent(cx, document, sublist);
201 }
202
203 true
206}