script/dom/html/
htmlfontelement.rs1use cssparser::match_ignore_ascii_case;
6use dom_struct::dom_struct;
7use html5ever::{LocalName, Prefix, local_name, ns};
8use js::rust::HandleObject;
9use style::attr::AttrValue;
10use style::color::AbsoluteColor;
11use style::str::{HTML_SPACE_CHARACTERS, read_numbers};
12use style::values::computed::font::{
13 FamilyName, FontFamilyNameSyntax, GenericFontFamily, SingleFontFamily,
14};
15use stylo_atoms::Atom;
16
17use crate::dom::attr::Attr;
18use crate::dom::bindings::codegen::Bindings::HTMLFontElementBinding::HTMLFontElementMethods;
19use crate::dom::bindings::inheritance::Castable;
20use crate::dom::bindings::root::{DomRoot, LayoutDom};
21use crate::dom::bindings::str::DOMString;
22use crate::dom::document::Document;
23use crate::dom::element::{Element, LayoutElementHelpers};
24use crate::dom::html::htmlelement::HTMLElement;
25use crate::dom::node::Node;
26use crate::dom::virtualmethods::VirtualMethods;
27use crate::script_runtime::CanGc;
28
29#[dom_struct]
30pub(crate) struct HTMLFontElement {
31 htmlelement: HTMLElement,
32}
33
34impl HTMLFontElement {
35 fn new_inherited(
36 local_name: LocalName,
37 prefix: Option<Prefix>,
38 document: &Document,
39 ) -> HTMLFontElement {
40 HTMLFontElement {
41 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
42 }
43 }
44
45 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
46 pub(crate) fn new(
47 local_name: LocalName,
48 prefix: Option<Prefix>,
49 document: &Document,
50 proto: Option<HandleObject>,
51 can_gc: CanGc,
52 ) -> DomRoot<HTMLFontElement> {
53 Node::reflect_node_with_proto(
54 Box::new(HTMLFontElement::new_inherited(local_name, prefix, document)),
55 document,
56 proto,
57 can_gc,
58 )
59 }
60
61 pub(crate) fn parse_face_attribute(face_value: Atom) -> Vec<SingleFontFamily> {
62 face_value
63 .split(',')
64 .map(|string| Self::parse_single_face_value_from_string(string.trim()))
65 .collect()
66 }
67
68 fn parse_single_face_value_from_string(string: &str) -> SingleFontFamily {
69 match_ignore_ascii_case! { string,
70 "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif),
71 "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
72 "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
73 "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
74 "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
75 "system-ui" => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
76 _ => {}
77 }
78
79 let name = string.to_owned().replace(['\'', '"'], "");
80 let syntax = if name == string {
81 FontFamilyNameSyntax::Identifiers
82 } else {
83 FontFamilyNameSyntax::Quoted
84 };
85
86 SingleFontFamily::FamilyName(FamilyName {
87 name: name.into(),
88 syntax,
89 })
90 }
91}
92
93impl HTMLFontElementMethods<crate::DomTypeHolder> for HTMLFontElement {
94 make_getter!(Color, "color");
96
97 make_legacy_color_setter!(SetColor, "color");
99
100 make_getter!(Face, "face");
102
103 make_atomic_setter!(SetFace, "face");
105
106 make_getter!(Size, "size");
108
109 fn SetSize(&self, value: DOMString, can_gc: CanGc) {
111 let element = self.upcast::<Element>();
112 element.set_attribute(&local_name!("size"), parse_size(&value), can_gc);
113 }
114}
115
116impl VirtualMethods for HTMLFontElement {
117 fn super_type(&self) -> Option<&dyn VirtualMethods> {
118 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
119 }
120
121 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
122 if attr.local_name() == &local_name!("color") ||
123 attr.local_name() == &local_name!("size") ||
124 attr.local_name() == &local_name!("face")
125 {
126 return true;
127 }
128
129 self.super_type()
130 .unwrap()
131 .attribute_affects_presentational_hints(attr)
132 }
133
134 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
135 match *name {
136 local_name!("face") => AttrValue::from_atomic(value.into()),
137 local_name!("color") => AttrValue::from_legacy_color(value.into()),
138 local_name!("size") => parse_size(&value),
139 _ => self
140 .super_type()
141 .unwrap()
142 .parse_plain_attribute(name, value),
143 }
144 }
145}
146
147pub(crate) trait HTMLFontElementLayoutHelpers {
148 fn get_color(self) -> Option<AbsoluteColor>;
149 fn get_face(self) -> Option<Atom>;
150 fn get_size(self) -> Option<u32>;
151}
152
153impl HTMLFontElementLayoutHelpers for LayoutDom<'_, HTMLFontElement> {
154 fn get_color(self) -> Option<AbsoluteColor> {
155 self.upcast::<Element>()
156 .get_attr_for_layout(&ns!(), &local_name!("color"))
157 .and_then(AttrValue::as_color)
158 .cloned()
159 }
160
161 fn get_face(self) -> Option<Atom> {
162 self.upcast::<Element>()
163 .get_attr_for_layout(&ns!(), &local_name!("face"))
164 .map(AttrValue::as_atom)
165 .cloned()
166 }
167
168 fn get_size(self) -> Option<u32> {
169 let size = self
170 .upcast::<Element>()
171 .get_attr_for_layout(&ns!(), &local_name!("size"));
172 match size {
173 Some(&AttrValue::UInt(_, s)) => Some(s),
174 _ => None,
175 }
176 }
177}
178
179fn parse_size(mut input: &str) -> AttrValue {
181 let original_input = input;
182 input = input.trim_matches(HTML_SPACE_CHARACTERS);
186
187 enum ParseMode {
188 RelativePlus,
189 RelativeMinus,
190 Absolute,
191 }
192 let mut input_chars = input.chars().peekable();
193 let parse_mode = match input_chars.peek() {
194 None => return AttrValue::String(original_input.into()),
196
197 Some(&'+') => {
199 let _ = input_chars.next(); ParseMode::RelativePlus
201 },
202 Some(&'-') => {
203 let _ = input_chars.next(); ParseMode::RelativeMinus
205 },
206 Some(_) => ParseMode::Absolute,
207 };
208
209 let mut value = match read_numbers(input_chars) {
211 (Some(v), _) if v >= 0 => v,
212 _ => return AttrValue::String(original_input.into()),
213 };
214
215 match parse_mode {
217 ParseMode::RelativePlus => value += 3,
218 ParseMode::RelativeMinus => value = 3 - value,
219 ParseMode::Absolute => (),
220 }
221
222 AttrValue::UInt(original_input.into(), value as u32)
224}