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; }