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 pub(crate) fn new(
46 local_name: LocalName,
47 prefix: Option<Prefix>,
48 document: &Document,
49 proto: Option<HandleObject>,
50 can_gc: CanGc,
51 ) -> DomRoot<HTMLFontElement> {
52 Node::reflect_node_with_proto(
53 Box::new(HTMLFontElement::new_inherited(local_name, prefix, document)),
54 document,
55 proto,
56 can_gc,
57 )
58 }
59
60 pub(crate) fn parse_face_attribute(face_value: Atom) -> Vec<SingleFontFamily> {
61 face_value
62 .split(',')
63 .map(|string| Self::parse_single_face_value_from_string(string.trim()))
64 .collect()
65 }
66
67 fn parse_single_face_value_from_string(string: &str) -> SingleFontFamily {
68 match_ignore_ascii_case! { string,
69 "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif),
70 "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
71 "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
72 "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
73 "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
74 "system-ui" => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
75 _ => {}
76 }
77
78 let name = string.to_owned().replace(['\'', '"'], "");
79 let syntax = if name == string {
80 FontFamilyNameSyntax::Identifiers
81 } else {
82 FontFamilyNameSyntax::Quoted
83 };
84
85 SingleFontFamily::FamilyName(FamilyName {
86 name: name.into(),
87 syntax,
88 })
89 }
90}
91
92impl HTMLFontElementMethods<crate::DomTypeHolder> for HTMLFontElement {
93 make_getter!(Color, "color");
95
96 make_legacy_color_setter!(SetColor, "color");
98
99 make_getter!(Face, "face");
101
102 make_atomic_setter!(SetFace, "face");
104
105 make_getter!(Size, "size");
107
108 fn SetSize(&self, value: DOMString, can_gc: CanGc) {
110 let element = self.upcast::<Element>();
111 element.set_attribute(&local_name!("size"), parse_size(&value), can_gc);
112 }
113}
114
115impl VirtualMethods for HTMLFontElement {
116 fn super_type(&self) -> Option<&dyn VirtualMethods> {
117 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
118 }
119
120 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
121 if attr.local_name() == &local_name!("color") ||
122 attr.local_name() == &local_name!("size") ||
123 attr.local_name() == &local_name!("face")
124 {
125 return true;
126 }
127
128 self.super_type()
129 .unwrap()
130 .attribute_affects_presentational_hints(attr)
131 }
132
133 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
134 match *name {
135 local_name!("face") => AttrValue::from_atomic(value.into()),
136 local_name!("color") => AttrValue::from_legacy_color(value.into()),
137 local_name!("size") => parse_size(&value),
138 _ => self
139 .super_type()
140 .unwrap()
141 .parse_plain_attribute(name, value),
142 }
143 }
144}
145
146pub(crate) trait HTMLFontElementLayoutHelpers {
147 fn get_color(self) -> Option<AbsoluteColor>;
148 fn get_face(self) -> Option<Atom>;
149 fn get_size(self) -> Option<u32>;
150}
151
152impl HTMLFontElementLayoutHelpers for LayoutDom<'_, HTMLFontElement> {
153 fn get_color(self) -> Option<AbsoluteColor> {
154 self.upcast::<Element>()
155 .get_attr_for_layout(&ns!(), &local_name!("color"))
156 .and_then(AttrValue::as_color)
157 .cloned()
158 }
159
160 fn get_face(self) -> Option<Atom> {
161 self.upcast::<Element>()
162 .get_attr_for_layout(&ns!(), &local_name!("face"))
163 .map(AttrValue::as_atom)
164 .cloned()
165 }
166
167 fn get_size(self) -> Option<u32> {
168 let size = self
169 .upcast::<Element>()
170 .get_attr_for_layout(&ns!(), &local_name!("size"));
171 match size {
172 Some(&AttrValue::UInt(_, s)) => Some(s),
173 _ => None,
174 }
175 }
176}
177
178fn parse_size(input: &DOMString) -> AttrValue {
180 let original_input = input;
181 let input = input.str();
185 let 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.str().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.str().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.str().into(), value as u32)
224}