mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
And hide internal but used values. System fonts are not exposed in the computed style so this should be fine. If we need the old values for some obscure reason, it's trivial to alias them to e.g., menu or so. Differential Revision: https://phabricator.services.mozilla.com/D163269
464 lines
18 KiB
Rust
464 lines
18 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
<%namespace name="helpers" file="/helpers.mako.rs" />
|
|
<% from data import SYSTEM_FONT_LONGHANDS %>
|
|
|
|
<%helpers:shorthand
|
|
name="font"
|
|
engines="gecko servo"
|
|
sub_properties="
|
|
font-style
|
|
font-variant-caps
|
|
font-weight
|
|
font-stretch
|
|
font-size
|
|
line-height
|
|
font-family
|
|
${'font-size-adjust' if engine == 'gecko' else ''}
|
|
${'font-kerning' if engine == 'gecko' else ''}
|
|
${'font-optical-sizing' if engine == 'gecko' else ''}
|
|
${'font-variant-alternates' if engine == 'gecko' else ''}
|
|
${'font-variant-east-asian' if engine == 'gecko' else ''}
|
|
${'font-variant-emoji' if engine == 'gecko' else ''}
|
|
${'font-variant-ligatures' if engine == 'gecko' else ''}
|
|
${'font-variant-numeric' if engine == 'gecko' else ''}
|
|
${'font-variant-position' if engine == 'gecko' else ''}
|
|
${'font-language-override' if engine == 'gecko' else ''}
|
|
${'font-feature-settings' if engine == 'gecko' else ''}
|
|
${'font-variation-settings' if engine == 'gecko' else ''}
|
|
${'font-palette' if engine == 'gecko' else ''}
|
|
"
|
|
derive_value_info="False"
|
|
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font"
|
|
>
|
|
use crate::computed_values::font_variant_caps::T::SmallCaps;
|
|
use crate::parser::Parse;
|
|
use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch};
|
|
#[cfg(feature = "gecko")]
|
|
use crate::properties::longhands::font_size;
|
|
use crate::properties::longhands::font_variant_caps;
|
|
use crate::values::specified::text::LineHeight;
|
|
use crate::values::specified::FontSize;
|
|
use crate::values::specified::font::{FontStretch, FontStretchKeyword};
|
|
#[cfg(feature = "gecko")]
|
|
use crate::values::specified::font::SystemFont;
|
|
|
|
<%
|
|
gecko_sub_properties = "kerning language_override size_adjust \
|
|
variant_alternates variant_east_asian \
|
|
variant_emoji variant_ligatures \
|
|
variant_numeric variant_position \
|
|
feature_settings variation_settings \
|
|
optical_sizing palette".split()
|
|
%>
|
|
% if engine == "gecko":
|
|
% for prop in gecko_sub_properties:
|
|
use crate::properties::longhands::font_${prop};
|
|
% endfor
|
|
% endif
|
|
use self::font_family::SpecifiedValue as FontFamily;
|
|
|
|
pub fn parse_value<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Longhands, ParseError<'i>> {
|
|
let mut nb_normals = 0;
|
|
let mut style = None;
|
|
let mut variant_caps = None;
|
|
let mut weight = None;
|
|
let mut stretch = None;
|
|
let size;
|
|
% if engine == "gecko":
|
|
if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {
|
|
return Ok(expanded! {
|
|
% for name in SYSTEM_FONT_LONGHANDS:
|
|
${name}: ${name}::SpecifiedValue::system_font(sys),
|
|
% endfor
|
|
line_height: LineHeight::normal(),
|
|
% for name in gecko_sub_properties + ["variant_caps"]:
|
|
font_${name}: font_${name}::get_initial_specified_value(),
|
|
% endfor
|
|
})
|
|
}
|
|
% endif
|
|
loop {
|
|
// Special-case 'normal' because it is valid in each of
|
|
// font-style, font-weight, font-variant and font-stretch.
|
|
// Leaves the values to None, 'normal' is the initial value for each of them.
|
|
if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
|
|
nb_normals += 1;
|
|
continue;
|
|
}
|
|
if style.is_none() {
|
|
if let Ok(value) = input.try_parse(|input| font_style::parse(context, input)) {
|
|
style = Some(value);
|
|
continue
|
|
}
|
|
}
|
|
if weight.is_none() {
|
|
if let Ok(value) = input.try_parse(|input| font_weight::parse(context, input)) {
|
|
weight = Some(value);
|
|
continue
|
|
}
|
|
}
|
|
if variant_caps.is_none() {
|
|
// The only variant-caps value allowed is small-caps (from CSS2); the added values
|
|
// defined by CSS Fonts 3 and later are not accepted.
|
|
// https://www.w3.org/TR/css-fonts-4/#font-prop
|
|
if input.try_parse(|input| input.expect_ident_matching("small-caps")).is_ok() {
|
|
variant_caps = Some(SmallCaps);
|
|
continue
|
|
}
|
|
}
|
|
if stretch.is_none() {
|
|
if let Ok(value) = input.try_parse(FontStretchKeyword::parse) {
|
|
stretch = Some(FontStretch::Keyword(value));
|
|
continue
|
|
}
|
|
}
|
|
size = Some(FontSize::parse(context, input)?);
|
|
break
|
|
}
|
|
|
|
let size = match size {
|
|
Some(s) => s,
|
|
None => {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
};
|
|
|
|
let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
|
|
Some(LineHeight::parse(context, input)?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
#[inline]
|
|
fn count<T>(opt: &Option<T>) -> u8 {
|
|
if opt.is_some() { 1 } else { 0 }
|
|
}
|
|
|
|
if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals) > 4 {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
|
|
let family = FontFamily::parse(context, input)?;
|
|
Ok(expanded! {
|
|
% for name in "style weight stretch variant_caps".split():
|
|
font_${name}: unwrap_or_initial!(font_${name}, ${name}),
|
|
% endfor
|
|
font_size: size,
|
|
line_height: line_height.unwrap_or(LineHeight::normal()),
|
|
font_family: family,
|
|
% if engine == "gecko":
|
|
% for name in gecko_sub_properties:
|
|
font_${name}: font_${name}::get_initial_specified_value(),
|
|
% endfor
|
|
% endif
|
|
})
|
|
}
|
|
|
|
% if engine == "gecko":
|
|
enum CheckSystemResult {
|
|
AllSystem(SystemFont),
|
|
SomeSystem,
|
|
None
|
|
}
|
|
% endif
|
|
|
|
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
|
% if engine == "gecko":
|
|
match self.check_system() {
|
|
CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
|
|
CheckSystemResult::SomeSystem => return Ok(()),
|
|
CheckSystemResult::None => {}
|
|
}
|
|
% endif
|
|
|
|
% if engine == "gecko":
|
|
if let Some(v) = self.font_optical_sizing {
|
|
if v != &font_optical_sizing::get_initial_specified_value() {
|
|
return Ok(());
|
|
}
|
|
}
|
|
if let Some(v) = self.font_variation_settings {
|
|
if v != &font_variation_settings::get_initial_specified_value() {
|
|
return Ok(());
|
|
}
|
|
}
|
|
if let Some(v) = self.font_palette {
|
|
if v != &font_palette::get_initial_specified_value() {
|
|
return Ok(());
|
|
}
|
|
}
|
|
if let Some(v) = self.font_variant_emoji {
|
|
if v != &font_variant_emoji::get_initial_specified_value() {
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
% for name in gecko_sub_properties:
|
|
% if name != "optical_sizing" and name != "variation_settings" and name != "palette" and name != "variant_emoji":
|
|
if self.font_${name} != &font_${name}::get_initial_specified_value() {
|
|
return Ok(());
|
|
}
|
|
% endif
|
|
% endfor
|
|
% endif
|
|
|
|
// Only font-stretch keywords are allowed as part as the font
|
|
// shorthand.
|
|
let font_stretch = match *self.font_stretch {
|
|
FontStretch::Keyword(kw) => kw,
|
|
FontStretch::Stretch(percentage) => {
|
|
match FontStretchKeyword::from_percentage(percentage.0.get()) {
|
|
Some(kw) => kw,
|
|
None => return Ok(()),
|
|
}
|
|
}
|
|
FontStretch::System(..) => return Ok(()),
|
|
};
|
|
|
|
// The only variant-caps value allowed in the shorthand is small-caps (from CSS2);
|
|
// the added values defined by CSS Fonts 3 and later are not supported.
|
|
// https://www.w3.org/TR/css-fonts-4/#font-prop
|
|
if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() &&
|
|
*self.font_variant_caps != SmallCaps {
|
|
return Ok(());
|
|
}
|
|
|
|
% for name in "style variant_caps weight".split():
|
|
if self.font_${name} != &font_${name}::get_initial_specified_value() {
|
|
self.font_${name}.to_css(dest)?;
|
|
dest.write_str(" ")?;
|
|
}
|
|
% endfor
|
|
|
|
if font_stretch != FontStretchKeyword::Normal {
|
|
font_stretch.to_css(dest)?;
|
|
dest.write_str(" ")?;
|
|
}
|
|
|
|
self.font_size.to_css(dest)?;
|
|
|
|
if *self.line_height != LineHeight::normal() {
|
|
dest.write_str(" / ")?;
|
|
self.line_height.to_css(dest)?;
|
|
}
|
|
|
|
dest.write_str(" ")?;
|
|
self.font_family.to_css(dest)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> LonghandsToSerialize<'a> {
|
|
% if engine == "gecko":
|
|
/// Check if some or all members are system fonts
|
|
fn check_system(&self) -> CheckSystemResult {
|
|
let mut sys = None;
|
|
let mut all = true;
|
|
|
|
% for prop in SYSTEM_FONT_LONGHANDS:
|
|
% if prop == "font_optical_sizing" or prop == "font_variation_settings" or prop == "font_palette":
|
|
if let Some(value) = self.${prop} {
|
|
% else:
|
|
{
|
|
let value = self.${prop};
|
|
% endif
|
|
match value.get_system() {
|
|
Some(s) => {
|
|
debug_assert!(sys.is_none() || s == sys.unwrap());
|
|
sys = Some(s);
|
|
}
|
|
None => {
|
|
all = false;
|
|
}
|
|
}
|
|
}
|
|
% endfor
|
|
if self.line_height != &LineHeight::normal() {
|
|
all = false
|
|
}
|
|
if all {
|
|
CheckSystemResult::AllSystem(sys.unwrap())
|
|
} else if sys.is_some() {
|
|
CheckSystemResult::SomeSystem
|
|
} else {
|
|
CheckSystemResult::None
|
|
}
|
|
}
|
|
% endif
|
|
}
|
|
|
|
<%
|
|
subprops_for_value_info = ["font_style", "font_weight", "font_stretch",
|
|
"font_variant_caps", "font_size", "font_family"]
|
|
subprops_for_value_info = [
|
|
"<longhands::{}::SpecifiedValue as SpecifiedValueInfo>".format(p)
|
|
for p in subprops_for_value_info
|
|
]
|
|
%>
|
|
impl SpecifiedValueInfo for Longhands {
|
|
const SUPPORTED_TYPES: u8 = 0
|
|
% for p in subprops_for_value_info:
|
|
| ${p}::SUPPORTED_TYPES
|
|
% endfor
|
|
;
|
|
|
|
fn collect_completion_keywords(f: KeywordsCollectFn) {
|
|
% for p in subprops_for_value_info:
|
|
${p}::collect_completion_keywords(f);
|
|
% endfor
|
|
% if engine == "gecko":
|
|
<SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
|
|
% endif
|
|
}
|
|
}
|
|
</%helpers:shorthand>
|
|
|
|
<%helpers:shorthand name="font-variant"
|
|
engines="gecko servo"
|
|
servo_pref="layout.legacy_layout",
|
|
flags="SHORTHAND_IN_GETCS"
|
|
sub_properties="font-variant-caps
|
|
${'font-variant-alternates' if engine == 'gecko' else ''}
|
|
${'font-variant-east-asian' if engine == 'gecko' else ''}
|
|
${'font-variant-emoji' if engine == 'gecko' else ''}
|
|
${'font-variant-ligatures' if engine == 'gecko' else ''}
|
|
${'font-variant-numeric' if engine == 'gecko' else ''}
|
|
${'font-variant-position' if engine == 'gecko' else ''}"
|
|
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
|
|
<% gecko_sub_properties = "alternates east_asian emoji ligatures numeric position".split() %>
|
|
<%
|
|
sub_properties = ["caps"]
|
|
if engine == "gecko":
|
|
sub_properties += gecko_sub_properties
|
|
%>
|
|
|
|
% for prop in sub_properties:
|
|
use crate::properties::longhands::font_variant_${prop};
|
|
% endfor
|
|
#[allow(unused_imports)]
|
|
use crate::values::specified::FontVariantLigatures;
|
|
|
|
pub fn parse_value<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Longhands, ParseError<'i>> {
|
|
% for prop in sub_properties:
|
|
let mut ${prop} = None;
|
|
% endfor
|
|
|
|
if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
|
|
// Leave the values to None, 'normal' is the initial value for all the sub properties.
|
|
} else if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
|
|
// The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties
|
|
// to their initial value.
|
|
% if engine == "gecko":
|
|
ligatures = Some(FontVariantLigatures::NONE);
|
|
% endif
|
|
} else {
|
|
let mut has_custom_value: bool = false;
|
|
loop {
|
|
if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() ||
|
|
input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
% for prop in sub_properties:
|
|
if ${prop}.is_none() {
|
|
if let Ok(value) = input.try_parse(|i| font_variant_${prop}::parse(context, i)) {
|
|
has_custom_value = true;
|
|
${prop} = Some(value);
|
|
continue
|
|
}
|
|
}
|
|
% endfor
|
|
|
|
break
|
|
}
|
|
|
|
if !has_custom_value {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
}
|
|
|
|
Ok(expanded! {
|
|
% for prop in sub_properties:
|
|
font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
|
|
% endfor
|
|
})
|
|
}
|
|
|
|
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
|
#[allow(unused_assignments)]
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
|
|
|
let has_none_ligatures =
|
|
% if engine == "gecko":
|
|
self.font_variant_ligatures == &FontVariantLigatures::NONE;
|
|
% else:
|
|
false;
|
|
% endif
|
|
|
|
const TOTAL_SUBPROPS: usize = ${len(sub_properties)};
|
|
let mut nb_normals = 0;
|
|
% for prop in sub_properties:
|
|
% if prop == "emoji":
|
|
if let Some(value) = self.font_variant_${prop} {
|
|
% else:
|
|
{
|
|
let value = self.font_variant_${prop};
|
|
% endif
|
|
if value == &font_variant_${prop}::get_initial_specified_value() {
|
|
nb_normals += 1;
|
|
}
|
|
}
|
|
% if prop == "emoji":
|
|
else {
|
|
// The property was disabled, so we count it as 'normal' for the purpose
|
|
// of deciding how the shorthand can be serialized.
|
|
nb_normals += 1;
|
|
}
|
|
% endif
|
|
% endfor
|
|
|
|
|
|
if nb_normals > 0 && nb_normals == TOTAL_SUBPROPS {
|
|
dest.write_str("normal")?;
|
|
} else if has_none_ligatures {
|
|
if nb_normals == TOTAL_SUBPROPS - 1 {
|
|
// Serialize to 'none' if 'font-variant-ligatures' is set to 'none' and all other
|
|
// font feature properties are reset to their initial value.
|
|
dest.write_str("none")?;
|
|
} else {
|
|
return Ok(())
|
|
}
|
|
} else {
|
|
let mut has_any = false;
|
|
% for prop in sub_properties:
|
|
% if prop == "emoji":
|
|
if let Some(value) = self.font_variant_${prop} {
|
|
% else:
|
|
{
|
|
let value = self.font_variant_${prop};
|
|
% endif
|
|
if value != &font_variant_${prop}::get_initial_specified_value() {
|
|
if has_any {
|
|
dest.write_str(" ")?;
|
|
}
|
|
has_any = true;
|
|
value.to_css(dest)?;
|
|
}
|
|
}
|
|
% endfor
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
</%helpers:shorthand>
|