Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  text.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Specified types for text properties.

use crate::parser::{Parse, ParserContext};
use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
use crate::values::computed;
use crate::values::computed::text::TextEmphasisStyle as ComputedTextEmphasisStyle;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::NumberOrAuto;
use crate::values::generics::text::{
    GenericHyphenateLimitChars, GenericInitialLetter, GenericTextDecorationLength, GenericTextIndent,
};
use crate::values::specified::length::LengthPercentage;
use crate::values::specified::{AllowQuirks, Integer, Number};
use crate::Zero;
use cssparser::Parser;
use icu_segmenter::GraphemeClusterSegmenter;
use std::fmt::{self, Write};
use style_traits::values::SequenceWriter;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use style_traits::{KeywordsCollectFn, SpecifiedValueInfo};

/// A specified type for the `initial-letter` property.
pub type InitialLetter = GenericInitialLetter<Number, Integer>;

/// A spacing value used by either the `letter-spacing` or `word-spacing` properties.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub enum Spacing {
    /// `normal`
    Normal,
    /// `<value>`
    Value(LengthPercentage),
}

impl Parse for Spacing {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input
            .try_parse(|i| i.expect_ident_matching("normal"))
            .is_ok()
        {
            return Ok(Spacing::Normal);
        }
        LengthPercentage::parse_quirky(context, input, AllowQuirks::Yes).map(Spacing::Value)
    }
}

/// A specified value for the `letter-spacing` property.
#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub struct LetterSpacing(pub Spacing);

impl ToComputedValue for LetterSpacing {
    type ComputedValue = computed::LetterSpacing;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        use computed::text::GenericLetterSpacing;
        match self.0 {
            Spacing::Normal => GenericLetterSpacing(computed::LengthPercentage::zero()),
            Spacing::Value(ref v) => GenericLetterSpacing(v.to_computed_value(context)),
        }
    }

    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        if computed.0.is_zero() {
            return LetterSpacing(Spacing::Normal);
        }
        LetterSpacing(Spacing::Value(ToComputedValue::from_computed_value(&computed.0)))
    }
}


/// A specified value for the `word-spacing` property.
#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub struct WordSpacing(pub Spacing);

impl ToComputedValue for WordSpacing {
    type ComputedValue = computed::WordSpacing;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match self.0 {
            Spacing::Normal => computed::LengthPercentage::zero(),
            Spacing::Value(ref v) => v.to_computed_value(context),
        }
    }

    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        WordSpacing(Spacing::Value(ToComputedValue::from_computed_value(computed)))
    }
}

/// A value for the `hyphenate-character` property.
#[derive(
    Clone,
    Debug,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum HyphenateCharacter {
    /// `auto`
    Auto,
    /// `<string>`
    String(crate::OwnedStr),
}

/// A value for the `hyphenate-limit-chars` property.
pub type HyphenateLimitChars = GenericHyphenateLimitChars<Integer>;

impl Parse for HyphenateLimitChars {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        type IntegerOrAuto = NumberOrAuto<Integer>;

        let total_word_length = IntegerOrAuto::parse(context, input)?;
        let pre_hyphen_length = input.try_parse(|i| IntegerOrAuto::parse(context, i)).unwrap_or(IntegerOrAuto::Auto);
        let post_hyphen_length = input.try_parse(|i| IntegerOrAuto::parse(context, i)).unwrap_or(pre_hyphen_length);
        Ok(Self {
            total_word_length,
            pre_hyphen_length,
            post_hyphen_length,
        })
    }
}

impl Parse for InitialLetter {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input
            .try_parse(|i| i.expect_ident_matching("normal"))
            .is_ok()
        {
            return Ok(Self::normal());
        }
        let size = Number::parse_at_least_one(context, input)?;
        let sink = input
            .try_parse(|i| Integer::parse_positive(context, i))
            .unwrap_or_else(|_| crate::Zero::zero());
        Ok(Self { size, sink })
    }
}

