Skip to content
Permalink
Browse files
getComputedStyle() on transform property should return function list
https://bugs.webkit.org/show_bug.cgi?id=23924

Reviewed by Antti Koivisto.

We should serialize the "transform" property to a series of individual functions to match
the individual operations found in the TransformOperations value. To determine when we should
be doing this, we pass a nullptr value as the renderer when calling valueForPropertyInStyle()
on the ComputedStyleExtractor in KeyframeEffect::getKeyframes() and WebAnimation::commitStyles().

Note the slight regression with first-letter and first-line tests where we now expose the "transform"
property not being rejected at parse-time, bug 245523 will address this.

* LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-pseudo/first-letter-allowed-properties-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-pseudo/first-line-allowed-properties-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-transforms/animation/transform-interpolation-computed-value-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-transforms/animation/transform-interpolation-inline-value-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animation/commitStyles-expected.txt:
* Source/WebCore/animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::getKeyframes):
* Source/WebCore/animation/WebAnimation.cpp:
(WebCore::WebAnimation::commitStyles):
* Source/WebCore/css/ComputedStyleExtractor.cpp:
(WebCore::computedTransform):

Canonical link: https://commits.webkit.org/254760@main
  • Loading branch information
graouts committed Sep 22, 2022
1 parent 1f756e6 commit ae44d0ca68fa6c38c22fad2efbc2dbdc45a4f581
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 135 deletions.
@@ -24,7 +24,7 @@ PASS KeyframeEffect.getKeyframes() returns expected values for animations with f
PASS KeyframeEffect.getKeyframes() returns expected values for animation with drop-shadow of filter property
PASS KeyframeEffect.getKeyframes() returns expected values for animations with text-shadow properties and missing keyframes
PASS KeyframeEffect.getKeyframes() returns expected values for animations with background-size properties and missing keyframes
FAIL KeyframeEffect.getKeyframes() returns expected values for animations with CSS variables as keyframe values assert_equals: value for 'transform' on Keyframe #1 should match expected "translate(100px)" but got "matrix(1, 0, 0, 1, 100, 0)"
FAIL KeyframeEffect.getKeyframes() returns expected values for animations with CSS variables as keyframe values assert_equals: value for 'transform' on Keyframe #1 should match expected "translate(100px)" but got "translate(100px, 0px)"
PASS KeyframeEffect.getKeyframes() returns expected values for animations with CSS variables as keyframe values in a shorthand property
PASS KeyframeEffect.getKeyframes() returns expected values for animations with a CSS variable which is overriden by the value in keyframe
PASS KeyframeEffect.getKeyframes() returns expected values for animations with only custom property in a keyframe
@@ -33,6 +33,6 @@ PASS verticalAlign should be applied to first-letter pseudo elements.
PASS wordSpacing should be applied to first-letter pseudo elements.
FAIL position should not be applied to first-letter pseudo elements. assert_equals: expected "static" but got "absolute"
FAIL transition should not be applied to first-letter pseudo elements. assert_equals: expected "all 0s ease 0s" but got "transform 1s ease 0s"
PASS transform should not be applied to first-letter pseudo elements.
FAIL transform should not be applied to first-letter pseudo elements. assert_equals: expected "none" but got "rotate(0deg)"
FAIL wordBreak should not be applied to first-letter pseudo elements. assert_equals: expected "normal" but got "break-all"

@@ -35,6 +35,6 @@ FAIL margin should not be applied to first-line pseudo elements. assert_equals:
FAIL padding should not be applied to first-line pseudo elements. assert_equals: expected "0px" but got "10px 20px 30px 40px"
FAIL position should not be applied to first-line pseudo elements. assert_equals: expected "static" but got "absolute"
FAIL transition should not be applied to first-line pseudo elements. assert_equals: expected "all 0s ease 0s" but got "transform 1s ease 0s"
PASS transform should not be applied to first-line pseudo elements.
FAIL transform should not be applied to first-line pseudo elements. assert_equals: expected "none" but got "rotate(0deg)"
FAIL wordBreak should not be applied to first-line pseudo elements. assert_equals: expected "normal" but got "break-all"

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -7,7 +7,7 @@ PASS Commits logical properties as physical properties
PASS Commits values calculated mid-interval
PASS Commits variable references as their computed values
PASS Commits custom variables
FAIL Commits em units as pixel values assert_approx_equals: expected 100 +/- 0.0001 but got 0
PASS Commits em units as pixel values
FAIL Commits relative line-height assert_equals: line-height is committed as a relative value expected "1.5" but got "15px"
PASS Commits transforms
FAIL Commits transforms as a transform list assert_equals: expected "translate(20px, 20px)" but got "matrix(1, 0, 0, 1, 20, 20)"
@@ -631,7 +631,6 @@ auto KeyframeEffect::getKeyframes(Document& document) -> Vector<ComputedKeyframe
}

auto* target = m_target.get();
auto* renderer = this->renderer();
auto* lastStyleChangeEventStyle = targetStyleable()->lastStyleChangeEventStyle();
auto& elementStyle = lastStyleChangeEventStyle ? *lastStyleChangeEventStyle : currentStyle();

