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