/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Specified types for text properties.
usecrate::parser::{Parse, ParserContext}; usecrate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode; usecrate::values::computed; usecrate::values::computed::text::TextEmphasisStyle as ComputedTextEmphasisStyle; usecrate::values::computed::{Context, ToComputedValue}; usecrate::values::generics::NumberOrAuto; usecrate::values::generics::text::{
GenericHyphenateLimitChars, GenericInitialLetter, GenericTextDecorationLength, GenericTextIndent,
}; usecrate::values::specified::length::LengthPercentage; usecrate::values::specified::{AllowQuirks, Integer, Number}; usecrate::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. pubtype 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)] pubenum Spacing { /// `normal`
Normal, /// `<value>`
Value(LengthPercentage),
}
/// A specified value for the `letter-spacing` property. #[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pubstruct LetterSpacing(pub Spacing);
impl ToComputedValue for LetterSpacing { type ComputedValue = computed::LetterSpacing;
/// A specified value for the `word-spacing` property. #[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pubstruct WordSpacing(pub Spacing);
impl ToComputedValue for WordSpacing { type ComputedValue = computed::WordSpacing;
/// A generic value for the `text-overflow` property. #[derive(
Clone,
Debug,
Eq,
MallocSizeOf,
PartialEq,
Parse,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(C, u8)] pubenum 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"). pubstruct 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 TextDecorationLine { #[inline] /// Returns the initial value of text-decoration-line pubfn 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.) pubenum TextTransformCase { /// No case transform.
None, /// All uppercase.
Uppercase, /// All lowercase.
Lowercase, /// Capitalize each word.
Capitalize, /// Automatic italicization of math variables. #[cfg(feature = "gecko")]
MathAuto,
}
/// 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;
impl TextTransform { /// Returns the initial value of text-transform #[inline] pubfn none() -> Self { Self::NONE
}
/// Returns whether the value is 'none' #[inline] pubfn 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)] pubenum TextAlignLast {
Auto,
Start,
End,
Left,
Right,
Center,
Justify,
}
/// Specified value of text-align property. #[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
)] pubenum 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
}
},
}
}
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())
},
}
}
// Handle a pair of keywords letmut 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> pubstruct 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(&mutself) -> bool { // Require one but not both of 'over' and 'under'. ifself.intersects(Self::OVER) == self.intersects(Self::UNDER) { returnfalse;
}
// If 'left' is present, 'right' must be absent. ifself.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)] pubenum 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)] pubenum 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,
}
/// 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 pubtype TextIndent = GenericTextIndent<LengthPercentage>;
// 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() { iflet 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. iflet 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)] pubenum TextDecorationSkipInk {
Auto,
None,
All,
}
/// Implements type for `text-decoration-thickness` property pubtype TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
/// Whether this is the `Auto` value. #[inline] pubfn 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 pubstruct 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 { ifself.contains(Self::LEFT | Self::RIGHT) { // left and right can't be mixed together. returnfalse;
} ifself.contains(Self::FROM_FONT | Self::UNDER) { // from-font and under can't be mixed together either. returnfalse;
} true
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.