diff --git a/components/style/cbindgen.toml b/components/style/cbindgen.toml index f36fd09a243..ae73adeda8c 100644 --- a/components/style/cbindgen.toml +++ b/components/style/cbindgen.toml @@ -105,6 +105,7 @@ include = [ "WordBreak", "Contain", "RestyleHint", + "TextDecorationLine", ] item_types = ["enums", "structs", "typedefs"] diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 30369dff62d..dbeedb9c7db 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3996,9 +3996,7 @@ fn static_assert() { <%self:impl_trait style_struct_name="Text" - skip_longhands="text-decoration-line text-overflow initial-letter"> - - ${impl_simple_type_with_conversion("text_decoration_line")} + skip_longhands="text-overflow initial-letter"> fn clear_overflow_sides_if_string(&mut self) { use crate::gecko_bindings::structs::nsStyleTextOverflowSide; @@ -4112,21 +4110,6 @@ fn static_assert() { InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink)) } } - - #[inline] - pub fn has_underline(&self) -> bool { - (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0 - } - - #[inline] - pub fn has_overline(&self) -> bool { - (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0 - } - - #[inline] - pub fn has_line_through(&self) -> bool { - (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0 - } // Set SVGPathData to StyleShapeSource. diff --git a/components/style/properties/longhands/text.mako.rs b/components/style/properties/longhands/text.mako.rs index 308de1b7575..723e26ae22b 100644 --- a/components/style/properties/longhands/text.mako.rs +++ b/components/style/properties/longhands/text.mako.rs @@ -5,16 +5,7 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> <% from data import Method %> -<% data.new_style_struct( - "Text", - inherited=False, - gecko_name="TextReset", - additional_methods=[ - Method("has_underline", "bool"), - Method("has_overline", "bool"), - Method("has_line_through", "bool"), - ] -) %> +<% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %> ${helpers.predefined_type( "text-overflow", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 40a5456c5e4..a470eb84b08 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2638,24 +2638,6 @@ pub mod style_structs { use crate::Zero; !self.outline_width.is_zero() } - % elif style_struct.name == "Text": - /// Whether the text decoration has an underline. - #[inline] - pub fn has_underline(&self) -> bool { - self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::UNDERLINE) - } - - /// Whether the text decoration has an overline. - #[inline] - pub fn has_overline(&self) -> bool { - self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::OVERLINE) - } - - /// Whether the text decoration has a line through. - #[inline] - pub fn has_line_through(&self) -> bool { - self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::LINE_THROUGH) - } % elif style_struct.name == "Box": /// Sets the display property, but without touching original_display, /// except when the adjustment comes from root or item display fixups. diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 911891bf33b..a1f68bf1a1b 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -19,7 +19,7 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; pub use crate::values::specified::TextAlignKeyword as TextAlign; -pub use crate::values::specified::TextEmphasisPosition; +pub use crate::values::specified::{TextEmphasisPosition, TextDecorationLine}; pub use crate::values::specified::{OverflowWrap, WordBreak}; /// A computed value for the `initial-letter` property. @@ -182,11 +182,11 @@ impl TextDecorationsInEffect { .clone(), }; - let text_style = style.get_text(); + let line = style.get_text().clone_text_decoration_line(); - result.underline |= text_style.has_underline(); - result.overline |= text_style.has_overline(); - result.line_through |= text_style.has_line_through(); + result.underline |= line.contains(TextDecorationLine::UNDERLINE); + result.overline |= line.contains(TextDecorationLine::OVERLINE); + result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH); result } diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 883084e649a..ab4e45fd1ee 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -22,8 +22,7 @@ use cssparser::{Parser, Token}; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; use style_traits::values::SequenceWriter; -use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; -use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use unicode_segmentation::UnicodeSegmentation; /// A specified type for the `initial-letter` property. @@ -255,119 +254,115 @@ impl ToComputedValue for TextOverflow { } } -macro_rules! impl_text_decoration_line { - { - $( - $(#[$($meta:tt)+])* - $ident:ident / $css:expr => $value:expr, - )+ - } => { - bitflags! { - #[derive(MallocSizeOf, ToComputedValue)] - /// Specified keyword values for the text-decoration-line property. - pub struct TextDecorationLine: u8 { - /// No text decoration line is specified - const NONE = 0; - $( - $(#[$($meta)+])* - const $ident = $value; - )+ - #[cfg(feature = "gecko")] - /// Only set by presentation attributes - /// - /// Setting this will mean that text-decorations use the color - /// specified by `color` in quirks mode. - /// - /// For example, this gives text - /// a red text decoration - const COLOR_OVERRIDE = 0x10; +bitflags! { + #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] + #[value_info(other_values = "none,underline,overline,line-through,blink")] + #[repr(C)] + /// Specified keyword values for the text-decoration-line property. + pub struct TextDecorationLine: u8 { + /// No text decoration line is specified. + const NONE = 0; + /// underline + const UNDERLINE = 1 << 0; + /// overline + const OVERLINE = 1 << 1; + /// line-through + const LINE_THROUGH = 1 << 2; + /// blink + const BLINK = 1 << 3; + /// Only set by presentation attributes + /// + /// Setting this will mean that text-decorations use the color + /// specified by `color` in quirks mode. + /// + /// For example, this gives text + /// a red text decoration + #[cfg(feature = "gecko")] + const COLOR_OVERRIDE = 0x10; + } +} + + +impl Parse for TextDecorationLine { + /// none | [ underline || overline || line-through || blink ] + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + 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(|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) } - impl Parse for TextDecorationLine { - /// none | [ underline || overline || line-through || blink ] - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let mut result = TextDecorationLine::NONE; - if input - .try(|input| input.expect_ident_matching("none")) - .is_ok() - { - return Ok(result); - } - - loop { - let result = input.try(|input| { - let ident = input.expect_ident().map_err(|_| ())?; - match_ignore_ascii_case! { ident, - $( - $css => { - if result.contains(TextDecorationLine::$ident) { - Err(()) - } else { - result.insert(TextDecorationLine::$ident); - Ok(()) - } - } - )+ - _ => Err(()), - } - }); - if result.is_err() { - break; - } - } - - if !result.is_empty() { - Ok(result) - } else { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - } - } - - impl ToCss for TextDecorationLine { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - if self.is_empty() { - return dest.write_str("none"); - } - - let mut writer = SequenceWriter::new(dest, " "); - $( - if self.contains(TextDecorationLine::$ident) { - writer.raw_item($css)?; - } - )+ - Ok(()) - } - } - - impl SpecifiedValueInfo for TextDecorationLine { - fn collect_completion_keywords(f: KeywordsCollectFn) { - f(&["none", $($css,)+]); - } + if !result.is_empty() { + Ok(result) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } } } -impl_text_decoration_line! { - /// Underline - UNDERLINE / "underline" => 1 << 0, - /// Overline - OVERLINE / "overline" => 1 << 1, - /// Line through - LINE_THROUGH / "line-through" => 1 << 2, - /// Blink - BLINK / "blink" => 1 << 3, -} +impl ToCss for TextDecorationLine { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + if self.is_empty() { + return dest.write_str("none"); + } -#[cfg(feature = "gecko")] -impl_bitflags_conversions!(TextDecorationLine); + 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 || *self == TextDecorationLine::COLOR_OVERRIDE); + + Ok(()) + } +} impl TextDecorationLine { #[inline]