script/dom/execcommand/commands/
removeformat.rs1use html5ever::local_name;
6use js::context::JSContext;
7use script_bindings::codegen::GenericBindings::RangeBinding::RangeMethods;
8use script_bindings::inheritance::Castable;
9
10use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
11use crate::dom::bindings::codegen::Bindings::TextBinding::TextMethods;
12use crate::dom::bindings::root::DomRoot;
13use crate::dom::document::Document;
14use crate::dom::element::Element;
15use crate::dom::execcommand::basecommand::CommandName;
16use crate::dom::execcommand::contenteditable::node::{move_preserving_ranges, split_the_parent};
17use crate::dom::html::htmlelement::HTMLElement;
18use crate::dom::selection::Selection;
19use crate::dom::text::Text;
20
21fn is_remove_format_candidate(element: &Element) -> bool {
23 matches!(
28 *element.local_name(),
29 local_name!("abbr") |
30 local_name!("acronym") |
31 local_name!("b") |
32 local_name!("bdi") |
33 local_name!("bdo") |
34 local_name!("big") |
35 local_name!("blink") |
36 local_name!("cite") |
37 local_name!("code") |
38 local_name!("dfn") |
39 local_name!("em") |
40 local_name!("font") |
41 local_name!("i") |
42 local_name!("ins") |
43 local_name!("kbd") |
44 local_name!("mark") |
45 local_name!("nobr") |
46 local_name!("q") |
47 local_name!("s") |
48 local_name!("samp") |
49 local_name!("small") |
50 local_name!("span") |
51 local_name!("strike") |
52 local_name!("strong") |
53 local_name!("sub") |
54 local_name!("sup") |
55 local_name!("tt") |
56 local_name!("u") |
57 local_name!("var")
58 )
59}
60
61pub(crate) fn execute_removeformat_command(
63 cx: &mut JSContext,
64 document: &Document,
65 selection: &Selection,
66) -> bool {
67 let active_range = selection
69 .active_range(cx)
70 .expect("Must always have an active range");
71 let mut elements = vec![];
72 active_range.for_each_effectively_contained_child(|node| {
73 if let Some(html_element) = node.downcast::<HTMLElement>() &&
74 is_remove_format_candidate(html_element.upcast())
75 {
76 elements.push(DomRoot::from_ref(node));
77 }
78 });
79 for element in elements {
81 let parent_element = element.GetParentNode().expect("Must always have a parent");
84 for child in element.children() {
85 move_preserving_ranges(cx, &child, |cx| {
86 parent_element.InsertBefore(cx, &child, Some(&element))
87 });
88 }
89 element.remove_self(cx);
91 }
92 let start_node = active_range.start_container();
98 let start_offset = active_range.start_offset();
99 if start_node.is_editable() &&
100 start_offset != 0 &&
101 start_offset != start_node.len() &&
102 let Some(start_text) = start_node.downcast::<Text>()
103 {
104 let Ok(start_text) = start_text.SplitText(cx, start_offset) else {
105 unreachable!("Must always be able to split");
106 };
107 let _ = active_range.SetStart(start_text.upcast(), 0);
108 }
109 let end_node = active_range.end_container();
114 let end_offset = active_range.end_offset();
115 if end_node.is_editable() &&
116 end_offset != 0 &&
117 end_offset != end_node.len() &&
118 let Some(end_text) = end_node.downcast::<Text>() &&
119 end_text.SplitText(cx, end_offset).is_err()
120 {
121 unreachable!("Must always be able to split");
122 };
123 let mut node_list = vec![];
125 active_range.for_each_effectively_contained_child(|node| {
126 if node.is_editable() {
127 node_list.push(DomRoot::from_ref(node));
128 }
129 });
130 for node in node_list {
134 while let Some(parent) = node.GetParentElement() {
135 if !is_remove_format_candidate(&parent) {
136 break;
137 }
138 if !node.same_editing_host(parent.upcast()) {
139 break;
140 }
141 split_the_parent(cx, &[&node]);
142 }
143 }
144 selection.set_the_selection_value(cx, None, CommandName::Subscript, document);
147 selection.set_the_selection_value(cx, None, CommandName::Bold, document);
148 selection.set_the_selection_value(cx, None, CommandName::FontName, document);
149 selection.set_the_selection_value(cx, None, CommandName::FontSize, document);
150 selection.set_the_selection_value(cx, None, CommandName::ForeColor, document);
151 selection.set_the_selection_value(cx, None, CommandName::HiliteColor, document);
152 selection.set_the_selection_value(cx, None, CommandName::Italic, document);
153 selection.set_the_selection_value(cx, None, CommandName::Strikethrough, document);
154 selection.set_the_selection_value(cx, None, CommandName::Underline, document);
155 true
157}