/// A generic value for the `text-overflow` property.
#[derive(
    Clone,
    Debug,
    Eq,
    MallocSizeOf,
    PartialEq,
    Parse,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum TextOverflowSide {
    /// Clip inline content.
    Clip,
    /// Render ellipsis to represent clipped inline content.
    Ellipsis,
    /// Render a given string to represent clipped inline content.
    String(crate::values::AtomString),
}

#[derive(
    Clone,
    Debug,
    Eq,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
/// text-overflow.
/// When the specified value only has one side, that's the "second"
/// side, and the sides are logical, so "second" means "end".  The
/// start side is Clip in that case.
///
/// When the specified value has two sides, those are our "first"
/// and "second" sides, and they are physical sides ("left" and
/// "right").
pub struct TextOverflow {
    /// First side
    pub first: TextOverflowSide,
    /// Second side
    pub second: TextOverflowSide,
    /// True if the specified value only has one side.
    pub sides_are_logical: bool,
}

impl Parse for TextOverflow {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<TextOverflow, ParseError<'i>> {
        let first = TextOverflowSide::parse(context, input)?;
        Ok(
            if let Ok(second) = input.try_parse(|input| TextOverflowSide::parse(context, input)) {
                Self {
                    first,
                    second,
                    sides_are_logical: false,
                }
            } else {
                Self {
                    first: TextOverflowSide::Clip,
                    second: first,
                    sides_are_logical: true,
                }
            },
        )
    }
}

impl TextOverflow {
    /// Returns the initial `text-overflow` value
    pub fn get_initial_value() -> TextOverflow {
        TextOverflow {
            first: TextOverflowSide::Clip,
            second: TextOverflowSide::Clip,
            sides_are_logical: true,
        }
    }
}

impl ToCss for TextOverflow {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        if self.sides_are_logical {
            debug_assert_eq!(self.first, TextOverflowSide::Clip);
            self.second.to_css(dest)?;
        } else {
            self.first.to_css(dest)?;
            dest.write_char(' ')?;
            self.second.to_css(dest)?;
        }
        Ok(())
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    PartialEq,
    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);
bitflags! {
    impl 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 <a href=foo><font color="red">text</font></a>
        /// a red text decoration
        #[cfg(feature = "gecko")]
        const COLOR_OVERRIDE = 0x10;
    }
}

impl Default for TextDecorationLine {
    fn default() -> Self {
        TextDecorationLine::NONE
    }
}

impl TextDecorationLine {
    #[inline]
    /// Returns the initial value of text-decoration-line
    pub fn none() -> Self {
        TextDecorationLine::NONE
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
/// Specified keyword values for case transforms in the text-transform property. (These are exclusive.)
pub enum TextTransformCase {
    /// No case transform.
    None,
    /// All uppercase.
    Uppercase,
    /// All lowercase.
    Lowercase,
    /// Capitalize each word.
    Capitalize,
    /// Automatic italicization of math variables.
    #[cfg(feature = "gecko")]
    MathAuto,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    PartialEq,
    Parse,
    Serialize,
    SpecifiedValueInfo,
    ToCss,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[cfg_attr(feature = "gecko", css(bitflags(
    single = "none,math-auto",
    mixed = "uppercase,lowercase,capitalize,full-width,full-size-kana",
    validate_mixed = "Self::validate_mixed_flags",
)))]
#[cfg_attr(not(feature = "gecko"), css(bitflags(
    single = "none",
    mixed = "uppercase,lowercase,capitalize,full-width,full-size-kana",
    validate_mixed = "Self::validate_mixed_flags",
)))]
#[repr(C)]
/// Specified value for the text-transform property.
/// (The spec grammar gives
/// `none | math-auto | [capitalize | uppercase | lowercase] || full-width || full-size-kana`.)
/// https://drafts.csswg.org/css-text-4/#text-transform-property
pub struct TextTransform(u8);
bitflags! {
    impl TextTransform: u8 {
        /// none
        const NONE = 0;
        /// All uppercase.
        const UPPERCASE = 1 << 0;
        /// All lowercase.
        const LOWERCASE = 1 << 1;
        /// Capitalize each word.
        const CAPITALIZE = 1 << 2;
        /// Automatic italicization of math variables.
        const MATH_AUTO = 1 << 3;

        /// All the case transforms, which are exclusive with each other.
        const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0 | Self::MATH_AUTO.0;

        /// full-width
        const FULL_WIDTH = 1 << 4;
        /// full-size-kana
        const FULL_SIZE_KANA = 1 << 5;
    }
}

