Bug 1808410 - Part 3: Create timeline for view(). r=emilio

Also, I add some similar tests but they don't use Named Timeline Range, which
is not supported by Gecko now (Bug 1824875).

Differential Revision: https://phabricator.services.mozilla.com/D173905
This commit is contained in:
Boris Chiou 2023-05-02 22:47:29 +00:00
parent 97804f78e4
commit 77a0fd63cf
5 changed files with 248 additions and 6 deletions

View File

@ -28,10 +28,22 @@ already_AddRefed<ViewTimeline> ViewTimeline::MakeNamed(
auto scroller = Scroller::Nearest(const_cast<Element*>(element), pseudo); auto scroller = Scroller::Nearest(const_cast<Element*>(element), pseudo);
// 2. Create timeline. // 2. Create timeline.
return RefPtr<ViewTimeline>( return MakeAndAddRef<ViewTimeline>(aDocument, scroller,
new ViewTimeline(aDocument, scroller, aStyleTimeline.GetAxis(), aStyleTimeline.GetAxis(), aSubject,
aSubject, aPseudoType, aStyleTimeline.GetInset())) aPseudoType, aStyleTimeline.GetInset());
.forget(); }
/* static */
already_AddRefed<ViewTimeline> ViewTimeline::MakeAnonymous(
Document* aDocument, const NonOwningAnimationTarget& aTarget,
StyleScrollAxis aAxis, const StyleViewTimelineInset& aInset) {
// view() finds the nearest scroll container from the animation target.
auto [element, pseudo] =
FindNearestScroller(aTarget.mElement, aTarget.mPseudoType);
Scroller scroller = Scroller::Nearest(const_cast<Element*>(element), pseudo);
return MakeAndAddRef<ViewTimeline>(aDocument, scroller, aAxis,
aTarget.mElement, aTarget.mPseudoType,
aInset);
} }
void ViewTimeline::ReplacePropertiesWith(Element* aSubjectElement, void ViewTimeline::ReplacePropertiesWith(Element* aSubjectElement,

View File

@ -18,6 +18,9 @@ namespace mozilla::dom {
* is a special case of ScrollTimeline. * is a special case of ScrollTimeline.
*/ */
class ViewTimeline final : public ScrollTimeline { class ViewTimeline final : public ScrollTimeline {
template <typename T, typename... Args>
friend already_AddRefed<T> mozilla::MakeAndAddRef(Args&&... aArgs);
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ViewTimeline, ScrollTimeline) NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ViewTimeline, ScrollTimeline)
@ -30,6 +33,10 @@ class ViewTimeline final : public ScrollTimeline {
Document* aDocument, Element* aSubject, PseudoStyleType aPseudoType, Document* aDocument, Element* aSubject, PseudoStyleType aPseudoType,
const StyleViewTimeline& aStyleTimeline); const StyleViewTimeline& aStyleTimeline);
static already_AddRefed<ViewTimeline> MakeAnonymous(
Document* aDocument, const NonOwningAnimationTarget& aTarget,
StyleScrollAxis aAxis, const StyleViewTimelineInset& aInset);
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override { JS::Handle<JSObject*> aGivenProto) override {
return nullptr; return nullptr;

View File

@ -270,8 +270,11 @@ static already_AddRefed<dom::AnimationTimeline> GetTimeline(
return ScrollTimeline::MakeAnonymous(aPresContext->Document(), aTarget, return ScrollTimeline::MakeAnonymous(aPresContext->Document(), aTarget,
scroll.axis, scroll.scroller); scroll.axis, scroll.scroller);
} }
case StyleAnimationTimeline::Tag::View: case StyleAnimationTimeline::Tag::View: {
// TODO: Support this in this patch series. Treat it as auto for now. const auto& view = aStyleTimeline.AsView();
return ViewTimeline::MakeAnonymous(aPresContext->Document(), aTarget,
view.axis, view.inset);
}
case StyleAnimationTimeline::Tag::Auto: case StyleAnimationTimeline::Tag::Auto:
return do_AddRef(aTarget.mElement->OwnerDoc()->Timeline()); return do_AddRef(aTarget.mElement->OwnerDoc()->Timeline());
} }

View File

