/* 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::values::computed::basic_shape::InsetRect as ComputedInsetRect; usecrate::values::computed::{Context, ToComputedValue}; usecrate::values::generics::basic_shape as generic; usecrate::values::generics::basic_shape::{Path, PolygonCoord}; usecrate::values::generics::position::{GenericPosition, GenericPositionOrAuto}; usecrate::values::generics::rect::Rect; usecrate::values::specified::angle::Angle; usecrate::values::specified::border::BorderRadius; usecrate::values::specified::image::Image; usecrate::values::specified::length::LengthPercentageOrAuto; usecrate::values::specified::url::SpecifiedUrl; usecrate::values::specified::{LengthPercentage, NonNegativeLengthPercentage, SVGPathData}; usecrate::Zero; use cssparser::Parser; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
/// A specified alias for FillRule. pubusecrate::values::generics::basic_shape::FillRule;
/// A specified `clip-path` value. pubtype ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>;
/// A specified `shape-outside` value. pubtype ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
/// A specified value for `at <position>` in circle() and ellipse(). // Note: its computed value is the same as computed::position::Position. We just want to always use // LengthPercentage as the type of its components, for basic shapes. pubtype ShapePosition = GenericPosition<LengthPercentage, LengthPercentage>;
/// The specified value of `inset()`. pubtype InsetRect = generic::GenericInsetRect<LengthPercentage, NonNegativeLengthPercentage>;
/// A specified circle. pubtype Circle = generic::Circle<ShapePosition, NonNegativeLengthPercentage>;
/// A specified ellipse. pubtype Ellipse = generic::Ellipse<ShapePosition, NonNegativeLengthPercentage>;
/// The specified value of `ShapeRadius`. pubtype ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
/// The specified value of `Polygon`. pubtype Polygon = generic::GenericPolygon<LengthPercentage>;
/// The specified value of `PathOrShapeFunction`. pubtype PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>;
/// The specified value of `ShapeCommand`. pubtype ShapeCommand = generic::GenericShapeCommand<Angle, LengthPercentage>;
/// The specified value of `xywh()`. /// Defines a rectangle via offsets from the top and left edge of the reference box, and a /// specified width and height. /// /// The four <length-percentage>s define, respectively, the inset from the left edge of the /// reference box, the inset from the top edge of the reference box, the width of the rectangle, /// and the height of the rectangle. /// /// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-xywh #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] pubstruct Xywh { /// The left edge of the reference box. pub x: LengthPercentage, /// The top edge of the reference box. pub y: LengthPercentage, /// The specified width. pub width: NonNegativeLengthPercentage, /// The specified height. pub height: NonNegativeLengthPercentage, /// The optional <border-radius> argument(s) define rounded corners for the inset rectangle /// using the border-radius shorthand syntax. pub round: BorderRadius,
}
/// Defines a rectangle via insets from the top and left edges of the reference box. /// /// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-rect #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] #[repr(C)] pubstruct ShapeRectFunction { /// The four <length-percentage>s define the position of the top, right, bottom, and left edges /// of a rectangle, respectively, as insets from the top edge of the reference box (for the /// first and third values) or the left edge of the reference box (for the second and fourth /// values). /// /// An auto value makes the edge of the box coincide with the corresponding edge of the /// reference box: it’s equivalent to 0% as the first (top) or fourth (left) value, and /// equivalent to 100% as the second (right) or third (bottom) value. pub rect: Rect<LengthPercentageOrAuto>, /// The optional <border-radius> argument(s) define rounded corners for the inset rectangle /// using the border-radius shorthand syntax. pub round: BorderRadius,
}
/// The specified value of <basic-shape-rect>. /// <basic-shape-rect> = <inset()> | <rect()> | <xywh()> /// /// https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pubenum BasicShapeRect { /// Defines an inset rectangle via insets from each edge of the reference box.
Inset(InsetRect), /// Defines a xywh function. #[css(function)]
Xywh(Xywh), /// Defines a rect function. #[css(function)]
Rect(ShapeRectFunction),
}
bitflags! { /// The flags to represent which basic shapes we would like to support. /// /// Different properties may use different subsets of <basic-shape>: /// e.g. /// clip-path: all basic shapes. /// motion-path: all basic shapes (but ignore fill-rule). /// shape-outside: inset(), circle(), ellipse(), polygon(). /// /// Also there are some properties we don't support for now: /// shape-inside: inset(), circle(), ellipse(), polygon(). /// SVG shape-inside and shape-subtract: circle(), ellipse(), polygon(). /// /// The spec issue proposes some better ways to clarify the usage of basic shapes, so for now /// we use the bitflags to choose the supported basic shapes for each property at the parse /// time. /// https://github.com/w3c/csswg-drafts/issues/7390 #[derive(Clone, Copy)] #[repr(C)] pubstruct AllowedBasicShapes: u8 { /// inset(). const INSET = 1 << 0; /// xywh(). const XYWH = 1 << 1; /// rect(). const RECT = 1 << 2; /// circle(). const CIRCLE = 1 << 3; /// ellipse(). const ELLIPSE = 1 << 4; /// polygon(). const POLYGON = 1 << 5; /// path(). const PATH = 1 << 6; /// shape(). const SHAPE = 1 << 7;
impl Parse for ShapeOutside { #[inline] fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { // Need to parse this here so that `Image::parse_with_cors_anonymous` // doesn't parse it. if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { return Ok(ShapeOutside::None);
}
fn convert_to_length_percentage<S: Side>(c: PositionComponent<S>) -> LengthPercentage { // Convert the value when parsing, to make sure we serialize it properly for both // specified and computed values. // https://drafts.csswg.org/css-shapes-1/#basic-shape-serialization match c { // Since <position> keywords stand in for percentages, keywords without an offset // turn into percentages.
PositionComponent::Center => LengthPercentage::from(Percentage::new(0.5)),
PositionComponent::Side(keyword, None) => {
Percentage::new(if keyword.is_start() { 0. } else { 1. }).into()
}, // Per spec issue, https://github.com/w3c/csswg-drafts/issues/8695, the part of // "avoiding calc() expressions where possible" and "avoiding calc() // transformations" will be removed from the spec, and we should follow the // css-values-4 for position, i.e. we make it as length-percentage always. // https://drafts.csswg.org/css-shapes-1/#basic-shape-serialization. // https://drafts.csswg.org/css-values-4/#typedef-position
PositionComponent::Side(keyword, Some(length)) => { if keyword.is_start() {
length
} else {
length.hundred_percent_minus(AllowedNumericType::All)
}
},
PositionComponent::Length(length) => length,
}
}
fn parse_fill_rule<'i, 't>(
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
expect_comma: bool,
) -> FillRule { match shape_type { // Per [1] and [2], we ignore `<fill-rule>` for outline shapes, so always use a default // value. // [1] https://github.com/w3c/csswg-drafts/issues/3468 // [2] https://github.com/w3c/csswg-drafts/issues/7390 // // Also, per [3] and [4], we would like the ignore `<file-rule>` from outline shapes, e.g. // offset-path, which means we don't parse it when setting `ShapeType::Outline`. // This should be web compatible because the shipped "offset-path:path()" doesn't have // `<fill-rule>` and "offset-path:polygon()" is a new feature and still behind the // preference. // [3] https://github.com/w3c/fxtf-drafts/issues/512#issuecomment-1545393321 // [4] https://github.com/w3c/fxtf-drafts/issues/512#issuecomment-1555330929
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(),
}
}
matchself { Self::Inset(ref inset) => inset.to_computed_value(context), Self::Xywh(ref xywh) => { // Given `xywh(x y w h)`, construct the equivalent inset() function, // `inset(y calc(100% - x - w) calc(100% - y - h) x)`. // // https://drafts.csswg.org/css-shapes-1/#basic-shape-computed-values // https://github.com/w3c/csswg-drafts/issues/9053 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); // calc(100% - x - w). let right = LengthPercentage::hundred_percent_minus_list(
&[&x, &w.0],
AllowedNumericType::All,
); // calc(100% - y - h). 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) => { // Given `rect(t r b l)`, the equivalent function is // `inset(t calc(100% - r) calc(100% - b) l)`. // // https://drafts.csswg.org/css-shapes-1/#basic-shape-computed-values fn compute_top_or_left(v: LengthPercentageOrAuto) -> LengthPercentage { match v { // it’s equivalent to 0% as the first (top) or fourth (left) value. // https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-rect
LengthPercentageOrAuto::Auto => LengthPercentage::zero_percent(),
LengthPercentageOrAuto::LengthPercentage(lp) => lp,
}
} fn compute_bottom_or_right(v: LengthPercentageOrAuto) -> LengthPercentage { match v { // It's equivalent to 100% as the second (right) or third (bottom) value. // So calc(100% - 100%) = 0%. // https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-rect
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),
);
impl generic::Shape<Angle, LengthPercentage> { /// Parse the inner arguments of a `shape` function. /// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#) 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/* no following comma */);
letmut first = true; let commands = input.parse_comma_separated(|i| { if first {
first = false;
// The starting point for the first shape-command. It adds an initial absolute // moveto to the list of path data commands, with the <coordinate-pair> measured // from the top-left corner of the reference
i.expect_ident_matching("from")?;
Ok(ShapeCommand::Move {
by_to: generic::ByTo::To,
point: generic::CoordinatePair::parse(context, i)?,
})
} else { // The further path data commands.
ShapeCommand::parse(context, i)
}
})?;
// We must have one starting point and at least one following <shape-command>. if commands.len() < 2 { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
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.