impl TextTransform {
    /// Returns the initial value of text-transform
    #[inline]
    pub fn none() -> Self {
        Self::NONE
    }

    /// Returns whether the value is 'none'
    #[inline]
    pub fn is_none(self) -> bool {
        self == Self::NONE
    }

    fn validate_mixed_flags(&self) -> bool {
        let case = self.intersection(Self::CASE_TRANSFORMS);
        // Case bits are exclusive with each other.
        case.is_empty() || case.bits().is_power_of_two()
    }
}

/// Specified and computed value of text-align-last.
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    FromPrimitive,
    Hash,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
#[repr(u8)]
pub enum TextAlignLast {
    Auto,
    Start,
    End,
    Left,
    Right,
    Center,
    Justify,
}

/// Specified value of text-align keyword value.
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    FromPrimitive,
    Hash,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
#[repr(u8)]
pub enum TextAlignKeyword {
    Start,
    Left,
    Right,
    Center,
    Justify,
    End,
    #[parse(aliases = "-webkit-center")]
    MozCenter,
    #[parse(aliases = "-webkit-left")]
    MozLeft,
    #[parse(aliases = "-webkit-right")]
    MozRight,
}

/// Specified value of text-align property.
#[derive(
    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
)]
pub enum TextAlign {
    /// Keyword value of text-align property.
    Keyword(TextAlignKeyword),
    /// `match-parent` value of text-align property. It has a different handling
    /// unlike other keywords.
    #[cfg(feature = "gecko")]
    MatchParent,
    /// This is how we implement the following HTML behavior from
    /// https://html.spec.whatwg.org/#tables-2:
    ///
    ///     User agents are expected to have a rule in their user agent style sheet
    ///     that matches th elements that have a parent node whose computed value
    ///     for the 'text-align' property is its initial value, whose declaration
    ///     block consists of just a single declaration that sets the 'text-align'
    ///     property to the value 'center'.
    ///
    /// Since selectors can't depend on the ancestor styles, we implement it with a
    /// magic value that computes to the right thing. Since this is an
    /// implementation detail, it shouldn't be exposed to web content.
    #[cfg(feature = "gecko")]
    #[parse(condition = "ParserContext::chrome_rules_enabled")]
    MozCenterOrInherit,
}

impl ToComputedValue for TextAlign {
    type ComputedValue = TextAlignKeyword;

    #[inline]
    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
        match *self {
            TextAlign::Keyword(key) => key,
            #[cfg(feature = "gecko")]
            TextAlign::MatchParent => {
                // on the root <html> element we should still respect the dir
                // but the parent dir of that element is LTR even if it's <html dir=rtl>
                // and will only be RTL if certain prefs have been set.
                // In that case, the default behavior here will set it to left,
                // but we want to set it to right -- instead set it to the default (`start`),
                // which will do the right thing in this case (but not the general case)
                if _context.builder.is_root_element {
                    return TextAlignKeyword::Start;
                }
                let parent = _context
                    .builder
                    .get_parent_inherited_text()
                    .clone_text_align();
                let ltr = _context.builder.inherited_writing_mode().is_bidi_ltr();
                match (parent, ltr) {
                    (TextAlignKeyword::Start, true) => TextAlignKeyword::Left,
                    (TextAlignKeyword::Start, false) => TextAlignKeyword::Right,
                    (TextAlignKeyword::End, true) => TextAlignKeyword::Right,
                    (TextAlignKeyword::End, false) => TextAlignKeyword::Left,
                    _ => parent,
                }
            },
            #[cfg(feature = "gecko")]
            TextAlign::MozCenterOrInherit => {
                let parent = _context
                    .builder
                    .get_parent_inherited_text()
                    .clone_text_align();
                if parent == TextAlignKeyword::Start {
                    TextAlignKeyword::Center
                } else {
                    parent
                }
            },
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        TextAlign::Keyword(*computed)
    }
}

