Bug 1809005 - Make scroll-timeline-{name|axis} be a coordinating list property group. r=emilio

Named scroll progress timelines are declared in the coordinated value list
constructed from the longhands of the scroll-timeline shorthand property,
which form a coordinating list property group with scroll-timeline-name as
the coordinating list base property.

In the meantime, we also update its shorthand to match the current spec.

Differential Revision: https://phabricator.services.mozilla.com/D166596
This commit is contained in:
Boris Chiou 2023-01-26 23:20:55 +00:00
Родитель 2fec6541cf
Коммит 933cc122ea
15 изменённых файлов: 141 добавлений и 202 удалений

Просмотреть файл

@ -109,13 +109,7 @@ already_AddRefed<ScrollTimeline> ScrollTimeline::FromAnonymousScroll(
// 1. the declaring element itself
// 2. that elements descendants
// 3. that elements following siblings and their descendants
// https://drafts.csswg.org/scroll-animations-1/rewrite#timeline-scope
//
// Note: It's unclear to us about the scope of scroll-timeline, so we
// intentionally don't let it cross the shadow dom boundary for now.
//
// FIXME: We may have to support global scope. This depends on the result of
// this spec issue: https://github.com/w3c/csswg-drafts/issues/7047
// https://drafts.csswg.org/scroll-animations-1/#timeline-scope
Element* result = nullptr;
StyleScrollAxis axis = StyleScrollAxis::Block;
for (Element* curr = aTarget.mElement; curr;
@ -132,10 +126,19 @@ already_AddRefed<ScrollTimeline> ScrollTimeline::FromAnonymousScroll(
continue;
}
const nsStyleUIReset* styleUIReset = style->StyleUIReset();
if (styleUIReset->mScrollTimelineName._0.AsAtom() == aName) {
result = e;
axis = styleUIReset->mScrollTimelineAxis;
const nsStyleUIReset* ui = style->StyleUIReset();
// Note: scroll-timeline is a coordinated property list, so we use the
// count of the base property, scroll-timeline-name, as the max length.
for (uint32_t i = 0; i < ui->mScrollTimelineNameCount; ++i) {
const auto& timeline = ui->mScrollTimelines[i];
if (timeline.GetName() == aName) {
result = e;
axis = timeline.GetAxis();
break;
}
}
if (result) {
break;
}
}

Просмотреть файл

@ -1141,6 +1141,11 @@ void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) {
EnsureStyleAutoArrayLength(base, aLen);
}
void Gecko_EnsureStyleScrollTimelineArrayLength(void* aArray, size_t aLen) {
auto* base = static_cast<nsStyleAutoArray<StyleScrollTimeline>*>(aArray);
EnsureStyleAutoArrayLength(base, aLen);
}
void Gecko_EnsureStyleViewTimelineArrayLength(void* aArray, size_t aLen) {
auto* base = static_cast<nsStyleAutoArray<StyleViewTimeline>*>(aArray);
EnsureStyleAutoArrayLength(base, aLen);

Просмотреть файл

@ -389,6 +389,7 @@ void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len,
void Gecko_EnsureStyleAnimationArrayLength(void* array, size_t len);
void Gecko_EnsureStyleTransitionArrayLength(void* array, size_t len);
void Gecko_EnsureStyleScrollTimelineArrayLength(void* array, size_t len);
void Gecko_EnsureStyleViewTimelineArrayLength(void* array, size_t len);
// Searches from the beginning of |keyframes| for a Keyframe object with the

Просмотреть файл

@ -3249,7 +3249,10 @@ nsStyleUIReset::nsStyleUIReset(const Document& aDocument)
mAnimationIterationCountCount(1),
mAnimationCompositionCount(1),
mAnimationTimelineCount(1),
mScrollTimelineAxis(StyleScrollAxis::Block),
mScrollTimelines(
nsStyleAutoArray<StyleScrollTimeline>::WITH_SINGLE_INITIAL_ELEMENT),
mScrollTimelineNameCount(1),
mScrollTimelineAxisCount(1),
mViewTimelines(
nsStyleAutoArray<StyleViewTimeline>::WITH_SINGLE_INITIAL_ELEMENT),
mViewTimelineNameCount(1),
@ -3288,8 +3291,9 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
mAnimationIterationCountCount(aSource.mAnimationIterationCountCount),
mAnimationCompositionCount(aSource.mAnimationCompositionCount),
mAnimationTimelineCount(aSource.mAnimationTimelineCount),
mScrollTimelineName(aSource.mScrollTimelineName),
mScrollTimelineAxis(aSource.mScrollTimelineAxis),
mScrollTimelines(aSource.mScrollTimelines.Clone()),
mScrollTimelineNameCount(aSource.mScrollTimelineNameCount),
mScrollTimelineAxisCount(aSource.mScrollTimelineAxisCount),
mViewTimelines(aSource.mViewTimelines.Clone()),
mViewTimelineNameCount(aSource.mViewTimelineNameCount),
mViewTimelineAxisCount(aSource.mViewTimelineAxisCount),
@ -3353,8 +3357,9 @@ nsChangeHint nsStyleUIReset::CalcDifference(
mWindowOpacity != aNewData.mWindowOpacity ||
mMozWindowInputRegionMargin != aNewData.mMozWindowInputRegionMargin ||
mMozWindowTransform != aNewData.mMozWindowTransform ||
mScrollTimelineName != aNewData.mScrollTimelineName ||
mScrollTimelineAxis != aNewData.mScrollTimelineAxis ||
mScrollTimelines != aNewData.mScrollTimelines ||
mScrollTimelineNameCount != aNewData.mScrollTimelineNameCount ||
mScrollTimelineAxisCount != aNewData.mScrollTimelineAxisCount ||
mViewTimelines != aNewData.mViewTimelines ||
mViewTimelineNameCount != aNewData.mViewTimelineNameCount ||
mViewTimelineAxisCount != aNewData.mViewTimelineAxisCount ||

Просмотреть файл

@ -1268,6 +1268,30 @@ struct StyleAnimation {
StyleAnimationTimeline mTimeline;
};
struct StyleScrollTimeline {
StyleScrollTimeline() = default;
explicit StyleScrollTimeline(const StyleScrollTimeline& aCopy) = default;
// SetInitialValues() are called when ensuring the array length. So basically
// we can rely on the default constructor to handle the new constructed
// elements.
void SetInitialValues() {}
const nsAtom* GetName() const { return mName._0.AsAtom(); }
StyleScrollAxis GetAxis() const { return mAxis; }
bool operator==(const StyleScrollTimeline& aOther) const {
return mName == aOther.mName && mAxis == aOther.mAxis;
}
bool operator!=(const StyleScrollTimeline& aOther) const {
return !(*this == aOther);
}
private:
StyleScrollTimelineName mName;
StyleScrollAxis mAxis = StyleScrollAxis::Block;
};
struct StyleViewTimeline {
StyleViewTimeline() = default;
explicit StyleViewTimeline(const StyleViewTimeline& aCopy) = default;
@ -1905,10 +1929,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
uint32_t mAnimationCompositionCount;
uint32_t mAnimationTimelineCount;
// FIXME: Bug 1809005: scroll-timeline-{name|axis} should be a coordinated
// list property group.
mozilla::StyleScrollTimelineName mScrollTimelineName;
mozilla::StyleScrollAxis mScrollTimelineAxis;
nsStyleAutoArray<mozilla::StyleScrollTimeline> mScrollTimelines;
uint32_t mScrollTimelineNameCount;
uint32_t mScrollTimelineAxisCount;
nsStyleAutoArray<mozilla::StyleViewTimeline> mViewTimelines;
uint32_t mViewTimelineNameCount;

Просмотреть файл

@ -13665,7 +13665,7 @@ if (IsCSSPropertyPrefEnabled("layout.css.scroll-driven-animations.enabled")) {
"\\2bounce",
"-\\2bounce",
],
invalid_values: ["bounce, abc", "abc bounce", "10px", "rgb(1, 2, 3)"],
invalid_values: ["abc bounce", "10px", "rgb(1, 2, 3)"],
};
gCSSProperties["scroll-timeline-axis"] = {
@ -13682,18 +13682,19 @@ if (IsCSSPropertyPrefEnabled("layout.css.scroll-driven-animations.enabled")) {
inherited: false,
type: CSS_TYPE_TRUE_SHORTHAND,
subproperties: ["scroll-timeline-name", "scroll-timeline-axis"],
initial_values: ["none block", "block none", "block", "none"],
initial_values: ["none block", "none"],
other_values: [
"auto inline",
"bounce inline",
"bounce vertical",
"horizontal bounce",
"inline \\32bounce",
"block -bounce",
"vertical \\32 0bounce",
"horizontal -\\32 0bounce",
"\\32bounce inline",
"-bounce block",
"\\32 0bounce vertical",
"-\\32 0bounce horizontal",
"a, b, c",
"a block, b inline, c vertical",
],
invalid_values: ["", "bounce bounce"],
invalid_values: ["", "bounce bounce", "horizontal a", "block abc"],
};
gCSSProperties["view-timeline-name"] = {

Просмотреть файл

@ -5,10 +5,12 @@
//! Rust helpers for Gecko's `nsStyleAutoArray`.
use crate::gecko_bindings::bindings::Gecko_EnsureStyleAnimationArrayLength;
use crate::gecko_bindings::bindings::Gecko_EnsureStyleScrollTimelineArrayLength;
use crate::gecko_bindings::bindings::Gecko_EnsureStyleTransitionArrayLength;
use crate::gecko_bindings::bindings::Gecko_EnsureStyleViewTimelineArrayLength;
use crate::gecko_bindings::structs::nsStyleAutoArray;
use crate::gecko_bindings::structs::{StyleAnimation, StyleTransition, StyleViewTimeline};
use crate::gecko_bindings::structs::{StyleAnimation, StyleTransition};
use crate::gecko_bindings::structs::{StyleScrollTimeline, StyleViewTimeline};
use std::iter::{once, Chain, IntoIterator, Once};
use std::ops::{Index, IndexMut};
use std::slice::{Iter, IterMut};
@ -88,6 +90,18 @@ impl nsStyleAutoArray<StyleViewTimeline> {
}
}
impl nsStyleAutoArray<StyleScrollTimeline> {
/// Ensures that the array has length at least the given length.
pub fn ensure_len(&mut self, len: usize) {
unsafe {
Gecko_EnsureStyleScrollTimelineArrayLength(
self as *mut nsStyleAutoArray<StyleScrollTimeline> as *mut _,
len,
);
}
}
}
impl<'a, T> IntoIterator for &'a mut nsStyleAutoArray<T> {
type Item = &'a mut T;
type IntoIter = Chain<Once<&'a mut T>, IterMut<'a, T>>;

Просмотреть файл

@ -1721,6 +1721,7 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-
animation-timing-function animation-composition animation-timeline
transition-duration transition-delay
transition-timing-function transition-property
scroll-timeline-name scroll-timeline-axis
view-timeline-name view-timeline-axis view-timeline-inset""" %>
<%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}">
@ -1905,6 +1906,9 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-
${impl_coordinated_property('animation', 'timeline', 'Timeline')}
${impl_coordinated_property('animation', 'timing_function', 'TimingFunction')}
${impl_coordinated_property('scroll_timeline', 'name', 'Name')}
${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}
${impl_coordinated_property('view_timeline', 'name', 'Name')}
${impl_coordinated_property('view_timeline', 'axis', 'Axis')}
${impl_coordinated_property('view_timeline', 'inset', 'Inset')}

Просмотреть файл

@ -333,6 +333,8 @@ ${helpers.predefined_type(
"scroll-timeline-name",
"ScrollTimelineName",
"computed::ScrollTimelineName::none()",
vector=True,
need_index=True,
engines="gecko",
animation_value_type="none",
gecko_pref="layout.css.scroll-driven-animations.enabled",
@ -344,6 +346,8 @@ ${helpers.predefined_type(
"scroll-timeline-axis",
"ScrollAxis",
"computed::ScrollAxis::default()",
vector=True,
need_index=True,
engines="gecko",
animation_value_type="none",
gecko_pref="layout.css.scroll-driven-animations.enabled",

Просмотреть файл

@ -320,63 +320,48 @@ macro_rules! try_parse_one {
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
use crate::parser::Parse;
use crate::values::specified::box_::{ScrollAxis, ScrollTimelineName};
use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name};
let mut name = None;
let mut axis = None;
// FIXME: Bug 1809005: The order of |name| and |axis| should be fixed.
loop {
// Note: When parsing positionally-ambiguous keywords in a property value, a
// <custom-ident> production can only claim the keyword if no other unfulfilled
// production can claim it. So we try to parse `scroll-timeline-axis` first.
//
// https://drafts.csswg.org/css-values-4/#custom-idents
let mut names = Vec::with_capacity(1);
let mut axes = Vec::with_capacity(1);
input.parse_comma_separated(|input| {
let name = scroll_timeline_name::single_value::parse(context, input)?;
let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i));
if axis.is_none() {
axis = input.try_parse(ScrollAxis::parse).ok();
}
names.push(name);
axes.push(axis.unwrap_or_default());
if name.is_none() {
if let Ok(value) = input.try_parse(|i| ScrollTimelineName::parse(context, i)) {
name = Some(value);
continue;
}
}
break;
}
// Must occur one or more.
if name.is_none() && axis.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(())
})?;
Ok(expanded! {
scroll_timeline_name: name.unwrap_or(ScrollTimelineName::none()),
scroll_timeline_axis: axis.unwrap_or_default(),
scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()),
scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
use crate::values::specified::box_::ScrollAxis;
let is_default_axis = self.scroll_timeline_axis == &ScrollAxis::default();
let is_default_name = self.scroll_timeline_name.0.is_none();
// Note: if both are default values, we serialize the default axis (because it is the
// first value per spec).
if !is_default_axis || (is_default_axis && is_default_name) {
self.scroll_timeline_axis.to_css(dest)?;
// If any value list length is differs then we don't do a shorthand serialization
// either.
let len = self.scroll_timeline_name.0.len();
if len != self.scroll_timeline_axis.0.len() {
return Ok(());
}
if !is_default_name {
if !is_default_axis {
dest.write_char(' ')?;
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
self.scroll_timeline_name.to_css(dest)?;
}
self.scroll_timeline_name.0[i].to_css(dest)?;
if self.scroll_timeline_axis.0[i] != Default::default() {
dest.write_char(' ')?;
self.scroll_timeline_axis.0[i].to_css(dest)?;
}
}
Ok(())
}
}
@ -416,14 +401,9 @@ macro_rules! try_parse_one {
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
let len = self.view_timeline_name.0.len();
// There should be at least one declared value
if len == 0 {
return Ok(());
}
// If any value list length is differs then we don't do a shorthand serialization
// either.
let len = self.view_timeline_name.0.len();
if len != self.view_timeline_axis.0.len() {
return Ok(());
}

Просмотреть файл

@ -1,111 +0,0 @@
[scroll-timeline-shorthand.tentative.html]
[e.style['scroll-timeline'\] = "none block" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "none inline" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "abc horizontal" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "abc inline" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "aBc inline" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "inline block" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "vertical block" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "horizontal block" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "a, b, c" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "a inline, b block, c vertical" should set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "block none" should not set the property value]
expected: FAIL
[e.style['scroll-timeline'\] = "inline abc" should not set the property value]
expected: FAIL
[Property scroll-timeline value 'none block']
expected: FAIL
[Property scroll-timeline value 'abc inline']
expected: FAIL
[Property scroll-timeline value 'none vertical']
expected: FAIL
[Property scroll-timeline value 'abc horizontal']
expected: FAIL
[Property scroll-timeline value 'inline block']
expected: FAIL
[Property scroll-timeline value 'vertical block']
expected: FAIL
[Property scroll-timeline value 'horizontal block']
expected: FAIL
[Property scroll-timeline value 'a, b, c']
expected: FAIL
[Property scroll-timeline value 'a inline, b block, c vertical']
expected: FAIL
[e.style['scroll-timeline'\] = "inline horizontal" should set scroll-timeline-axis]
expected: FAIL
[e.style['scroll-timeline'\] = "inline horizontal" should set scroll-timeline-name]
expected: FAIL
[e.style['scroll-timeline'\] = "abc vertical, def" should set scroll-timeline-axis]
expected: FAIL
[e.style['scroll-timeline'\] = "abc vertical, def" should set scroll-timeline-name]
expected: FAIL
[e.style['scroll-timeline'\] = "abc vertical, def" should not set unrelated longhands]
expected: FAIL
[e.style['scroll-timeline'\] = "abc, def" should set scroll-timeline-axis]
expected: FAIL
[e.style['scroll-timeline'\] = "abc, def" should set scroll-timeline-name]
expected: FAIL
[e.style['scroll-timeline'\] = "abc, def" should not set unrelated longhands]
expected: FAIL
[e.style['scroll-timeline'\] = "abc, def" should set scroll-timeline-axis]
expected: FAIL
[e.style['scroll-timeline'\] = "abc, def" should set scroll-timeline-name]
expected: FAIL
[e.style['scroll-timeline'\] = "abc, def" should not set unrelated longhands]
expected: FAIL
[Shorthand contraction of scroll-timeline-name:abc;scroll-timeline-axis:inline]
expected: FAIL
[Shorthand contraction of scroll-timeline-name:a, b;scroll-timeline-axis:inline, block]
expected: FAIL
[Shorthand contraction of scroll-timeline-name:none, none;scroll-timeline-axis:block, block]
expected: FAIL
[Shorthand contraction of scroll-timeline-name:a, b, c;scroll-timeline-axis:inline, inline]
expected: FAIL
[Shorthand contraction of scroll-timeline-name:a, b;scroll-timeline-axis:inline, inline, inline]
expected: FAIL

Просмотреть файл

@ -1,10 +1,8 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-axis">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
</head>
<style>
#outer { scroll-timeline-axis: inline; }
#target { scroll-timeline-axis: vertical; }
@ -21,6 +19,10 @@ test_computed_value('scroll-timeline-axis', 'block');
test_computed_value('scroll-timeline-axis', 'inline');
test_computed_value('scroll-timeline-axis', 'vertical');
test_computed_value('scroll-timeline-axis', 'horizontal');
test_computed_value('scroll-timeline-axis', 'block, inline');
test_computed_value('scroll-timeline-axis', 'inline, block');
test_computed_value('scroll-timeline-axis', 'block, vertical, horizontal, inline');
test_computed_value('scroll-timeline-axis', 'inline, inline, inline, inline');
test(() => {
let style = getComputedStyle(document.getElementById('target'));

Просмотреть файл

@ -1,10 +1,8 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-axis">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<div id="target"></div>
<script>
@ -18,10 +16,16 @@ test_valid_value('scroll-timeline-axis', 'block');
test_valid_value('scroll-timeline-axis', 'inline');
test_valid_value('scroll-timeline-axis', 'vertical');
test_valid_value('scroll-timeline-axis', 'horizontal');
test_valid_value('scroll-timeline-axis', 'block, inline');
test_valid_value('scroll-timeline-axis', 'inline, block');
test_valid_value('scroll-timeline-axis', 'block, vertical, horizontal, inline');
test_valid_value('scroll-timeline-axis', 'inline, inline, inline, inline');
test_invalid_value('scroll-timeline-axis', 'abc');
test_invalid_value('scroll-timeline-axis', '10px');
test_invalid_value('scroll-timeline-axis', 'auto');
test_invalid_value('scroll-timeline-axis', 'none');
test_invalid_value('scroll-timeline-axis', 'block inline');
test_invalid_value('scroll-timeline-axis', 'block / inline');
</script>

Просмотреть файл

@ -1,6 +1,5 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-name">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
@ -19,7 +18,11 @@ test_computed_value('scroll-timeline-name', 'unset', 'none');
test_computed_value('scroll-timeline-name', 'revert', 'none');
test_computed_value('scroll-timeline-name', 'none');
test_computed_value('scroll-timeline-name', 'test');
test_computed_value('scroll-timeline-name', 'tEst');
test_computed_value('scroll-timeline-name', 'foo, bar');
test_computed_value('scroll-timeline-name', 'bar, foo');
test_computed_value('scroll-timeline-name', 'a, b, c, D, e');
test_computed_value('scroll-timeline-name', 'none, none');
test_computed_value('scroll-timeline-name', 'a, b, c, none, d, e');
test(() => {
let style = getComputedStyle(document.getElementById('target'));

Просмотреть файл

@ -1,12 +1,9 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-name">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<div id="target"></div>
<script>
test_valid_value('scroll-timeline-name', 'initial');
@ -18,6 +15,10 @@ test_valid_value('scroll-timeline-name', 'none');
test_valid_value('scroll-timeline-name', 'abc');
test_valid_value('scroll-timeline-name', ' abc', 'abc');
test_valid_value('scroll-timeline-name', 'aBc');
test_valid_value('scroll-timeline-name', 'foo, bar');
test_valid_value('scroll-timeline-name', 'bar, foo');
test_valid_value('scroll-timeline-name', 'none, none');
test_valid_value('scroll-timeline-name', 'a, none, b');
test_valid_value('scroll-timeline-name', 'auto');
test_invalid_value('scroll-timeline-name', 'default');