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 
5 <%namespace name="helpers" file="/helpers.mako.rs" />
6 <% from data import Method, to_camel_case, to_rust_ident, to_camel_case_lower, SYSTEM_FONT_LONGHANDS %>
7 
8 <% data.new_style_struct("Font", inherited=True) %>
9 
10 ${helpers.predefined_type(
11     "font-family",
12     "FontFamily",
13     engines="gecko servo-2013 servo-2020",
14     initial_value="computed::FontFamily::serif()",
15     animation_value_type="discrete",
16     spec="https://drafts.csswg.org/css-fonts/#propdef-font-family",
17     servo_restyle_damage="rebuild_and_reflow",
18 )}
19 
20 ${helpers.predefined_type(
21     "font-style",
22     "FontStyle",
23     engines="gecko servo-2013 servo-2020",
24     initial_value="computed::FontStyle::normal()",
25     initial_specified_value="specified::FontStyle::normal()",
26     animation_value_type="FontStyle",
27     spec="https://drafts.csswg.org/css-fonts/#propdef-font-style",
28     servo_restyle_damage="rebuild_and_reflow",
29 )}
30 
31 <% font_variant_caps_custom_consts= { "small-caps": "SMALLCAPS",
32                                       "all-small-caps": "ALLSMALL",
33                                       "petite-caps": "PETITECAPS",
34                                       "all-petite-caps": "ALLPETITE",
35                                       "titling-caps": "TITLING" } %>
36 
37 ${helpers.single_keyword_system(
38     "font-variant-caps",
39     "normal small-caps",
40     engines="gecko servo-2013 servo-2020",
41     extra_gecko_values="all-small-caps petite-caps all-petite-caps unicase titling-caps",
42     gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
43     gecko_ffi_name="mFont.variantCaps",
44     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
45     custom_consts=font_variant_caps_custom_consts,
46     animation_value_type="discrete",
47     servo_restyle_damage="rebuild_and_reflow",
48 )}
49 
50 ${helpers.predefined_type(
51     "font-weight",
52     "FontWeight",
53     engines="gecko servo-2013 servo-2020",
54     initial_value="computed::FontWeight::normal()",
55     initial_specified_value="specified::FontWeight::normal()",
56     animation_value_type="Number",
57     spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight",
58     servo_restyle_damage="rebuild_and_reflow",
59 )}
60 
61 ${helpers.predefined_type(
62     "font-size",
63     "FontSize",
64     engines="gecko servo-2013 servo-2020",
65     initial_value="computed::FontSize::medium()",
66     initial_specified_value="specified::FontSize::medium()",
67     animation_value_type="NonNegativeLength",
68     allow_quirks="Yes",
69     spec="https://drafts.csswg.org/css-fonts/#propdef-font-size",
70     servo_restyle_damage="rebuild_and_reflow",
71 )}
72 
73 ${helpers.predefined_type(
74     "font-size-adjust",
75     "FontSizeAdjust",
76     engines="gecko",
77     initial_value="computed::FontSizeAdjust::none()",
78     initial_specified_value="specified::FontSizeAdjust::none()",
79     animation_value_type="FontSizeAdjust",
80     spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust",
81 )}
82 
83 ${helpers.predefined_type(
84     "font-synthesis",
85     "FontSynthesis",
86     engines="gecko",
87     initial_value="specified::FontSynthesis::get_initial_value()",
88     animation_value_type="discrete",
89     spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis",
90 )}
91 
92 ${helpers.predefined_type(
93     "font-stretch",
94     "FontStretch",
95     engines="gecko servo-2013 servo-2020",
96     initial_value="computed::FontStretch::hundred()",
97     initial_specified_value="specified::FontStretch::normal()",
98     animation_value_type="Percentage",
99     spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
100     servo_restyle_damage="rebuild_and_reflow",
101 )}
102 
103 ${helpers.single_keyword_system(
104     "font-kerning",
105     "auto none normal",
106     engines="gecko",
107     gecko_ffi_name="mFont.kerning",
108     gecko_constant_prefix="NS_FONT_KERNING",
109     spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
110     animation_value_type="discrete",
111 )}
112 
113 ${helpers.predefined_type(
114     "font-variant-alternates",
115     "FontVariantAlternates",
116     engines="gecko",
117     initial_value="computed::FontVariantAlternates::get_initial_value()",
118     initial_specified_value="specified::FontVariantAlternates::get_initial_specified_value()",
119     animation_value_type="discrete",
120     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates",
121 )}
122 
123 ${helpers.predefined_type(
124     "font-variant-east-asian",
125     "FontVariantEastAsian",
126     engines="gecko",
127     initial_value="computed::FontVariantEastAsian::empty()",
128     initial_specified_value="specified::FontVariantEastAsian::empty()",
129     animation_value_type="discrete",
130     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian",
131 )}
132 
133 ${helpers.predefined_type(
134     "font-variant-ligatures",
135     "FontVariantLigatures",
136     engines="gecko",
137     initial_value="computed::FontVariantLigatures::empty()",
138     initial_specified_value="specified::FontVariantLigatures::empty()",
139     animation_value_type="discrete",
140     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures",
141 )}
142 
143 ${helpers.predefined_type(
144     "font-variant-numeric",
145     "FontVariantNumeric",
146     engines="gecko",
147     initial_value="computed::FontVariantNumeric::empty()",
148     initial_specified_value="specified::FontVariantNumeric::empty()",
149     animation_value_type="discrete",
150     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric",
151 )}
152 
153 ${helpers.single_keyword_system(
154     "font-variant-position",
155     "normal sub super",
156     engines="gecko",
157     gecko_ffi_name="mFont.variantPosition",
158     gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
159     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
160     animation_value_type="discrete",
161 )}
162 
163 ${helpers.predefined_type(
164     "font-feature-settings",
165     "FontFeatureSettings",
166     engines="gecko",
167     initial_value="computed::FontFeatureSettings::normal()",
168     initial_specified_value="specified::FontFeatureSettings::normal()",
169     extra_prefixes="moz:layout.css.prefixes.font-features",
170     animation_value_type="discrete",
171     spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings",
172 )}
173 
174 ${helpers.predefined_type(
175     "font-variation-settings",
176     "FontVariationSettings",
177     engines="gecko",
178     gecko_pref="layout.css.font-variations.enabled",
179     has_effect_on_gecko_scrollbars=False,
180     initial_value="computed::FontVariationSettings::normal()",
181     initial_specified_value="specified::FontVariationSettings::normal()",
182     animation_value_type="ComputedValue",
183     spec="https://drafts.csswg.org/css-fonts-4/#propdef-font-variation-settings"
184 )}
185 
186 ${helpers.predefined_type(
187     "font-language-override",
188     "FontLanguageOverride",
189     engines="gecko",
190     initial_value="computed::FontLanguageOverride::zero()",
191     initial_specified_value="specified::FontLanguageOverride::normal()",
192     animation_value_type="discrete",
193     extra_prefixes="moz:layout.css.prefixes.font-features",
194     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override",
195 )}
196 
197 ${helpers.single_keyword_system(
198     "font-optical-sizing",
199     "auto none",
200     engines="gecko",
201     gecko_pref="layout.css.font-variations.enabled",
202     has_effect_on_gecko_scrollbars=False,
203     gecko_ffi_name="mFont.opticalSizing",
204     gecko_constant_prefix="NS_FONT_OPTICAL_SIZING",
205     animation_value_type="discrete",
206     spec="https://www.w3.org/TR/css-fonts-4/#font-optical-sizing-def",
207 )}
208 
209 ${helpers.predefined_type(
210     "-x-lang",
211     "XLang",
212     engines="gecko",
213     initial_value="computed::XLang::get_initial_value()",
214     animation_value_type="none",
215     enabled_in="",
216     spec="Internal (not web-exposed)",
217 )}
218 
219 ${helpers.predefined_type(
220     "-moz-script-size-multiplier",
221     "MozScriptSizeMultiplier",
222     engines="gecko",
223     initial_value="computed::MozScriptSizeMultiplier::get_initial_value()",
224     animation_value_type="none",
225     gecko_ffi_name="mScriptSizeMultiplier",
226     enabled_in="",
227     spec="Internal (not web-exposed)",
228 )}
229 
230 ${helpers.predefined_type(
231     "-moz-script-level",
232     "MozScriptLevel",
233     "0",
234     engines="gecko",
235     animation_value_type="none",
236     enabled_in="ua",
237     gecko_ffi_name="mScriptLevel",
238     spec="Internal (not web-exposed)",
239 )}
240 
241 ${helpers.single_keyword(
242     "-moz-math-display",
243     "inline block",
244     engines="gecko",
245     gecko_constant_prefix="NS_MATHML_DISPLAYSTYLE",
246     gecko_ffi_name="mMathDisplay",
247     enabled_in="ua",
248     spec="Internal (not web-exposed)",
249     animation_value_type="none",
250 )}
251 
252 ${helpers.single_keyword(
253     "-moz-math-variant",
254     """none normal bold italic bold-italic script bold-script
255     fraktur double-struck bold-fraktur sans-serif
256     bold-sans-serif sans-serif-italic sans-serif-bold-italic
257     monospace initial tailed looped stretched""",
258     engines="gecko",
259     gecko_constant_prefix="NS_MATHML_MATHVARIANT",
260     gecko_ffi_name="mMathVariant",
261     spec="Internal (not web-exposed)",
262     animation_value_type="none",
263     enabled_in="",
264     needs_conversion=True,
265 )}
266 
267 ${helpers.predefined_type(
268     "-moz-script-min-size",
269     "MozScriptMinSize",
270     "specified::MozScriptMinSize::get_initial_value()",
271     engines="gecko",
272     animation_value_type="none",
273     enabled_in="",
274     gecko_ffi_name="mScriptMinSize",
275     spec="Internal (not web-exposed)",
276 )}
277 
278 ${helpers.predefined_type(
279     "-x-text-zoom",
280     "XTextZoom",
281     "computed::XTextZoom(true)",
282     engines="gecko",
283     animation_value_type="none",
284     enabled_in="",
285     spec="Internal (not web-exposed)",
286 )}
287 
288 % if engine == "gecko":
289     pub mod system_font {
290         //! We deal with system fonts here
291         //!
292         //! System fonts can only be set as a group via the font shorthand.
293         //! They resolve at compute time (not parse time -- this lets the
294         //! browser respond to changes to the OS font settings).
295         //!
296         //! While Gecko handles these as a separate property and keyword
297         //! values on each property indicating that the font should be picked
298         //! from the -x-system-font property, we avoid this. Instead,
299         //! each font longhand has a special SystemFont variant which contains
300         //! the specified system font. When the cascade function (in helpers)
301         //! detects that a value has a system font, it will resolve it, and
302         //! cache it on the ComputedValues. After this, it can be just fetched
303         //! whenever a font longhand on the same element needs the system font.
304         //!
305         //! When a longhand property is holding a SystemFont, it's serialized
306         //! to an empty string as if its value comes from a shorthand with
307         //! variable reference. We may want to improve this behavior at some
308         //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
309 
310         use app_units::Au;
311         use cssparser::{Parser, ToCss};
312         use crate::values::computed::font::GenericFontFamily;
313         use crate::properties::longhands;
314         use std::fmt;
315         use std::hash::{Hash, Hasher};
316         use style_traits::ParseError;
317         use crate::values::computed::{ToComputedValue, Context};
318 
319         <%
320             system_fonts = """caption icon menu message-box small-caption status-bar
321                               -moz-window -moz-document -moz-workspace -moz-desktop
322                               -moz-info -moz-dialog -moz-button -moz-pull-down-menu
323                               -moz-list -moz-field""".split()
324             kw_font_props = """font_variant_caps
325                                font_kerning font_variant_position font_variant_ligatures
326                                font_variant_east_asian font_variant_numeric
327                                font_optical_sizing""".split()
328             kw_cast = """font_variant_caps font_kerning font_variant_position
329                          font_optical_sizing""".split()
330         %>
331         #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq,
332                  SpecifiedValueInfo, ToCss, ToShmem)]
333         pub enum SystemFont {
334             % for font in system_fonts:
335                 ${to_camel_case(font)},
336             % endfor
337         }
338 
339         // ComputedValues are compared at times
340         // so we need these impls. We don't want to
341         // add Eq to Number (which contains a float)
342         // so instead we have an eq impl which skips the
343         // cached values
344         impl PartialEq for ComputedSystemFont {
eq(&self, other: &Self) -> bool345             fn eq(&self, other: &Self) -> bool {
346                 self.system_font == other.system_font
347             }
348         }
349         impl Eq for ComputedSystemFont {}
350 
351         impl Hash for ComputedSystemFont {
hash<H: Hasher>(&self, hasher: &mut H)352             fn hash<H: Hasher>(&self, hasher: &mut H) {
353                 self.system_font.hash(hasher)
354             }
355         }
356 
357         impl ToComputedValue for SystemFont {
358             type ComputedValue = ComputedSystemFont;
359 
to_computed_value(&self, cx: &Context) -> Self::ComputedValue360             fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
361                 use crate::gecko_bindings::bindings;
362                 use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont};
363                 use std::mem;
364                 use crate::values::computed::Percentage;
365                 use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
366                 use crate::values::generics::NonNegative;
367 
368                 let id = match *self {
369                     % for font in system_fonts:
370                         SystemFont::${to_camel_case(font)} => {
371                             LookAndFeel_FontID::eFont_${to_camel_case(font.replace("-moz-", ""))}
372                         }
373                     % endfor
374                 };
375 
376                 let mut system = mem::MaybeUninit::<nsFont>::uninit();
377                 let system = unsafe {
378                     bindings::Gecko_nsFont_InitSystem(
379                         system.as_mut_ptr(),
380                         id as i32,
381                         cx.style().get_font().gecko(),
382                         cx.device().document()
383                     );
384                     &mut *system.as_mut_ptr()
385                 };
386                 let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
387                 let font_stretch = FontStretch(NonNegative(Percentage(unsafe {
388                     bindings::Gecko_FontStretch_ToFloat(system.stretch)
389                 })));
390                 let font_style = FontStyle::from_gecko(system.style);
391                 let ret = ComputedSystemFont {
392                     font_family: FontFamily {
393                         families: FontFamilyList::SharedFontList(
394                             unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
395                         ),
396                         is_system_font: true,
397                     },
398                     font_size: FontSize {
399                         size: NonNegative(cx.maybe_zoom_text(Au(system.size).into())),
400                         keyword_info: None
401                     },
402                     font_weight,
403                     font_stretch,
404                     font_style,
405                     font_size_adjust: longhands::font_size_adjust::computed_value
406                                                ::T::from_gecko_adjust(system.sizeAdjust),
407                     % for kwprop in kw_font_props:
408                         ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
409                             system.${to_camel_case_lower(kwprop.replace('font_', ''))}
410                             % if kwprop in kw_cast:
411                                 as u32
412                             % endif
413                         ),
414                     % endfor
415                     font_language_override: longhands::font_language_override::computed_value
416                                                      ::T(system.languageOverride),
417                     font_feature_settings: longhands::font_feature_settings::get_initial_value(),
418                     font_variation_settings: longhands::font_variation_settings::get_initial_value(),
419                     font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
420                     system_font: *self,
421                     default_font_type: system.fontlist.mDefaultFontType,
422                 };
423                 unsafe { bindings::Gecko_nsFont_Destroy(system); }
424                 ret
425             }
426 
from_computed_value(_: &ComputedSystemFont) -> Self427             fn from_computed_value(_: &ComputedSystemFont) -> Self {
428                 unreachable!()
429             }
430         }
431 
432         #[inline]
433         /// Compute and cache a system font
434         ///
435         /// Must be called before attempting to compute a system font
436         /// specified value
resolve_system_font(system: SystemFont, context: &mut Context)437         pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
438             // Checking if context.cached_system_font.is_none() isn't enough,
439             // if animating from one system font to another the cached system font
440             // may change
441             if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) {
442                 let computed = system.to_computed_value(context);
443                 context.cached_system_font = Some(computed);
444             }
445         }
446 
447         #[derive(Clone, Debug)]
448         pub struct ComputedSystemFont {
449             % for name in SYSTEM_FONT_LONGHANDS:
450                 pub ${name}: longhands::${name}::computed_value::T,
451             % endfor
452             pub system_font: SystemFont,
453             pub default_font_type: GenericFontFamily,
454         }
455 
456         impl SystemFont {
parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>457             pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
458                 try_match_ident_ignore_ascii_case! { input,
459                     % for font in system_fonts:
460                         "${font}" => Ok(SystemFont::${to_camel_case(font)}),
461                     % endfor
462                 }
463             }
464         }
465 
466         impl ToCss for SystemFont {
to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write467             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
468                 // We may want to do something better in the future, see
469                 // w3c/csswg-drafts#1586.
470                 dest.write_str("-moz-use-system-font")
471             }
472         }
473     }
474 % else:
475     pub mod system_font {
476         use cssparser::Parser;
477 
478         // We don't parse system fonts, but in the interest of not littering
479         // a lot of code with `if engine == "gecko"` conditionals, we have a
480         // dummy system font module that does nothing
481 
482         #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
483         /// void enum for system font, can never exist
484         pub enum SystemFont {}
485         impl SystemFont {
parse(_: &mut Parser) -> Result<Self, ()>486             pub fn parse(_: &mut Parser) -> Result<Self, ()> {
487                 Err(())
488             }
489         }
490     }
491 % endif
492 
493 ${helpers.single_keyword(
494     "-moz-osx-font-smoothing",
495     "auto grayscale",
496     engines="gecko",
497     gecko_constant_prefix="NS_FONT_SMOOTHING",
498     gecko_ffi_name="mFont.smoothing",
499     gecko_pref="layout.css.osx-font-smoothing.enabled",
500     has_effect_on_gecko_scrollbars=False,
501     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)",
502     animation_value_type="discrete",
503 )}
504 
505 ${helpers.predefined_type(
506     "-moz-font-smoothing-background-color",
507     "color::MozFontSmoothingBackgroundColor",
508     "computed::color::MozFontSmoothingBackgroundColor::transparent()",
509     engines="gecko",
510     animation_value_type="none",
511     gecko_ffi_name="mFont.fontSmoothingBackgroundColor",
512     enabled_in="chrome",
513     spec="None (Nonstandard internal property)",
514 )}
515 
516 ${helpers.predefined_type(
517     "-moz-min-font-size-ratio",
518     "Percentage",
519     "computed::Percentage::hundred()",
520     engines="gecko",
521     animation_value_type="none",
522     enabled_in="ua",
523     spec="Nonstandard (Internal-only)",
524 )}
525