@@ -704,7 +703,7 @@ auto KeyframeEffect::getKeyframes(Document& document) -> Vector<ComputedKeyframe
}
}
if (styleString.isEmpty()) {
if (auto cssValue = computedStyleExtractor.valueForPropertyInStyle(style, cssPropertyId, renderer))
if (auto cssValue = computedStyleExtractor.valueForPropertyInStyle(style, cssPropertyId, nullptr))
styleString = cssValue->cssText();
}
computedKeyframe.styleStrings.set(cssPropertyId, styleString);
@@ -1512,7 +1512,7 @@ ExceptionOr<void> WebAnimation::commitStyles()
effect->animation()->resolve(*animatedStyle, { nullptr });
WTF::switchOn(property,
[&] (CSSPropertyID propertyId) {
if (auto cssValue = computedStyleExtractor.valueForPropertyInStyle(*animatedStyle, propertyId, renderer))
if (auto cssValue = computedStyleExtractor.valueForPropertyInStyle(*animatedStyle, propertyId, nullptr))
inlineStyle->setPropertyInternal(propertyId, cssValue->cssText(), false);
},
[&] (AtomString customProperty) {
@@ -52,17 +52,22 @@
#include "GridPositionsResolver.h"
#include "NodeRenderStyle.h"
#include "Pair.h"
#include "PerspectiveTransformOperation.h"
#include "QuotesData.h"
#include "Rect.h"
#include "RenderBlock.h"
#include "RenderBox.h"
#include "RenderGrid.h"
#include "RenderInline.h"
#include "RotateTransformOperation.h"
#include "SVGElement.h"
#include "ScaleTransformOperation.h"
#include "SkewTransformOperation.h"
#include "StylePropertyShorthand.h"
#include "StylePropertyShorthandFunctions.h"
#include "StyleScope.h"
#include "Styleable.h"
#include "TranslateTransformOperation.h"

namespace WebCore {

@@ -725,18 +730,155 @@ static bool rendererCanBeTransformed(RenderObject* renderer)

static Ref<CSSValue> computedTransform(RenderElement* renderer, const RenderStyle& style)
{
if (!rendererCanBeTransformed(renderer) || !style.hasTransform())
return CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
auto& cssValuePool = CSSValuePool::singleton();

TransformationMatrix transform;
style.applyTransform(transform, renderer->transformReferenceBoxRect(style), { });
if (!style.hasTransform() || is<RenderInline>(renderer))
return cssValuePool.createIdentifierValue(CSSValueNone);

// Note that this does not flatten to an affine transform if ENABLE(3D_TRANSFORMS) is off, by design.
if (renderer) {
TransformationMatrix transform;
style.applyTransform(transform, renderer->transformReferenceBoxRect(style), { });
auto list = CSSValueList::createSpaceSeparated();
list->append(matrixTransformValue(transform, style));
return list;
}

// FIXME: Need to print out individual functions (https://bugs.webkit.org/show_bug.cgi?id=23924)
auto list = CSSValueList::createSpaceSeparated();
list->append(matrixTransformValue(transform, style));
return list;

auto translateLengthAsCSSValue = [&](const Length& length) {
if (length.isZero())
return cssValuePool.createValue(0, CSSUnitType::CSS_PX);
return ComputedStyleExtractor::zoomAdjustedPixelValueForLength(length, style);
};

for (auto& operation : style.transform().operations()) {
RefPtr<CSSFunctionValue> functionValue;
switch (operation->type()) {
// translate
case TransformOperation::TRANSLATE_X:
functionValue = CSSFunctionValue::create(CSSValueTranslateX);
functionValue->append(translateLengthAsCSSValue(downcast<TranslateTransformOperation>(*operation).x()));
break;
case TransformOperation::TRANSLATE_Y:
functionValue = CSSFunctionValue::create(CSSValueTranslateY);
functionValue->append(translateLengthAsCSSValue(downcast<TranslateTransformOperation>(*operation).y()));
break;
case TransformOperation::TRANSLATE_Z:
functionValue = CSSFunctionValue::create(CSSValueTranslateZ);
functionValue->append(translateLengthAsCSSValue(downcast<TranslateTransformOperation>(*operation).z()));
break;
case TransformOperation::TRANSLATE:
case TransformOperation::TRANSLATE_3D: {
auto& translate = downcast<TranslateTransformOperation>(*operation);
functionValue = CSSFunctionValue::create(translate.is3DOperation() ? CSSValueTranslate3d : CSSValueTranslate);
functionValue->append(translateLengthAsCSSValue(translate.x()));
functionValue->append(translateLengthAsCSSValue(translate.y()));
if (translate.is3DOperation())
functionValue->append(translateLengthAsCSSValue(translate.z()));
break;
}
// scale
case TransformOperation::SCALE_X:
functionValue = CSSFunctionValue::create(CSSValueScaleX);
functionValue->append(cssValuePool.createValue(downcast<ScaleTransformOperation>(*operation).x(), CSSUnitType::CSS_NUMBER));
break;
case TransformOperation::SCALE_Y:
functionValue = CSSFunctionValue::create(CSSValueScaleX);
functionValue->append(cssValuePool.createValue(downcast<ScaleTransformOperation>(*operation).y(), CSSUnitType::CSS_NUMBER));
break;
case TransformOperation::SCALE_Z:
functionValue = CSSFunctionValue::create(CSSValueScaleZ);
functionValue->append(cssValuePool.createValue(downcast<ScaleTransformOperation>(*operation).z(), CSSUnitType::CSS_NUMBER));
break;
case TransformOperation::SCALE:
case TransformOperation::SCALE_3D: {
auto& scale = downcast<ScaleTransformOperation>(*operation);
functionValue = CSSFunctionValue::create(scale.is3DOperation() ? CSSValueScale3d : CSSValueScale);
functionValue->append(cssValuePool.createValue(scale.x(), CSSUnitType::CSS_NUMBER));
if (scale.z() == 1)
functionValue->append(cssValuePool.createValue(scale.y(), CSSUnitType::CSS_NUMBER));
else {
functionValue->append(cssValuePool.createValue(scale.y(), CSSUnitType::CSS_NUMBER));
functionValue->append(cssValuePool.createValue(scale.z(), CSSUnitType::CSS_NUMBER));
}
break;
}
// rotate
case TransformOperation::ROTATE_X:
functionValue = CSSFunctionValue::create(CSSValueRotateX);
functionValue->append(cssValuePool.createValue(downcast<RotateTransformOperation>(*operation).x(), CSSUnitType::CSS_NUMBER));
break;
case TransformOperation::ROTATE_Y:
functionValue = CSSFunctionValue::create(CSSValueRotateX);
functionValue->append(cssValuePool.createValue(downcast<RotateTransformOperation>(*operation).y(), CSSUnitType::CSS_NUMBER));
break;
case TransformOperation::ROTATE: {
auto& rotate = downcast<RotateTransformOperation>(*operation);
functionValue = CSSFunctionValue::create(CSSValueRotate);
if (!rotate.x() && !rotate.y() && rotate.z())
functionValue->append(cssValuePool.createValue(rotate.angle(), CSSUnitType::CSS_DEG));
else {
functionValue->append(cssValuePool.createValue(rotate.x(), CSSUnitType::CSS_NUMBER));
if (rotate.y())
functionValue->append(cssValuePool.createValue(rotate.y(), CSSUnitType::CSS_NUMBER));
functionValue->append(cssValuePool.createValue(rotate.angle(), CSSUnitType::CSS_DEG));
}
break;
}
case TransformOperation::ROTATE_3D: {
auto& rotate = downcast<RotateTransformOperation>(*operation);
functionValue = CSSFunctionValue::create(CSSValueRotate3d);
functionValue->append(cssValuePool.createValue(rotate.x(), CSSUnitType::CSS_NUMBER));
functionValue->append(cssValuePool.createValue(rotate.y(), CSSUnitType::CSS_NUMBER));
functionValue->append(cssValuePool.createValue(rotate.z(), CSSUnitType::CSS_NUMBER));
functionValue->append(cssValuePool.createValue(rotate.angle(), CSSUnitType::CSS_DEG));
break;
}
// skew
case TransformOperation::SKEW_X:
functionValue = CSSFunctionValue::create(CSSValueSkewX);
functionValue->append(cssValuePool.createValue(downcast<SkewTransformOperation>(*operation).angleX(), CSSUnitType::CSS_DEG));
break;
case TransformOperation::SKEW_Y:
functionValue = CSSFunctionValue::create(CSSValueSkewX);
functionValue->append(cssValuePool.createValue(downcast<SkewTransformOperation>(*operation).angleY(), CSSUnitType::CSS_DEG));
break;
case TransformOperation::SKEW: {
auto& skew = downcast<SkewTransformOperation>(*operation);
functionValue = CSSFunctionValue::create(CSSValueSkew);
functionValue->append(cssValuePool.createValue(skew.angleX(), CSSUnitType::CSS_DEG));
if (skew.angleY())
functionValue->append(cssValuePool.createValue(skew.angleY(), CSSUnitType::CSS_DEG));
break;
}
// perspective
case TransformOperation::PERSPECTIVE:
functionValue = CSSFunctionValue::create(CSSValuePerspective);
if (auto perspective = downcast<PerspectiveTransformOperation>(*operation).perspective())
functionValue->append(ComputedStyleExtractor::zoomAdjustedPixelValueForLength(*perspective, style));
else
functionValue->append(cssValuePool.createIdentifierValue(CSSValueNone));
break;
// matrix
case TransformOperation::MATRIX:
case TransformOperation::MATRIX_3D: {
TransformationMatrix transform;
operation->apply(transform, { });
functionValue = matrixTransformValue(transform, style);
break;
}
case TransformOperation::IDENTITY:
case TransformOperation::NONE:
continue;
}

list->append(functionValue.releaseNonNull());
}

if (list->length())
return list;

return cssValuePool.createIdentifierValue(CSSValueNone);
}

// https://drafts.csswg.org/css-transforms-2/#propdef-translate

0 comments on commit ae44d0c

Please sign in to comment.