/* 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::derives::*; usecrate::logical_geometry::{LogicalAxis, LogicalSide, PhysicalSide, WritingMode}; 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::length::GenericAnchorSizeFunction; 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::{AspectRatio as GenericAspectRatio, GenericAnchorSide}; usecrate::values::generics::position::{GenericAnchorFunction, GenericInset, TreeScoped}; usecrate::values::generics::position::{IsTreeScoped, Position as GenericPosition}; usecrate::values::specified; usecrate::values::specified::align::AlignFlags; usecrate::values::specified::percentage::NoCalcPercentage; usecrate::values::specified::{AllowQuirks, Integer, LengthPercentage, NonNegativeNumber}; usecrate::values::{AtomIdent, DashedIdent}; usecrate::Atom; use cssparser::{match_ignore_ascii_case, Parser}; use num_traits::FromPrimitive; 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}; use thin_vec::ThinVec;
/// 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, ToTyped)] #[typed(todo_derive_fields)] 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)
}
}
impl Parse for AnchorNameIdent { 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(AnchorNameIdent(ArcSlice::from_iter(idents.drain(..))))
}
}
impl Parse for ScopedNameList { 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::<[AtomIdent; 8]>::new();
idents.push(AtomIdent::new(DashedIdent::from_ident(location, first)?.0)); while input.try_parse(|input| input.expect_comma()).is_ok() {
idents.push(AtomIdent::new(DashedIdent::parse(context, input)?.0));
}
Ok(Self(ArcSlice::from_iter(idents.drain(..))))
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(u8)] /// How to swap values for the automatically-generated position tactic. pubenum PositionTryFallbacksTryTacticKeyword { /// Swap the values in the block axis.
FlipBlock, /// Swap the values in the inline axis.
FlipInline, /// Swap the values in the start properties.
FlipStart, /// Swap the values in the X axis.
FlipX, /// Swap the values in the Y axis.
FlipY,
}
#[derive(
Clone,
Debug,
Default,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)] #[repr(transparent)] /// 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( #[css(iterable)] pub ThinVec<PositionTryFallbacksTryTacticKeyword>,
);
impl Parse for PositionTryFallbacksTryTactic { fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { letmut result = ThinVec::with_capacity(5); // Collect up to 5 keywords, disallowing duplicates. for _ in0..5 { iflet Ok(kw) = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse) { if result.contains(&kw) { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
result.push(kw);
} else { break;
}
} if result.is_empty() { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(Self(result))
}
}
/// Returns whether this is the `none` value. pubfn is_none(&self) -> bool { self.0.is_empty()
}
}
impl Parse for PositionTryFallbacksList { 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(..))))
}
}
impl PositionVisibility { #[inline] /// Returns the initial value of position-visibility pubfn always() -> Self { Self::ALWAYS
}
}
/// A value indicating which high level group in the formal grammar a /// PositionAreaKeyword or PositionArea belongs to. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubenum PositionAreaType { /// X || Y
Physical, /// block || inline
Logical, /// self-block || self-inline
SelfLogical, /// start|end|span-* {1,2}
Inferred, /// self-start|self-end|span-self-* {1,2}
SelfInferred, /// center, span-all
Common, /// none
None,
}
/// A three-bit value that represents the axis in which position-area operates on. /// Represented as 4 bits: axis type (physical or logical), direction type (physical or logical), /// axis value. /// /// There are two special values on top (Inferred and None) that represent ambiguous or axis-less /// keywords, respectively. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] #[allow(missing_docs)] pubenum PositionAreaAxis {
Horizontal = 0b000,
Vertical = 0b001,
X = 0b010,
Y = 0b011,
Block = 0b110,
Inline = 0b111,
Inferred = 0b100,
None = 0b101,
}
impl PositionAreaAxis { /// Whether this axis is physical or not. pubfn is_physical(self) -> bool {
(selfas u8 & 0b100) == 0
}
/// Whether the direction is logical or not. fn is_flow_relative_direction(self) -> bool { self == Self::Inferred || (selfas u8 & 0b10) != 0
}
/// Whether this axis goes first in the canonical syntax. fn is_canonically_first(self) -> bool { self != Self::Inferred && (selfas u8) & 1 == 0
}
/// Specifies which tracks(s) on the axis that the position-area span occupies. /// Represented as 3 bits: start, center, end track. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] pubenum PositionAreaTrack { /// First track
Start = 0b001, /// First and center.
SpanStart = 0b011, /// Last track.
End = 0b100, /// Last and center.
SpanEnd = 0b110, /// Center track.
Center = 0b010, /// All tracks
SpanAll = 0b111,
}
/// The shift to the left needed to set the axis. pubconst AXIS_SHIFT: usize = 3; /// The mask used to extract the axis. pubconst AXIS_MASK: u8 = 0b111u8 << AXIS_SHIFT; /// The mask used to extract the track. pubconst TRACK_MASK: u8 = 0b111u8; /// The self-wm bit. pubconst SELF_WM: u8 = 1u8 << 6;
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
FromPrimitive,
)] #[allow(missing_docs)] #[repr(u8)] /// Possible values for the `position-area` property's keywords. /// Represented by [0z xxx yyy], where z means "self wm resolution", xxxx is the axis (as in /// PositionAreaAxis) and yyy is the PositionAreaTrack /// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-area pubenum PositionAreaKeyword { #[default]
None = (PositionAreaAxis::None as u8) << AXIS_SHIFT,
// Common (shared) keywords:
Center = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::Center as u8,
SpanAll = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanAll as u8,
// Inferred-axis edges:
Start = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
End = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
SpanStart =
((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanEnd = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
// Purely physical edges:
Left = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
Right = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
Top = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
Bottom = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
// Flow-relative physical-axis edges:
XStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
XEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
YStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
YEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
// Logical edges:
BlockStart = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
BlockEnd = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
InlineStart = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
InlineEnd = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
// Composite values with Span:
SpanLeft =
((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanRight =
((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
SpanTop =
((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanBottom =
((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
// Flow-relative physical-axis edges:
SpanXStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanXEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
SpanYStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanYEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
// Logical edges:
SpanBlockStart =
((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanBlockEnd =
((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
SpanInlineStart =
((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
SpanInlineEnd =
((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
// Values using the Self element's writing-mode:
SelfStart = SELF_WM | (Self::Start as u8),
SelfEnd = SELF_WM | (Self::End as u8),
SpanSelfStart = SELF_WM | (Self::SpanStart as u8),
SpanSelfEnd = SELF_WM | (Self::SpanEnd as u8),
SelfXStart = SELF_WM | (Self::XStart as u8),
SelfXEnd = SELF_WM | (Self::XEnd as u8),
SelfYStart = SELF_WM | (Self::YStart as u8),
SelfYEnd = SELF_WM | (Self::YEnd as u8),
SelfBlockStart = SELF_WM | (Self::BlockStart as u8),
SelfBlockEnd = SELF_WM | (Self::BlockEnd as u8),
SelfInlineStart = SELF_WM | (Self::InlineStart as u8),
SelfInlineEnd = SELF_WM | (Self::InlineEnd as u8),
SpanSelfXStart = SELF_WM | (Self::SpanXStart as u8),
SpanSelfXEnd = SELF_WM | (Self::SpanXEnd as u8),
SpanSelfYStart = SELF_WM | (Self::SpanYStart as u8),
SpanSelfYEnd = SELF_WM | (Self::SpanYEnd as u8),
SpanSelfBlockStart = SELF_WM | (Self::SpanBlockStart as u8),
SpanSelfBlockEnd = SELF_WM | (Self::SpanBlockEnd as u8),
SpanSelfInlineStart = SELF_WM | (Self::SpanInlineStart as u8),
SpanSelfInlineEnd = SELF_WM | (Self::SpanInlineEnd as u8),
}
/// Returns true if this is the none keyword. pubfn is_none(&self) -> bool {
*self == Self::None
}
/// Whether we're one of the self-wm keywords. pubfn self_wm(self) -> bool {
(selfas u8 & SELF_WM) != 0
}
/// Get this keyword's axis. pubfn axis(self) -> PositionAreaAxis {
PositionAreaAxis::from_u8((selfas u8 >> AXIS_SHIFT) & 0b111).unwrap()
}
/// Returns this keyword but with the axis swapped by the argument. pubfn with_axis(self, axis: PositionAreaAxis) -> Self { Self::from_u8(((selfas u8) & !AXIS_MASK) | ((axis as u8) << AXIS_SHIFT)).unwrap()
}
/// If this keyword uses an inferred axis, replaces it. pubfn with_inferred_axis(self, axis: PositionAreaAxis) -> Self { ifself.axis() == PositionAreaAxis::Inferred { self.with_axis(axis)
} else { self
}
}
/// Get this keyword's track, or None if we're the `None` keyword. pubfn track(self) -> Option<PositionAreaTrack> { let result = PositionAreaTrack::from_u8(selfas u8 & TRACK_MASK);
debug_assert_eq!(
result.is_none(), self.is_none(), "Only the none keyword has no track"
);
result
}
fn to_physical( self,
cb_wm: WritingMode,
self_wm: WritingMode,
inferred_axis: LogicalAxis,
) -> Self { let wm = ifself.self_wm() { self_wm } else { cb_wm }; let axis = self.axis(); if !axis.is_flow_relative_direction() { returnself;
} let Some(logical_axis) = axis.to_logical(wm, inferred_axis) else { returnself;
}; let Some(track) = self.track() else {
debug_assert!(false, "How did we end up with no track here? {self:?}"); returnself;
}; let start = track.start(); let logical_side = match logical_axis {
LogicalAxis::Block => { if start {
LogicalSide::BlockStart
} else {
LogicalSide::BlockEnd
}
},
LogicalAxis::Inline => { if start {
LogicalSide::InlineStart
} else {
LogicalSide::InlineEnd
}
},
}; let physical_side = logical_side.to_physical(wm); let physical_start = matches!(physical_side, PhysicalSide::Top | PhysicalSide::Left); let new_track = if physical_start != start {
track.flip()
} else {
track
}; let new_axis = if matches!(physical_side, PhysicalSide::Top | PhysicalSide::Bottom) {
PositionAreaAxis::Vertical
} else {
PositionAreaAxis::Horizontal
}; Self::from_u8(new_track as u8 | ((new_axis as u8) << AXIS_SHIFT)).unwrap()
}
fn flip_track(self) -> Self { let Some(old_track) = self.track() else { returnself;
}; let new_track = old_track.flip(); Self::from_u8((selfas u8 & !TRACK_MASK) | new_track as u8).unwrap()
}
/// Returns a value for the self-alignment properties in order to resolve /// `normal`, in terms of the containing block's writing mode. /// /// Note that the caller must have converted the position-area to physical /// values. /// /// <https://drafts.csswg.org/css-anchor-position/#position-area-alignment> pubfn to_self_alignment(self, axis: LogicalAxis, cb_wm: &WritingMode) -> Option<AlignFlags> { let track = self.track()?;
Some(match track { // "If the only the center track in an axis is selected, the default alignment in that axis is center."
PositionAreaTrack::Center => AlignFlags::CENTER, // "If all three tracks are selected, the default alignment in that axis is anchor-center."
PositionAreaTrack::SpanAll => AlignFlags::ANCHOR_CENTER, // "Otherwise, the default alignment in that axis is toward the non-specified side track: if it’s // specifying the “start” track of its axis, the default alignment in that axis is end; etc."
_ => {
debug_assert_eq!(self.group_type(), PositionAreaType::Physical); if axis == LogicalAxis::Inline { // For the inline axis, map 'start' to 'end' unless the axis is inline-reversed, // meaning that its logical flow is counter to physical coordinates and therefore // physical 'start' already corresponds to logical 'end'. if track.start() == cb_wm.intersects(WritingMode::INLINE_REVERSED) {
AlignFlags::START
} else {
AlignFlags::END
}
} else { // For the block axis, only vertical-rl has reversed flow and therefore // does not map 'start' to 'end' here. if track.start() == cb_wm.is_vertical_rl() {
AlignFlags::START
} else {
AlignFlags::END
}
}
},
})
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)] #[repr(C)] #[typed(todo_derive_fields)] /// 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,
}
/// Get the high-level grammar group of this. pubfn get_type(&self) -> PositionAreaType { let first = self.first.group_type(); let second = self.second.group_type(); if matches!(second, PositionAreaType::None | PositionAreaType::Common) { return first;
} if first == PositionAreaType::Common { return second;
} if first != second { return PositionAreaType::None;
} let first_axis = self.first.axis(); if first_axis != PositionAreaAxis::Inferred
&& first_axis.is_canonically_first() == self.second.axis().is_canonically_first()
{ return PositionAreaType::None;
}
first
}
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 });
}
let pair_type = Self { first, second }.get_type(); if pair_type == PositionAreaType::None { // Mismatched types or what not. return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
} // For types that have a canonical order, remove 'span-all' (the default behavior; // unnecessary for keyword pairs with a known order). if matches!(
pair_type,
PositionAreaType::Physical | PositionAreaType::Logical | PositionAreaType::SelfLogical
) { if second == PositionAreaKeyword::SpanAll { // Span-all is the default behavior, so specifying `span-all` is // superfluous.
second = PositionAreaKeyword::None;
} elseif first == PositionAreaKeyword::SpanAll {
first = second;
second = PositionAreaKeyword::None;
}
} if first == second {
second = PositionAreaKeyword::None;
} letmut result = Self { first, second };
result.canonicalize_order();
Ok(result)
}
fn canonicalize_order(&mutself) { let first_axis = self.first.axis(); if first_axis.is_canonically_first() || self.second.is_none() { return;
} let second_axis = self.second.axis(); if first_axis == second_axis { // Inferred or axis-less keywords. return;
} if second_axis.is_canonically_first()
|| (second_axis == PositionAreaAxis::None && first_axis != PositionAreaAxis::Inferred)
{
std::mem::swap(&mutself.first, &mutself.second);
}
}
fn make_missing_second_explicit(&mutself) { if !self.second.is_none() { return;
} let axis = self.first.axis(); if matches!(axis, PositionAreaAxis::Inferred | PositionAreaAxis::None) { self.second = self.first; return;
} self.second = PositionAreaKeyword::SpanAll; if !axis.is_canonically_first() {
std::mem::swap(&mutself.first, &mutself.second);
}
}
/// Turns this <position-area> value into a physical <position-area>. pubfn to_physical(mutself, cb_wm: WritingMode, self_wm: WritingMode) -> Self { self.make_missing_second_explicit(); // If both axes are None, to_physical and canonicalize_order are not useful. // The first value refers to the block axis, the second to the inline axis; // but as a physical type, they will be interpreted as the x- and y-axis // respectively, so if the writing mode is horizontal we need to swap the // values (block -> y, inline -> x). ifself.first.axis() == PositionAreaAxis::None
&& self.second.axis() == PositionAreaAxis::None
&& !cb_wm.is_vertical()
{
std::mem::swap(&mutself.first, &mutself.second);
} else { self.first = self.first.to_physical(cb_wm, self_wm, LogicalAxis::Block); self.second = self.second.to_physical(cb_wm, self_wm, LogicalAxis::Inline); self.canonicalize_order();
} self
}
#[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,
ToTyped,
)] #[repr(C)] #[typed(todo_derive_fields)] /// 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,
ToTyped,
)] #[typed(todo_derive_fields)] 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.