script/dom/html/
htmlfontelement.rs1use cssparser::match_ignore_ascii_case;
6use dom_struct::dom_struct;
7use html5ever::{LocalName, Prefix, local_name, ns};
8use js::context::JSContext;
9use js::rust::HandleObject;
10use style::attr::AttrValue;
11use style::color::AbsoluteColor;
12use style::str::{HTML_SPACE_CHARACTERS, read_numbers};
13use style::values::computed::font::{
14 FamilyName, FontFamilyNameSyntax, GenericFontFamily, SingleFontFamily,
15};
16use stylo_atoms::Atom;
17
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;
24use crate::dom::element::attributes::storage::AttrRef;
25use crate::dom::html::htmlelement::HTMLElement;
26use crate::dom::node::Node;
27use crate::dom::virtualmethods::VirtualMethods;
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 cx: &mut JSContext,
47 local_name: LocalName,
48 prefix: Option<Prefix>,
49 document: &Document,
50 proto: Option<HandleObject>,
51 ) -> DomRoot<HTMLFontElement> {
52 Node::reflect_node_with_proto(
53 cx,
54 Box::new(HTMLFontElement::new_inherited(local_name, prefix, document)),
55 document,
56 proto,
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!(cx, SetFace, "face");
104
105 make_getter!(Size, "size");
107
108 fn SetSize(&self, cx: &mut JSContext, value: DOMString) {
110 let element = self.upcast::<Element>();
111 element.set_attribute(cx, &local_name!("size"), parse_size(&value));
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: AttrRef<'_>) -> 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
146impl LayoutDom<'_, HTMLFontElement> {
147 pub(crate) fn get_color(self) -> Option<AbsoluteColor> {
148 let color = self
149 .upcast::<Element>()
150 .get_attr_for_layout(&ns!(), &local_name!("color"));
151 match color {
152 Some(AttrValue::Color(_, color)) => *color,
153 _ => None,
154 }
155 }
156
157 pub(crate) fn get_face(self) -> Option<Atom> {
158 let face = self
159 .upcast::<Element>()
160 .get_attr_for_layout(&ns!(), &local_name!("face"));
161 match face {
162 Some(AttrValue::Atom(s)) => Some(s.clone()),
163 _ => None,
164 }
165 }
166
167 pub(crate) 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 value = value.clamp(1, 7);
225
226 AttrValue::UInt(String::from(original_input.str()).into(), value as u32)
228}