fn fill_mode_is_default_and_shape_exists(
    fill: &TextEmphasisFillMode,
    shape: &Option<TextEmphasisShapeKeyword>,
) -> bool {
    shape.is_some() && fill.is_filled()
}

/// Specified value of text-emphasis-style property.
///
/// https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[allow(missing_docs)]
pub enum TextEmphasisStyle {
    /// [ <fill> || <shape> ]
    Keyword {
        #[css(contextual_skip_if = "fill_mode_is_default_and_shape_exists")]
        fill: TextEmphasisFillMode,
        shape: Option<TextEmphasisShapeKeyword>,
    },
    /// `none`
    None,
    /// `<string>` (of which only the first grapheme cluster will be used).
    String(crate::OwnedStr),
}

/// Fill mode for the text-emphasis-style property
#[derive(
    Clone,
    Copy,
    Debug,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToCss,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(u8)]
pub enum TextEmphasisFillMode {
    /// `filled`
    Filled,
    /// `open`
    Open,
}

impl TextEmphasisFillMode {
    /// Whether the value is `filled`.
    #[inline]
    pub fn is_filled(&self) -> bool {
        matches!(*self, TextEmphasisFillMode::Filled)
    }
}

/// Shape keyword for the text-emphasis-style property
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToCss,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(u8)]
pub enum TextEmphasisShapeKeyword {
    /// `dot`
    Dot,
    /// `circle`
    Circle,
    /// `double-circle`
    DoubleCircle,
    /// `triangle`
    Triangle,
    /// `sesame`
    Sesame,
}

impl ToComputedValue for TextEmphasisStyle {
    type ComputedValue = ComputedTextEmphasisStyle;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            TextEmphasisStyle::Keyword { fill, shape } => {
                let shape = shape.unwrap_or_else(|| {
                    // FIXME(emilio, bug 1572958): This should set the
                    // rule_cache_conditions properly.
                    //
                    // Also should probably use WritingMode::is_vertical rather
                    // than the computed value of the `writing-mode` property.
                    if context.style().get_inherited_box().clone_writing_mode() ==
                        SpecifiedWritingMode::HorizontalTb
                    {
                        TextEmphasisShapeKeyword::Circle
                    } else {
                        TextEmphasisShapeKeyword::Sesame
                    }
                });
                ComputedTextEmphasisStyle::Keyword { fill, shape }
            },
            TextEmphasisStyle::None => ComputedTextEmphasisStyle::None,
            TextEmphasisStyle::String(ref s) => {
                // FIXME(emilio): Doing this at computed value time seems wrong.
                // The spec doesn't say that this should be a computed-value
                // time operation. This is observable from getComputedStyle().
                //
                // Note that the first grapheme cluster boundary should always be the start of the string.
                let first_grapheme_end = GraphemeClusterSegmenter::new()
                    .segment_str(s)
                    .nth(1)
                    .unwrap_or(0);
                ComputedTextEmphasisStyle::String(s[0..first_grapheme_end].to_string().into())
            },
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            ComputedTextEmphasisStyle::Keyword { fill, shape } => TextEmphasisStyle::Keyword {
                fill,
                shape: Some(shape),
            },
            ComputedTextEmphasisStyle::None => TextEmphasisStyle::None,
            ComputedTextEmphasisStyle::String(ref string) => {
                TextEmphasisStyle::String(string.clone())
            },
        }
    }
}

impl Parse for TextEmphasisStyle {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input
            .try_parse(|input| input.expect_ident_matching("none"))
            .is_ok()
        {
            return Ok(TextEmphasisStyle::None);
        }

        if let Ok(s) = input.try_parse(|i| i.expect_string().map(|s| s.as_ref().to_owned())) {
            // Handle <string>
            return Ok(TextEmphasisStyle::String(s.into()));
        }

        // Handle a pair of keywords
        let mut shape = input.try_parse(TextEmphasisShapeKeyword::parse).ok();
        let fill = input.try_parse(TextEmphasisFillMode::parse).ok();
        if shape.is_none() {
            shape = input.try_parse(TextEmphasisShapeKeyword::parse).ok();
        }

