diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py index de36199e77a..047d129a2ff 100644 --- a/components/style/properties/counted_unknown_properties.py +++ b/components/style/properties/counted_unknown_properties.py @@ -10,7 +10,8 @@ # "offset-distance", # "offset-path", # "offset-rotate", -# "offset" +# "offset", +# "text-underline-position", COUNTED_UNKNOWN_PROPERTIES = [ "-webkit-font-smoothing", "-webkit-tap-highlight-color", @@ -40,7 +41,6 @@ COUNTED_UNKNOWN_PROPERTIES = [ "baseline-shift", "-webkit-hyphenate-character", "page", - "text-underline-position", "-webkit-highlight", "background-repeat-x", "-webkit-padding-end", diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 87ec87a7fa5..15ab380789b 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -385,6 +385,7 @@ class Longhand(object): "TextDecorationLine", "TextEmphasisPosition", "TextTransform", + "TextUnderlinePosition", "TouchAction", "TransformStyle", "UserSelect", diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index ae568c99295..9784e2529e9 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -391,6 +391,18 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset", )} +// text underline position +${helpers.predefined_type( + "text-underline-position", + "TextUnderlinePosition", + "computed::TextUnderlinePosition::AUTO", + engines="gecko", + animation_value_type="discrete", + gecko_pref="layout.css.text-underline-position.enabled", + has_effect_on_gecko_scrollbars=False, + spec="https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property", +)} + // text decoration skip ink ${helpers.predefined_type( "text-decoration-skip-ink", diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 6e6f20ad0fe..bf3fb6cbb1a 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -79,6 +79,7 @@ pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing}; pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{TextDecorationLength, TextDecorationSkipInk}; +pub use self::text::TextUnderlinePosition; pub use self::time::Time; pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; pub use self::transform::{TransformOrigin, TransformStyle, Translate}; diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 4cccd248d1c..81f037843b0 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -22,6 +22,7 @@ pub use crate::values::specified::TextAlignKeyword as TextAlign; pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; pub use crate::values::specified::{TextDecorationSkipInk, TextTransform}; +pub use crate::values::specified::TextUnderlinePosition; /// A computed value for the `initial-letter` property. pub type InitialLetter = GenericInitialLetter; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index bc7a8203dee..856a7f0ecea 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -84,6 +84,7 @@ pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAl pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextTransform}; +pub use self::text::TextUnderlinePosition; pub use self::time::Time; pub use self::transform::{Rotate, Scale, Transform}; pub use self::transform::{TransformOrigin, TransformStyle, Translate}; diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index cc4bcee1a55..860a7f5caf8 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -1054,3 +1054,98 @@ impl TextDecorationLength { matches!(*self, GenericTextDecorationLength::Auto) } } + +bitflags! { + #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] + #[value_info(other_values = "auto,under,left,right")] + #[repr(C)] + /// Specified keyword values for the text-underline-position property. + /// (Non-exclusive, but not all combinations are allowed: only `under` may occur + /// together with either `left` or `right`.) + /// https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property + pub struct TextUnderlinePosition: u8 { + /// Use automatic positioning below the alphabetic baseline. + const AUTO = 0; + /// Below the glyph box. + const UNDER = 1 << 0; + /// In vertical mode, place to the left of the text. + const LEFT = 1 << 1; + /// In vertical mode, place to the right of the text. + const RIGHT = 1 << 2; + } +} + +impl Parse for TextUnderlinePosition { + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let mut result = TextUnderlinePosition::empty(); + + loop { + let location = input.current_source_location(); + let ident = match input.next() { + Ok(&Token::Ident(ref ident)) => ident, + Ok(other) => return Err(location.new_unexpected_token_error(other.clone())), + Err(..) => break, + }; + + match_ignore_ascii_case! { ident, + "auto" if result.is_empty() => { + return Ok(result); + }, + "under" if !result.intersects(TextUnderlinePosition::UNDER) => { + result.insert(TextUnderlinePosition::UNDER); + }, + "left" if !result.intersects(TextUnderlinePosition::LEFT | + TextUnderlinePosition::RIGHT) => { + result.insert(TextUnderlinePosition::LEFT); + }, + "right" if !result.intersects(TextUnderlinePosition::LEFT | + TextUnderlinePosition::RIGHT) => { + result.insert(TextUnderlinePosition::RIGHT); + }, + _ => return Err(location.new_custom_error( + SelectorParseErrorKind::UnexpectedIdent(ident.clone()) + )), + } + } + + if !result.is_empty() { + Ok(result) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + } +} + +impl ToCss for TextUnderlinePosition { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + if self.is_empty() { + return dest.write_str("auto"); + } + + let mut writer = SequenceWriter::new(dest, " "); + let mut any = false; + + macro_rules! maybe_write { + ($ident:ident => $str:expr) => { + if self.contains(TextUnderlinePosition::$ident) { + any = true; + writer.raw_item($str)?; + } + }; + } + + maybe_write!(UNDER => "under"); + maybe_write!(LEFT => "left"); + maybe_write!(RIGHT => "right"); + + debug_assert!(any); + + Ok(()) + } +}