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::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    // https://html.spec.whatwg.org/multipage/#dom-font-color
95    make_getter!(Color, "color");
96
97    // https://html.spec.whatwg.org/multipage/#dom-font-color
98    make_legacy_color_setter!(SetColor, "color");
99
100    // https://html.spec.whatwg.org/multipage/#dom-font-face
101    make_getter!(Face, "face");
102
103    // https://html.spec.whatwg.org/multipage/#dom-font-face
104    make_atomic_setter!(SetFace, "face");
105
106    // https://html.spec.whatwg.org/multipage/#dom-font-size
107    make_getter!(Size, "size");
108
109    // https://html.spec.whatwg.org/multipage/#dom-font-size
110    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
179/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
180fn parse_size(mut input: &str) -> AttrValue {
181    let original_input = input;
182    // Steps 1 & 2 are not relevant
183
184    // Step 3
185    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.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.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    // Steps 10, 11, 12
223    AttrValue::UInt(original_input.into(), value as u32)
224}