        if shape.is_none() && fill.is_none() {
            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        // If a shape keyword is specified but neither filled nor open is
        // specified, filled is assumed.
        let fill = fill.unwrap_or(TextEmphasisFillMode::Filled);

        // We cannot do the same because the default `<shape>` depends on the
        // computed writing-mode.
        Ok(TextEmphasisStyle::Keyword { fill, shape })
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    PartialEq,
    Parse,
    Serialize,
    SpecifiedValueInfo,
    ToCss,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
#[css(bitflags(
    single = "auto",
    mixed = "over,under,left,right",
    validate_mixed = "Self::validate_and_simplify"
))]
/// Values for text-emphasis-position:
/// <https://drafts.csswg.org/css-text-decor/#text-emphasis-position-property>
pub struct TextEmphasisPosition(u8);
bitflags! {
    impl TextEmphasisPosition: u8 {
        /// Automatically choose mark position based on language.
        const AUTO = 1 << 0;
        /// Draw marks over the text in horizontal writing mode.
        const OVER = 1 << 1;
        /// Draw marks under the text in horizontal writing mode.
        const UNDER = 1 << 2;
        /// Draw marks to the left of the text in vertical writing mode.
        const LEFT = 1 << 3;
        /// Draw marks to the right of the text in vertical writing mode.
        const RIGHT = 1 << 4;
    }
}

impl TextEmphasisPosition {
    fn validate_and_simplify(&mut self) -> bool {
        // Require one but not both of 'over' and 'under'.
        if self.intersects(Self::OVER) == self.intersects(Self::UNDER) {
            return false;
        }

        // If 'left' is present, 'right' must be absent.
        if self.intersects(Self::LEFT) {
            return !self.intersects(Self::RIGHT);
        }

        self.remove(Self::RIGHT); // Right is the default
        true
    }
}

/// Values for the `word-break` property.
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
pub enum WordBreak {
    Normal,
    BreakAll,
    KeepAll,
    /// The break-word value, needed for compat.
    ///
    /// Specifying `word-break: break-word` makes `overflow-wrap` behave as
    /// `anywhere`, and `word-break` behave like `normal`.
    #[cfg(feature = "gecko")]
    BreakWord,
}

/// Values for the `text-justify` CSS property.
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
pub enum TextJustify {
    Auto,
    None,
    InterWord,
    // See https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
    // and https://github.com/w3c/csswg-drafts/issues/6156 for the alias.
    #[parse(aliases = "distribute")]
    InterCharacter,
}

/// Values for the `-moz-control-character-visibility` CSS property.
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
pub enum MozControlCharacterVisibility {
    Hidden,
    Visible,
}

#[cfg(feature = "gecko")]
impl Default for MozControlCharacterVisibility {
    fn default() -> Self {
        if static_prefs::pref!("layout.css.control-characters.visible") {
            Self::Visible
        } else {
            Self::Hidden
        }
    }
}

/// Values for the `line-break` property.
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
pub enum LineBreak {
    Auto,
    Loose,
    Normal,
    Strict,
    Anywhere,
}

/// Values for the `overflow-wrap` property.
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
pub enum OverflowWrap {
    Normal,
    BreakWord,
    Anywhere,
}

/// A specified value for the `text-indent` property
/// which takes the grammar of [<length-percentage>] && hanging? && each-line?
///
/// https://drafts.csswg.org/css-text/#propdef-text-indent
pub type TextIndent = GenericTextIndent<LengthPercentage>;

impl Parse for TextIndent {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let mut length = None;
        let mut hanging = false;
        let mut each_line = false;

        // The length-percentage and the two possible keywords can occur in any order.
        while !input.is_exhausted() {
            // If we haven't seen a length yet, try to parse one.
            if length.is_none() {
                if let Ok(len) = input
                    .try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
                {
                    length = Some(len);
                    continue;
                }
            }

            if static_prefs::pref!("layout.css.text-indent-keywords.enabled") {
                // Check for the keywords (boolean flags).
                try_match_ident_ignore_ascii_case! { input,
                    "hanging" if !hanging => hanging = true,
                    "each-line" if !each_line => each_line = true,
                }
                continue;
            }

            // If we reach here, there must be something that we failed to parse;
            // just break and let the caller deal with it.
            break;
        }

