use crate::derives::*;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::basic_shape::InsetRect as ComputedInsetRect;
use crate::values::computed::{
Context, LengthPercentage as ComputedLengthPercentage, ToComputedValue,
};
use crate::values::generics::basic_shape as generic;
use crate::values::generics::basic_shape::{Path, PolygonCoord};
use crate::values::generics::position::GenericPositionOrAuto;
use crate::values::generics::rect::Rect;
use crate::values::specified::angle::Angle;
use crate::values::specified::border::BorderRadius;
use crate::values::specified::image::Image;
use crate::values::specified::length::LengthPercentageOrAuto;
use crate::values::specified::position::Position;
use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage, SVGPathData};
use crate::values::CSSFloat;
use crate::Zero;
use cssparser::{match_ignore_ascii_case, Parser};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
pub use crate::values::generics::basic_shape::FillRule;
pub type ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>;
pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
pub type BasicShape = generic::GenericBasicShape<Angle, Position, LengthPercentage, BasicShapeRect>;
pub type InsetRect = generic::GenericInsetRect<LengthPercentage>;
pub type Circle = generic::Circle<Position, LengthPercentage>;
pub type Ellipse = generic::Ellipse<Position, LengthPercentage>;
pub type ShapeRadius = generic::ShapeRadius<LengthPercentage>;
pub type Polygon = generic::GenericPolygon<LengthPercentage>;
pub type PathOrShapeFunction =
generic::GenericPathOrShapeFunction<Angle, Position, LengthPercentage>;
pub type ShapeCommand = generic::GenericShapeCommand<Angle, Position, LengthPercentage>;
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
pub struct Xywh {
pub x: LengthPercentage,
pub y: LengthPercentage,
pub width: NonNegativeLengthPercentage,
pub height: NonNegativeLengthPercentage,
pub round: BorderRadius,
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
#[repr(C)]
pub struct ShapeRectFunction {
pub rect: Rect<LengthPercentageOrAuto>,
pub round: BorderRadius,
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub enum BasicShapeRect {
Inset(InsetRect),
#[css(function)]
Xywh(Xywh),
#[css(function)]
Rect(ShapeRectFunction),
}
pub enum ShapeType {
Filled,
Outline,
}
bitflags! {
#[derive(Clone, Copy)]
#[repr(C)]
pub struct AllowedBasicShapes: u8 {
const INSET = 1 << 0;
const XYWH = 1 << 1;
const RECT = 1 << 2;
const CIRCLE = 1 << 3;
const ELLIPSE = 1 << 4;
const POLYGON = 1 << 5;
const PATH = 1 << 6;
const SHAPE = 1 << 7;
const ALL =
Self::INSET.bits() |
Self::XYWH.bits() |
Self::RECT.bits() |
Self::CIRCLE.bits() |
Self::ELLIPSE.bits() |
Self::POLYGON.bits() |
Self::PATH.bits() |
Self::SHAPE.bits();
const SHAPE_OUTSIDE =
Self::INSET.bits() |
Self::XYWH.bits() |
Self::RECT.bits() |
Self::CIRCLE.bits() |
Self::ELLIPSE.bits() |
Self::POLYGON.bits();
}
}
fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
to_shape: impl FnOnce(Box<BasicShape>, ReferenceBox) -> R,
to_reference_box: impl FnOnce(ReferenceBox) -> R,
flags: AllowedBasicShapes,
) -> Result<R, ParseError<'i>>
where
ReferenceBox: Default + Parse,
{
let mut shape = None;
let mut ref_box = None;
loop {
if shape.is_none() {
shape = input
.try_parse(|i| BasicShape::parse(context, i, flags, ShapeType::Filled))
.ok();
}
if ref_box.is_none() {
ref_box = input.try_parse(|i| ReferenceBox::parse(context, i)).ok();
if ref_box.is_some() {
continue;
}
}
break;
}
if let Some(shp) = shape {
return Ok(to_shape(Box::new(shp), ref_box.unwrap_or_default()));
}
match ref_box {
Some(r) => Ok(to_reference_box(r)),
None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
}
}
impl Parse for ClipPath {
#[inline]
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(ClipPath::None);
}
if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
return Ok(ClipPath::Url(url));
}
parse_shape_or_box(
context,
input,
ClipPath::Shape,
ClipPath::Box,
AllowedBasicShapes::ALL,
)
}
}
impl Parse for ShapeOutside {
#[inline]
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(ShapeOutside::None);
}
if let Ok(image) = input.try_parse(|i| Image::parse_with_cors_anonymous(context, i)) {
debug_assert_ne!(image, Image::None);
return Ok(ShapeOutside::Image(image));
}
parse_shape_or_box(
context,
input,
ShapeOutside::Shape,
ShapeOutside::Box,
AllowedBasicShapes::SHAPE_OUTSIDE,
)
}
}
impl BasicShape {
pub fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
flags: AllowedBasicShapes,
shape_type: ShapeType,
) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location();
let function = input.expect_function()?.clone();
input.parse_nested_block(move |i| {
match_ignore_ascii_case! { &function,
"inset" if flags.contains(AllowedBasicShapes::INSET) => {
InsetRect::parse_function_arguments(context, i)
.map(BasicShapeRect::Inset)
.map(BasicShape::Rect)
},
"xywh" if flags.contains(AllowedBasicShapes::XYWH) => {
Xywh::parse_function_arguments(context, i)
.map(BasicShapeRect::Xywh)
.map(BasicShape::Rect)
},
"rect" if flags.contains(AllowedBasicShapes::RECT) => {
ShapeRectFunction::parse_function_arguments(context, i)
.map(BasicShapeRect::Rect)
.map(BasicShape::Rect)
},
"circle" if flags.contains(AllowedBasicShapes::CIRCLE) => {
Circle::parse_function_arguments(context, i)
.map(BasicShape::Circle)
},
"ellipse" if flags.contains(AllowedBasicShapes::ELLIPSE) => {
Ellipse::parse_function_arguments(context, i)
.map(BasicShape::Ellipse)
},
"polygon" if flags.contains(AllowedBasicShapes::POLYGON) => {
Polygon::parse_function_arguments(context, i, shape_type)
.map(BasicShape::Polygon)
},
"path" if flags.contains(AllowedBasicShapes::PATH) => {
Path::parse_function_arguments(i, shape_type)
.map(PathOrShapeFunction::Path)
.map(BasicShape::PathOrShape)
},
"shape"
if flags.contains(AllowedBasicShapes::SHAPE)
&& static_prefs::pref!("layout.css.basic-shape-shape.enabled") =>
{
generic::Shape::parse_function_arguments(context, i, shape_type)
.map(PathOrShapeFunction::Shape)
.map(BasicShape::PathOrShape)
},
_ => Err(location
.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))),
}
})
}
}
impl Parse for InsetRect {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("inset")?;
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
}
}
fn parse_round<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<BorderRadius, ParseError<'i>> {
if input
.try_parse(|i| i.expect_ident_matching("round"))
.is_ok()
{
return BorderRadius::parse(context, input);
}
Ok(BorderRadius::zero())
}
impl InsetRect {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let rect = Rect::parse_with(context, input, LengthPercentage::parse)?;
let round = parse_round(context, input)?;
Ok(generic::InsetRect { rect, round })
}
}
fn parse_at_position<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<GenericPositionOrAuto<Position>, ParseError<'i>> {
if input.try_parse(|i| i.expect_ident_matching("at")).is_ok() {
Position::parse(context, input).map(GenericPositionOrAuto::Position)
} else {
Ok(GenericPositionOrAuto::Auto)
}
}
impl Parse for Circle {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("circle")?;
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
}
}
impl Circle {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let radius = input
.try_parse(|i| ShapeRadius::parse(context, i))
.unwrap_or_default();
let position = parse_at_position(context, input)?;
Ok(generic::Circle { radius, position })
}
}
impl Parse for Ellipse {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("ellipse")?;
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
}
}
impl Ellipse {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let (semiaxis_x, semiaxis_y) = input
.try_parse(|i| -> Result<_, ParseError> {
Ok((
ShapeRadius::parse(context, i)?,
ShapeRadius::parse(context, i)?,
))
})
.unwrap_or_default();
let position = parse_at_position(context, input)?;
Ok(generic::Ellipse {
semiaxis_x,
semiaxis_y,
position,
})
}
}
fn parse_fill_rule<'i, 't>(
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
expect_comma: bool,
) -> FillRule {
match shape_type {
ShapeType::Outline => Default::default(),
ShapeType::Filled => input
.try_parse(|i| -> Result<_, ParseError> {
let fill = FillRule::parse(i)?;
if expect_comma {
i.expect_comma()?;
}
Ok(fill)
})
.unwrap_or_default(),
}
}
impl Parse for Polygon {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("polygon")?;
input.parse_nested_block(|i| Self::parse_function_arguments(context, i, ShapeType::Filled))
}
}
impl Polygon {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
) -> Result<Self, ParseError<'i>> {
let fill = parse_fill_rule(input, shape_type, true );
let coordinates = input
.parse_comma_separated(|i| {
Ok(PolygonCoord(
LengthPercentage::parse(context, i)?,
LengthPercentage::parse(context, i)?,
))
})?
.into();
Ok(Polygon { fill, coordinates })
}
}
impl Path {
fn parse_function_arguments<'i, 't>(
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
) -> Result<Self, ParseError<'i>> {
use crate::values::specified::svg_path::AllowEmpty;
let fill = parse_fill_rule(input, shape_type, true );
let path = SVGPathData::parse(input, AllowEmpty::No)?;
Ok(Path { fill, path })
}
}
fn round_to_css<W>(round: &BorderRadius, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if !round.is_zero() {
dest.write_str(" round ")?;
round.to_css(dest)?;
}
Ok(())
}
impl ToCss for Xywh {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.x.to_css(dest)?;
dest.write_char(' ')?;
self.y.to_css(dest)?;
dest.write_char(' ')?;
self.width.to_css(dest)?;
dest.write_char(' ')?;
self.height.to_css(dest)?;
round_to_css(&self.round, dest)
}
}
impl Xywh {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let x = LengthPercentage::parse(context, input)?;
let y = LengthPercentage::parse(context, input)?;
let width = NonNegativeLengthPercentage::parse(context, input)?;
let height = NonNegativeLengthPercentage::parse(context, input)?;
let round = parse_round(context, input)?;
Ok(Xywh {
x,
y,
width,
height,
round,
})
}
}
impl ToCss for ShapeRectFunction {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.rect.0.to_css(dest)?;
dest.write_char(' ')?;
self.rect.1.to_css(dest)?;
dest.write_char(' ')?;
self.rect.2.to_css(dest)?;
dest.write_char(' ')?;
self.rect.3.to_css(dest)?;
round_to_css(&self.round, dest)
}
}
impl ShapeRectFunction {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let rect = Rect::parse_all_components_with(context, input, LengthPercentageOrAuto::parse)?;
let round = parse_round(context, input)?;
Ok(ShapeRectFunction { rect, round })
}
}
impl ToComputedValue for BasicShapeRect {
type ComputedValue = ComputedInsetRect;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
use crate::values::computed::LengthPercentage;
use crate::values::computed::LengthPercentageOrAuto;
use style_traits::values::specified::AllowedNumericType;
match self {
Self::Inset(ref inset) => inset.to_computed_value(context),
Self::Xywh(ref xywh) => {
let x = xywh.x.to_computed_value(context);
let y = xywh.y.to_computed_value(context);
let w = xywh.width.to_computed_value(context);
let h = xywh.height.to_computed_value(context);
let right = LengthPercentage::hundred_percent_minus_list(
&[&x, &w.0],
AllowedNumericType::All,
);
let bottom = LengthPercentage::hundred_percent_minus_list(
&[&y, &h.0],
AllowedNumericType::All,
);
ComputedInsetRect {
rect: Rect::new(y, right, bottom, x),
round: xywh.round.to_computed_value(context),
}
},
Self::Rect(ref rect) => {
fn compute_top_or_left(v: LengthPercentageOrAuto) -> LengthPercentage {
match v {
LengthPercentageOrAuto::Auto => LengthPercentage::zero_percent(),
LengthPercentageOrAuto::LengthPercentage(lp) => lp,
}
}
fn compute_bottom_or_right(v: LengthPercentageOrAuto) -> LengthPercentage {
match v {
LengthPercentageOrAuto::Auto => LengthPercentage::zero_percent(),
LengthPercentageOrAuto::LengthPercentage(lp) => {
LengthPercentage::hundred_percent_minus(lp, AllowedNumericType::All)
},
}
}
let round = rect.round.to_computed_value(context);
let rect = rect.rect.to_computed_value(context);
let rect = Rect::new(
compute_top_or_left(rect.0),
compute_bottom_or_right(rect.1),
compute_bottom_or_right(rect.2),
compute_top_or_left(rect.3),
);
ComputedInsetRect { rect, round }
},
}
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Self::Inset(ToComputedValue::from_computed_value(computed))
}
}
impl generic::Shape<Angle, Position, LengthPercentage> {
fn parse_function_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
) -> Result<Self, ParseError<'i>> {
let fill = parse_fill_rule(input, shape_type, false );
let mut first = true;
let commands = input.parse_comma_separated(|i| {
if first {
first = false;
i.expect_ident_matching("from")?;
Ok(ShapeCommand::Move {
point: generic::CommandEndPoint::parse_endpoint_as_abs(context, i)?,
})
} else {
ShapeCommand::parse(context, i)
}
})?;
if commands.len() < 2 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(Self {
fill,
commands: commands.into(),
})
}
}
impl Parse for ShapeCommand {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
use crate::values::generics::basic_shape::{
ArcRadii, ArcSize, ArcSweep, AxisEndPoint, CommandEndPoint, ControlPoint,
};
Ok(try_match_ident_ignore_ascii_case! { input,
"close" => Self::Close,
"move" => {
let point = CommandEndPoint::parse(context, input)?;
Self::Move { point }
},
"line" => {
let point = CommandEndPoint::parse(context, input)?;
Self::Line { point }
},
"hline" => {
let x = AxisEndPoint::parse_hline(context, input)?;
Self::HLine { x }
},
"vline" => {
let y = AxisEndPoint::parse_vline(context, input)?;
Self::VLine { y }
},
"curve" => {
let point = CommandEndPoint::parse(context, input)?;
input.expect_ident_matching("with")?;
let control1 = ControlPoint::parse(context, input, point.is_abs())?;
if input.try_parse(|i| i.expect_delim('/')).is_ok() {
let control2 = ControlPoint::parse(context, input, point.is_abs())?;
Self::CubicCurve {
point,
control1,
control2,
}
} else {
Self::QuadCurve {
point,
control1,
}
}
},
"smooth" => {
let point = CommandEndPoint::parse(context, input)?;
if input.try_parse(|i| i.expect_ident_matching("with")).is_ok() {
let control2 = ControlPoint::parse(context, input, point.is_abs())?;
Self::SmoothCubic {
point,
control2,
}
} else {
Self::SmoothQuad { point }
}
},
"arc" => {
let point = CommandEndPoint::parse(context, input)?;
input.expect_ident_matching("of")?;
let rx = LengthPercentage::parse(context, input)?;
let ry = input.try_parse(|i| LengthPercentage::parse(context, i)).ok();
let radii = ArcRadii { rx, ry: ry.into() };
let mut arc_sweep = None;
let mut arc_size = None;
let mut rotate = None;
loop {
if arc_sweep.is_none() {
arc_sweep = input.try_parse(ArcSweep::parse).ok();
}
if arc_size.is_none() {
arc_size = input.try_parse(ArcSize::parse).ok();
if arc_size.is_some() {
continue;
}
}
if rotate.is_none()
&& input
.try_parse(|i| i.expect_ident_matching("rotate"))
.is_ok()
{
rotate = Some(Angle::parse(context, input)?);
continue;
}
break;
}
Self::Arc {
point,
radii,
arc_sweep: arc_sweep.unwrap_or(ArcSweep::Ccw),
arc_size: arc_size.unwrap_or(ArcSize::Small),
rotate: rotate.unwrap_or(Angle::zero()),
}
},
})
}
}
impl Parse for generic::CoordinatePair<LengthPercentage> {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let x = LengthPercentage::parse(context, input)?;
let y = LengthPercentage::parse(context, input)?;
Ok(Self::new(x, y))
}
}
impl generic::ControlPoint<Position, LengthPercentage> {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
is_end_point_abs: bool,
) -> Result<Self, ParseError<'i>> {
use generic::ControlReference;
let coord = input.try_parse(|i| generic::CoordinatePair::parse(context, i));
if is_end_point_abs && coord.is_err() {
let pos = Position::parse(context, input)?;
return Ok(Self::Absolute(pos));
}
let coord = coord?;
let mut reference = if is_end_point_abs {
ControlReference::Origin
} else {
ControlReference::Start
};
if input.try_parse(|i| i.expect_ident_matching("from")).is_ok() {
reference = ControlReference::parse(input)?;
}
Ok(Self::Relative(generic::RelativeControlPoint {
coord,
reference,
}))
}
}
impl Parse for generic::CommandEndPoint<Position, LengthPercentage> {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if ByTo::parse(input)?.is_abs() {
Self::parse_endpoint_as_abs(context, input)
} else {
let point = generic::CoordinatePair::parse(context, input)?;
Ok(Self::ByCoordinate(point))
}
}
}
impl generic::CommandEndPoint<Position, LengthPercentage> {
fn parse_endpoint_as_abs<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let point = Position::parse(context, input)?;
Ok(generic::CommandEndPoint::ToPosition(point))
}
}
impl generic::AxisEndPoint<LengthPercentage> {
pub fn parse_hline<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
use cssparser::Token;
use generic::{AxisPosition, AxisPositionKeyword};
if !ByTo::parse(input)?.is_abs() {
return Ok(Self::ByCoordinate(LengthPercentage::parse(context, input)?));
}
let x = AxisPosition::parse(context, input)?;
if let AxisPosition::Keyword(
_word @ (AxisPositionKeyword::Top
| AxisPositionKeyword::Bottom
| AxisPositionKeyword::YStart
| AxisPositionKeyword::YEnd),
) = &x
{
let location = input.current_source_location();
let token = Token::Ident(x.to_css_string().into());
return Err(location.new_unexpected_token_error(token));
}
Ok(Self::ToPosition(x))
}
pub fn parse_vline<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
use cssparser::Token;
use generic::{AxisPosition, AxisPositionKeyword};
if !ByTo::parse(input)?.is_abs() {
return Ok(Self::ByCoordinate(LengthPercentage::parse(context, input)?));
}
let y = AxisPosition::parse(context, input)?;
if let AxisPosition::Keyword(
_word @ (AxisPositionKeyword::Left
| AxisPositionKeyword::Right
| AxisPositionKeyword::XStart
| AxisPositionKeyword::XEnd),
) = &y
{
let location = input.current_source_location();
let token = Token::Ident(y.to_css_string().into());
return Err(location.new_unexpected_token_error(token));
}
Ok(Self::ToPosition(y))
}
}
impl ToComputedValue for generic::AxisPosition<LengthPercentage> {
type ComputedValue = generic::AxisPosition<ComputedLengthPercentage>;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match self {
Self::LengthPercent(lp) => {
Self::ComputedValue::LengthPercent(lp.to_computed_value(context))
},
Self::Keyword(word) => {
let lp = LengthPercentage::Percentage(word.as_percentage());
Self::ComputedValue::LengthPercent(lp.to_computed_value(context))
},
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
match computed {
Self::ComputedValue::LengthPercent(lp) => {
Self::LengthPercent(LengthPercentage::from_computed_value(lp))
},
_ => unreachable!("Invalid state: computed value cannot be a keyword."),
}
}
}
impl ToComputedValue for generic::AxisPosition<CSSFloat> {
type ComputedValue = Self;
fn to_computed_value(&self, _context: &Context) -> Self {
*self
}
fn from_computed_value(computed: &Self) -> Self {
*computed
}
}
#[derive(Clone, Copy, Debug, Parse, PartialEq)]
enum ByTo {
By,
To,
}
impl ByTo {
#[inline]
pub fn is_abs(&self) -> bool {
matches!(self, ByTo::To)
}
}