diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-computed.tentative-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-computed.tentative-expected.txt
new file mode 100644
index 0000000000000..e66f85f447c5a
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-computed.tentative-expected.txt
@@ -0,0 +1,29 @@
+
+PASS Property clip-path value 'shape(from 20px 40px, line to 20px 30px)'
+PASS Property clip-path value 'shape(from 20px 40px, line to 20px 30px )'
+PASS Property clip-path value 'shape(from 0 0, line to 100% 100%)'
+PASS Property clip-path value 'shape(from 20px 40px, move to 20px 30px, line by 20px 30px)'
+PASS Property clip-path value 'shape(from 20px 40px, move to 20px 30px, hline to 100px)'
+PASS Property clip-path value 'shape(from 20px 40px, move to 20px 30px, hline by 100%)'
+PASS Property clip-path value 'shape(from 20px 40px, move to 20px 30px, vline to 100px)'
+PASS Property clip-path value 'shape(from 20px 40px, move to 20px 30px, vline by 100%)'
+PASS Property clip-path value 'shape(from 20px 40px, curve by 20px 20px using 10px 30px)'
+PASS Property clip-path value 'shape(from 20px 40px, curve by 20px 20px using 10px 30px 12px 32px)'
+PASS Property clip-path value 'shape(from 20px 40px, smooth by 20px 20px)'
+PASS Property clip-path value 'shape(from 20px 40px, smooth by 20px 20px using 12px 32px)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10%)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 0)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 0)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% rotate 0deg)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20%)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% cw)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% large)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of -10% -20% large)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% rotate 1deg)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% cw rotate 12deg)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% cw rotate 3.14159265rad)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% large rotate 12deg)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% cw large)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% large cw)'
+PASS Property clip-path value 'shape(from 20px 40px, arc by 20px 20px of 10% 20% rotate 12deg large)'
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-computed.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-computed.tentative.html
new file mode 100644
index 0000000000000..28f622f03bdd5
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-computed.tentative.html
@@ -0,0 +1,53 @@
+
+
+
+
+CSS Shapes Module Level 2: computed values for the shape() function
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-invalid.tentative-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-invalid.tentative-expected.txt
new file mode 100644
index 0000000000000..9f4601093492d
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-invalid.tentative-expected.txt
@@ -0,0 +1,14 @@
+
+PASS e.style['clip-path'] = "shape(from 20px 40px line to 20px 30px)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px line to 20px 30px,)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px, 40px, line to 20px, 30px)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, curve by 20px 20px, using 10px 30px 12px 32px)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, curve by 20px 20px using 10px 30px, 12px 32px)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, smooth by 20px 20px using 10px 30px 12px 32px)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, curve by 20px 20px via 10px 30px 12px 32px)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% 12deg)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% rotate 12deg rotate 13deg)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% cw large 12deg)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% small large)" should not set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% cw ccw)" should not set the property value
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-invalid.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-invalid.tentative.html
new file mode 100644
index 0000000000000..76f0fcc94d6d2
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-invalid.tentative.html
@@ -0,0 +1,30 @@
+
+
+
+
+CSS Shapes Module Level 1: parsing the shape() function
+
+
+
+
+
+
+
+
+
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative-expected.txt
new file mode 100644
index 0000000000000..0f2ff5f0cf0c2
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative-expected.txt
@@ -0,0 +1,29 @@
+
+PASS e.style['clip-path'] = "shape(from 20px 40px, line to 20px 30px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, line to 20px 30px )" should set the property value
+PASS e.style['clip-path'] = "shape(from 0 0, line to 100% 100%)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, move to 20px 30px, line by 20px 30px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, move to 20px 30px, hline to 100px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, move to 20px 30px, hline by 100%)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, move to 20px 30px, vline to 100px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, move to 20px 30px, vline by 100%)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, curve by 20px 20px using 10px 30px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, curve by 20px 20px using 10px 30px 12px 32px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, smooth by 20px 20px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, smooth by 20px 20px using 12px 32px)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10%)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 0)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 0)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% rotate 0deg)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20%)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% cw)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% large)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of -10% -20% large)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% rotate 1deg)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% cw rotate 12deg)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% cw rotate 0.52rad)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% large rotate 12deg)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% cw large)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% large cw)" should set the property value
+PASS e.style['clip-path'] = "shape(from 20px 40px, arc by 20px 20px of 10% 20% rotate 12deg large)" should set the property value
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative.html
new file mode 100644
index 0000000000000..16656a668f4af
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative.html
@@ -0,0 +1,50 @@
+
+
+
+
+CSS Shapes Module Level 2: parsing the shape() function
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
index 1f558b163fcce..e76fab6b824f3 100644
--- a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
+++ b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
@@ -1254,7 +1254,7 @@ CSSScrollbarWidthEnabled:
CSSShapeFunctionEnabled:
type: bool
- status: unstable
+ status: testable
category: css
humanReadableName: "CSS shape() function"
humanReadableDescription: "Enable the CSS shape() function"
diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt
index 69af20240ee62..3328f33677187 100644
--- a/Source/WebCore/Sources.txt
+++ b/Source/WebCore/Sources.txt
@@ -826,6 +826,7 @@ crypto/keys/CryptoKeyRSA.cpp
crypto/keys/CryptoKeyRSAComponents.cpp
crypto/keys/CryptoKeyRaw.cpp
css/BasicShapeConversion.cpp
+css/BasicShapesShapeSegmentConversion.cpp
css/CSSAnchorValue.cpp
css/CSSAspectRatioValue.cpp
css/CSSBackgroundRepeatValue.cpp
@@ -2801,6 +2802,7 @@ rendering/shapes/RectangleShape.cpp
rendering/shapes/Shape.cpp
rendering/shapes/ShapeOutsideInfo.cpp
rendering/style/BasicShapes.cpp
+rendering/style/BasicShapesShape.cpp
rendering/style/BorderData.cpp
rendering/style/BorderValue.cpp
rendering/style/ContentData.cpp
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 6b155b379fb05..9c4dc2ac8298e 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -7305,6 +7305,10 @@
0FCF33230F2B9715004B6795 /* ColorCG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ColorCG.cpp; sourceTree = ""; };
0FCF332A0F2B9A25004B6795 /* WebLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebLayer.mm; sourceTree = ""; };
0FCF332B0F2B9A25004B6795 /* WebLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebLayer.h; sourceTree = ""; };
+ 0FD2C6A12C58485E00ED7278 /* BasicShapesShapeSegmentConversion.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BasicShapesShapeSegmentConversion.cpp; sourceTree = ""; };
+ 0FD2C6A22C58485E00ED7278 /* BasicShapesShapeSegmentConversion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BasicShapesShapeSegmentConversion.h; sourceTree = ""; };
+ 0FD2C6A32C5848A900ED7278 /* BasicShapesShape.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BasicShapesShape.h; sourceTree = ""; };
+ 0FD2C6A42C5848A900ED7278 /* BasicShapesShape.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BasicShapesShape.cpp; sourceTree = ""; };
0FD3080C117CF7E700A791F7 /* RenderFrameBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderFrameBase.cpp; sourceTree = ""; };
0FD3080D117CF7E700A791F7 /* RenderFrameBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderFrameBase.h; sourceTree = ""; };
0FD308D3117D168400A791F7 /* RenderIFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderIFrame.cpp; sourceTree = ""; };
@@ -33496,6 +33500,8 @@
children = (
FBD6AF8215EF21A3008B7110 /* BasicShapes.cpp */,
FBD6AF8315EF21A3008B7110 /* BasicShapes.h */,
+ 0FD2C6A42C5848A900ED7278 /* BasicShapesShape.cpp */,
+ 0FD2C6A32C5848A900ED7278 /* BasicShapesShape.h */,
0F283A9123563126004794CA /* BorderData.cpp */,
BC5EB5E00E81BE8700B25965 /* BorderData.h */,
BCD34EEC28ECB6D800472470 /* BorderValue.cpp */,
@@ -35973,6 +35979,8 @@
4BAFD0DA21921EAD00C0AB64 /* typedom */,
FBD6AF8415EF21D4008B7110 /* BasicShapeConversion.cpp */,
FBD6AF8515EF21D4008B7110 /* BasicShapeConversion.h */,
+ 0FD2C6A12C58485E00ED7278 /* BasicShapesShapeSegmentConversion.cpp */,
+ 0FD2C6A22C58485E00ED7278 /* BasicShapesShapeSegmentConversion.h */,
713785EE28D9F4C50092D9F2 /* ComputedStyleExtractor.cpp */,
713785EF28D9F4C50092D9F2 /* ComputedStyleExtractor.h */,
A80E6CDA0A1989CA007FB8C5 /* Counter.h */,
diff --git a/Source/WebCore/css/BasicShapeConversion.cpp b/Source/WebCore/css/BasicShapeConversion.cpp
index 721dc947bf2d8..38dce34ec1e63 100644
--- a/Source/WebCore/css/BasicShapeConversion.cpp
+++ b/Source/WebCore/css/BasicShapeConversion.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
+ * Copyright (C) 2042 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,16 +32,21 @@
#include "BasicShapeConversion.h"
#include "BasicShapes.h"
+#include "BasicShapesShape.h"
+#include "BasicShapesShapeSegmentConversion.h"
#include "CSSBasicShapes.h"
#include "CSSCalcNegateNode.h"
#include "CSSCalcOperationNode.h"
#include "CSSCalcPrimitiveValueNode.h"
#include "CSSPrimitiveValueMappings.h"
+#include "CSSShapeSegmentValue.h"
#include "CSSValuePair.h"
#include "CSSValuePool.h"
#include "CalculationValue.h"
#include "LengthFunctions.h"
#include "RenderStyle.h"
+#include "RenderStyleInlines.h"
+#include "StyleBuilderState.h"
#include "SVGPathByteStream.h"
namespace WebCore {
@@ -94,6 +100,9 @@ Ref valueForBasicShape(const RenderStyle& style, const BasicShape& bas
auto createPair = [&](const LengthSize& size) {
return CSSValuePair::create(createValue(size.width), createValue(size.height));
};
+ auto createCoordinatePair = [&](const LengthPoint& point) {
+ return CSSValuePair::createNoncoalescing(createValue(point.x()), createValue(point.y()));
+ };
auto createReflectedSumValue = [&](const Length& a, const Length& b) {
auto reflected = convertTo100PercentMinusLengthSum(a, b);
return CSSPrimitiveValue::create(reflected, style);
@@ -160,6 +169,18 @@ Ref valueForBasicShape(const RenderStyle& style, const BasicShape& bas
createPair(rect.topLeftRadius()), createPair(rect.topRightRadius()),
createPair(rect.bottomRightRadius()), createPair(rect.bottomLeftRadius()));
}
+ case BasicShape::Type::Shape: {
+ auto& shape = uncheckedDowncast(basicShape);
+
+ CSSValueListBuilder segments;
+
+ for (auto& segment : shape.segments()) {
+ Ref value = toCSSShapeSegmentValue(style, segment);
+ segments.append(WTFMove(value));
+ }
+
+ return CSSShapeValue::create(shape.windRule(), createCoordinatePair(shape.startPoint()), WTFMove(segments));
+ }
}
RELEASE_ASSERT_NOT_REACHED();
}
@@ -182,6 +203,15 @@ static LengthSize convertToLengthSize(const CSSToLengthConversionData& conversio
return { convertToLength(conversionData, value->protectedFirst()), convertToLength(conversionData, value->protectedSecond()) };
}
+static LengthPoint convertToLengthPoint(const CSSToLengthConversionData& conversionData, const CSSValue& value)
+{
+ RefPtr pairValue = dynamicDowncast(value);
+ if (!pairValue)
+ return { };
+
+ return { convertToLength(conversionData, pairValue->first()), convertToLength(conversionData, pairValue->second()) };
+}
+
static BasicShapeCenterCoordinate convertToCenterCoordinate(const CSSToLengthConversionData& conversionData, const CSSValue* value)
{
CSSValueID keyword = CSSValueTop;
@@ -239,9 +269,11 @@ static BasicShapeRadius cssValueToBasicShapeRadius(const CSSToLengthConversionDa
return BasicShapeRadius(convertToLength(conversionData, *radius));
}
-Ref basicShapeForValue(const CSSToLengthConversionData& conversionData, const CSSValue& value, float zoom)
+Ref basicShapeForValue(const CSSValue& value, const Style::BuilderState& builderState)
{
- if (auto* circleValue = dynamicDowncast(value)) {
+ auto& conversionData = builderState.cssToLengthConversionData();
+
+ if (RefPtr circleValue = dynamicDowncast(value)) {
auto circle = BasicShapeCircle::create();
circle->setRadius(cssValueToBasicShapeRadius(conversionData, circleValue->protectedRadius().get()));
circle->setCenterX(convertToCenterCoordinate(conversionData, circleValue->protectedCenterX().get()));
@@ -249,7 +281,8 @@ Ref basicShapeForValue(const CSSToLengthConversionData& conversionDa
circle->setPositionWasOmitted(!circleValue->centerX() && !circleValue->centerY());
return circle;
}
- if (auto* ellipseValue = dynamicDowncast(value)) {
+
+ if (RefPtr ellipseValue = dynamicDowncast(value)) {
auto ellipse = BasicShapeEllipse::create();
ellipse->setRadiusX(cssValueToBasicShapeRadius(conversionData, ellipseValue->protectedRadiusX().get()));
ellipse->setRadiusY(cssValueToBasicShapeRadius(conversionData, ellipseValue->protectedRadiusY().get()));
@@ -258,14 +291,16 @@ Ref basicShapeForValue(const CSSToLengthConversionData& conversionDa
ellipse->setPositionWasOmitted(!ellipseValue->centerX() && !ellipseValue->centerY());
return ellipse;
}
- if (auto* polygonValue = dynamicDowncast(value)) {
+
+ if (RefPtr polygonValue = dynamicDowncast(value)) {
auto polygon = BasicShapePolygon::create();
polygon->setWindRule(polygonValue->windRule());
for (unsigned i = 0; i < polygonValue->size(); i += 2)
polygon->appendPoint(convertToLength(conversionData, *polygonValue->protectedItem(i)), convertToLength(conversionData, *polygonValue->protectedItem(i + 1)));
return polygon;
}
- if (auto* rectValue = dynamicDowncast(value)) {
+
+ if (RefPtr rectValue = dynamicDowncast(value)) {
auto rect = BasicShapeInset::create();
rect->setTop(convertToLength(conversionData, rectValue->protectedTop()));
rect->setRight(convertToLength(conversionData, rectValue->protectedRight()));
@@ -277,7 +312,8 @@ Ref basicShapeForValue(const CSSToLengthConversionData& conversionDa
rect->setBottomLeftRadius(convertToLengthSize(conversionData, rectValue->protectedBottomLeftRadius().get()));
return rect;
}
- if (auto* rectValue = dynamicDowncast(value)) {
+
+ if (RefPtr rectValue = dynamicDowncast(value)) {
auto rect = BasicShapeXywh::create();
rect->setInsetX(convertToLength(conversionData, rectValue->protectedInsetX().get()));
rect->setInsetY(convertToLength(conversionData, rectValue->protectedInsetY().get()));
@@ -290,7 +326,8 @@ Ref basicShapeForValue(const CSSToLengthConversionData& conversionDa
rect->setBottomLeftRadius(convertToLengthSize(conversionData, rectValue->protectedBottomLeftRadius().get()));
return rect;
}
- if (auto* rectValue = dynamicDowncast(value)) {
+
+ if (RefPtr rectValue = dynamicDowncast(value)) {
auto rect = BasicShapeRect::create();
rect->setTop(convertToLengthOrAuto(conversionData, rectValue->protectedTop()));
rect->setRight(convertToLengthOrAuto(conversionData, rectValue->protectedRight()));
@@ -304,20 +341,40 @@ Ref basicShapeForValue(const CSSToLengthConversionData& conversionDa
return rect;
}
- if (auto* pathValue = dynamicDowncast(value))
- return basicShapePathForValue(*pathValue, zoom);
+ if (RefPtr pathValue = dynamicDowncast(value))
+ return basicShapePathForValue(*pathValue, builderState);
+
+ if (RefPtr shapeValue = dynamicDowncast(value))
+ return basicShapeShapeForValue(*shapeValue, builderState);
RELEASE_ASSERT_NOT_REACHED();
}
-Ref basicShapePathForValue(const CSSPathValue& value, float zoom)
+Ref basicShapePathForValue(const CSSPathValue& value, const Style::BuilderState& builderState)
{
auto path = BasicShapePath::create(value.pathData().copy());
path->setWindRule(value.windRule());
- path->setZoom(zoom);
+ path->setZoom(builderState.style().usedZoom());
return path;
}
+Ref basicShapeShapeForValue(const CSSShapeValue& shapeValue, const Style::BuilderState& builderState)
+{
+ Vector segments;
+ segments.reserveInitialCapacity(shapeValue.length());
+
+ for (auto& segment : shapeValue) {
+ RefPtr shapeSegment = dynamicDowncast(segment);
+ if (!shapeSegment) {
+ ASSERT_NOT_REACHED();
+ continue;
+ }
+ segments.append(fromCSSShapeSegmentValue(*shapeSegment, builderState));
+ }
+
+ return BasicShapeShape::create(shapeValue.windRule(), convertToLengthPoint(builderState.cssToLengthConversionData(), shapeValue.protectedFromCoordinates().get()), WTFMove(segments));
+}
+
float floatValueForCenterCoordinate(const BasicShapeCenterCoordinate& center, float boxDimension)
{
float offset = floatValueForLength(center.length(), boxDimension);
diff --git a/Source/WebCore/css/BasicShapeConversion.h b/Source/WebCore/css/BasicShapeConversion.h
index 8371d3797e412..003c97ecfad63 100644
--- a/Source/WebCore/css/BasicShapeConversion.h
+++ b/Source/WebCore/css/BasicShapeConversion.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
+ * Copyright (C) 2042 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,10 +34,16 @@
namespace WebCore {
+namespace Style {
+class BuilderState;
+}
+
class BasicShape;
class BasicShapeCenterCoordinate;
class BasicShapePath;
+class BasicShapeShape;
class CSSPathValue;
+class CSSShapeValue;
class CSSToLengthConversionData;
class CSSValue;
class RenderStyle;
@@ -46,8 +53,9 @@ enum class SVGPathConversion : bool { None, ForceAbsolute };
Ref valueForBasicShape(const RenderStyle&, const BasicShape&, SVGPathConversion = SVGPathConversion::None);
Ref valueForSVGPath(const BasicShapePath&, SVGPathConversion = SVGPathConversion::None);
-Ref basicShapeForValue(const CSSToLengthConversionData&, const CSSValue&, float zoom = 1);
-Ref basicShapePathForValue(const CSSPathValue&, float zoom = 1);
+Ref basicShapeForValue(const CSSValue&, const Style::BuilderState&);
+Ref basicShapePathForValue(const CSSPathValue&, const Style::BuilderState&);
+Ref basicShapeShapeForValue(const CSSShapeValue&, const Style::BuilderState&);
float floatValueForCenterCoordinate(const BasicShapeCenterCoordinate&, float);
diff --git a/Source/WebCore/css/BasicShapesShapeSegmentConversion.cpp b/Source/WebCore/css/BasicShapesShapeSegmentConversion.cpp
new file mode 100644
index 0000000000000..9e46615f2d951
--- /dev/null
+++ b/Source/WebCore/css/BasicShapesShapeSegmentConversion.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 Noam Rosenthal All rights reserved.
+ * Copyright (C) 2024 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BasicShapesShapeSegmentConversion.h"
+
+#include "BasicShapesShape.h"
+#include "CSSPrimitiveValueMappings.h"
+#include "CSSShapeSegmentValue.h"
+#include "CSSValue.h"
+#include "CSSValuePair.h"
+
+namespace WebCore {
+
+static auto lengthToCSSValue(const Length& value, const RenderStyle& style)
+{
+ return CSSPrimitiveValue::create(value, style);
+}
+
+static auto lengthPointToCSSValue(const LengthPoint& value, const RenderStyle& style)
+{
+ return CSSValuePair::createNoncoalescing(
+ CSSPrimitiveValue::create(value.x(), style),
+ CSSPrimitiveValue::create(value.y(), style));
+}
+
+static auto lengthSizeToCSSValue(const LengthSize& value, const RenderStyle& style)
+{
+ return CSSValuePair::create(
+ CSSPrimitiveValue::create(value.width, style),
+ CSSPrimitiveValue::create(value.height, style));
+}
+
+Ref toCSSShapeSegmentValue(const RenderStyle& style, const BasicShapeShape::ShapeSegment& segment)
+{
+ return WTF::switchOn(segment,
+ [&](const ShapeMoveSegment& segment) {
+ return CSSShapeSegmentValue::createMove(segment.affinity(), lengthPointToCSSValue(segment.offset(), style));
+ },
+ [&](const ShapeLineSegment& segment) {
+ return CSSShapeSegmentValue::createLine(segment.affinity(), lengthPointToCSSValue(segment.offset(), style));
+ },
+ [&](const ShapeHorizontalLineSegment& segment) {
+ return CSSShapeSegmentValue::createHorizontalLine(segment.affinity(), lengthToCSSValue(segment.length(), style));
+ },
+ [&](const ShapeVerticalLineSegment& segment) {
+ return CSSShapeSegmentValue::createVerticalLine(segment.affinity(), lengthToCSSValue(segment.length(), style));
+ },
+ [&](const ShapeCurveSegment& segment) {
+ if (segment.controlPoint2())
+ return CSSShapeSegmentValue::createCubicCurve(segment.affinity(), lengthPointToCSSValue(segment.offset(), style), lengthPointToCSSValue(segment.controlPoint1(), style), lengthPointToCSSValue(segment.controlPoint2().value(), style));
+
+ return CSSShapeSegmentValue::createQuadraticCurve(segment.affinity(), lengthPointToCSSValue(segment.offset(), style), lengthPointToCSSValue(segment.controlPoint1(), style));
+ },
+ [&](const ShapeSmoothSegment& segment) {
+ if (segment.intermediatePoint())
+ return CSSShapeSegmentValue::createSmoothCubicCurve(segment.affinity(), lengthPointToCSSValue(segment.offset(), style), lengthPointToCSSValue(segment.intermediatePoint().value(), style));
+
+ return CSSShapeSegmentValue::createSmoothQuadraticCurve(segment.affinity(), lengthPointToCSSValue(segment.offset(), style));
+ },
+ [&](const ShapeArcSegment& segment) {
+ return CSSShapeSegmentValue::createArc(segment.affinity(), lengthPointToCSSValue(segment.offset(), style), lengthSizeToCSSValue(segment.ellipseSize(), style),
+ segment.sweep() == RotationDirection::Clockwise ? CSSValueCw : CSSValueCcw,
+ segment.arcSize() == ShapeArcSegment::ArcSize::Large ? CSSValueLarge : CSSValueSmall,
+ CSSPrimitiveValue::create(segment.angle(), CSSUnitType::CSS_DEG));
+ },
+ [&](const ShapeCloseSegment&) {
+ return CSSShapeSegmentValue::createClose();
+ }
+ );
+}
+
+BasicShapeShape::ShapeSegment fromCSSShapeSegmentValue(const CSSShapeSegmentValue& cssShapeSegment, const Style::BuilderState& builderState)
+{
+ return cssShapeSegment.toShapeSegment(builderState);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/BasicShapesShapeSegmentConversion.h b/Source/WebCore/css/BasicShapesShapeSegmentConversion.h
new file mode 100644
index 0000000000000..1337e8aee7499
--- /dev/null
+++ b/Source/WebCore/css/BasicShapesShapeSegmentConversion.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 Noam Rosenthal All rights reserved.
+ * Copyright (C) 2024 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS†AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "BasicShapesShape.h"
+#include
+
+namespace WebCore {
+
+namespace Style {
+class BuilderState;
+}
+
+class CSSShapeSegmentValue;
+class RenderStyle;
+
+Ref toCSSShapeSegmentValue(const RenderStyle&, const BasicShapeShape::ShapeSegment&);
+BasicShapeShape::ShapeSegment fromCSSShapeSegmentValue(const CSSShapeSegmentValue&, const Style::BuilderState&);
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/CSSBasicShapes.cpp b/Source/WebCore/css/CSSBasicShapes.cpp
index eeba2d637bbba..71b8e42cc2b89 100644
--- a/Source/WebCore/css/CSSBasicShapes.cpp
+++ b/Source/WebCore/css/CSSBasicShapes.cpp
@@ -541,8 +541,8 @@ Ref CSSShapeValue::create(WindRule windRule, Ref&&
return adoptRef(*new CSSShapeValue(windRule, WTFMove(fromCoordinates), WTFMove(shapeSegments)));
}
-CSSShapeValue::CSSShapeValue(WindRule windRule, Ref&& fromCoordinates, CSSValueListBuilder&& shapeCommands)
- : CSSValueContainingVector(ShapeClass, CommaSeparator, WTFMove(shapeCommands))
+CSSShapeValue::CSSShapeValue(WindRule windRule, Ref&& fromCoordinates, CSSValueListBuilder&& shapeSegments)
+ : CSSValueContainingVector(ShapeClass, CommaSeparator, WTFMove(shapeSegments))
, m_fromCoordinates(WTFMove(fromCoordinates))
, m_windRule(windRule)
{
@@ -550,14 +550,28 @@ CSSShapeValue::CSSShapeValue(WindRule windRule, Ref&& fromCoordina
String CSSShapeValue::customCSSText() const
{
- ASSERT_NOT_IMPLEMENTED_YET();
- return ""_s;
+ StringBuilder builder;
+ builder.append("shape("_s);
+
+ if (windRule() == WindRule::EvenOdd)
+ builder.append("evenodd "_s);
+
+ builder.append("from "_s, m_fromCoordinates->cssText(), ", "_s);
+ serializeItems(builder);
+
+ builder.append(')');
+ return builder.toString();
}
-bool CSSShapeValue::equals(const CSSShapeValue&) const
+bool CSSShapeValue::equals(const CSSShapeValue& other) const
{
- ASSERT_NOT_IMPLEMENTED_YET();
- return false;
+ if (windRule() != other.windRule())
+ return false;
+
+ if (!compareCSSValue(m_fromCoordinates, other.m_fromCoordinates))
+ return false;
+
+ return itemsEqual(other);
}
} // namespace WebCore
diff --git a/Source/WebCore/css/CSSShapeSegmentValue.cpp b/Source/WebCore/css/CSSShapeSegmentValue.cpp
index 1325a0b28af16..493a746942128 100644
--- a/Source/WebCore/css/CSSShapeSegmentValue.cpp
+++ b/Source/WebCore/css/CSSShapeSegmentValue.cpp
@@ -29,8 +29,9 @@
#include "BasicShapes.h"
#include "CSSPrimitiveValue.h"
-#include "CSSToLengthConversionData.h"
#include "CSSValuePair.h"
+#include "CalculationValue.h"
+#include "StyleBuilderState.h"
#include
#include
@@ -117,7 +118,7 @@ String CSSShapeSegmentValue::customCSSText() const
ASSERT(m_data);
- const auto command = [&]() {
+ const auto segmentName = [&]() {
switch (type()) {
case SegmentType::Move:
return "move"_s;
@@ -152,7 +153,7 @@ String CSSShapeSegmentValue::customCSSText() const
case SegmentType::CubicCurve:
case SegmentType::QuadraticCurve:
case SegmentType::SmoothCubicCurve:
- return " via "_s;
+ return " using "_s;
case SegmentType::Arc:
return " of "_s;
default:
@@ -163,7 +164,7 @@ String CSSShapeSegmentValue::customCSSText() const
StringBuilder builder;
auto byTo = m_data->affinity == CoordinateAffinity::Absolute ? " to "_s : " by "_s;
- builder.append(command(), byTo, m_data->offset->cssText(), conjunction());
+ builder.append(segmentName(), byTo, m_data->offset->cssText(), conjunction());
switch (m_data->type) {
case SegmentDataType::Base:
@@ -199,5 +200,75 @@ String CSSShapeSegmentValue::customCSSText() const
return builder.toString();
}
-} // namespace WebCore
+BasicShapeShape::ShapeSegment CSSShapeSegmentValue::toShapeSegment(const Style::BuilderState& builderState) const
+{
+ auto toLength = [&](const CSSValue& value) -> Length {
+ RefPtr primitiveValue = dynamicDowncast(value);
+ if (!primitiveValue)
+ return Length(0, LengthType::Fixed);
+
+ return primitiveValue->convertToLength(builderState.cssToLengthConversionData());
+ };
+
+ auto toLengthPoint = [&](const CSSValue& value) -> LengthPoint {
+ RefPtr pairValue = dynamicDowncast(value);
+ if (!pairValue)
+ return { };
+
+ return LengthPoint { toLength(pairValue->first()), toLength(pairValue->second()) };
+ };
+
+ auto toLengthSize = [&](const CSSValue& value) -> LengthSize {
+ RefPtr pairValue = dynamicDowncast(value);
+ if (!pairValue)
+ return { };
+ return LengthSize { toLength(pairValue->first()), toLength(pairValue->second()) };
+ };
+
+ auto toDegrees = [](const CSSValue& value) {
+ RefPtr angleValue = dynamicDowncast(value);
+ if (!angleValue || !angleValue->isAngle())
+ return 0.0;
+
+ return angleValue->computeDegrees();
+ };
+
+ switch (type()) {
+ case SegmentType::Move:
+ return ShapeMoveSegment(m_data->affinity, toLengthPoint(m_data->offset));
+ case SegmentType::Line:
+ return ShapeLineSegment(m_data->affinity, toLengthPoint(m_data->offset));
+ case SegmentType::HorizontalLine:
+ return ShapeHorizontalLineSegment(m_data->affinity, toLength(m_data->offset));
+ case SegmentType::VerticalLine:
+ return ShapeVerticalLineSegment(m_data->affinity, toLength(m_data->offset));
+ case SegmentType::CubicCurve: {
+ auto& twoPointData = static_cast(*m_data);
+ return ShapeCurveSegment(twoPointData.affinity, toLengthPoint(twoPointData.offset), toLengthPoint(twoPointData.p1), toLengthPoint(twoPointData.p2));
+ }
+ case SegmentType::QuadraticCurve: {
+ auto& onePointData = static_cast(*m_data);
+ return ShapeCurveSegment(onePointData.affinity, toLengthPoint(onePointData.offset), toLengthPoint(onePointData.p1));
+ }
+ case SegmentType::SmoothCubicCurve: {
+ auto& onePointData = static_cast(*m_data);
+ return ShapeSmoothSegment(onePointData.affinity, toLengthPoint(onePointData.offset), toLengthPoint(onePointData.p1));
+ }
+ case SegmentType::SmoothQuadraticCurve:
+ return ShapeSmoothSegment(m_data->affinity, toLengthPoint(m_data->offset));
+ case SegmentType::Arc: {
+ auto& arcData = static_cast(*m_data);
+ auto arcSweep = arcData.arcSweep == CSSValueCcw ? RotationDirection::Counterclockwise : RotationDirection::Clockwise;
+ auto arcSize = arcData.arcSize == CSSValueLarge ? ShapeArcSegment::ArcSize::Large : ShapeArcSegment::ArcSize::Small;
+ return ShapeArcSegment(m_data->affinity, toLengthPoint(arcData.offset), toLengthSize(arcData.radius), arcSweep, arcSize, toDegrees(arcData.angle));
+ }
+ case SegmentType::Close:
+ return ShapeCloseSegment();
+ }
+
+ ASSERT_NOT_REACHED();
+ return ShapeCloseSegment();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/CSSShapeSegmentValue.h b/Source/WebCore/css/CSSShapeSegmentValue.h
index 4af281ff5a3ef..b3f0db5eb15dc 100644
--- a/Source/WebCore/css/CSSShapeSegmentValue.h
+++ b/Source/WebCore/css/CSSShapeSegmentValue.h
@@ -26,6 +26,7 @@
#pragma once
+#include "BasicShapesShape.h"
#include "CSSPrimitiveValue.h"
#include "CSSValueKeywords.h"
#include "CSSValueList.h"
@@ -36,6 +37,10 @@
namespace WebCore {
+namespace Style {
+class BuilderState;
+}
+
class CSSToLengthConversionData;
enum class CoordinateAffinity : uint8_t;
@@ -74,6 +79,8 @@ class CSSShapeSegmentValue : public CSSValue {
static Ref createArc(CoordinateAffinity, Ref&& offset, Ref&& radius, CSSValueID sweep, CSSValueID size, Ref&& angle);
+ BasicShapeShape::ShapeSegment toShapeSegment(const Style::BuilderState&) const;
+
private:
enum class SegmentDataType : uint8_t { Base, OnePoint, TwoPoint, Arc };
diff --git a/Source/WebCore/css/CSSToStyleMap.cpp b/Source/WebCore/css/CSSToStyleMap.cpp
index 73089dc03b4b8..4206297e502cc 100644
--- a/Source/WebCore/css/CSSToStyleMap.cpp
+++ b/Source/WebCore/css/CSSToStyleMap.cpp
@@ -182,7 +182,7 @@ void CSSToStyleMap::mapFillRepeat(CSSPropertyID propertyID, FillLayer& layer, co
layer.setRepeat(FillRepeatXY { repeatX, repeatY });
}
-static inline bool convertToLengthSize(const CSSValue& value, CSSToLengthConversionData conversionData, LengthSize& size)
+static inline bool convertToLengthSize(const CSSValue& value, const CSSToLengthConversionData& conversionData, LengthSize& size)
{
if (value.isPair()) {
size.width = downcast(value.first()).convertToLength(conversionData);
diff --git a/Source/WebCore/css/CSSValueKeywords.in b/Source/WebCore/css/CSSValueKeywords.in
index d0f9ef9479013..e5e085da19022 100644
--- a/Source/WebCore/css/CSSValueKeywords.in
+++ b/Source/WebCore/css/CSSValueKeywords.in
@@ -1357,7 +1357,7 @@ ccw
// small
by
close
-via
+using
of
// @font-face src
diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
index 1450347fc66e7..5fb5f53160a16 100644
--- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
+++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
@@ -86,6 +86,7 @@
#include "CSSRectValue.h"
#include "CSSReflectValue.h"
#include "CSSScrollValue.h"
+#include "CSSShapeSegmentValue.h"
#include "CSSSubgridValue.h"
#include "CSSTimingFunctionValue.h"
#include "CSSTransformListValue.h"
@@ -99,6 +100,7 @@
#include "ColorInterpolation.h"
#include "FontCustomPlatformData.h"
#include "FontFace.h"
+#include "LengthPoint.h"
#include "Logging.h"
#include "RenderStyleConstants.h"
#include "SVGPathByteStream.h"
@@ -2561,7 +2563,7 @@ static RefPtr consumeBasicShapeEllipse(CSSParserTokenRange& arg
static RefPtr consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
{
- WindRule rule = WindRule::NonZero;
+ auto rule = WindRule::NonZero;
if (identMatches(args.peek().id())) {
if (args.consumeIncludingWhitespace().id() == CSSValueEvenodd)
rule = WindRule::EvenOdd;
@@ -2586,7 +2588,7 @@ static RefPtr consumeBasicShapePolygon(CSSParserTokenRange& arg
static RefPtr consumeBasicShapePath(CSSParserTokenRange& args, OptionSet options)
{
- WindRule rule = WindRule::NonZero;
+ auto rule = WindRule::NonZero;
if (identMatches(args.peek().id())) {
if (options.contains(RejectFillRule))
return nullptr;
@@ -2606,6 +2608,227 @@ static RefPtr consumeBasicShapePath(CSSParserTokenRange& args, Opt
return CSSPathValue::create(WTFMove(byteStream), rule);
}
+static RefPtr consumeCoordinatePair(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+ auto xDimension = consumeLengthOrPercent(range, context.mode);
+ if (!xDimension)
+ return nullptr;
+
+ auto yDimension = consumeLengthOrPercent(range, context.mode);
+ if (!yDimension)
+ return nullptr;
+
+ return CSSValuePair::createNoncoalescing(xDimension.releaseNonNull(), yDimension.releaseNonNull());
+}
+
+static RefPtr consumeShapeCommand(CSSParserTokenRange& range, const CSSParserContext& context, OptionSet)
+{
+ if (range.peek().type() != IdentToken)
+ return nullptr;
+
+ auto consumeAffinity = [&]() -> std::optional {
+ if (range.peek().type() != IdentToken)
+ return std::nullopt;
+
+ CSSValueID token = range.peek().id();
+ if (token != CSSValueBy && token != CSSValueTo)
+ return std::nullopt;
+
+ if (!consumeIdent(range))
+ return std::nullopt;
+
+ return token == CSSValueBy ? CoordinateAffinity::Relative : CoordinateAffinity::Absolute;
+ };
+
+ auto atEndOfCommand = [&] () {
+ return range.atEnd() || range.peek().type() == CommaToken;
+ };
+
+ auto id = range.consumeIncludingWhitespace().id();
+ switch(id) {
+ case CSSValueMove: {
+ // = move
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ return CSSShapeSegmentValue::createMove(*affinityValue, toCoordinates.releaseNonNull());
+ }
+ case CSSValueLine: {
+ // = line
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ return CSSShapeSegmentValue::createLine(*affinityValue, toCoordinates.releaseNonNull());
+ }
+ case CSSValueHline:
+ case CSSValueVline: {
+ // = [hline | vline]
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto length = consumeLengthOrPercent(range, context.mode);
+ if (!length)
+ return nullptr;
+
+ if (id == CSSValueHline)
+ return CSSShapeSegmentValue::createHorizontalLine(*affinityValue, length.releaseNonNull());
+
+ return CSSShapeSegmentValue::createVerticalLine(*affinityValue, length.releaseNonNull());
+ }
+ case CSSValueCurve: {
+ // = curve using {1,2}
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ if (!consumeIdent(range))
+ return nullptr;
+
+ auto controlPoint1 = consumeCoordinatePair(range, context);
+ if (!controlPoint1)
+ return nullptr;
+
+ auto controlPoint2 = consumeCoordinatePair(range, context);
+ if (controlPoint2)
+ return CSSShapeSegmentValue::createCubicCurve(*affinityValue, toCoordinates.releaseNonNull(), controlPoint1.releaseNonNull(), controlPoint2.releaseNonNull());
+
+ return CSSShapeSegmentValue::createQuadraticCurve(*affinityValue, toCoordinates.releaseNonNull(), controlPoint1.releaseNonNull());
+ }
+ case CSSValueSmooth: {
+ // = smooth [using ]?
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ if (consumeIdent(range)) {
+ auto controlPoint = consumeCoordinatePair(range, context);
+ if (!controlPoint)
+ return nullptr;
+
+ return CSSShapeSegmentValue::createSmoothCubicCurve(*affinityValue, toCoordinates.releaseNonNull(), controlPoint.releaseNonNull());
+ }
+
+ return CSSShapeSegmentValue::createSmoothQuadraticCurve(*affinityValue, toCoordinates.releaseNonNull());
+ }
+ case CSSValueArc: {
+ // arc of {1,2} [ || || rotate ]?
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ if (!consumeIdent(range))
+ return nullptr;
+
+ auto radiusX = consumeLengthOrPercent(range, context.mode);
+ auto radiusY = radiusX;
+ if (auto value = consumeLengthOrPercent(range, context.mode))
+ radiusY = value;
+
+ std::optional sweep;
+ std::optional size;
+ RefPtr angle;
+
+ while (!atEndOfCommand()) {
+ auto ident = consumeIdent(range);
+ if (!ident)
+ return nullptr;
+
+ switch (ident->valueID()) {
+ case CSSValueCw:
+ case CSSValueCcw:
+ if (sweep)
+ return nullptr;
+ sweep = ident->valueID();
+ break;
+ case CSSValueLarge:
+ case CSSValueSmall:
+ if (size)
+ return nullptr;
+ size = ident->valueID();
+ break;
+ case CSSValueRotate:
+ if (angle)
+ return nullptr;
+ angle = consumeAngle(range, context.mode, UnitlessQuirk::Forbid, UnitlessZeroQuirk::Forbid);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!angle)
+ angle = CSSPrimitiveValue::create(0, CSSUnitType::CSS_DEG);
+
+ auto radius = CSSValuePair::create(radiusX.releaseNonNull(), radiusY.releaseNonNull());
+ return CSSShapeSegmentValue::createArc(*affinityValue, toCoordinates.releaseNonNull(), WTFMove(radius), sweep.value_or(CSSValueCcw), size.value_or(CSSValueSmall), angle.releaseNonNull());
+ }
+ case CSSValueClose:
+ return CSSShapeSegmentValue::createClose();
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ return nullptr;
+}
+
+// https://drafts.csswg.org/css-shapes-2/#shape-function
+static RefPtr consumeBasicShapeShape(CSSParserTokenRange& range, const CSSParserContext& context, OptionSet options)
+{
+ if (!context.cssShapeFunctionEnabled)
+ return nullptr;
+
+ // shape() = shape( <'fill-rule'>? from , #)
+ auto rule = WindRule::NonZero;
+ if (identMatches(range.peek().id())) {
+ if (range.consumeIncludingWhitespace().id() == CSSValueEvenodd)
+ rule = WindRule::EvenOdd;
+ }
+
+ if (!consumeIdent(range))
+ return nullptr;
+
+ auto fromCoordinates = consumeCoordinatePair(range, context);
+ if (!fromCoordinates)
+ return nullptr;
+
+ if (!consumeCommaIncludingWhitespace(range))
+ return nullptr;
+
+ CSSValueListBuilder commands;
+ do {
+ auto command = consumeShapeCommand(range, context, options);
+ if (!command)
+ return nullptr;
+
+ commands.append(command.releaseNonNull());
+ } while (consumeCommaIncludingWhitespace(range));
+
+ return CSSShapeValue::create(rule, fromCoordinates.releaseNonNull(), WTFMove(commands));
+}
+
template static void complete4Sides(std::array& sides)
{
if (!sides[1])
@@ -2745,6 +2968,9 @@ static RefPtr consumeBasicShape(CSSParserTokenRange& range, const CSSP
result = consumeBasicShapeXywh(args, context);
else if (id == CSSValuePath)
result = consumeBasicShapePath(args, options);
+ else if (id == CSSValueShape)
+ result = consumeBasicShapeShape(args, context, options);
+
if (!result || !args.atEnd())
return nullptr;
diff --git a/Source/WebCore/rendering/style/BasicShapes.cpp b/Source/WebCore/rendering/style/BasicShapes.cpp
index df9778f198a7a..38012373a0fcf 100644
--- a/Source/WebCore/rendering/style/BasicShapes.cpp
+++ b/Source/WebCore/rendering/style/BasicShapes.cpp
@@ -141,6 +141,8 @@ static const Path& cachedTransformedByteStreamPath(const SVGPathByteStream& stre
return cache.get().get(SVGPathTransformedByteStream { stream, zoom, offset });
}
+// MARK: -
+
Ref BasicShapeCircle::create(BasicShapeCenterCoordinate&& centerX, BasicShapeCenterCoordinate&& centerY, BasicShapeRadius&& radius)
{
return adoptRef(*new BasicShapeCircle(WTFMove(centerX), WTFMove(centerY), WTFMove(radius)));
@@ -226,6 +228,8 @@ void BasicShapeCircle::dump(TextStream& ts) const
ts.dumpProperty("radius", radius());
}
+// MARK: -
+
Ref BasicShapeEllipse::create(BasicShapeCenterCoordinate&& centerX, BasicShapeCenterCoordinate&& centerY, BasicShapeRadius&& radiusX, BasicShapeRadius&& radiusY)
{
return adoptRef(*new BasicShapeEllipse(WTFMove(centerX), WTFMove(centerY), WTFMove(radiusX), WTFMove(radiusY)));
@@ -327,6 +331,8 @@ void BasicShapeEllipse::dump(TextStream& ts) const
ts.dumpProperty("radius-y", radiusY());
}
+// MARK: -
+
Ref BasicShapeRect::create(Length&& top, Length&& right, Length&& bottom, Length&& left, LengthSize&& topLeftRadius, LengthSize&& topRightRadius, LengthSize&& bottomRightRadius, LengthSize&& bottomLeftRadius)
{
return adoptRef(*new BasicShapeRect(WTFMove(top), WTFMove(right), WTFMove(bottom), WTFMove(left), WTFMove(topLeftRadius), WTFMove(topRightRadius), WTFMove(bottomRightRadius), WTFMove(bottomLeftRadius)));
@@ -434,6 +440,8 @@ void BasicShapeRect::dump(TextStream& ts) const
ts.dumpProperty("bottom-left-radius", bottomLeftRadius());
}
+// MARK: -
+
Ref BasicShapeXywh::create(Length&& insetX, Length&& insetY, Length&& width, Length&& height, LengthSize&& topLeftRadius, LengthSize&& topRightRadius, LengthSize&& bottomRightRadius, LengthSize&& bottomLeftRadius)
{
return adoptRef(*new BasicShapeXywh(WTFMove(insetX), WTFMove(insetY), WTFMove(width), WTFMove(height), WTFMove(topLeftRadius), WTFMove(topRightRadius), WTFMove(bottomRightRadius), WTFMove(bottomLeftRadius)));
@@ -539,6 +547,8 @@ void BasicShapeXywh::dump(TextStream& ts) const
}
+// MARK: -
+
Ref BasicShapePolygon::create(WindRule windRule, Vector&& values)
{
return adoptRef(*new BasicShapePolygon(windRule, WTFMove(values)));
@@ -687,6 +697,8 @@ void BasicShapePath::dump(TextStream& ts) const
// FIXME: print the byte stream?
}
+// MARK: -
+
Ref BasicShapeInset::create(Length&& right, Length&& top, Length&& bottom, Length&& left, LengthSize&& topLeftRadius, LengthSize&& topRightRadius, LengthSize&& bottomRightRadius, LengthSize&& bottomLeftRadius)
{
return adoptRef(*new BasicShapeInset(WTFMove(right), WTFMove(top), WTFMove(bottom), WTFMove(left), WTFMove(topLeftRadius), WTFMove(topRightRadius), WTFMove(bottomRightRadius), WTFMove(bottomLeftRadius)));
@@ -796,6 +808,17 @@ static TextStream& operator<<(TextStream& ts, BasicShapeRadius::Type radiusType)
return ts;
}
+// MARK: -
+
+TextStream& operator<<(TextStream& ts, CoordinateAffinity affinity)
+{
+ switch (affinity) {
+ case CoordinateAffinity::Relative: ts << "relative"_s; break;
+ case CoordinateAffinity::Absolute: ts << "absolute"_s; break;
+ }
+ return ts;
+}
+
TextStream& operator<<(TextStream& ts, const BasicShapeRadius& radius)
{
ts.dumpProperty("value", radius.value());
diff --git a/Source/WebCore/rendering/style/BasicShapes.h b/Source/WebCore/rendering/style/BasicShapes.h
index 8bc6356f3b6fc..d1a356f693d39 100644
--- a/Source/WebCore/rendering/style/BasicShapes.h
+++ b/Source/WebCore/rendering/style/BasicShapes.h
@@ -55,17 +55,19 @@ enum class CoordinateAffinity : uint8_t {
};
class BasicShape : public RefCounted {
+ WTF_MAKE_FAST_ALLOCATED;
public:
virtual ~BasicShape() = default;
- enum class Type {
+ enum class Type : uint8_t {
Polygon,
Path,
Circle,
Ellipse,
Inset,
Rect,
- Xywh
+ Xywh,
+ Shape
};
virtual Ref clone() const = 0;
@@ -495,6 +497,7 @@ class BasicShapeXywh final : public BasicShape {
LengthSize m_bottomLeftRadius;
};
+WTF::TextStream& operator<<(WTF::TextStream&, CoordinateAffinity);
WTF::TextStream& operator<<(WTF::TextStream&, const BasicShapeRadius&);
WTF::TextStream& operator<<(WTF::TextStream&, const BasicShapeCenterCoordinate&);
WTF::TextStream& operator<<(WTF::TextStream&, const BasicShape&);
diff --git a/Source/WebCore/rendering/style/BasicShapesShape.cpp b/Source/WebCore/rendering/style/BasicShapesShape.cpp
new file mode 100644
index 0000000000000..18ecc7adc9c52
--- /dev/null
+++ b/Source/WebCore/rendering/style/BasicShapesShape.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BasicShapesShape.h"
+
+#include "AnimationUtilities.h"
+#include "BasicShapeConversion.h"
+#include "CalculationValue.h"
+#include "FloatRect.h"
+#include "FloatRoundedRect.h"
+#include "LengthFunctions.h"
+#include "Path.h"
+#include "RenderBox.h"
+#include
+#include
+#include
+
+namespace WebCore {
+
+Ref BasicShapeShape::create(WindRule windRule, const CoordinatePair& startPoint, Vector&& commands)
+{
+ return adoptRef(* new BasicShapeShape(windRule, startPoint, WTFMove(commands)));
+}
+
+BasicShapeShape::BasicShapeShape(WindRule windRule, const CoordinatePair& startPoint, Vector&& commands)
+ : m_startPoint(startPoint)
+ , m_windRule(windRule)
+ , m_segments(WTFMove(commands))
+{
+}
+
+Ref BasicShapeShape::clone() const
+{
+ auto segmentsCopy = m_segments;
+ return BasicShapeShape::create(windRule(), startPoint(), WTFMove(segmentsCopy));
+}
+
+Path BasicShapeShape::path(const FloatRect&) const
+{
+ // Not yet implemented.
+ return Path();
+}
+
+bool BasicShapeShape::canBlend(const BasicShape&) const
+{
+ // Not yet implemented.
+ return false;
+}
+
+Ref BasicShapeShape::blend(const BasicShape&, const BlendingContext&) const
+{
+ // Not yet implemented.
+ return BasicShapeShape::clone(); // FIXME wrong.
+}
+
+bool BasicShapeShape::operator==(const BasicShape& other) const
+{
+ if (type() != other.type())
+ return false;
+
+ const auto& otherShape = downcast(other);
+ if (windRule() != otherShape.windRule())
+ return false;
+
+ if (startPoint() != otherShape.startPoint())
+ return false;
+
+ return segments() == otherShape.segments();
+}
+
+void BasicShapeShape::dump(TextStream& stream) const
+{
+ stream.dumpProperty("wind rule", windRule());
+ stream.dumpProperty("start point", startPoint());
+ stream << segments();
+}
+
+TextStream& operator<<(TextStream& stream, const BasicShapeShape::ShapeSegment& segment)
+{
+ WTF::switchOn(segment,
+ [&](const ShapeMoveSegment& segment) {
+ stream << "move" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.offset();
+ },
+ [&](const ShapeLineSegment& segment) {
+ stream << "line" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.offset();
+ },
+ [&](const ShapeHorizontalLineSegment& segment) {
+ stream << "hline" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.length();
+ },
+ [&](const ShapeVerticalLineSegment& segment) {
+ stream << "vline" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.length();
+ },
+ [&](const ShapeCurveSegment& segment) {
+ stream << "curve" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.offset() << " using " << segment.controlPoint1();
+ if (segment.controlPoint2())
+ stream << " " << segment.controlPoint2().value();
+ },
+ [&](const ShapeSmoothSegment& segment) {
+ stream << "smooth" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.offset();
+ if (segment.intermediatePoint())
+ stream << " using " << segment.intermediatePoint().value();
+ },
+ [&](const ShapeArcSegment& segment) {
+ stream << "arc" << (segment.affinity() == CoordinateAffinity::Relative ? " by "_s : " to "_s) << segment.offset() << " of " << segment.ellipseSize();
+ stream << " " << segment.sweep() << (segment.arcSize() == ShapeArcSegment::ArcSize::Small ? " small " : " large ") << " " << segment.angle() << "deg";
+ },
+ [&](const ShapeCloseSegment&) {
+ stream << "close";
+ }
+ );
+
+ return stream;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/BasicShapesShape.h b/Source/WebCore/rendering/style/BasicShapesShape.h
new file mode 100644
index 0000000000000..ba24fb5237840
--- /dev/null
+++ b/Source/WebCore/rendering/style/BasicShapesShape.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2024 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "BasicShapes.h"
+#include "LengthPoint.h"
+#include "LengthSize.h"
+#include "RotationDirection.h"
+
+namespace WTF {
+class TextStream;
+}
+
+namespace WebCore {
+
+using CoordinatePair = LengthPoint;
+
+class ShapeSegmentBase {
+public:
+ explicit ShapeSegmentBase(CoordinateAffinity affinity)
+ : m_affinity(affinity)
+ { }
+
+ bool operator==(const ShapeSegmentBase&) const = default;
+
+ CoordinateAffinity affinity() const { return m_affinity; }
+
+private:
+ const CoordinateAffinity m_affinity;
+};
+
+class ShapeMoveSegment final : public ShapeSegmentBase {
+public:
+ ShapeMoveSegment(CoordinateAffinity affinity, CoordinatePair&& offset)
+ : ShapeSegmentBase(affinity)
+ , m_offset(WTFMove(offset))
+ {
+ }
+
+ const CoordinatePair& offset() const { return m_offset; }
+
+ bool operator==(const ShapeMoveSegment&) const = default;
+
+private:
+ CoordinatePair m_offset;
+};
+
+class ShapeLineSegment final : public ShapeSegmentBase {
+public:
+ ShapeLineSegment(CoordinateAffinity affinity, CoordinatePair&& offset)
+ : ShapeSegmentBase(affinity)
+ , m_offset(WTFMove(offset))
+ {
+ }
+
+ const CoordinatePair& offset() const { return m_offset; }
+
+ bool operator==(const ShapeLineSegment&) const = default;
+
+private:
+ CoordinatePair m_offset;
+};
+
+class ShapeHorizontalLineSegment final : public ShapeSegmentBase {
+public:
+ ShapeHorizontalLineSegment(CoordinateAffinity affinity, Length&& length)
+ : ShapeSegmentBase(affinity)
+ , m_length(WTFMove(length))
+ {
+ }
+
+ Length length() const { return m_length; }
+
+ bool operator==(const ShapeHorizontalLineSegment&) const = default;
+
+private:
+ Length m_length;
+};
+
+class ShapeVerticalLineSegment final : public ShapeSegmentBase {
+public:
+ ShapeVerticalLineSegment(CoordinateAffinity affinity, Length&& length)
+ : ShapeSegmentBase(affinity)
+ , m_length(WTFMove(length))
+ {
+ }
+
+ Length length() const { return m_length; }
+
+ bool operator==(const ShapeVerticalLineSegment&) const = default;
+
+private:
+ Length m_length;
+};
+
+class ShapeCurveSegment final : public ShapeSegmentBase {
+public:
+ ShapeCurveSegment(CoordinateAffinity affinity, CoordinatePair&& offset, CoordinatePair&& controlPoint1, std::optional&& controlPoint2 = std::nullopt)
+ : ShapeSegmentBase(affinity)
+ , m_offset(WTFMove(offset))
+ , m_controlPoint1(WTFMove(controlPoint1))
+ , m_controlPoint2(WTFMove(controlPoint2))
+ {
+ }
+
+ const CoordinatePair& offset() const { return m_offset; }
+ const CoordinatePair& controlPoint1() const { return m_controlPoint1; }
+ const std::optional& controlPoint2() const { return m_controlPoint2; };
+
+ bool operator==(const ShapeCurveSegment&) const = default;
+
+private:
+ CoordinatePair m_offset;
+ CoordinatePair m_controlPoint1;
+ std::optional m_controlPoint2;
+};
+
+class ShapeSmoothSegment final : public ShapeSegmentBase {
+public:
+ ShapeSmoothSegment(CoordinateAffinity affinity, CoordinatePair&& offset, std::optional&& intermediatePoint = std::nullopt)
+ : ShapeSegmentBase(affinity)
+ , m_offset(WTFMove(offset))
+ , m_intermediatePoint(WTFMove(intermediatePoint))
+ {
+ }
+
+ const CoordinatePair& offset() const { return m_offset; }
+ const std::optional& intermediatePoint() const { return m_intermediatePoint; };
+
+ bool operator==(const ShapeSmoothSegment&) const = default;
+
+private:
+ CoordinatePair m_offset;
+ std::optional m_intermediatePoint;
+};
+
+class ShapeArcSegment final : public ShapeSegmentBase {
+public:
+ enum class ArcSize : uint8_t { Small, Large };
+ using AngleDegrees = double;
+
+ ShapeArcSegment(CoordinateAffinity affinity, CoordinatePair&& offset, LengthSize&& ellipseSize, RotationDirection sweep, ArcSize arcSize, AngleDegrees angle)
+ : ShapeSegmentBase(affinity)
+ , m_offset(WTFMove(offset))
+ , m_ellipseSize(WTFMove(ellipseSize))
+ , m_arcSweep(sweep)
+ , m_arcSize(arcSize)
+ , m_angle(angle)
+ {
+ }
+
+ const CoordinatePair& offset() const { return m_offset; }
+ const LengthSize& ellipseSize() const { return m_ellipseSize; }
+ RotationDirection sweep() const { return m_arcSweep; }
+ ArcSize arcSize() const { return m_arcSize; }
+ AngleDegrees angle() const { return m_angle; }
+
+ bool operator==(const ShapeArcSegment&) const = default;
+
+private:
+ CoordinatePair m_offset;
+ LengthSize m_ellipseSize;
+ RotationDirection m_arcSweep { RotationDirection::Counterclockwise };
+ ArcSize m_arcSize { ArcSize::Small };
+ AngleDegrees m_angle { 0 };
+};
+
+class ShapeCloseSegment {
+public:
+ bool operator==(const ShapeCloseSegment&) const = default;
+};
+
+// https://drafts.csswg.org/css-shapes-2/#shape-function
+class BasicShapeShape final : public BasicShape {
+public:
+ Ref clone() const final;
+
+ Type type() const final { return Type::Shape; }
+ const CoordinatePair& startPoint() const { return m_startPoint; }
+
+ Path path(const FloatRect&) const final;
+
+ bool canBlend(const BasicShape&) const final;
+ Ref blend(const BasicShape& from, const BlendingContext&) const final;
+
+ bool operator==(const BasicShape&) const final;
+
+ void dump(TextStream&) const final;
+
+ using ShapeSegment = std::variant<
+ ShapeMoveSegment,
+ ShapeLineSegment,
+ ShapeHorizontalLineSegment,
+ ShapeVerticalLineSegment,
+ ShapeCurveSegment,
+ ShapeSmoothSegment,
+ ShapeArcSegment,
+ ShapeCloseSegment
+ >;
+
+ const Vector& segments() const { return m_segments; }
+
+ static Ref create(WindRule, const CoordinatePair&, Vector&&);
+
+private:
+ BasicShapeShape(WindRule, const CoordinatePair&, Vector&&);
+
+ CoordinatePair m_startPoint;
+ const WindRule m_windRule { WindRule::NonZero };
+ Vector m_segments;
+};
+
+WTF::TextStream& operator<<(WTF::TextStream&, const BasicShapeShape::ShapeSegment&);
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_BASIC_SHAPE(BasicShapeShape, BasicShape::Type::Shape)
diff --git a/Source/WebCore/style/StyleBuilderConverter.h b/Source/WebCore/style/StyleBuilderConverter.h
index a8ecb8b92c930..c4de70c8dd0bf 100644
--- a/Source/WebCore/style/StyleBuilderConverter.h
+++ b/Source/WebCore/style/StyleBuilderConverter.h
@@ -760,10 +760,10 @@ inline RefPtr BuilderConverter::convertRayPathOperation(BuilderSt
return RayPathOperation::create(rayValue.angle()->computeDegrees(), size, rayValue.isContaining());
}
-inline RefPtr BuilderConverter::convertSVGPath(BuilderState&, const CSSValue& value)
+inline RefPtr BuilderConverter::convertSVGPath(BuilderState& builderState, const CSSValue& value)
{
if (auto* pathValue = dynamicDowncast(value))
- return basicShapePathForValue(*pathValue);
+ return basicShapePathForValue(*pathValue, builderState);
ASSERT(is(value));
ASSERT(downcast(value).valueID() == CSSValueNone);
@@ -799,7 +799,7 @@ inline RefPtr BuilderConverter::convertPathOperation(BuilderState
if (is(singleValue))
operation = convertRayPathOperation(builderState, singleValue);
else if (!singleValue.isValueID())
- operation = ShapePathOperation::create(basicShapeForValue(builderState.cssToLengthConversionData(), singleValue, builderState.style().usedZoom()));
+ operation = ShapePathOperation::create(basicShapeForValue(singleValue, builderState));
else
referenceBox = fromCSSValue(singleValue);
};
@@ -1084,7 +1084,7 @@ inline RefPtr BuilderConverter::convertShapeValue(BuilderState& buil
auto referenceBox = CSSBoxType::BoxMissing;
auto processSingleValue = [&](const CSSValue& currentValue) {
if (!currentValue.isValueID())
- shape = basicShapeForValue(builderState.cssToLengthConversionData(), currentValue);
+ shape = basicShapeForValue(currentValue, builderState);
else
referenceBox = fromCSSValue(currentValue);
};
diff --git a/Source/WebCore/style/StyleBuilderState.h b/Source/WebCore/style/StyleBuilderState.h
index f2697e1f3fda0..a08b725174eff 100644
--- a/Source/WebCore/style/StyleBuilderState.h
+++ b/Source/WebCore/style/StyleBuilderState.h
@@ -68,6 +68,8 @@ class BuilderState {
Builder& builder() { return m_builder; }
RenderStyle& style() { return m_style; }
+ const RenderStyle& style() const { return m_style; }
+
const RenderStyle& parentStyle() const { return m_context.parentStyle; }
const RenderStyle* rootElementStyle() const { return m_context.rootElementStyle; }