script/dom/execcommand/commands/
insertlinebreak.rs1use js::context::JSContext;
6use script_bindings::codegen::GenericBindings::NodeBinding::NodeMethods;
7use script_bindings::codegen::GenericBindings::RangeBinding::RangeMethods;
8use script_bindings::codegen::GenericBindings::SelectionBinding::SelectionMethods;
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::{NodeOrString, is_allowed_child};
15use crate::dom::execcommand::contenteditable::selection::SelectionDeletionStripWrappers;
16use crate::dom::selection::Selection;
17use crate::dom::text::Text;
18
19pub(crate) fn execute_insert_line_break_command(
21 cx: &mut JSContext,
22 document: &Document,
23 selection: &Selection,
24) -> bool {
25 selection.delete_the_selection(
27 cx,
28 document,
29 Default::default(),
30 SelectionDeletionStripWrappers::NoStrip,
31 Default::default(),
32 );
33
34 let mut active_range = selection
37 .active_range(cx)
38 .expect("Must always have an active range.");
39 if !active_range.start_container().is_editable_or_editing_host() {
40 return true;
41 }
42
43 if active_range.start_container().is::<Element>() &&
46 !is_allowed_child(
47 NodeOrString::String("br".to_owned()),
48 NodeOrString::from_node(&active_range.start_container(), cx.no_gc()),
49 )
50 {
51 return false;
52 }
53
54 if !active_range.start_container().is::<Element>() &&
57 !is_allowed_child(
58 NodeOrString::String("br".to_owned()),
59 NodeOrString::from_node(
60 &active_range
61 .start_container()
62 .GetParentNode()
63 .expect("Must always have a parent."),
64 cx.no_gc(),
65 ),
66 )
67 {
68 return false;
69 }
70
71 if active_range.start_container().is::<Text>() && active_range.start_offset() == 0 {
76 if selection
77 .Collapse(
78 cx,
79 active_range.start_container().GetParentNode().as_deref(),
80 active_range.start_container().index(),
81 )
82 .is_err()
83 {
84 unreachable!("Should always be able to collapse the selection.");
85 }
86 active_range = selection
87 .active_range(cx)
88 .expect("Must always have an active range");
89 }
90
91 if active_range.start_container().is::<Text>() &&
96 active_range.start_offset() == active_range.start_container().len()
97 {
98 if selection
99 .Collapse(
100 cx,
101 active_range.start_container().GetParentNode().as_deref(),
102 1 + active_range.start_container().index(),
103 )
104 .is_err()
105 {
106 unreachable!("Should always be able to collapse the selection.");
107 }
108 active_range = selection
109 .active_range(cx)
110 .expect("Must always have an active range");
111 }
112
113 let br = document.create_element(cx, "br");
115
116 let br_node = DomRoot::upcast(br);
118 if active_range.InsertNode(cx, &br_node).is_err() {
119 unreachable!("The node should always be insertable.");
120 }
121
122 if selection
125 .Collapse(cx, br_node.GetParentNode().as_deref(), 1 + br_node.index())
126 .is_err()
127 {
128 unreachable!("Should always be able to collapse the selection.");
129 }
130
131 true
137}