1use script_bindings::inheritance::Castable;
6use style::properties::PropertyDeclarationId;
7use style::properties::generated::LonghandId;
8use style_traits::ToCss;
9
10use crate::dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
11use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
12use crate::dom::bindings::codegen::Bindings::HTMLFontElementBinding::HTMLFontElementMethods;
13use crate::dom::bindings::str::DOMString;
14use crate::dom::document::Document;
15use crate::dom::element::Element;
16use crate::dom::execcommand::commands::defaultparagraphseparator::execute_default_paragraph_separator_command;
17use crate::dom::execcommand::commands::delete::execute_delete_command;
18use crate::dom::execcommand::commands::fontsize::{
19 execute_fontsize_command, font_size_loosely_equivalent, value_for_fontsize_command,
20};
21use crate::dom::execcommand::commands::stylewithcss::execute_style_with_css_command;
22use crate::dom::html::htmlelement::HTMLElement;
23use crate::dom::html::htmlfontelement::HTMLFontElement;
24use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
25use crate::dom::selection::Selection;
26use crate::script_runtime::CanGc;
27
28#[derive(Default, Clone, Copy, MallocSizeOf)]
29pub(crate) enum DefaultSingleLineContainerName {
30 #[default]
31 Div,
32 Paragraph,
33}
34
35impl From<DefaultSingleLineContainerName> for DOMString {
36 fn from(default_single_line_container_name: DefaultSingleLineContainerName) -> Self {
37 match default_single_line_container_name {
38 DefaultSingleLineContainerName::Div => DOMString::from("div"),
39 DefaultSingleLineContainerName::Paragraph => DOMString::from("p"),
40 }
41 }
42}
43
44#[derive(Clone, Copy, Eq, PartialEq)]
46#[expect(unused)] pub(crate) enum CssPropertyName {
48 BackgroundColor,
49 FontSize,
50 FontWeight,
51 FontStyle,
52 TextDecorationLine,
53}
54
55impl CssPropertyName {
56 fn resolved_value_for_node(&self, element: &Element) -> Option<DOMString> {
57 let style = element.style()?;
58
59 Some(
60 match self {
61 CssPropertyName::BackgroundColor => style.clone_background_color().to_css_string(),
62 CssPropertyName::FontSize => {
63 return element
77 .upcast::<Node>()
78 .inclusive_ancestors(ShadowIncluding::No)
79 .find_map(|ancestor| {
80 if let Some(ancestor_font) = ancestor.downcast::<HTMLFontElement>() {
81 Some(ancestor_font.Size())
82 } else {
83 self.value_set_for_style(ancestor.downcast::<Element>()?)
84 }
85 })
86 .or_else(|| {
87 let pixels = style.get_font().font_size.computed_size().px();
88 Some(format!("{}px", pixels).into())
89 });
90 },
91 CssPropertyName::FontWeight => style.clone_font_weight().to_css_string(),
92 CssPropertyName::FontStyle => style.clone_font_style().to_css_string(),
93 CssPropertyName::TextDecorationLine => {
94 style.clone_text_decoration_line().to_css_string()
95 },
96 }
97 .into(),
98 )
99 }
100
101 pub(crate) fn value_set_for_style(&self, element: &Element) -> Option<DOMString> {
105 let style_attribute = element.style_attribute().borrow();
106 let declarations = style_attribute.as_ref()?;
107 let document = element.owner_document();
108 let shared_lock = document.style_shared_lock();
109 let read_lock = shared_lock.read();
110 let style = declarations.read_with(&read_lock);
111
112 let longhand_id = match self {
113 CssPropertyName::BackgroundColor => LonghandId::BackgroundColor,
114 CssPropertyName::FontSize => LonghandId::FontSize,
115 CssPropertyName::FontWeight => LonghandId::FontWeight,
116 CssPropertyName::FontStyle => LonghandId::FontStyle,
117 CssPropertyName::TextDecorationLine => LonghandId::TextDecorationLine,
118 };
119 style
120 .get(PropertyDeclarationId::Longhand(longhand_id))
121 .and_then(|value| {
122 let mut dest = String::new();
123 value.0.to_css(&mut dest).ok()?;
124 Some(dest.into())
125 })
126 }
127
128 fn property_name(&self) -> DOMString {
129 match self {
130 CssPropertyName::BackgroundColor => "background-color",
131 CssPropertyName::FontSize => "font-size",
132 CssPropertyName::FontWeight => "font-weight",
133 CssPropertyName::FontStyle => "font-style",
134 CssPropertyName::TextDecorationLine => "text-decoration-line",
135 }
136 .into()
137 }
138
139 pub(crate) fn set_for_element(
140 &self,
141 cx: &mut js::context::JSContext,
142 element: &HTMLElement,
143 new_value: DOMString,
144 ) {
145 let style = element.Style(CanGc::from_cx(cx));
146
147 let _ = style.SetProperty(cx, self.property_name(), new_value, "".into());
148 }
149
150 pub(crate) fn remove_from_element(
151 &self,
152 cx: &mut js::context::JSContext,
153 element: &HTMLElement,
154 ) {
155 let _ = element
156 .Style(CanGc::from_cx(cx))
157 .RemoveProperty(cx, self.property_name());
158 }
159
160 pub(crate) fn value_for_element(
161 &self,
162 cx: &mut js::context::JSContext,
163 element: &HTMLElement,
164 ) -> DOMString {
165 element
166 .Style(CanGc::from_cx(cx))
167 .GetPropertyValue(self.property_name())
168 }
169}
170
171#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
172#[expect(unused)] pub(crate) enum CommandName {
174 BackColor,
175 Bold,
176 Copy,
177 CreateLink,
178 Cut,
179 DefaultParagraphSeparator,
180 Delete,
181 FontName,
182 FontSize,
183 ForeColor,
184 FormatBlock,
185 ForwardDelete,
186 HiliteColor,
187 Indent,
188 InsertHorizontalRule,
189 InsertHtml,
190 InsertLineBreak,
191 InsertOrderedList,
192 InsertParagraph,
193 InsertText,
194 InsertUnorderedList,
195 Italic,
196 JustifyCenter,
197 JustifyFull,
198 JustifyLeft,
199 JustifyRight,
200 Outdent,
201 Paste,
202 Redo,
203 SelectAll,
204 Strikethrough,
205 StyleWithCss,
206 Subscript,
207 Superscript,
208 Underline,
209 Undo,
210 Unlink,
211 Usecss,
212}
213
214impl CommandName {
215 pub(crate) fn is_indeterminate(&self) -> bool {
217 false
218 }
219
220 pub(crate) fn current_state(&self, document: &Document) -> Option<bool> {
222 Some(match self {
223 CommandName::StyleWithCss => {
224 document.css_styling_flag()
227 },
228 _ => return None,
229 })
230 }
231
232 pub(crate) fn current_value(
234 &self,
235 cx: &mut js::context::JSContext,
236 document: &Document,
237 ) -> Option<DOMString> {
238 Some(match self {
239 CommandName::DefaultParagraphSeparator => {
240 document.default_single_line_container_name().into()
243 },
244 CommandName::FontSize => value_for_fontsize_command(cx, document)?,
245 _ => return None,
246 })
247 }
248
249 pub(crate) fn are_equivalent_values(
251 &self,
252 first: Option<&DOMString>,
253 second: Option<&DOMString>,
254 ) -> bool {
255 match (first, second) {
256 (None, None) => true,
258 (Some(first_str), Some(second_str)) => {
259 match self {
261 CommandName::Bold => {
262 first_str == second_str ||
266 matches!(
267 (first_str.str().as_ref(), second_str.str().as_ref()),
268 ("bold", "700") |
269 ("700", "bold") |
270 ("normal", "400") |
271 ("400", "normal")
272 )
273 },
274 _ => first_str == second_str,
276 }
277 },
278 _ => false,
279 }
280 }
281
282 pub(crate) fn are_loosely_equivalent_values(
284 &self,
285 first: Option<&DOMString>,
286 second: Option<&DOMString>,
287 ) -> bool {
288 if self.are_equivalent_values(first, second) {
290 return true;
291 }
292 if let (CommandName::FontSize, Some(first), Some(second)) = (self, first, second) {
297 font_size_loosely_equivalent(first, second)
298 } else {
299 false
300 }
301 }
302
303 pub(crate) fn relevant_css_property(&self) -> Option<CssPropertyName> {
305 Some(match self {
308 CommandName::FontSize => CssPropertyName::FontSize,
309 CommandName::Bold => CssPropertyName::FontWeight,
310 CommandName::Italic => CssPropertyName::FontStyle,
311 _ => return None,
313 })
314 }
315
316 pub(crate) fn resolved_value_for_node(&self, element: &Element) -> Option<DOMString> {
317 let property = self.relevant_css_property()?;
318 property.resolved_value_for_node(element)
319 }
320
321 pub(crate) fn is_enabled_in_plaintext_only_state(&self) -> bool {
322 matches!(
323 self,
324 CommandName::Copy |
325 CommandName::Cut |
326 CommandName::DefaultParagraphSeparator |
327 CommandName::FormatBlock |
328 CommandName::ForwardDelete |
329 CommandName::InsertHtml |
330 CommandName::InsertLineBreak |
331 CommandName::InsertParagraph |
332 CommandName::InsertText |
333 CommandName::Paste |
334 CommandName::Redo |
335 CommandName::StyleWithCss |
336 CommandName::Undo |
337 CommandName::Usecss |
338 CommandName::Delete
339 )
340 }
341
342 pub(crate) fn execute(
344 &self,
345 cx: &mut js::context::JSContext,
346 document: &Document,
347 selection: &Selection,
348 value: DOMString,
349 ) -> bool {
350 match self {
351 CommandName::DefaultParagraphSeparator => {
352 execute_default_paragraph_separator_command(document, value)
353 },
354 CommandName::Delete => execute_delete_command(cx, document, selection),
355 CommandName::FontSize => execute_fontsize_command(cx, document, selection, value),
356 CommandName::StyleWithCss => execute_style_with_css_command(document, value),
357 _ => false,
358 }
359 }
360}