@ -1,27 +1,36 @@
[animation-timeline-view-functional-notation.tentative.html] [animation-timeline-view-functional-notation.tentative.html]
[animation-timeline: view()] [animation-timeline: view()]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(50px)] [animation-timeline: view(50px)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(auto 50px)] [animation-timeline: view(auto 50px)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(inline)] [animation-timeline: view(inline)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(horizontal)] [animation-timeline: view(horizontal)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(vertical)] [animation-timeline: view(vertical)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(horizontal 50px)] [animation-timeline: view(horizontal 50px)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(), view(inline)] [animation-timeline: view(), view(inline)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875
[animation-timeline: view(inline) changes to view(inline 50px)] [animation-timeline: view(inline) changes to view(inline 50px)]
expected: FAIL expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1824875

View File

@ -8,6 +8,20 @@
<script src="/web-animations/testcommon.js"></script> <script src="/web-animations/testcommon.js"></script>
<script src="support/testcommon.js"></script> <script src="support/testcommon.js"></script>
<style> <style>
@keyframes fade-in-out-without-timeline-range {
0% { opacity: 0; }
40% { opacity: 1; }
60% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes fade-out-without-timeline-range {
0% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes change-font-size-without-timeline-range {
0% { font-size: 10px; }
100% { font-size: 30px; }
}
@keyframes fade-in-out { @keyframes fade-in-out {
entry 0% { opacity: 0; } entry 0% { opacity: 0; }
entry 100% { opacity: 1; } entry 100% { opacity: 1; }
@ -96,6 +110,203 @@ async function scrollTop(element, value) {
await waitForNextFrame(); await waitForNextFrame();
} }
// ---------------------------------
// Tests without timeline range name
// ---------------------------------
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-in-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view()";
// So the range is [200px, 500px].
await scrollTop(container, 200);
assert_equals(getComputedStyle(div).opacity, '0', 'At 0%');
await scrollTop(container, 260);
assert_equals(getComputedStyle(div).opacity, '0.5', 'At 20%');
await scrollTop(container, 320);
assert_equals(getComputedStyle(div).opacity, '1', 'At 40%');
await scrollTop(container, 380);
assert_equals(getComputedStyle(div).opacity, '1', 'At 60%');
await scrollTop(container, 440);
assert_equals(getComputedStyle(div).opacity, '0.5', 'At 80%');
await scrollTop(container, 500);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view() without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-in-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(50px)";
// So the range is [250px, 450px].
await scrollTop(container, 250);
assert_equals(getComputedStyle(div).opacity, '0', 'At 0%');
await scrollTop(container, 290);
assert_equals(getComputedStyle(div).opacity, '0.5', 'At 20%');
await scrollTop(container, 330);
assert_equals(getComputedStyle(div).opacity, '1', 'At 40%');
await scrollTop(container, 370);
assert_equals(getComputedStyle(div).opacity, '1', 'At 60%');
await scrollTop(container, 410);
assert_equals(getComputedStyle(div).opacity, '0.5', 'At 80%');
await scrollTop(container, 450);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(50px) without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-in-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(auto 50px)";
// So the range is [250px, 500px].
await scrollTop(container, 250);
assert_equals(getComputedStyle(div).opacity, '0', 'At 0%');
await scrollTop(container, 300);
assert_equals(getComputedStyle(div).opacity, '0.5', 'At 20%');
await scrollTop(container, 350);
assert_equals(getComputedStyle(div).opacity, '1', 'At 40%');
await scrollTop(container, 400);
assert_equals(getComputedStyle(div).opacity, '1', 'At 60%');
await scrollTop(container, 450);
assert_equals(getComputedStyle(div).opacity, '0.5', 'At 80%');
await scrollTop(container, 500);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(auto 50px) without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(inline)";
// So the range is [-200px, 100px], but it is impossible to scroll to the
// negative part.
await scrollLeft(container, 0);
assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333,
0.00001, 'At 66.7%');
// Note: 20% for each 60px.
await scrollLeft(container, 40);
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%');
await scrollLeft(container, 100);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(inline) without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(horizontal)";
// So the range is [-200px, 100px], but it is impossible to scroll to the
// negative part.
await scrollLeft(container, 0);
assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333,
0.00001, 'At 66.7%');
// Note: 20% for each 60px.
await scrollLeft(container, 40);
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%');
await scrollLeft(container, 100);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(horizontal) without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['target', 'content']);
div.style.animation = "fade-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(vertical)";
// So the range is [-200px, 100px], but it is impossible to scroll to the
// negative part.
await scrollTop(container, 0);
assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333,
0.00001, 'At 66.7%');
// Note: 20% for each 60px.
await scrollTop(container, 40);
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%');
await scrollTop(container, 100);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(vertical) without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(horizontal 50px)";
// So the range is [-150px, 50px], but it is impossible to scroll to the
// negative part.
// Note: 25% for each 50px.
await scrollLeft(container, 0);
assert_equals(getComputedStyle(div).opacity, '0.25', 'At 75%');
await scrollLeft(container, 10);
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%');
await scrollLeft(container, 50);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(horizontal 50px) without timeline range name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-out-without-timeline-range 1s linear, " +
"change-font-size-without-timeline-range 1s linear";
div.style.animationTimeline = "view(50px), view(inline 50px)";
await scrollLeft(container, 0);
assert_equals(getComputedStyle(div).fontSize, '25px', 'At 75% inline');
await scrollLeft(container, 10);
assert_equals(getComputedStyle(div).fontSize, '26px', 'At 80% inline');
await scrollLeft(container, 50);
assert_equals(getComputedStyle(div).fontSize, '30px', 'At 100% inline');
await scrollLeft(container, 0);
await scrollTop(container, 0);
assert_equals(getComputedStyle(div).opacity, '0.25', 'At 75% block');
await scrollTop(container, 10);
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80% block');
await scrollTop(container, 50);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100% block');
await scrollLeft(container, 10);
await scrollTop(container, 10);
assert_equals(getComputedStyle(div).fontSize, '26px', 'At 80% inline');
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80% block');
}, 'animation-timeline: view(50px), view(inline 50px) without timeline range ' +
'name');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['target', 'content']);
container.style.overflow = 'hidden';
div.style.animation = "fade-out-without-timeline-range 1s linear";
div.style.animationTimeline = "view(inline)";
await scrollLeft(container, 0);
assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333,
0.00001, 'At 66.7%');
await scrollLeft(container, 40);
assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%');
await scrollLeft(container, 100);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
div.style.animationTimeline = "view(inline 50px)";
await scrollLeft(container, 0);
assert_equals(getComputedStyle(div).opacity, '0.25', 'At 75%');
await scrollLeft(container, 50);
assert_equals(getComputedStyle(div).opacity, '0', 'At 100%');
}, 'animation-timeline: view(inline) changes to view(inline 50px), without' +
'timeline range name');
// ---------------------------------
// Tests with timeline range name
// ---------------------------------
promise_test(async t => { promise_test(async t => {
let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']);
div.style.animation = "fade-in-out 1s linear"; div.style.animation = "fade-in-out 1s linear";