script/dom/execcommand/commands/
insertparagraph.rs1use html5ever::local_name;
6use js::context::JSContext;
7use script_bindings::inheritance::Castable;
8
9use crate::dom::Node;
10use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
11use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
12use crate::dom::bindings::codegen::Bindings::RangeBinding::RangeMethods;
13use crate::dom::bindings::codegen::Bindings::TextBinding::TextMethods;
14use crate::dom::bindings::root::DomRoot;
15use crate::dom::comment::Comment;
16use crate::dom::document::Document;
17use crate::dom::element::Element;
18use crate::dom::execcommand::contenteditable::node::{
19 NodeOrString, is_allowed_child, node_matches_local_name, split_the_parent, wrap_node_list,
20};
21use crate::dom::html::htmlbrelement::HTMLBRElement;
22use crate::dom::iterators::ShadowIncluding;
23use crate::dom::selection::Selection;
24use crate::dom::text::Text;
25
26pub(crate) fn execute_insert_paragraph_command(
28 cx: &mut JSContext,
29 document: &Document,
30 selection: &Selection,
31) -> bool {
32 selection.delete_the_selection(
34 cx,
35 document,
36 Default::default(),
37 Default::default(),
38 Default::default(),
39 );
40 let active_range = selection
42 .active_range()
43 .expect("Must always have an active range");
44 let mut node = active_range.start_container();
45 let mut offset = active_range.start_offset();
46 if !node.is_editable_or_editing_host() {
49 return true;
50 }
51 if offset != 0 &&
54 offset != node.len() &&
55 let Some(text_node) = node.downcast::<Text>() &&
56 text_node.SplitText(cx, offset).is_err()
57 {
58 unreachable!("Must always be able to split");
59 }
60 if node.is::<Text>() && offset == node.len() {
63 offset = 1 + node.index();
64 node = node.GetParentNode().expect("Must always have a parent");
65 }
66 if node.is::<Text>() || node.is::<Comment>() {
69 offset = node.index();
70 node = node.GetParentNode().expect("Must always have a parent");
71 }
72 selection.collapse_current_range(&node, offset);
74 let mut container = node.clone();
76 while !container.is_single_line_container() &&
80 let Some(parent) = container.GetParentNode() &&
81 parent.is_editable() &&
82 parent.same_editing_host(&node)
83 {
84 container = parent;
85 }
86 if container.is_editable() &&
89 container.is_single_line_container() &&
90 container.same_editing_host(&node) &&
91 node_matches_local_name!(container, local_name!("p") | local_name!("div"))
92 {
93 let mut outer_container = container.clone();
95 while !node_matches_local_name!(
98 outer_container,
99 local_name!("dd") | local_name!("dt") | local_name!("li")
100 ) && let Some(parent) = outer_container.GetParentNode() &&
101 parent.is_editable()
102 {
103 outer_container = parent;
104 }
105 if node_matches_local_name!(
107 outer_container,
108 local_name!("dd") | local_name!("dt") | local_name!("li")
109 ) {
110 container = outer_container;
111 }
112 }
113 if !container.is_editable() ||
115 !container.same_editing_host(&node) ||
116 !container.is_single_line_container()
117 {
118 let tag = document.default_single_line_container_name();
120 let new_range = active_range.block_extend(cx, document);
122 let mut node_list = if let Some(eligible_node) = new_range
124 .contained_children()
125 .ok()
126 .and_then(|contained_children| {
127 contained_children
128 .contained_children
129 .into_iter()
130 .find(|node| {
131 is_allowed_child(
132 NodeOrString::Node(node.clone()),
133 NodeOrString::String("p".to_owned()),
134 )
135 })
136 }) {
137 vec![eligible_node]
138 } else {
139 if !is_allowed_child(
143 NodeOrString::String(tag.str().to_owned()),
144 NodeOrString::Node(active_range.start_container()),
145 ) {
146 return true;
147 }
148 let container = document.create_element(cx, tag.str());
150 let container = container.upcast::<Node>();
151 if active_range.InsertNode(cx, container).is_err() {
153 unreachable!("Must always be able to insert");
154 }
155 let br = document.create_element(cx, "br");
158 if container.AppendChild(cx, br.upcast()).is_err() {
159 unreachable!("Must always be able to append");
160 }
161 selection.collapse_current_range(container, 0);
163 return true;
165 };
166 while let Some(next_of_last) = node_list
169 .iter()
170 .last()
171 .and_then(|node| node.GetNextSibling())
172 .filter(|next_of_last| {
173 is_allowed_child(
174 NodeOrString::Node(DomRoot::from_ref(next_of_last)),
175 NodeOrString::String("p".to_owned()),
176 )
177 })
178 {
179 node_list.push(next_of_last);
180 }
181 container = wrap_node_list(
185 cx,
186 node_list,
187 |_| false,
188 |cx| Some(DomRoot::upcast(document.create_element(cx, tag.str()))),
189 )
190 .expect("Must always be able to wrap");
191 }
192 if node_matches_local_name!(
194 container,
195 local_name!("address") | local_name!("listing") | local_name!("pre")
196 ) {
197 let br = document.create_element(cx, "br");
199 if active_range.InsertNode(cx, br.upcast()).is_err() {
201 unreachable!("Must always be able to insert");
202 }
203 selection.collapse_current_range(&node, offset + 1);
205 if container
209 .children()
210 .last()
211 .is_some_and(|child| *child == *br.upcast())
212 {
213 let br = document.create_element(cx, "br");
214 if active_range.InsertNode(cx, br.upcast()).is_err() {
215 unreachable!("Must always be able to insert");
216 }
217 }
218 return true;
220 }
221 if node_matches_local_name!(
224 container,
225 local_name!("li") | local_name!("dt") | local_name!("dd")
226 ) && (container.children_count() == 0 ||
227 (container.children_count() == 1 &&
228 container
229 .children()
230 .next()
231 .expect("has one child")
232 .is::<HTMLBRElement>()))
233 {
234 split_the_parent(cx, &[&container]);
236 if container.children_count() == 0 {
239 let br = document.create_element(cx, "br");
240 if container.AppendChild(cx, br.upcast()).is_err() {
241 unreachable!("Must always be able to append");
242 }
243 }
244 if node_matches_local_name!(container, local_name!("dd") | local_name!("dt")) &&
248 container.is_no_allowed_child_in_same_editing_host()
249 {
250 container = container
251 .downcast::<Element>()
252 .expect("Must always be an element")
253 .set_the_tag_name(cx, document.default_single_line_container_name().str());
254 }
255 container.fix_disallowed_ancestors(cx, document);
257 return true;
259 }
260 let new_line_range = document.CreateRange(cx);
263 new_line_range.set_start(&active_range.start_container(), active_range.start_offset());
264 new_line_range.set_end(&container, container.len());
265 while new_line_range.start_offset() == 0 &&
269 !new_line_range
270 .start_container()
271 .is_prohibited_paragraph_child()
272 {
273 let start = new_line_range.start_container();
274 new_line_range.set_start(
275 &start.GetParentNode().expect("Must always have a parent"),
276 start.index(),
277 );
278 }
279 while new_line_range.start_offset() == new_line_range.start_container().len() &&
283 !new_line_range
284 .start_container()
285 .is_prohibited_paragraph_child()
286 {
287 let start = new_line_range.start_container();
288 new_line_range.set_start(
289 &start.GetParentNode().expect("Must always have a parent"),
290 1 + start.index(),
291 );
292 }
293 let end_of_line = new_line_range
295 .contained_children()
296 .is_ok_and(|contained_children| {
297 let contained_children = contained_children.contained_children;
298 contained_children.is_empty() ||
299 (contained_children.len() == 1 && contained_children[0].is::<HTMLBRElement>())
300 });
301 let container_as_element = container
304 .downcast::<Element>()
305 .expect("Must always be an element");
306 let container_name = container_as_element.local_name();
307 let new_container_name = if end_of_line &&
308 matches!(
309 *container_name,
310 local_name!("h1") |
311 local_name!("h2") |
312 local_name!("h3") |
313 local_name!("h4") |
314 local_name!("h5") |
315 local_name!("h6")
316 ) {
317 document
318 .default_single_line_container_name()
319 .str()
320 .to_owned()
321 } else
322 if end_of_line && container_name == &local_name!("dt") {
324 "dd".to_owned()
325 } else
326 if end_of_line && container_name == &local_name!("dd") {
328 "dt".to_owned()
329 } else {
330 container_name.to_string()
332 };
333 let new_container = document.create_element(cx, &new_container_name);
335 container_as_element.copy_all_attributes_to_other_element(cx, &new_container);
337 new_container.remove_attribute_by_name(cx, &local_name!("id"));
339 let new_container_node = DomRoot::upcast(new_container);
341 if container
342 .GetParentNode()
343 .expect("Must always have a parent")
344 .InsertBefore(
345 cx,
346 &new_container_node,
347 container.GetNextSibling().as_deref(),
348 )
349 .is_err()
350 {
351 unreachable!("Must always be able to insert");
352 }
353 let Ok(contained_nodes) = new_line_range.contained_children() else {
355 unreachable!("Must always have contained children");
356 };
357 let Ok(frag) = new_line_range.ExtractContents(cx) else {
359 unreachable!("Must always be able to extract");
360 };
361 let frag_as_node = frag.upcast::<Node>();
362 for descendant in frag_as_node.traverse_preorder(ShadowIncluding::No) {
365 if !contained_nodes.contained_children.contains(&descendant) &&
366 let Some(descendant) = descendant.downcast::<Element>()
367 {
368 descendant.remove_attribute_by_name(cx, &local_name!("id"));
369 }
370 }
371 if new_container_node.AppendChild(cx, frag_as_node).is_err() {
373 unreachable!("Must always be able to append");
374 }
375 loop {
378 let Some(last_child) = container.children().last() else {
379 break;
380 };
381 if !last_child.is_prohibited_paragraph_child() {
382 break;
383 }
384 container = last_child;
385 }
386 let mut new_container_node = new_container_node;
389 loop {
390 let Some(last_child) = new_container_node.children().last() else {
391 break;
392 };
393 if !last_child.is_prohibited_paragraph_child() {
394 break;
395 }
396 new_container_node = last_child;
397 }
398 if container.children().all(|child| child.is_invisible()) {
402 let br = document.create_element(cx, "br");
403 if container.AppendChild(cx, br.upcast()).is_err() {
404 unreachable!("Must always be able to append");
405 }
406 }
407 if new_container_node
411 .children()
412 .all(|child| child.is_invisible())
413 {
414 let br = document.create_element(cx, "br");
415 if new_container_node.AppendChild(cx, br.upcast()).is_err() {
416 unreachable!("Must always be able to append");
417 }
418 }
419 selection.collapse_current_range(&new_container_node, 0);
421 true
423}