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.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.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative.html
new file mode 100644
index 0000000000000..ae64b39a2f192
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-functions/shape-function-valid.tentative.html
@@ -0,0 +1,58 @@
+
+
+
+
+CSS Shapes Module Level 2: parsing the 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..e93449ecef457 100644
--- a/Source/WebCore/css/BasicShapeConversion.cpp
+++ b/Source/WebCore/css/BasicShapeConversion.cpp
@@ -31,11 +31,14 @@
#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"
@@ -94,6 +97,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 +166,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 +200,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;
@@ -241,7 +268,7 @@ static BasicShapeRadius cssValueToBasicShapeRadius(const CSSToLengthConversionDa
Ref basicShapeForValue(const CSSToLengthConversionData& conversionData, const CSSValue& value, float zoom)
{
- if (auto* circleValue = dynamicDowncast(value)) {
+ 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 +276,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 +286,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 +307,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 +321,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,9 +336,12 @@ Ref basicShapeForValue(const CSSToLengthConversionData& conversionDa
return rect;
}
- if (auto* pathValue = dynamicDowncast(value))
+ if (RefPtr pathValue = dynamicDowncast(value))
return basicShapePathForValue(*pathValue, zoom);
+ if (RefPtr shapeValue = dynamicDowncast(value))
+ return basicShapeShapeForValue(*shapeValue, conversionData);
+
RELEASE_ASSERT_NOT_REACHED();
}
@@ -318,6 +353,23 @@ Ref basicShapePathForValue(const CSSPathValue& value, float zoom
return path;
}
+Ref basicShapeShapeForValue(const CSSShapeValue& shapeValue, const CSSToLengthConversionData& conversionData)
+{
+ Vector segments;
+ segments.reserveInitialCapacity(shapeValue.length());
+
+ for (auto& segment : shapeValue) {
+ RefPtr shapeSegment = dynamicDowncast(segment);
+ if (!shapeSegment) {
+ ASSERT_NOT_REACHED();
+ continue;
+ }
+ segments.append(fromCSSShapeSegmentValue(conversionData, *shapeSegment));
+ }
+
+ return BasicShapeShape::create(shapeValue.windRule(), convertToLengthPoint(conversionData, 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..e395fea44d51e 100644
--- a/Source/WebCore/css/BasicShapeConversion.h
+++ b/Source/WebCore/css/BasicShapeConversion.h
@@ -36,7 +36,9 @@ namespace WebCore {
class BasicShape;
class BasicShapeCenterCoordinate;
class BasicShapePath;
+class BasicShapeShape;
class CSSPathValue;
+class CSSShapeValue;
class CSSToLengthConversionData;
class CSSValue;
class RenderStyle;
@@ -48,6 +50,7 @@ Ref valueForSVGPath(const BasicShapePath&, SVGPathConversion = SVGPath
Ref basicShapeForValue(const CSSToLengthConversionData&, const CSSValue&, float zoom = 1);
Ref basicShapePathForValue(const CSSPathValue&, float zoom = 1);
+Ref basicShapeShapeForValue(const CSSShapeValue&, const CSSToLengthConversionData&);
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..47985094a10af
--- /dev/null
+++ b/Source/WebCore/css/BasicShapesShapeSegmentConversion.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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();
+ }
+ );
+}
+
+#pragma mark -
+
+BasicShapeShape::ShapeSegment fromCSSShapeSegmentValue(const CSSToLengthConversionData& conversionData, const CSSShapeSegmentValue& cssShapeSegment)
+{
+ return cssShapeSegment.toShapeSegment(conversionData);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/BasicShapesShapeSegmentConversion.h b/Source/WebCore/css/BasicShapesShapeSegmentConversion.h
new file mode 100644
index 0000000000000..35fdde0c64351
--- /dev/null
+++ b/Source/WebCore/css/BasicShapesShapeSegmentConversion.h
@@ -0,0 +1,45 @@
+/*
+ * 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 {
+
+class CSSShapeSegmentValue;
+class CSSToLengthConversionData;
+class RenderStyle;
+
+Ref toCSSShapeSegmentValue(const RenderStyle&, const BasicShapeShape::ShapeSegment&);
+BasicShapeShape::ShapeSegment fromCSSShapeSegmentValue(const CSSToLengthConversionData&, const CSSShapeSegmentValue&);
+
+} // 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..4d26d187c39c1 100644
--- a/Source/WebCore/css/CSSShapeSegmentValue.cpp
+++ b/Source/WebCore/css/CSSShapeSegmentValue.cpp
@@ -31,6 +31,7 @@
#include "CSSPrimitiveValue.h"
#include "CSSToLengthConversionData.h"
#include "CSSValuePair.h"
+#include "CalculationValue.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,76 @@ String CSSShapeSegmentValue::customCSSText() const
return builder.toString();
}
-} // namespace WebCore
+BasicShapeShape::ShapeSegment CSSShapeSegmentValue::toShapeSegment(const CSSToLengthConversionData& conversionData) const
+{
+ auto toLength = [&](const CSSValue& value) -> Length {
+ RefPtr primitiveValue = dynamicDowncast(value);
+ if (!primitiveValue)
+ return Length(0, LengthType::Fixed);
+
+ return primitiveValue->convertToLength(conversionData);
+ };
+
+ 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..6e6133e77f2ce 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"
@@ -74,6 +75,8 @@ class CSSShapeSegmentValue : public CSSValue {
static Ref createArc(CoordinateAffinity, Ref&& offset, Ref&& radius, CSSValueID sweep, CSSValueID size, Ref&& angle);
+ BasicShapeShape::ShapeSegment toShapeSegment(const CSSToLengthConversionData&) const;
+
private:
enum class SegmentDataType : uint8_t { Base, OnePoint, TwoPoint, Arc };
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..bebe8737b1109 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"
@@ -2606,6 +2608,217 @@ 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();
+ if (id == CSSValueMove) {
+ // = move
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ return CSSShapeSegmentValue::createMove(*affinityValue, toCoordinates.releaseNonNull());
+ } else if (id == CSSValueLine) {
+ // = line
+ auto affinityValue = consumeAffinity();
+ if (!affinityValue)
+ return nullptr;
+
+ auto toCoordinates = consumeCoordinatePair(range, context);
+ if (!toCoordinates)
+ return nullptr;
+
+ return CSSShapeSegmentValue::createLine(*affinityValue, toCoordinates.releaseNonNull());
+ } else if (id == CSSValueHline || id == 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());
+ } else if (id == CSSValueCurve) {
+ // = curve via {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());
+ } else if (id == CSSValueSmooth) {
+ // = smooth [via ]?
+ 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());
+ } else if (id == 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());
+
+ } else if (id == CSSValueClose)
+ return CSSShapeSegmentValue::createClose();
+
+ 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 , #)
+ WindRule 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 +2958,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..f7630a59821a3
--- /dev/null
+++ b/Source/WebCore/rendering/style/BasicShapesShape.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 Adobe Systems Incorporated. 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.
+ */
+
+#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..7516ee1361208
--- /dev/null
+++ b/Source/WebCore/rendering/style/BasicShapesShape.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012 Adobe Systems Incorporated. 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 "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)