mirror of
https://github.com/servo/servo.git
synced 2025-10-03 18:19:14 +01:00
We don't use them[1], and these are generally not properties that authors would be able to set via the font shorthand anyways. Let's simplify the code. This fixes the font-variant bug and also unblocks further clean-ups of these properties in the future. [1]: https://searchfox.org/mozilla-central/rev/59f0bf3c13dd455d9f5415b89178de701ea6b850/widget/LookAndFeelTypes.ipdlh#12-18 Differential Revision: https://phabricator.services.mozilla.com/D160352
455 lines
17 KiB
Rust
455 lines
17 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_size, font_weight, font_stretch};
|
|
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(SystemFont::parse) {
|
|
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;
|
|
}
|
|
}
|
|
% 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>
|