diff --git a/Libraries/LibWeb/CSS/ComputedProperties.cpp b/Libraries/LibWeb/CSS/ComputedProperties.cpp index b0016abf739..9ea4229f8da 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.cpp +++ b/Libraries/LibWeb/CSS/ComputedProperties.cpp @@ -656,15 +656,16 @@ TransformOrigin ComputedProperties::transform_origin() const }; auto const& value = property(PropertyID::TransformOrigin); - if (!value.is_value_list() || value.as_value_list().size() != 2) + if (!value.is_value_list() || value.as_value_list().size() != 3) return {}; auto const& list = value.as_value_list(); auto x_value = length_percentage_with_keywords_resolved(list.values()[0]); auto y_value = length_percentage_with_keywords_resolved(list.values()[1]); - if (!x_value.has_value() || !y_value.has_value()) + auto z_value = length_percentage_for_style_value(list.values()[2]); + if (!x_value.has_value() || !y_value.has_value() || !z_value.has_value()) return {}; - return { x_value.value(), y_value.value() }; + return { x_value.value(), y_value.value(), z_value.value() }; } Optional ComputedProperties::accent_color(Layout::NodeWithStyle const& node) const diff --git a/Libraries/LibWeb/CSS/ComputedValues.h b/Libraries/LibWeb/CSS/ComputedValues.h index 9c751b67550..4ab2924316d 100644 --- a/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Libraries/LibWeb/CSS/ComputedValues.h @@ -354,6 +354,7 @@ struct WhiteSpaceTrimData { struct TransformOrigin { CSS::LengthPercentage x { Percentage(50) }; CSS::LengthPercentage y { Percentage(50) }; + CSS::LengthPercentage z { Percentage(0) }; }; struct ShadowData { diff --git a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp index 373229c45da..19a0708a30e 100644 --- a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp @@ -3806,7 +3806,6 @@ RefPtr Parser::parse_transform_value(TokenStream Parser::parse_transform_origin_value(TokenStream& tokens) { enum class Axis { @@ -3851,13 +3850,14 @@ RefPtr Parser::parse_transform_origin_value(TokenStream const& x_value, NonnullRefPtr const& y_value) -> NonnullRefPtr { + auto make_list = [&transaction](NonnullRefPtr const& x_value, NonnullRefPtr const& y_value, NonnullRefPtr const& z_value) -> NonnullRefPtr { transaction.commit(); - return StyleValueList::create(StyleValueVector { x_value, y_value }, StyleValueList::Separator::Space); + return StyleValueList::create(StyleValueVector { x_value, y_value, z_value }, StyleValueList::Separator::Space); }; - switch (tokens.remaining_token_count()) { - case 1: { + static CSSStyleValue const& zero_value = LengthStyleValue::create(Length::make_px(0)); + + if (tokens.remaining_token_count() == 1) { auto single_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens)); if (!single_value.has_value()) return nullptr; @@ -3866,60 +3866,70 @@ RefPtr Parser::parse_transform_origin_value(TokenStreamaxis) { case Axis::None: case Axis::X: - return make_list(single_value->offset, CSSKeywordValue::create(Keyword::Center)); + return make_list(single_value->offset, CSSKeywordValue::create(Keyword::Center), zero_value); case Axis::Y: - return make_list(CSSKeywordValue::create(Keyword::Center), single_value->offset); + return make_list(CSSKeywordValue::create(Keyword::Center), single_value->offset, zero_value); } VERIFY_NOT_REACHED(); } - case 2: { - auto first_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens)); - auto second_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens)); - if (!first_value.has_value() || !second_value.has_value()) + + if (tokens.remaining_token_count() > 3) + return nullptr; + + auto first_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens)); + auto second_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens)); + auto third_value = parse_length_value(tokens); + + if ((first_value->offset->is_length() || first_value->offset->is_percentage()) && second_value->axis == Axis::X) + return nullptr; + if ((second_value->offset->is_length() || second_value->offset->is_percentage()) && first_value->axis == Axis::Y) + return nullptr; + + if (!first_value.has_value() || !second_value.has_value()) + return nullptr; + + if (!third_value) + third_value = zero_value; + + RefPtr x_value; + RefPtr y_value; + + if (first_value->axis == Axis::X) { + x_value = first_value->offset; + } else if (first_value->axis == Axis::Y) { + y_value = first_value->offset; + } + + if (second_value->axis == Axis::X) { + if (x_value) return nullptr; - - RefPtr x_value; - RefPtr y_value; - - if (first_value->axis == Axis::X) { - x_value = first_value->offset; - } else if (first_value->axis == Axis::Y) { - y_value = first_value->offset; - } - - if (second_value->axis == Axis::X) { - if (x_value) - return nullptr; - x_value = second_value->offset; - // Put the other in Y since its axis can't have been X - y_value = first_value->offset; - } else if (second_value->axis == Axis::Y) { - if (y_value) - return nullptr; + x_value = second_value->offset; + // Put the other in Y since its axis can't have been X + y_value = first_value->offset; + } else if (second_value->axis == Axis::Y) { + if (y_value) + return nullptr; + y_value = second_value->offset; + // Put the other in X since its axis can't have been Y + x_value = first_value->offset; + } else { + if (x_value) { + VERIFY(!y_value); y_value = second_value->offset; - // Put the other in X since its axis can't have been Y - x_value = first_value->offset; } else { - if (x_value) { - VERIFY(!y_value); - y_value = second_value->offset; - } else { - VERIFY(!x_value); - x_value = second_value->offset; - } + VERIFY(!x_value); + x_value = second_value->offset; } - // If two or more values are defined and either no value is a keyword, or the only used keyword is center, - // then the first value represents the horizontal position (or offset) and the second represents the vertical position (or offset). - // FIXME: A third value always represents the Z position (or offset) and must be of type . - if (first_value->axis == Axis::None && second_value->axis == Axis::None) { - x_value = first_value->offset; - y_value = second_value->offset; - } - return make_list(x_value.release_nonnull(), y_value.release_nonnull()); } + // If two or more values are defined and either no value is a keyword, or the only used keyword is center, + // then the first value represents the horizontal position (or offset) and the second represents the vertical position (or offset). + // A third value always represents the Z position (or offset) and must be of type . + if (first_value->axis == Axis::None && second_value->axis == Axis::None) { + x_value = first_value->offset; + y_value = second_value->offset; } - return nullptr; + return make_list(x_value.release_nonnull(), y_value.release_nonnull(), third_value.release_nonnull()); } RefPtr Parser::parse_transition_value(TokenStream& tokens) diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt b/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt index b1ed7a9d55a..437fcca9e23 100644 --- a/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt +++ b/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt @@ -123,9 +123,9 @@ All supported properties and their default values exposed from CSSStylePropertie 'WebkitTransform': 'none' 'webkitTransform': 'none' '-webkit-transform': 'none' -'WebkitTransformOrigin': '50% 50%' -'webkitTransformOrigin': '50% 50%' -'-webkit-transform-origin': '50% 50%' +'WebkitTransformOrigin': '50% 50% 0px' +'webkitTransformOrigin': '50% 50% 0px' +'-webkit-transform-origin': '50% 50% 0px' 'WebkitTransition': 'all' 'webkitTransition': 'all' '-webkit-transition': 'all' @@ -630,8 +630,8 @@ All supported properties and their default values exposed from CSSStylePropertie 'transform': 'none' 'transformBox': 'view-box' 'transform-box': 'view-box' -'transformOrigin': '50% 50%' -'transform-origin': '50% 50%' +'transformOrigin': '50% 50% 0px' +'transform-origin': '50% 50% 0px' 'transition': 'all' 'transitionBehavior': 'normal' 'transition-behavior': 'normal' diff --git a/Tests/LibWeb/Text/expected/css/calc-coverage.txt b/Tests/LibWeb/Text/expected/css/calc-coverage.txt index 5c74168d9f9..4057ce572b5 100644 --- a/Tests/LibWeb/Text/expected/css/calc-coverage.txt +++ b/Tests/LibWeb/Text/expected/css/calc-coverage.txt @@ -150,8 +150,8 @@ text-indent: 'calc(2%)' -> '2%' text-indent: 'calc(2% * var(--n))' -> 'calc(2 * 2%)' top: 'calc(2%)' -> '2%' top: 'calc(2% * var(--n))' -> 'calc(2 * 2%)' -transform-origin: 'calc(2px) calc(2%)' -> '2px 2%' -transform-origin: 'calc(2px * var(--n)) calc(2% * var(--n))' -> '4px calc(2 * 2%)' +transform-origin: 'calc(2px) calc(2%)' -> '2px 2% 0px' +transform-origin: 'calc(2px * var(--n)) calc(2% * var(--n))' -> '4px calc(2 * 2%) 0px' transition-delay: 'calc(2s)' -> '2s' transition-delay: 'calc(2s * var(--n))' -> '4s' transition-duration: 'calc(2s)' -> '2s' diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt index 463174cb750..2567c7acf23 100644 --- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt @@ -221,7 +221,7 @@ top: auto touch-action: auto transform: none transform-box: view-box -transform-origin: 50% 50% +transform-origin: 50% 50% 0px transition-behavior: normal transition-delay: 0s transition-duration: 0s diff --git a/Tests/LibWeb/Text/expected/css/transform-origin-serialization.txt b/Tests/LibWeb/Text/expected/css/transform-origin-serialization.txt index c0614686aae..1782ff76fad 100644 --- a/Tests/LibWeb/Text/expected/css/transform-origin-serialization.txt +++ b/Tests/LibWeb/Text/expected/css/transform-origin-serialization.txt @@ -1,7 +1,7 @@ -center => center center -10px => 10px center -25% => 25% center -left 20% => left 20% -20px bottom => 20px bottom -top right => right top +center => center center 0px +10px => 10px center 0px +25% => 25% center 0px +left 20% => left 20% 0px +20px bottom => 20px bottom 0px +top right => right top 0px "center" => (invalid) diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt index 32fbb15ecca..a56d28ff0bc 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt @@ -1,8 +1,8 @@ Harness status: OK -Found 203 tests +Found 204 tests -199 Pass +200 Pass 4 Fail Pass accent-color Pass border-collapse @@ -192,6 +192,7 @@ Pass top Pass touch-action Fail transform Pass transform-box +Pass transform-origin Pass transition-behavior Pass transition-delay Pass transition-duration diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-invalid.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-invalid.txt index 680c1faea29..c4208dc9e85 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-invalid.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-invalid.txt @@ -2,13 +2,12 @@ Harness status: OK Found 10 tests -8 Pass -2 Fail +10 Pass Pass e.style['transform-origin'] = "1px 2px 3%" should not set the property value Pass e.style['transform-origin'] = "1px 2px left" should not set the property value Pass e.style['transform-origin'] = "1px 2px 3px 4px" should not set the property value -Fail e.style['transform-origin'] = "1px left" should not set the property value -Fail e.style['transform-origin'] = "top 1px" should not set the property value +Pass e.style['transform-origin'] = "1px left" should not set the property value +Pass e.style['transform-origin'] = "top 1px" should not set the property value Pass e.style['transform-origin'] = "right left" should not set the property value Pass e.style['transform-origin'] = "top bottom" should not set the property value Pass e.style['transform-origin'] = "bottom 10% right 20%" should not set the property value diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-valid.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-valid.txt index 66b3b461050..356a2df8b2d 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-valid.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-transforms/parsing/transform-origin-valid.txt @@ -2,8 +2,7 @@ Harness status: OK Found 16 tests -12 Pass -4 Fail +16 Pass Pass e.style['transform-origin'] = "left" should set the property value Pass e.style['transform-origin'] = "center" should set the property value Pass e.style['transform-origin'] = "right" should set the property value @@ -15,8 +14,8 @@ Pass e.style['transform-origin'] = "-4%" should set the property value Pass e.style['transform-origin'] = "left center" should set the property value Pass e.style['transform-origin'] = "center top" should set the property value Pass e.style['transform-origin'] = "right -4%" should set the property value -Fail e.style['transform-origin'] = "-1px bottom 5px" should set the property value -Fail e.style['transform-origin'] = "center left 6px" should set the property value +Pass e.style['transform-origin'] = "-1px bottom 5px" should set the property value +Pass e.style['transform-origin'] = "center left 6px" should set the property value Pass e.style['transform-origin'] = "top center" should set the property value -Fail e.style['transform-origin'] = "bottom right 7px" should set the property value -Fail e.style['transform-origin'] = "-1px -2px -3px" should set the property value \ No newline at end of file +Pass e.style['transform-origin'] = "bottom right 7px" should set the property value +Pass e.style['transform-origin'] = "-1px -2px -3px" should set the property value \ No newline at end of file