Skip to main content

script/dom/html/
htmlfontelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    // https://html.spec.whatwg.org/multipage/#dom-font-color
94    make_getter!(Color, "color");
95
96    // https://html.spec.whatwg.org/multipage/#dom-font-color
97    make_legacy_color_setter!(SetColor, "color");
98
99    // https://html.spec.whatwg.org/multipage/#dom-font-face
100    make_getter!(Face, "face");
101
102    // https://html.spec.whatwg.org/multipage/#dom-font-face
103    make_atomic_setter!(cx, SetFace, "face");
104
105    // https://html.spec.whatwg.org/multipage/#dom-font-size
106    make_getter!(Size, "size");
107
108    /// <https://html.spec.whatwg.org/multipage/#dom-font-size>
109    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
178/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
179fn parse_size(input: &DOMString) -> AttrValue {
180    let original_input = input;
181    // Steps 1 & 2 are not relevant
182
183    // Step 3
184    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        // Step 4
195        None => return AttrValue::String(original_input.str().into()),
196
197        // Step 5
198        Some(&'+') => {
199            let _ = input_chars.next(); // consume the '+'
200            ParseMode::RelativePlus
201        },
202        Some(&'-') => {
203            let _ = input_chars.next(); // consume the '-'
204            ParseMode::RelativeMinus
205        },
206        Some(_) => ParseMode::Absolute,
207    };
208
209    // Steps 6, 7, 8
210    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    // Step 9
216    match parse_mode {
217        ParseMode::RelativePlus => value += 3,
218        ParseMode::RelativeMinus => value = 3 - value,
219        ParseMode::Absolute => (),
220    }
221
222    // Step 10: If value is greater than 7, let it be 7.
223    // Step 11: If value is less than 1, let it be 1.
224    value = value.clamp(1, 7);
225
226    // Step 12
227    AttrValue::UInt(String::from(original_input.str()).into(), value as u32)
228}