summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values/specified/basic_shape.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
commitdef92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch)
tree2ef34b9ad8bb9a9220e05d60352558b15f513894 /servo/components/style/values/specified/basic_shape.rs
parentAdding debian version 125.0.3-1. (diff)
downloadfirefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz
firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/values/specified/basic_shape.rs')
-rw-r--r--servo/components/style/values/specified/basic_shape.rs206
1 files changed, 198 insertions, 8 deletions
diff --git a/servo/components/style/values/specified/basic_shape.rs b/servo/components/style/values/specified/basic_shape.rs
index 526296b735..8ed32c40c5 100644
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -14,6 +14,7 @@ use crate::values::generics::basic_shape as generic;
use crate::values::generics::basic_shape::{Path, PolygonCoord};
use crate::values::generics::position::{GenericPosition, 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;
@@ -40,6 +41,7 @@ pub type ShapePosition = GenericPosition<LengthPercentage, LengthPercentage>;
/// A specified basic shape.
pub type BasicShape = generic::GenericBasicShape<
+ Angle,
ShapePosition,
LengthPercentage,
NonNegativeLengthPercentage,
@@ -61,6 +63,12 @@ pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
/// The specified value of `Polygon`.
pub type Polygon = generic::GenericPolygon<LengthPercentage>;
+/// The specified value of `PathOrShapeFunction`.
+pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>;
+
+/// The specified value of `ShapeCommand`.
+pub type 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.
@@ -168,8 +176,8 @@ bitflags! {
const POLYGON = 1 << 5;
/// path().
const PATH = 1 << 6;
- // TODO: Bug 1823463. Add shape().
- // const SHAPE = 1 << 7;
+ /// shape().
+ const SHAPE = 1 << 7;
/// All flags.
const ALL =
@@ -179,7 +187,8 @@ bitflags! {
Self::CIRCLE.bits() |
Self::ELLIPSE.bits() |
Self::POLYGON.bits() |
- Self::PATH.bits();
+ Self::PATH.bits() |
+ Self::SHAPE.bits();
/// For shape-outside.
const SHAPE_OUTSIDE =
@@ -329,7 +338,17 @@ impl BasicShape {
.map(BasicShape::Polygon)
},
"path" if flags.contains(AllowedBasicShapes::PATH) => {
- Path::parse_function_arguments(i, shape_type).map(BasicShape::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()))),
@@ -490,7 +509,11 @@ impl Ellipse {
}
}
-fn parse_fill_rule<'i, 't>(input: &mut Parser<'i, 't>, shape_type: ShapeType) -> FillRule {
+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.
@@ -508,7 +531,9 @@ fn parse_fill_rule<'i, 't>(input: &mut Parser<'i, 't>, shape_type: ShapeType) ->
ShapeType::Filled => input
.try_parse(|i| -> Result<_, ParseError> {
let fill = FillRule::parse(i)?;
- i.expect_comma()?;
+ if expect_comma {
+ i.expect_comma()?;
+ }
Ok(fill)
})
.unwrap_or_default(),
@@ -532,7 +557,7 @@ impl Polygon {
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
) -> Result<Self, ParseError<'i>> {
- let fill = parse_fill_rule(input, shape_type);
+ let fill = parse_fill_rule(input, shape_type, true /* has comma */);
let coordinates = input
.parse_comma_separated(|i| {
Ok(PolygonCoord(
@@ -554,7 +579,7 @@ impl Path {
) -> Result<Self, ParseError<'i>> {
use crate::values::specified::svg_path::AllowEmpty;
- let fill = parse_fill_rule(input, shape_type);
+ let fill = parse_fill_rule(input, shape_type, true /* has comma */);
let path = SVGPathData::parse(input, AllowEmpty::No)?;
Ok(Path { fill, path })
}
@@ -717,3 +742,168 @@ impl ToComputedValue for BasicShapeRect {
Self::Inset(ToComputedValue::from_computed_value(computed))
}
}
+
+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 */);
+
+ let mut 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));
+ }
+
+ 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::{ArcSize, ArcSweep, ByTo, CoordinatePair};
+
+ // <shape-command> = <move-command> | <line-command> | <hv-line-command> |
+ // <curve-command> | <smooth-command> | <arc-command> | close
+ Ok(try_match_ident_ignore_ascii_case! { input,
+ "close" => Self::Close,
+ "move" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ Self::Move { by_to, point }
+ },
+ "line" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ Self::Line { by_to, point }
+ },
+ "hline" => {
+ let by_to = ByTo::parse(input)?;
+ let x = LengthPercentage::parse(context, input)?;
+ Self::HLine { by_to, x }
+ },
+ "vline" => {
+ let by_to = ByTo::parse(input)?;
+ let y = LengthPercentage::parse(context, input)?;
+ Self::VLine { by_to, y }
+ },
+ "curve" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ input.expect_ident_matching("via")?;
+ let control1 = CoordinatePair::parse(context, input)?;
+ match input.try_parse(|i| CoordinatePair::parse(context, i)) {
+ Ok(control2) => Self::CubicCurve {
+ by_to,
+ point,
+ control1,
+ control2,
+ },
+ Err(_) => Self::QuadCurve {
+ by_to,
+ point,
+ control1,
+ },
+ }
+ },
+ "smooth" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ if input.try_parse(|i| i.expect_ident_matching("via")).is_ok() {
+ let control2 = CoordinatePair::parse(context, input)?;
+ Self::SmoothCubic {
+ by_to,
+ point,
+ control2,
+ }
+ } else {
+ Self::SmoothQuad { by_to, point }
+ }
+ },
+ "arc" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ input.expect_ident_matching("of")?;
+ let rx = LengthPercentage::parse(context, input)?;
+ let ry = input
+ .try_parse(|i| LengthPercentage::parse(context, i))
+ .unwrap_or(rx.clone());
+ let radii = CoordinatePair::new(rx, ry);
+
+ // [<arc-sweep> || <arc-size> || rotate <angle>]?
+ 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 {
+ by_to,
+ 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))
+ }
+}