style: Allow to derive Parse/ToCss/SpecifiedValueInfo on bitflags

We keep getting this pattern of properties that have a set of joint and
disjoint flags, and copy-pasting or writing the same parsing and
serialization code in slightly different ways.

container-type is one such type, and I think we should have a single way
of dealing with this, thus implement deriving for various traits for
bitflags, with an attribute that says which flags are single vs mixed.

See docs and properties I ported. The remaining ones I left TODOs with,
they are a bit trickier but can be ported with some care.

Differential Revision: https://phabricator.services.mozilla.com/D142418
This commit is contained in:
Emilio Cobos Álvarez 2023-06-18 13:54:10 +02:00 committed by Martin Robinson
parent 19a43aa7da
commit f30837baf1
8 changed files with 222 additions and 312 deletions

View file

@ -232,8 +232,8 @@ impl ToComputedValue for TextOverflow {
}
bitflags! {
#[derive(MallocSizeOf, Serialize, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
#[value_info(other_values = "none,underline,overline,line-through,blink")]
#[derive(MallocSizeOf, Parse, Serialize, SpecifiedValueInfo, ToCss, ToComputedValue, ToResolvedValue, ToShmem)]
#[css(bitflags(single = "none", mixed = "underline,overline,line-through,blink"))]
#[repr(C)]
/// Specified keyword values for the text-decoration-line property.
pub struct TextDecorationLine: u8 {
@ -265,94 +265,6 @@ impl Default for TextDecorationLine {
}
}
impl Parse for TextDecorationLine {
/// none | [ underline || overline || line-through || blink ]
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let mut result = TextDecorationLine::empty();
// NOTE(emilio): this loop has this weird structure because we run this
// code to parse the text-decoration shorthand as well, so we need to
// ensure we don't return an error if we don't consume the whole thing
// because we find an invalid identifier or other kind of token.
loop {
let flag: Result<_, ParseError<'i>> = input.try_parse(|input| {
let flag = try_match_ident_ignore_ascii_case! { input,
"none" if result.is_empty() => TextDecorationLine::NONE,
"underline" => TextDecorationLine::UNDERLINE,
"overline" => TextDecorationLine::OVERLINE,
"line-through" => TextDecorationLine::LINE_THROUGH,
"blink" => TextDecorationLine::BLINK,
};
Ok(flag)
});
let flag = match flag {
Ok(flag) => flag,
Err(..) => break,
};
if flag.is_empty() {
return Ok(TextDecorationLine::NONE);
}
if result.contains(flag) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
result.insert(flag)
}
if !result.is_empty() {
Ok(result)
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
}
impl ToCss for TextDecorationLine {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if self.is_empty() {
return dest.write_str("none");
}
#[cfg(feature = "gecko")]
{
if *self == TextDecorationLine::COLOR_OVERRIDE {
return Ok(());
}
}
let mut writer = SequenceWriter::new(dest, " ");
let mut any = false;
macro_rules! maybe_write {
($ident:ident => $str:expr) => {
if self.contains(TextDecorationLine::$ident) {
any = true;
writer.raw_item($str)?;
}
};
}
maybe_write!(UNDERLINE => "underline");
maybe_write!(OVERLINE => "overline");
maybe_write!(LINE_THROUGH => "line-through");
maybe_write!(BLINK => "blink");
debug_assert!(any);
Ok(())
}
}
impl TextDecorationLine {
#[inline]
/// Returns the initial value of text-decoration-line
@ -399,6 +311,7 @@ impl TextTransform {
}
}
// TODO: This can be simplified by deriving it.
impl Parse for TextTransform {
fn parse<'i, 't>(
_context: &ParserContext,
@ -1194,6 +1107,7 @@ bitflags! {
}
}
// TODO: This can be derived with some care.
impl Parse for TextUnderlinePosition {
fn parse<'i, 't>(
_context: &ParserContext,