diff --git a/devtools/server/actors/animation-type-longhand.js b/devtools/server/actors/animation-type-longhand.js index 51feaae0d34f..a7a187da3f85 100644 --- a/devtools/server/actors/animation-type-longhand.js +++ b/devtools/server/actors/animation-type-longhand.js @@ -210,7 +210,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [ "min-block-size", "-moz-min-font-size-ratio", "min-inline-size", - "offset-path", + "offset-rotate", "padding-block-end", "padding-block-start", "padding-inline-end", @@ -268,6 +268,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [ "mask-position-y", "mask-size", "object-position", + "offset-path", "order", "perspective-origin", "shape-outside", diff --git a/devtools/shared/css/generated/properties-db.js b/devtools/shared/css/generated/properties-db.js index 7ecdf0e99393..a0724b47d1cc 100644 --- a/devtools/shared/css/generated/properties-db.js +++ b/devtools/shared/css/generated/properties-db.js @@ -3090,6 +3090,7 @@ exports.CSS_PROPERTIES = { "translate", "offset-path", "offset-distance", + "offset-rotate", "scroll-behavior", "scroll-snap-align", "scroll-snap-type", @@ -8309,6 +8310,21 @@ exports.CSS_PROPERTIES = { "unset" ] }, + "offset-rotate": { + "isInherited": false, + "subproperties": [ + "offset-rotate" + ], + "supports": [], + "values": [ + "auto", + "inherit", + "initial", + "reverse", + "revert", + "unset" + ] + }, "opacity": { "isInherited": false, "subproperties": [ @@ -10655,6 +10671,10 @@ exports.PREFERENCES = [ "-moz-osx-font-smoothing", "layout.css.osx-font-smoothing.enabled" ], + [ + "offset-rotate", + "layout.css.motion-path.enabled" + ], [ "overflow-anchor", "layout.css.scroll-anchoring.enabled" diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 702274b46487..c217ddfb6c96 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -9951,7 +9951,7 @@ Maybe nsLayoutUtils::ResolveMotionPath(const nsIFrame* aFrame) { return Nothing(); } - gfx::Float angle = 0.0; + double directionAngle = 0.0; Point point; if (display->mOffsetPath.IsPath()) { const Span& path = @@ -9999,16 +9999,22 @@ Maybe nsLayoutUtils::ResolveMotionPath(const nsIFrame* aFrame) { } Point tangent; point = gfxPath->ComputePointAtLength(usedDistance, &tangent); - // Bug 1429301 - Implement offset-rotate for motion path. - // After implement offset-rotate, |angle| will be adjusted more. - // For now, the default value of offset-rotate is "auto", so we use the - // directional tangent vector. - angle = atan2(tangent.y, tangent.x); + directionAngle = (double)atan2(tangent.y, tangent.x); // In Radian. } else { // Bug 1480665: Implement ray() function. NS_WARNING("Unsupported offset-path value"); } + const StyleOffsetRotate& rotate = display->mOffsetRotate; + // If |rotate.auto_| is true, the element should be rotated by the angle of + // the direction (i.e. directional tangent vector) of the offset-path, and the + // computed value of is added to this. + // Otherwise, the element has a constant clockwise rotation transformation + // applied to it by the specified rotation angle. (i.e. Don't need to + // consider the direction of the path.) + gfx::Float angle = static_cast( + (rotate.auto_ ? directionAngle : 0.0) + rotate.angle.ToRadians()); + // Compute the offset for motion path translate. // We need to resolve transform-origin here to calculate the correct path // translate. (i.e. Center transform-origin on the path.) diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 0030321c1686..551917f2a385 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -409,6 +409,7 @@ cbindgen-types = [ { gecko = "StyleFontFaceSourceListComponent", servo = "font_face::FontFaceSourceListComponent" }, { gecko = "StyleFontLanguageOverride", servo = "values::computed::font::FontLanguageOverride" }, { gecko = "StyleOffsetPath", servo = "values::computed::motion::OffsetPath" }, + { gecko = "StyleOffsetRotate", servo = "values::computed::motion::OffsetRotate" }, { gecko = "StylePathCommand", servo = "values::specified::svg_path::PathCommand" }, { gecko = "StyleUnicodeRange", servo = "cssparser::UnicodeRange" }, { gecko = "StyleOverflowWrap", servo = "values::computed::OverflowWrap" }, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 800df6f37fbf..c00d5ab77073 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2956,6 +2956,7 @@ nsStyleDisplay::nsStyleDisplay(const Document& aDocument) mTransformBox(StyleGeometryBox::BorderBox), mOffsetPath(StyleOffsetPath::None()), mOffsetDistance(LengthPercentage::Zero()), + mOffsetRotate{true, StyleAngle{0.0}}, mTransformOrigin{LengthPercentage::FromPercentage(0.5), LengthPercentage::FromPercentage(0.5), {0.}}, @@ -3021,6 +3022,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) mTransformBox(aSource.mTransformBox), mOffsetPath(aSource.mOffsetPath), mOffsetDistance(aSource.mOffsetDistance), + mOffsetRotate(aSource.mOffsetRotate), mTransformOrigin(aSource.mTransformOrigin), mChildPerspective(aSource.mChildPerspective), mPerspectiveOrigin(aSource.mPerspectiveOrigin), @@ -3061,15 +3063,14 @@ static inline nsChangeHint CompareTransformValues( } static inline nsChangeHint CompareMotionValues( - const StyleOffsetPath& aOffsetPath, const LengthPercentage& aOffsetDistance, - const StyleOffsetPath& aNewOffsetPath, - const LengthPercentage& aNewOffsetDistance) { - if (aOffsetPath == aNewOffsetPath) { - if (aOffsetDistance == aNewOffsetDistance) { + const nsStyleDisplay& aDisplay, const nsStyleDisplay& aNewDisplay) { + if (aDisplay.mOffsetPath == aNewDisplay.mOffsetPath) { + if (aDisplay.mOffsetDistance == aNewDisplay.mOffsetDistance && + aDisplay.mOffsetRotate == aNewDisplay.mOffsetRotate) { return nsChangeHint(0); } - if (aOffsetPath.IsNone()) { + if (aDisplay.mOffsetPath.IsNone()) { return nsChangeHint_NeutralChange; } } @@ -3079,7 +3080,7 @@ static inline nsChangeHint CompareMotionValues( // Set the same hints as what we use for transform because motion path is // a kind of transform and will be combined with other transforms. nsChangeHint result = nsChangeHint_UpdateTransformLayer; - if (!aOffsetPath.IsNone() && !aNewOffsetPath.IsNone()) { + if (!aDisplay.mOffsetPath.IsNone() && !aNewDisplay.mOffsetPath.IsNone()) { result |= nsChangeHint_UpdatePostTransformOverflow; } else { result |= nsChangeHint_UpdateOverflow; @@ -3208,9 +3209,7 @@ nsChangeHint nsStyleDisplay::CalcDifference( transformHint |= CompareTransformValues(mRotate, aNewData.mRotate); transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate); transformHint |= CompareTransformValues(mScale, aNewData.mScale); - transformHint |= - CompareMotionValues(mOffsetPath, mOffsetDistance, aNewData.mOffsetPath, - aNewData.mOffsetDistance); + transformHint |= CompareMotionValues(*this, aNewData); if (mTransformOrigin != aNewData.mTransformOrigin) { transformHint |= nsChangeHint_UpdateTransformLayer | diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index ba7cfcd394df..661d58008655 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1737,6 +1737,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { mozilla::StyleOffsetPath mOffsetPath; mozilla::LengthPercentage mOffsetDistance; + mozilla::StyleOffsetRotate mOffsetRotate; mozilla::StyleTransformOrigin mTransformOrigin; mozilla::StylePerspective mChildPerspective; diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 4197bec4bfaa..5119ffd7b7f2 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -8747,6 +8747,15 @@ if (IsCSSPropertyPrefEnabled("layout.css.motion-path.enabled")) { other_values: [ "10px", "10%", "190%", "-280%", "calc(30px + 40%)" ], invalid_values: [ "none", "45deg" ] }; + + gCSSProperties["offset-rotate"] = { + domProp: "offsetRotate", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "reverse", "0deg", "0rad reverse", "-45deg", "5turn auto" ], + invalid_values: [ "none", "10px", "reverse 0deg reverse", "reverse auto" ] + }; } if (IsCSSPropertyPrefEnabled("layout.css.clip-path-path.enabled")) { diff --git a/servo/components/style/properties/data.py b/servo/components/style/properties/data.py index a7a3479974bb..e771607b6d99 100644 --- a/servo/components/style/properties/data.py +++ b/servo/components/style/properties/data.py @@ -332,6 +332,7 @@ class Longhand(object): "MozScriptMinSize", "MozScriptSizeMultiplier", "NonNegativeNumber", + "OffsetRotate", "Opacity", "OutlineStyle", "Overflow", diff --git a/servo/components/style/properties/longhands/box.mako.rs b/servo/components/style/properties/longhands/box.mako.rs index d6c3366c4b70..da9e5b2e767c 100644 --- a/servo/components/style/properties/longhands/box.mako.rs +++ b/servo/components/style/properties/longhands/box.mako.rs @@ -384,6 +384,18 @@ ${helpers.predefined_type( servo_restyle_damage="reflow_out_of_flow" )} +// Motion Path Module Level 1 +${helpers.predefined_type( + "offset-rotate", + "OffsetRotate", + "computed::OffsetRotate::auto()", + products="gecko", + animation_value_type="none", + gecko_pref="layout.css.motion-path.enabled", + spec="https://drafts.fxtf.org/motion-1/#offset-rotate-property", + servo_restyle_damage="reflow_out_of_flow" +)} + // CSSOM View Module // https://www.w3.org/TR/cssom-view-1/ ${helpers.single_keyword( diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index b8e3c0eca174..add2ad1a40a7 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -65,7 +65,7 @@ pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageO pub use self::list::ListStyleType; pub use self::list::MozListReversed; pub use self::list::{QuotePair, Quotes}; -pub use self::motion::OffsetPath; +pub use self::motion::{OffsetPath, OffsetRotate}; pub use self::outline::OutlineStyle; pub use self::percentage::{NonNegativePercentage, Percentage}; pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex}; diff --git a/servo/components/style/values/computed/motion.rs b/servo/components/style/values/computed/motion.rs index a5f1fa885c3d..932d8074262b 100644 --- a/servo/components/style/values/computed/motion.rs +++ b/servo/components/style/values/computed/motion.rs @@ -4,7 +4,41 @@ //! Computed types for CSS values that are related to motion path. +use crate::values::computed::Angle; +use crate::Zero; + /// A computed offset-path. The computed value is as specified value. /// /// https://drafts.fxtf.org/motion-1/#offset-path-property pub use crate::values::specified::motion::OffsetPath; + +#[inline] +fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool { + *auto && angle.is_zero() +} + +/// A computed offset-rotate. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)] +#[repr(C)] +pub struct OffsetRotate { + /// If auto is false, this is a fixed angle which indicates a + /// constant clockwise rotation transformation applied to it by this + /// specified rotation angle. Otherwise, the angle will be added to + /// the angle of the direction in layout. + #[css(represents_keyword)] + pub auto: bool, + /// The angle value. + #[css(contextual_skip_if = "is_auto_zero_angle")] + pub angle: Angle, +} + +impl OffsetRotate { + /// Returns "auto 0deg". + #[inline] + pub fn auto() -> Self { + OffsetRotate { + auto: true, + angle: Zero::zero(), + } + } +} diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 4cb7305fab88..409f5abe1616 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -66,7 +66,7 @@ pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageO pub use self::list::ListStyleType; pub use self::list::MozListReversed; pub use self::list::{QuotePair, Quotes}; -pub use self::motion::OffsetPath; +pub use self::motion::{OffsetPath, OffsetRotate}; pub use self::outline::OutlineStyle; pub use self::percentage::Percentage; pub use self::position::{GridAutoFlow, GridTemplateAreas, Position}; diff --git a/servo/components/style/values/specified/motion.rs b/servo/components/style/values/specified/motion.rs index 73072ac61991..97609ea20b01 100644 --- a/servo/components/style/values/specified/motion.rs +++ b/servo/components/style/values/specified/motion.rs @@ -5,7 +5,10 @@ //! Specified types for CSS values that are related to motion path. use crate::parser::{Parse, ParserContext}; -use crate::values::specified::SVGPathData; +use crate::values::computed::motion::OffsetRotate as ComputedOffsetRotate; +use crate::values::computed::{Context, ToComputedValue}; +use crate::values::specified::{Angle, SVGPathData}; +use crate::Zero; use cssparser::Parser; use style_traits::{ParseError, StyleParseErrorKind}; @@ -75,3 +78,104 @@ impl Parse for OffsetPath { }) } } + +/// The direction of offset-rotate. +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[repr(u8)] +pub enum OffsetRotateDirection { + /// Unspecified direction keyword. + #[css(skip)] + None, + /// 0deg offset (face forward). + Auto, + /// 180deg offset (face backward). + Reverse, +} + +impl OffsetRotateDirection { + /// Returns true if it is none (i.e. the keyword is not specified). + #[inline] + fn is_none(&self) -> bool { + *self == OffsetRotateDirection::None + } +} + +#[inline] +fn direction_specified_and_angle_is_zero(direction: &OffsetRotateDirection, angle: &Angle) -> bool { + !direction.is_none() && angle.is_zero() +} + +/// The specified offset-rotate. +/// The syntax is: "[ auto | reverse ] || " +/// +/// https://drafts.fxtf.org/motion-1/#offset-rotate-property +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +pub struct OffsetRotate { + /// [auto | reverse]. + #[css(skip_if = "OffsetRotateDirection::is_none")] + direction: OffsetRotateDirection, + /// . + /// If direction is None, this is a fixed angle which indicates a + /// constant clockwise rotation transformation applied to it by this + /// specified rotation angle. Otherwise, the angle will be added to + /// the angle of the direction in layout. + #[css(contextual_skip_if = "direction_specified_and_angle_is_zero")] + angle: Angle, +} + +impl Parse for OffsetRotate { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let location = input.current_source_location(); + let mut direction = input.try(OffsetRotateDirection::parse); + let angle = input.try(|i| Angle::parse(context, i)); + if direction.is_err() { + // The direction and angle could be any order, so give it a change to parse + // direction again. + direction = input.try(OffsetRotateDirection::parse); + } + + if direction.is_err() && angle.is_err() { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(OffsetRotate { + direction: direction.unwrap_or(OffsetRotateDirection::None), + angle: angle.unwrap_or(Zero::zero()), + }) + } +} + +impl ToComputedValue for OffsetRotate { + type ComputedValue = ComputedOffsetRotate; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + use crate::values::computed::Angle as ComputedAngle; + + ComputedOffsetRotate { + auto: !self.direction.is_none(), + angle: if self.direction == OffsetRotateDirection::Reverse { + // The computed value should always convert "reverse" into "auto". + // e.g. "reverse calc(20deg + 10deg)" => "auto 210deg" + self.angle.to_computed_value(context) + ComputedAngle::from_degrees(180.0) + } else { + self.angle.to_computed_value(context) + }, + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + OffsetRotate { + direction: if computed.auto { + OffsetRotateDirection::Auto + } else { + OffsetRotateDirection::None + }, + angle: ToComputedValue::from_computed_value(&computed.angle), + } + } +} diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml index 3c82d575a55b..794a9b9bbfc6 100644 --- a/servo/ports/geckolib/cbindgen.toml +++ b/servo/ports/geckolib/cbindgen.toml @@ -70,6 +70,7 @@ include = [ "OverflowWrap", "TimingFunction", "OffsetPath", + "OffsetRotate", "UnicodeRange", "UserSelect", "Float", diff --git a/testing/web-platform/meta/css/motion/animation/offset-rotate-interpolation.html.ini b/testing/web-platform/meta/css/motion/animation/offset-rotate-interpolation.html.ini index 5cd874af2b66..2c441e505afd 100644 --- a/testing/web-platform/meta/css/motion/animation/offset-rotate-interpolation.html.ini +++ b/testing/web-platform/meta/css/motion/animation/offset-rotate-interpolation.html.ini @@ -1,7 +1,4 @@ [offset-rotate-interpolation.html] - ["100deg" and "180deg" are valid offset-rotate values] - expected: FAIL - [Animation between "100deg" and "180deg" at progress -1] expected: FAIL @@ -20,9 +17,6 @@ [Animation between "100deg" and "180deg" at progress 2] expected: FAIL - ["auto 100deg" and "reverse" are valid offset-rotate values] - expected: FAIL - [Animation between "auto 100deg" and "reverse" at progress -1] expected: FAIL @@ -41,9 +35,6 @@ [Animation between "auto 100deg" and "reverse" at progress 2] expected: FAIL - ["reverse 90deg" and "360deg" are valid offset-rotate values] - expected: FAIL - [Animation between "reverse 90deg" and "360deg" at progress -1] expected: FAIL @@ -62,9 +53,6 @@ [Animation between "reverse 90deg" and "360deg" at progress 2] expected: FAIL - ["6rad" and "auto" are valid offset-rotate values] - expected: FAIL - [Animation between "6rad" and "auto" at progress -1] expected: FAIL @@ -73,13 +61,3 @@ [Animation between "6rad" and "auto" at progress 0.125] expected: FAIL - - [Animation between "6rad" and "auto" at progress 0.875] - expected: FAIL - - [Animation between "6rad" and "auto" at progress 1] - expected: FAIL - - [Animation between "6rad" and "auto" at progress 2] - expected: FAIL - diff --git a/testing/web-platform/meta/css/motion/offset-supports-calc.html.ini b/testing/web-platform/meta/css/motion/offset-supports-calc.html.ini index d27d1404806f..2cbf7cc89945 100644 --- a/testing/web-platform/meta/css/motion/offset-supports-calc.html.ini +++ b/testing/web-platform/meta/css/motion/offset-supports-calc.html.ini @@ -8,9 +8,6 @@ [offset-distance supports calc] expected: FAIL - [offset-rotate supports calc] - expected: FAIL - [offset-anchor supports calc] expected: FAIL diff --git a/testing/web-platform/meta/css/motion/parsing/offset-rotate-computed.html.ini b/testing/web-platform/meta/css/motion/parsing/offset-rotate-computed.html.ini deleted file mode 100644 index 27c16eda2ccd..000000000000 --- a/testing/web-platform/meta/css/motion/parsing/offset-rotate-computed.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[offset-rotate-computed.html] - [Motion Path Module Level 1: getComputedValue().offsetRotate] - expected: FAIL - diff --git a/testing/web-platform/meta/css/motion/parsing/offset-rotate-parsing-valid.html.ini b/testing/web-platform/meta/css/motion/parsing/offset-rotate-parsing-valid.html.ini deleted file mode 100644 index f21b63c1ce40..000000000000 --- a/testing/web-platform/meta/css/motion/parsing/offset-rotate-parsing-valid.html.ini +++ /dev/null @@ -1,43 +0,0 @@ -[offset-rotate-parsing-valid.html] - [Serialization should round-trip after setting e.style['offset-rotate'\] = "5turn auto"] - expected: FAIL - - [Serialization should round-trip after setting e.style['offset-rotate'\] = "0rad reverse"] - expected: FAIL - - [e.style['offset-rotate'\] = "auto" should set the property value] - expected: FAIL - - [Serialization should round-trip after setting e.style['offset-rotate'\] = "auto"] - expected: FAIL - - [e.style['offset-rotate'\] = "reverse" should set the property value] - expected: FAIL - - [Serialization should round-trip after setting e.style['offset-rotate'\] = "reverse"] - expected: FAIL - - [e.style['offset-rotate'\] = "-400deg" should set the property value] - expected: FAIL - - [Serialization should round-trip after setting e.style['offset-rotate'\] = "-400deg"] - expected: FAIL - - [e.style['offset-rotate'\] = "auto 5turn" should set the property value] - expected: FAIL - - [Serialization should round-trip after setting e.style['offset-rotate'\] = "auto 5turn"] - expected: FAIL - - [e.style['offset-rotate'\] = "reverse 0rad" should set the property value] - expected: FAIL - - [Serialization should round-trip after setting e.style['offset-rotate'\] = "reverse 0rad"] - expected: FAIL - - [e.style['offset-rotate'\] = "5turn auto" should set the property value] - expected: FAIL - - [e.style['offset-rotate'\] = "0rad reverse" should set the property value] - expected: FAIL - diff --git a/testing/web-platform/tests/css/motion/inheritance.html b/testing/web-platform/tests/css/motion/inheritance.html index 8af4f9b91a83..4a82f32cd586 100644 --- a/testing/web-platform/tests/css/motion/inheritance.html +++ b/testing/web-platform/tests/css/motion/inheritance.html @@ -19,7 +19,8 @@ assert_not_inherited('offset-anchor', 'auto', '2px 3px'); assert_not_inherited('offset-distance', '0px', '4px'); assert_not_inherited('offset-path', 'none', 'path("M 5 6 H 7")'); assert_not_inherited('offset-position', 'auto', '8px 9px'); -assert_not_inherited('offset-rotate', 'auto 0deg', '90deg'); +// https://github.com/w3c/fxtf-drafts/issues/340 +assert_not_inherited('offset-rotate', ['auto 0deg', 'auto'], '90deg'); diff --git a/testing/web-platform/tests/css/motion/offset-rotate-003.html b/testing/web-platform/tests/css/motion/offset-rotate-003.html new file mode 100644 index 000000000000..8f52024a6fc6 --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-rotate-003.html @@ -0,0 +1,25 @@ + + + + CSS Motion Path: offset-rotate + + + + + + +
+ + diff --git a/testing/web-platform/tests/css/motion/offset-rotate-004.html b/testing/web-platform/tests/css/motion/offset-rotate-004.html new file mode 100644 index 000000000000..33783d423b50 --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-rotate-004.html @@ -0,0 +1,37 @@ + + + + CSS Motion Path: offset-rotate + + + + + + + +
+ + diff --git a/testing/web-platform/tests/css/motion/offset-rotate-005.html b/testing/web-platform/tests/css/motion/offset-rotate-005.html new file mode 100644 index 000000000000..9b1c40ed112c --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-rotate-005.html @@ -0,0 +1,37 @@ + + + + CSS Motion Path: offset-rotate + + + + + + + +
+ + diff --git a/testing/web-platform/tests/css/motion/parsing/offset-rotate-computed.html b/testing/web-platform/tests/css/motion/parsing/offset-rotate-computed.html index a29d25ccdf25..56705769fb7b 100644 --- a/testing/web-platform/tests/css/motion/parsing/offset-rotate-computed.html +++ b/testing/web-platform/tests/css/motion/parsing/offset-rotate-computed.html @@ -13,7 +13,8 @@
diff --git a/testing/web-platform/tests/css/support/computed-testcommon.js b/testing/web-platform/tests/css/support/computed-testcommon.js index 2cc19c176fa4..bb9673e9785d 100644 --- a/testing/web-platform/tests/css/support/computed-testcommon.js +++ b/testing/web-platform/tests/css/support/computed-testcommon.js @@ -6,23 +6,37 @@ * * @param {string} property The name of the CSS property being tested. * @param {string} specified A specified value for the property. - * @param {string} computed The expected computed value. If omitted, - defaults to specified. + * @param {string|array} computed The expected computed value, + * or an array of permitted computed value. + * If omitted, defaults to specified. */ function test_computed_value(property, specified, computed) { if (!computed) computed = specified; + + let computedDesc = "'" + computed + "'"; + if (Array.isArray(computed)) + computedDesc = '[' + computed.map(e => "'" + e + "'").join(' or ') + ']'; + test(() => { const target = document.getElementById('target'); if (!getComputedStyle(target)[property]) return; target.style[property] = ''; target.style[property] = specified; - assert_equals(getComputedStyle(target)[property], computed); - if (computed !== specified) { - target.style[property] = ''; - target.style[property] = computed; - assert_equals(getComputedStyle(target)[property], computed, 'computed value should round-trip'); + + let readValue = getComputedStyle(target)[property]; + if (Array.isArray(computed)) { + assert_in_array(readValue, computed); + } else { + assert_equals(readValue, computed); } - }, "Property " + property + " value '" + specified + "' computes to '" + computed + "'"); + if (readValue !== specified) { + target.style[property] = ''; + target.style[property] = readValue; + assert_equals(getComputedStyle(target)[property], readValue, + 'computed value should round-trip'); + } + }, "Property " + property + " value '" + specified + "' computes to " + + computedDesc); } diff --git a/testing/web-platform/tests/css/support/inheritance-testcommon.js b/testing/web-platform/tests/css/support/inheritance-testcommon.js index 9229f1268ea8..8d76fbc539db 100644 --- a/testing/web-platform/tests/css/support/inheritance-testcommon.js +++ b/testing/web-platform/tests/css/support/inheritance-testcommon.js @@ -3,14 +3,22 @@ (function() { function assert_initial(property, initial) { + let initialDesc = initial; + if (Array.isArray(initial)) + initialDesc = '[' + initial.map(e => "'" + e + "'").join(' or ') + ']'; + test(() => { const target = document.getElementById('target'); if (!getComputedStyle(target)[property]) return; target.style[property] = 'initial'; - assert_equals(getComputedStyle(target)[property], initial); + if (Array.isArray(initial)) { + assert_in_array(getComputedStyle(target)[property], initial); + } else { + assert_equals(getComputedStyle(target)[property], initial); + } target.style[property] = ''; - }, 'Property ' + property + ' has initial value ' + initial); + }, 'Property ' + property + ' has initial value ' + initialDesc); } /** @@ -18,10 +26,12 @@ function assert_initial(property, initial) { * * The current document must have an element #target within element #container. * - * @param {string} property The name of the CSS property being tested. - * @param {string} initial The computed value for 'initial'. - * @param {string} other An arbitrary value for the property that round - * trips and is distinct from the initial value. + * @param {string} property The name of the CSS property being tested. + * @param {string|array} initial The computed value for 'initial' or a list + * of acceptable computed value serializations. + * @param {string} other An arbitrary value for the property that + * round trips and is distinct from the initial + * value. */ function assert_inherited(property, initial, other) { assert_initial(property, initial); @@ -54,10 +64,12 @@ function assert_inherited(property, initial, other) { * * The current document must have an element #target within element #container. * - * @param {string} property The name of the CSS property being tested. - * @param {string} initial The computed value for 'initial'. - * @param {string} other An arbitrary value for the property that round - * trips and is distinct from the initial value. + * @param {string} property The name of the CSS property being tested. + * @param {string|array} initial The computed value for 'initial' or a list + * of acceptable computed value serializations. + * @param {string} other An arbitrary value for the property that + * round trips and is distinct from the initial + * value. */ function assert_not_inherited(property, initial, other) { assert_initial(property, initial);