/* 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/. */
usecrate::parser::{Parse, ParserContext}; usecrate::selector_map::PrecomputedHashMap; usecrate::str::HTML_SPACE_CHARACTERS; usecrate::values::computed::LengthPercentage as ComputedLengthPercentage; usecrate::values::computed::{Context, Percentage, ToComputedValue}; usecrate::values::generics::position::Position as GenericPosition; usecrate::values::generics::position::PositionComponent as GenericPositionComponent; usecrate::values::generics::position::PositionOrAuto as GenericPositionOrAuto; usecrate::values::generics::position::ZIndex as GenericZIndex; usecrate::values::generics::position::{AnchorSide, AspectRatio as GenericAspectRatio}; usecrate::values::generics::position::{GenericAnchorFunction, GenericInset}; usecrate::values::specified; usecrate::values::specified::{AllowQuirks, Integer, LengthPercentage, NonNegativeNumber}; usecrate::values::DashedIdent; usecrate::{Atom, Zero}; use cssparser::Parser; use selectors::parser::SelectorParseErrorKind; use servo_arc::Arc; use smallvec::{smallvec, SmallVec}; use std::collections::hash_map::Entry; use std::fmt::{self, Write}; use style_traits::arc_slice::ArcSlice; use style_traits::values::specified::AllowedNumericType; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
/// The specified value of a CSS `<position>` pubtype Position = GenericPosition<HorizontalPosition, VerticalPosition>;
/// The specified value of an `auto | <position>`. pubtype PositionOrAuto = GenericPositionOrAuto<Position>;
/// The specified value of a horizontal position. pubtype HorizontalPosition = PositionComponent<HorizontalPositionKeyword>;
/// The specified value of a vertical position. pubtype VerticalPosition = PositionComponent<VerticalPositionKeyword>;
/// The specified value of a component of a CSS `<position>`. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pubenum PositionComponent<S> { /// `center`
Center, /// `<length-percentage>`
Length(LengthPercentage), /// `<side> <length-percentage>?`
Side(S, Option<LengthPercentage>),
}
/// A keyword for the X direction. #[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[allow(missing_docs)] #[repr(u8)] pubenum HorizontalPositionKeyword {
Left,
Right,
}
/// A keyword for the Y direction. #[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[allow(missing_docs)] #[repr(u8)] pubenum VerticalPositionKeyword {
Top,
Bottom,
}
impl Parse for Position { fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { let position = Self::parse_three_value_quirky(context, input, AllowQuirks::No)?; if position.is_three_value_syntax() { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(position)
}
}
impl Position { /// Parses a `<bg-position>`, with quirks. pubfn parse_three_value_quirky<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> { match input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {
Ok(x_pos @ PositionComponent::Center) => { iflet Ok(y_pos) =
input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
{ return Ok(Self::new(x_pos, y_pos));
} let x_pos = input
.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
.unwrap_or(x_pos); let y_pos = PositionComponent::Center; return Ok(Self::new(x_pos, y_pos));
},
Ok(PositionComponent::Side(x_keyword, lp)) => { if input
.try_parse(|i| i.expect_ident_matching("center"))
.is_ok()
{ let x_pos = PositionComponent::Side(x_keyword, lp); let y_pos = PositionComponent::Center; return Ok(Self::new(x_pos, y_pos));
} iflet Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) { let y_lp = input
.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
.ok(); let x_pos = PositionComponent::Side(x_keyword, lp); let y_pos = PositionComponent::Side(y_keyword, y_lp); return Ok(Self::new(x_pos, y_pos));
} let x_pos = PositionComponent::Side(x_keyword, None); let y_pos = lp.map_or(PositionComponent::Center, PositionComponent::Length); return Ok(Self::new(x_pos, y_pos));
},
Ok(x_pos @ PositionComponent::Length(_)) => { iflet Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) { let y_pos = PositionComponent::Side(y_keyword, None); return Ok(Self::new(x_pos, y_pos));
} iflet Ok(y_lp) =
input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
{ let y_pos = PositionComponent::Length(y_lp); return Ok(Self::new(x_pos, y_pos));
} let y_pos = PositionComponent::Center; let _ = input.try_parse(|i| i.expect_ident_matching("center")); return Ok(Self::new(x_pos, y_pos));
},
Err(_) => {},
} let y_keyword = VerticalPositionKeyword::parse(input)?; let lp_and_x_pos: Result<_, ParseError> = input.try_parse(|i| { let y_lp = i
.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
.ok(); iflet Ok(x_keyword) = i.try_parse(HorizontalPositionKeyword::parse) { let x_lp = i
.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
.ok(); let x_pos = PositionComponent::Side(x_keyword, x_lp); return Ok((y_lp, x_pos));
};
i.expect_ident_matching("center")?; let x_pos = PositionComponent::Center;
Ok((y_lp, x_pos))
}); iflet Ok((y_lp, x_pos)) = lp_and_x_pos { let y_pos = PositionComponent::Side(y_keyword, y_lp); return Ok(Self::new(x_pos, y_pos));
} let x_pos = PositionComponent::Center; let y_pos = PositionComponent::Side(y_keyword, None);
Ok(Self::new(x_pos, y_pos))
}
impl<S: Side> PositionComponent<S> { /// The initial specified value of a position component, i.e. the start side. pubfn initial_specified_value() -> Self {
PositionComponent::Side(S::start(), None)
}
}
/// Returns whether this is the `none` value. pubfn is_none(&self) -> bool { self.0.is_empty()
}
}
impl Parse for AnchorName { fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); let first = input.expect_ident()?; if first.eq_ignore_ascii_case("none") { return Ok(Self::none());
} // The common case is probably just to have a single anchor name, so // space for four on the stack should be plenty. letmut idents: SmallVec<[DashedIdent; 4]> =
smallvec![DashedIdent::from_ident(location, first,)?]; while input.try_parse(|input| input.expect_comma()).is_ok() {
idents.push(DashedIdent::parse(context, input)?);
}
Ok(AnchorName(ArcSlice::from_iter(idents.drain(..))))
}
}
/// Returns whether this is the `none` value. pubfn is_none(&self) -> bool {
*self == Self::None
}
}
impl Parse for AnchorScope { fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); let first = input.expect_ident()?; if first.eq_ignore_ascii_case("none") { return Ok(Self::None);
} if first.eq_ignore_ascii_case("all") { return Ok(Self::All);
} // Authors using more than a handful of anchored elements is likely // uncommon, so we only pre-allocate for 8 on the stack here. letmut idents: SmallVec<[DashedIdent; 8]> =
smallvec![DashedIdent::from_ident(location, first,)?]; while input.try_parse(|input| input.expect_comma()).is_ok() {
idents.push(DashedIdent::parse(context, input)?);
}
Ok(AnchorScope::Idents(ArcSlice::from_iter(idents.drain(..))))
}
}
/// Returns whether this is the `auto` value. pubfn is_auto(&self) -> bool {
*self == Self::Auto
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(u8)] /// How to swap values for the automatically-generated position tactic. pubenum PositionTryFallbacksTryTacticKeyword { /// Magic value for no change. #[css(skip)] #[default]
None, /// Swap the values in the block axis.
FlipBlock, /// Swap the values in the inline axis.
FlipInline, /// Swap the values in the start properties.
FlipStart,
}
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(C)] /// Changes for the automatically-generated position option. /// Note that this is order-dependent - e.g. `flip-start flip-inline` != `flip-inline flip-start`. /// /// https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-fallbacks-try-tactic pubstruct PositionTryFallbacksTryTactic( pub PositionTryFallbacksTryTacticKeyword, pub PositionTryFallbacksTryTacticKeyword, pub PositionTryFallbacksTryTacticKeyword,
);
impl Parse for PositionTryFallbacksTryTactic { fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { let first = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse)?; let second = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse).unwrap_or_default(); let third = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse).unwrap_or_default(); if first == second || first == third || (!second.is_none() && second == third) { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(Self(first, second, third))
}
}
/// Returns whether this is the `none` value. pubfn is_none(&self) -> bool { self.0.is_empty()
}
}
impl Parse for PositionTryFallbacks { fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { return Ok(Self::none());
} // The common case is unlikely to include many alternate positioning // styles, so space for four on the stack should typically be enough. letmut items: SmallVec<[PositionTryFallbacksItem; 4]> =
smallvec![PositionTryFallbacksItem::parse(context, input)?]; while input.try_parse(|input| input.expect_comma()).is_ok() {
items.push(PositionTryFallbacksItem::parse(context, input)?);
}
Ok(Self(ArcSlice::from_iter(items.drain(..))))
}
}
#[inline] fn is_compatible_pairing(first: PositionAreaKeyword, second: PositionAreaKeyword) -> bool { if first.is_none() || second.is_none() { // `none` is not allowed as one of the keywords when two keywords are // provided. returnfalse;
} if first.is_common() || second.is_common() { returntrue;
} if first.is_horizontal() { return second.is_vertical();
} if first.is_vertical() { return second.is_horizontal();
} if first.is_block() { return second.is_inline();
} if first.is_inline() { return second.is_block();
} if first.is_self_block() { return second.is_self_inline();
} if first.is_self_inline() { return second.is_self_block();
} if first.is_inferred_logical() { return second.is_inferred_logical();
} if first.is_self_inferred_logical() { return second.is_self_inferred_logical();
}
debug_assert!(false, "Not reached");
// Return false to increase the chances of this being reported to us if we // ever were to get here. false
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(C)] /// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-area pubstruct PositionArea { /// First keyword, if any. pub first: PositionAreaKeyword, /// Second keyword, if any. #[css(skip_if = "PositionAreaKeyword::is_none")] pub second: PositionAreaKeyword,
}
location = input.current_source_location(); let second = input.try_parse(PositionAreaKeyword::parse); iflet Ok(PositionAreaKeyword::None) = second { // `none` is only allowed as a single value return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
} letmut second = second.unwrap_or(PositionAreaKeyword::None); if second.is_none() { // Either there was no second keyword and try_parse returned a // BasicParseErrorKind::EndOfInput, or else the second "keyword" // was invalid. We assume the former case here, and if it's the // latter case then our caller detects the error (try_parse will, // have rewound, leaving an unparsed token). return Ok(Self { first, second });
}
if !is_compatible_pairing(first, second) { return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
// Normalize by applying the shortest serialization principle: // https://drafts.csswg.org/cssom/#serializing-css-values if first.is_inferred_logical() ||
second.is_inferred_logical() ||
first.is_self_inferred_logical() ||
second.is_self_inferred_logical() ||
(first.is_common() && second.is_common())
{ // In these cases we must not change the order of the keywords // since their meaning is inferred from their order. However, if // both keywords are the same, only one should be set. if first == second {
second = PositionAreaKeyword::None;
}
} elseif second == PositionAreaKeyword::SpanAll { // Span-all is the default behavior, so specifying `span-all` is // superfluous.
second = PositionAreaKeyword::None;
} elseif first == PositionAreaKeyword::SpanAll { // Same here, but the non-superfluous keyword must come first.
first = second;
second = PositionAreaKeyword::None;
} elseif first.is_vertical() ||
second.is_horizontal() ||
first.is_inline() ||
second.is_block() ||
first.is_self_inline() ||
second.is_self_block()
{ // Canonical order is horizontal before vertical, block before inline.
std::mem::swap(&mut first, &mut second);
}
#[repr(u8)] #[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] /// Masonry auto-placement algorithm packing. pubenum MasonryPlacement { /// Place the item in the track(s) with the smallest extent so far.
Pack, /// Place the item after the last item, from start to end.
Next,
}
#[repr(u8)] #[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] /// Masonry auto-placement algorithm item sorting option. pubenum MasonryItemOrder { /// Place all items with a definite placement before auto-placed items.
DefiniteFirst, /// Place items in `order-modified document order`.
Ordered,
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(C)] /// Controls how the Masonry layout algorithm works /// specifying exactly how auto-placed items get flowed in the masonry axis. pubstruct MasonryAutoFlow { /// Specify how to pick a auto-placement track. #[css(contextual_skip_if = "is_pack_with_non_default_order")] pub placement: MasonryPlacement, /// Specify how to pick an item to place. #[css(skip_if = "is_item_order_definite_first")] pub order: MasonryItemOrder,
}
#[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(C)] /// https://drafts.csswg.org/css-grid/#named-grid-area pubstruct TemplateAreas { /// `named area` containing for each template area #[css(skip)] pub areas: crate::OwnedSlice<NamedArea>, /// The simplified CSS strings for serialization purpose. /// https://drafts.csswg.org/css-grid/#serialize-template // Note: We also use the length of `strings` when computing the explicit grid end line number // (i.e. row number). #[css(iterable)] pub strings: crate::OwnedSlice<crate::OwnedStr>, /// The number of columns of the grid. #[css(skip)] pub width: u32,
}
/// A range of rows or columns. Using this instead of std::ops::Range for FFI /// purposes. #[repr(C)] #[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)] pubstruct UnsignedRange { /// The start of the range. pub start: u32, /// The end of the range. pub end: u32,
}
#[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)] #[repr(C)] /// Not associated with any particular grid item, but can be referenced from the /// grid-placement properties. pubstruct NamedArea { /// Name of the `named area` pub name: Atom, /// Rows of the `named area` pub rows: UnsignedRange, /// Columns of the `named area` pub columns: UnsignedRange,
}
/// Tokenize the string into a list of the tokens, /// using longest-match semantics struct TemplateAreasTokenizer<'a>(&'a str);
impl<'a> Iterator for TemplateAreasTokenizer<'a> { type Item = Result<Option<&'a str>, ()>;
fn next(&mutself) -> Option<Self::Item> { let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS); if rest.is_empty() { return None;
} if rest.starts_with('.') { self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..]; return Some(Ok(None));
} if !rest.starts_with(is_name_code_point) { return Some(Err(()));
} let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len()); let token = &rest[..token_len]; self.0 = &rest[token_len..];
Some(Ok(Some(token)))
}
}
fn is_name_code_point(c: char) -> bool {
c >= 'A' && c <= 'Z' ||
c >= 'a' && c <= 'z' ||
c >= '\u{80}' ||
c == '_' ||
c >= '0' && c <= '9' ||
c == '-'
}
/// This property specifies named grid areas. /// /// The syntax of this property also provides a visualization of the structure /// of the grid, making the overall layout of the grid container easier to /// understand. #[repr(C, u8)] #[derive(
Clone,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] pubenum GridTemplateAreas { /// The `none` value.
None, /// The actual value.
Areas(TemplateAreasArc),
}
impl GridTemplateAreas { #[inline] /// Get default value as `none` pubfn none() -> GridTemplateAreas {
GridTemplateAreas::None
}
}
/// A specified value for the `z-index` property. pubtype ZIndex = GenericZIndex<Integer>;
/// A specified value for the `aspect-ratio` property. pubtype AspectRatio = GenericAspectRatio<NonNegativeNumber>;
let location = input.current_source_location(); letmut auto = input.try_parse(|i| i.expect_ident_matching("auto")); let ratio = input.try_parse(|i| Ratio::parse(context, i)); if auto.is_err() {
auto = input.try_parse(|i| i.expect_ident_matching("auto"));
}
if auto.is_err() && ratio.is_err() { return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(AspectRatio {
auto: auto.is_ok(),
ratio: match ratio {
Ok(ratio) => PreferredRatio::Ratio(ratio),
Err(..) => PreferredRatio::None,
},
})
}
}
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.