        // The length-percentage value is required for the declaration to be valid.
        if let Some(length) = length {
            Ok(Self {
                length,
                hanging,
                each_line,
            })
        } else {
            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }
    }
}

/// Implements text-decoration-skip-ink which takes the keywords auto | none | all
///
/// https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property
#[repr(u8)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[allow(missing_docs)]
pub enum TextDecorationSkipInk {
    Auto,
    None,
    All,
}

/// Implements type for `text-decoration-thickness` property
pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;

impl TextDecorationLength {
    /// `Auto` value.
    #[inline]
    pub fn auto() -> Self {
        GenericTextDecorationLength::Auto
    }

    /// Whether this is the `Auto` value.
    #[inline]
    pub fn is_auto(&self) -> bool {
        matches!(*self, GenericTextDecorationLength::Auto)
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[css(bitflags(
    single = "auto",
    mixed = "from-font,under,left,right",
    validate_mixed = "Self::validate_mixed_flags",
))]
#[repr(C)]
/// Specified keyword values for the text-underline-position property.
/// (Non-exclusive, but not all combinations are allowed: the spec grammar gives
/// `auto | [ from-font | under ] || [ left | right ]`.)
/// https://drafts.csswg.org/css-text-decor-4/#text-underline-position-property
pub struct TextUnderlinePosition(u8);
bitflags! {
    impl TextUnderlinePosition: u8 {
        /// Use automatic positioning below the alphabetic baseline.
        const AUTO = 0;
        /// Use underline position from the first available font.
        const FROM_FONT = 1 << 0;
        /// Below the glyph box.
        const UNDER = 1 << 1;
        /// In vertical mode, place to the left of the text.
        const LEFT = 1 << 2;
        /// In vertical mode, place to the right of the text.
        const RIGHT = 1 << 3;
    }
}

impl TextUnderlinePosition {
    fn validate_mixed_flags(&self) -> bool {
        if self.contains(Self::LEFT | Self::RIGHT) {
            // left and right can't be mixed together.
            return false;
        }
        if self.contains(Self::FROM_FONT | Self::UNDER) {
            // from-font and under can't be mixed together either.
            return false;
        }
        true
    }
}

impl ToCss for TextUnderlinePosition {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> 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!(FROM_FONT => "from-font");
        maybe_write!(UNDER => "under");
        maybe_write!(LEFT => "left");
        maybe_write!(RIGHT => "right");

        debug_assert!(any);

        Ok(())
    }
}

/// Values for `ruby-position` property
#[repr(u8)]
#[derive(
    Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[allow(missing_docs)]
pub enum RubyPosition {
    AlternateOver,
    AlternateUnder,
    Over,
    Under,
}

impl Parse for RubyPosition {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<RubyPosition, ParseError<'i>> {
        // Parse alternate before
        let alternate = input
            .try_parse(|i| i.expect_ident_matching("alternate"))
            .is_ok();
        if alternate && input.is_exhausted() {
            return Ok(RubyPosition::AlternateOver);
        }
        // Parse over / under
        let over = try_match_ident_ignore_ascii_case! { input,
            "over" => true,
            "under" => false,
        };
        // Parse alternate after
        let alternate = alternate ||
            input
                .try_parse(|i| i.expect_ident_matching("alternate"))
                .is_ok();

        Ok(match (over, alternate) {
            (true, true) => RubyPosition::AlternateOver,
            (false, true) => RubyPosition::AlternateUnder,
            (true, false) => RubyPosition::Over,
            (false, false) => RubyPosition::Under,
        })
    }
}

impl ToCss for RubyPosition {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        dest.write_str(match self {
            RubyPosition::AlternateOver => "alternate",
            RubyPosition::AlternateUnder => "alternate under",
            RubyPosition::Over => "over",
            RubyPosition::Under => "under",
        })
    }
}

impl SpecifiedValueInfo for RubyPosition {
    fn collect_completion_keywords(f: KeywordsCollectFn) {
        f(&["alternate", "over", "under"])
    }
}

[ Dauer der Verarbeitung: 0.37 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge