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::attr::Attr;
19use crate::dom::bindings::codegen::Bindings::HTMLFontElementBinding::HTMLFontElementMethods;
20use crate::dom::bindings::inheritance::Castable;
21use crate::dom::bindings::root::{DomRoot, LayoutDom};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::document::Document;
24use crate::dom::element::Element;
25use crate::dom::html::htmlelement::HTMLElement;
26use crate::dom::node::Node;
27use crate::dom::virtualmethods::VirtualMethods;
28use crate::script_runtime::CanGc;
29
30#[dom_struct]
31pub(crate) struct HTMLFontElement {
32 htmlelement: HTMLElement,
33}
34
35impl HTMLFontElement {
36 fn new_inherited(
37 local_name: LocalName,
38 prefix: Option<Prefix>,
39 document: &Document,
40 ) -> HTMLFontElement {
41 HTMLFontElement {
42 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
43 }
44 }
45
46 pub(crate) fn new(
47 cx: &mut JSContext,
48 local_name: LocalName,
49 prefix: Option<Prefix>,
50 document: &Document,
51 proto: Option<HandleObject>,
52 ) -> DomRoot<HTMLFontElement> {
53 Node::reflect_node_with_proto(
54 cx,
55 Box::new(HTMLFontElement::new_inherited(local_name, prefix, document)),
56 document,
57 proto,
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!(cx, SetFace, "face");
105
106 make_getter!(Size, "size");
108
109 fn SetSize(&self, cx: &mut JSContext, value: DOMString) {
111 let element = self.upcast::<Element>();
112 element.set_attribute(&local_name!("size"), parse_size(&value), CanGc::from_cx(cx));
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
147impl LayoutDom<'_, HTMLFontElement> {
148 pub(crate) fn get_color(self) -> Option<AbsoluteColor> {
149 self.upcast::<Element>()
150 .get_attr_for_layout(&ns!(), &local_name!("color"))
151 .and_then(AttrValue::as_color)
152 .cloned()
153 }
154
155 pub(crate) fn get_face(self) -> Option<Atom> {
156 let face = self
157 .upcast::<Element>()
158 .get_attr_for_layout(&ns!(), &local_name!("face"));
159 match face {
160 Some(AttrValue::Atom(s)) => Some(s.clone()),
161 _ => None,
162 }
163 }
164
165 pub(crate) fn get_size(self) -> Option<u32> {
166 let size = self
167 .upcast::<Element>()
168 .get_attr_for_layout(&ns!(), &local_name!("size"));
169 match size {
170 Some(&AttrValue::UInt(_, s)) => Some(s),
171 _ => None,
172 }
173 }
174}
175
176fn parse_size(input: &DOMString) -> AttrValue {
178 let original_input = input;
179 let input = input.str();
183 let input = input.trim_matches(HTML_SPACE_CHARACTERS);
184
185 enum ParseMode {
186 RelativePlus,
187 RelativeMinus,
188 Absolute,
189 }
190 let mut input_chars = input.chars().peekable();
191 let parse_mode = match input_chars.peek() {
192 None => return AttrValue::String(original_input.str().into()),
194
195 Some(&'+') => {
197 let _ = input_chars.next(); ParseMode::RelativePlus
199 },
200 Some(&'-') => {
201 let _ = input_chars.next(); ParseMode::RelativeMinus
203 },
204 Some(_) => ParseMode::Absolute,
205 };
206
207 let mut value = match read_numbers(input_chars) {
209 (Some(v), _) if v >= 0 => v,
210 _ => return AttrValue::String(original_input.str().into()),
211 };
212
213 match parse_mode {
215 ParseMode::RelativePlus => value += 3,
216 ParseMode::RelativeMinus => value = 3 - value,
217 ParseMode::Absolute => (),
218 }
219
220 value = value.clamp(1, 7);
223
224 AttrValue::UInt(original_input.str().into(), value as u32)
226}