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>
<%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
- }
%self:impl_trait>
// 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]