mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #17310 - chenpighead:stylo-font-variant, r=xidorn
stylo: support font-variant shorthand From gecko bug: [Bug 1356134](https://bugzilla.mozilla.org/show_bug.cgi?id=1356134) <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17310) <!-- Reviewable:end -->
This commit is contained in:
commit
c6da6ba060
2 changed files with 119 additions and 85 deletions
|
@ -1376,21 +1376,21 @@ ${helpers.single_keyword_system("font-kerning",
|
||||||
return Ok(SpecifiedValue::Value(result))
|
return Ok(SpecifiedValue::Value(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
while let Ok(flag) = input.try(|input| {
|
||||||
let flag = match_ignore_ascii_case! { &ident,
|
Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
|
||||||
"stylistic" => Some(STYLISTIC),
|
"stylistic" => STYLISTIC,
|
||||||
"historical-forms" => Some(HISTORICAL_FORMS),
|
"historical-forms" => HISTORICAL_FORMS,
|
||||||
"styleset" => Some(STYLESET),
|
"styleset" => STYLESET,
|
||||||
"character-variant" => Some(CHARACTER_VARIANT),
|
"character-variant" => CHARACTER_VARIANT,
|
||||||
"swash" => Some(SWASH),
|
"swash" => SWASH,
|
||||||
"ornaments" => Some(ORNAMENTS),
|
"ornaments" => ORNAMENTS,
|
||||||
"annotation" => Some(ANNOTATION),
|
"annotation" => ANNOTATION,
|
||||||
_ => None,
|
_ => return Err(()),
|
||||||
};
|
})
|
||||||
let flag = match flag {
|
}) {
|
||||||
Some(flag) if !result.intersects(flag) => flag,
|
if result.intersects(flag) {
|
||||||
_ => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
|
return Err(StyleParseError::UnspecifiedError.into())
|
||||||
};
|
}
|
||||||
result.insert(flag);
|
result.insert(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1406,9 +1406,9 @@ ${helpers.single_keyword_system("font-kerning",
|
||||||
macro_rules! exclusive_value {
|
macro_rules! exclusive_value {
|
||||||
(($value:ident, $set:expr) => $ident:ident) => {
|
(($value:ident, $set:expr) => $ident:ident) => {
|
||||||
if $value.intersects($set) {
|
if $value.intersects($set) {
|
||||||
None
|
return Err(())
|
||||||
} else {
|
} else {
|
||||||
Some($ident)
|
$ident
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1521,8 +1521,8 @@ macro_rules! exclusive_value {
|
||||||
return Ok(SpecifiedValue::Value(result))
|
return Ok(SpecifiedValue::Value(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
while let Ok(flag) = input.try(|input| {
|
||||||
let flag = match_ignore_ascii_case! { &ident,
|
Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
|
||||||
"jis78" =>
|
"jis78" =>
|
||||||
exclusive_value!((result, ${east_asian_variant_values}) => JIS78),
|
exclusive_value!((result, ${east_asian_variant_values}) => JIS78),
|
||||||
"jis83" =>
|
"jis83" =>
|
||||||
|
@ -1541,12 +1541,9 @@ macro_rules! exclusive_value {
|
||||||
exclusive_value!((result, ${east_asian_width_values}) => PROPORTIONAL_WIDTH),
|
exclusive_value!((result, ${east_asian_width_values}) => PROPORTIONAL_WIDTH),
|
||||||
"ruby" =>
|
"ruby" =>
|
||||||
exclusive_value!((result, RUBY) => RUBY),
|
exclusive_value!((result, RUBY) => RUBY),
|
||||||
_ => None,
|
_ => return Err(()),
|
||||||
};
|
})
|
||||||
let flag = match flag {
|
}) {
|
||||||
Some(flag) => flag,
|
|
||||||
None => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
|
|
||||||
};
|
|
||||||
result.insert(flag);
|
result.insert(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1656,6 +1653,10 @@ macro_rules! exclusive_value {
|
||||||
pub fn get_initial_specified_value() -> SpecifiedValue {
|
pub fn get_initial_specified_value() -> SpecifiedValue {
|
||||||
SpecifiedValue::Value(VariantLigatures::empty())
|
SpecifiedValue::Value(VariantLigatures::empty())
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_none_specified_value() -> SpecifiedValue {
|
||||||
|
SpecifiedValue::Value(NONE)
|
||||||
|
}
|
||||||
|
|
||||||
/// normal | none |
|
/// normal | none |
|
||||||
/// [ <common-lig-values> ||
|
/// [ <common-lig-values> ||
|
||||||
|
@ -1681,8 +1682,8 @@ macro_rules! exclusive_value {
|
||||||
return Ok(SpecifiedValue::Value(NONE))
|
return Ok(SpecifiedValue::Value(NONE))
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
while let Ok(flag) = input.try(|input| {
|
||||||
let flag = match_ignore_ascii_case! { &ident,
|
Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
|
||||||
"common-ligatures" =>
|
"common-ligatures" =>
|
||||||
exclusive_value!((result, ${common_lig_values}) => COMMON_LIGATURES),
|
exclusive_value!((result, ${common_lig_values}) => COMMON_LIGATURES),
|
||||||
"no-common-ligatures" =>
|
"no-common-ligatures" =>
|
||||||
|
@ -1699,12 +1700,9 @@ macro_rules! exclusive_value {
|
||||||
exclusive_value!((result, ${contextual_alt_values}) => CONTEXTUAL),
|
exclusive_value!((result, ${contextual_alt_values}) => CONTEXTUAL),
|
||||||
"no-contextual" =>
|
"no-contextual" =>
|
||||||
exclusive_value!((result, ${contextual_alt_values}) => NO_CONTEXTUAL),
|
exclusive_value!((result, ${contextual_alt_values}) => NO_CONTEXTUAL),
|
||||||
_ => None,
|
_ => return Err(()),
|
||||||
};
|
})
|
||||||
let flag = match flag {
|
}) {
|
||||||
Some(flag) => flag,
|
|
||||||
None => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
|
|
||||||
};
|
|
||||||
result.insert(flag);
|
result.insert(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1832,14 +1830,14 @@ macro_rules! exclusive_value {
|
||||||
return Ok(SpecifiedValue::Value(result))
|
return Ok(SpecifiedValue::Value(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
while let Ok(flag) = input.try(|input| {
|
||||||
let flag = match_ignore_ascii_case! { &ident,
|
Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
|
||||||
"ordinal" =>
|
"ordinal" =>
|
||||||
exclusive_value!((result, ORDINAL) => ORDINAL),
|
exclusive_value!((result, ORDINAL) => ORDINAL),
|
||||||
"slashed-zero" =>
|
"slashed-zero" =>
|
||||||
exclusive_value!((result, SLASHED_ZERO) => SLASHED_ZERO),
|
exclusive_value!((result, SLASHED_ZERO) => SLASHED_ZERO),
|
||||||
"lining-nums" =>
|
"lining-nums" =>
|
||||||
exclusive_value!((result, ${numeric_figure_values}) => LINING_NUMS ),
|
exclusive_value!((result, ${numeric_figure_values}) => LINING_NUMS),
|
||||||
"oldstyle-nums" =>
|
"oldstyle-nums" =>
|
||||||
exclusive_value!((result, ${numeric_figure_values}) => OLDSTYLE_NUMS),
|
exclusive_value!((result, ${numeric_figure_values}) => OLDSTYLE_NUMS),
|
||||||
"proportional-nums" =>
|
"proportional-nums" =>
|
||||||
|
@ -1850,12 +1848,9 @@ macro_rules! exclusive_value {
|
||||||
exclusive_value!((result, ${numeric_fraction_values}) => DIAGONAL_FRACTIONS),
|
exclusive_value!((result, ${numeric_fraction_values}) => DIAGONAL_FRACTIONS),
|
||||||
"stacked-fractions" =>
|
"stacked-fractions" =>
|
||||||
exclusive_value!((result, ${numeric_fraction_values}) => STACKED_FRACTIONS),
|
exclusive_value!((result, ${numeric_fraction_values}) => STACKED_FRACTIONS),
|
||||||
_ => None,
|
_ => return Err(()),
|
||||||
};
|
})
|
||||||
let flag = match flag {
|
}) {
|
||||||
Some(flag) => flag,
|
|
||||||
None => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
|
|
||||||
};
|
|
||||||
result.insert(flag);
|
result.insert(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,66 +232,105 @@
|
||||||
${'font-variant-numeric' if product == 'gecko' or data.testing else ''}
|
${'font-variant-numeric' if product == 'gecko' or data.testing else ''}
|
||||||
${'font-variant-position' if product == 'gecko' or data.testing else ''}"
|
${'font-variant-position' if product == 'gecko' or data.testing else ''}"
|
||||||
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
|
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
|
||||||
use properties::longhands::font_variant_caps;
|
|
||||||
<% gecko_sub_properties = "alternates east_asian ligatures numeric position".split() %>
|
<% gecko_sub_properties = "alternates east_asian ligatures numeric position".split() %>
|
||||||
% if product == "gecko" or data.testing:
|
<%
|
||||||
% for prop in gecko_sub_properties:
|
sub_properties = ["caps"]
|
||||||
use properties::longhands::font_variant_${prop};
|
if product == "gecko" or data.testing:
|
||||||
% endfor
|
sub_properties += gecko_sub_properties
|
||||||
% endif
|
%>
|
||||||
|
|
||||||
|
% for prop in sub_properties:
|
||||||
|
use properties::longhands::font_variant_${prop};
|
||||||
|
% endfor
|
||||||
|
|
||||||
pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||||
-> Result<Longhands, ParseError<'i>> {
|
-> Result<Longhands, ParseError<'i>> {
|
||||||
let mut nb_normals = 0;
|
% for prop in sub_properties:
|
||||||
let mut caps = None;
|
let mut ${prop} = None;
|
||||||
loop {
|
% endfor
|
||||||
// Special-case 'normal' because it is valid in each of
|
|
||||||
// all sub properties.
|
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||||||
// Leaves the values to None, 'normal' is the initial value for each of them.
|
// Leave the values to None, 'normal' is the initial value for all the sub properties.
|
||||||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
} else if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||||
nb_normals += 1;
|
// The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties
|
||||||
continue;
|
// to their initial value.
|
||||||
}
|
% if product == "gecko" or data.testing:
|
||||||
if caps.is_none() {
|
ligatures = Some(font_variant_ligatures::get_none_specified_value());
|
||||||
if let Ok(value) = input.try(|input| font_variant_caps::parse(context, input)) {
|
% endif
|
||||||
caps = Some(value);
|
} else {
|
||||||
continue
|
let mut has_custom_value: bool = false;
|
||||||
|
loop {
|
||||||
|
if input.try(|input| input.expect_ident_matching("normal")).is_ok() ||
|
||||||
|
input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||||
|
return Err(StyleParseError::UnspecifiedError.into())
|
||||||
}
|
}
|
||||||
|
% for prop in sub_properties:
|
||||||
|
if ${prop}.is_none() {
|
||||||
|
if let Ok(value) = input.try(|i| font_variant_${prop}::parse(context, i)) {
|
||||||
|
has_custom_value = true;
|
||||||
|
${prop} = Some(value);
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_custom_value {
|
||||||
|
return Err(StyleParseError::UnspecifiedError.into())
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn count<T>(opt: &Option<T>) -> u8 {
|
|
||||||
if opt.is_some() { 1 } else { 0 }
|
|
||||||
}
|
|
||||||
let count = count(&caps) + nb_normals;
|
|
||||||
if count == 0 || count > 1 {
|
|
||||||
return Err(StyleParseError::UnspecifiedError.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expanded! {
|
Ok(expanded! {
|
||||||
font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
|
% for prop in sub_properties:
|
||||||
// FIXME: Bug 1356134 - parse all sub properties.
|
font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
|
||||||
% if product == "gecko" or data.testing:
|
% endfor
|
||||||
% for name in gecko_sub_properties:
|
|
||||||
font_variant_${name}: font_variant_${name}::get_initial_specified_value(),
|
|
||||||
% endfor
|
|
||||||
% endif
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||||
|
#[allow(unused_assignments)]
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
|
||||||
% if product == "gecko" or data.testing:
|
let has_none_ligatures =
|
||||||
% for name in gecko_sub_properties:
|
% if product == "gecko" or data.testing:
|
||||||
// FIXME: Bug 1356134 - handle all sub properties.
|
self.font_variant_ligatures == &font_variant_ligatures::get_none_specified_value();
|
||||||
if self.font_variant_${name} != &font_variant_${name}::get_initial_specified_value() {
|
% else:
|
||||||
return Ok(());
|
false;
|
||||||
|
% endif
|
||||||
|
|
||||||
|
const TOTAL_SUBPROPS: usize = ${len(sub_properties)};
|
||||||
|
let mut nb_normals = 0;
|
||||||
|
% for prop in sub_properties:
|
||||||
|
if self.font_variant_${prop} == &font_variant_${prop}::get_initial_specified_value() {
|
||||||
|
nb_normals += 1;
|
||||||
}
|
}
|
||||||
% endfor
|
% endfor
|
||||||
% endif
|
|
||||||
|
|
||||||
self.font_variant_caps.to_css(dest)?;
|
|
||||||
|
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 self.font_variant_${prop} != &font_variant_${prop}::get_initial_specified_value() {
|
||||||
|
if has_any {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
}
|
||||||
|
has_any = true;
|
||||||
|
self.font_variant_${prop}.to_css(dest)?;
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue