mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1771282 - Introduce ScrollTimelineAnimationTracker. r=hiro
It's possible to change the timeline if the animation is in pending. So we still need an animation tracker to track the scroll-linked animations. Besides, per the spec, we should keep this animation in pending if its timeline is inactive. So in this patch, we always put the scroll-linked animations into ScrollTimelineAnimationTracker, and if we change the timeline but the animation is still in pending, we move the animation into the correct animation tracker if needed. Using two different animation trackers because we would like to trigger scroll-linked animations after frame construction and reflow, and don't want to ensure the paint is scheduled. Note: 1. All tests in scroll-timeline-dynamic.tentative.html are failed. We will fix them in Bug 1774275. 2. Drop `animation-duration: infinite` from progress-based-animation-animation-longhand-properties.tentative.html, because infinite is not defined in animation-duration in [css-animations-1]. Differential Revision: https://phabricator.services.mozilla.com/D159650
This commit is contained in:
parent
512ea500ee
commit
722dd73447
@ -29,6 +29,7 @@
|
||||
#include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr
|
||||
#include "nsTransitionManager.h" // For CSSTransition
|
||||
#include "PendingAnimationTracker.h" // For PendingAnimationTracker
|
||||
#include "ScrollTimelineAnimationTracker.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
@ -267,6 +268,9 @@ void Animation::SetTimelineNoUpdate(AnimationTimeline* aTimeline) {
|
||||
if (!aTimeline) {
|
||||
MaybeQueueCancelEvent(activeTime);
|
||||
}
|
||||
|
||||
UpdatePendingAnimationTracker(oldTimeline, aTimeline);
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
@ -904,12 +908,9 @@ void Animation::TriggerNow() {
|
||||
}
|
||||
|
||||
// If we don't have an active timeline we can't trigger the animation.
|
||||
// For non monotonically increasing timelines, we call this function in Play()
|
||||
// and Pause() immediately,
|
||||
//
|
||||
// For monotonically increasing timelines, this is a test-only method that we
|
||||
// don't expect to be used in conjunction with animations without an active
|
||||
// timeline so generate a warning if we do find ourselves in that situation.
|
||||
// However, this is a test-only method that we don't expect to be used in
|
||||
// conjunction with animations without an active timeline so generate
|
||||
// a warning if we do find ourselves in that situation.
|
||||
if (!mTimeline || mTimeline->GetCurrentTimeAsDuration().IsNull()) {
|
||||
NS_WARNING("Failed to trigger an animation with an active timeline");
|
||||
return;
|
||||
@ -918,6 +919,31 @@ void Animation::TriggerNow() {
|
||||
FinishPendingAt(mTimeline->GetCurrentTimeAsDuration().Value());
|
||||
}
|
||||
|
||||
bool Animation::TryTriggerNowForFiniteTimeline() {
|
||||
// Normally we expect the play state to be pending but when an animation
|
||||
// is cancelled and its rendered document can't be reached, we can end up
|
||||
// with the animation still in a pending player tracker even after it is
|
||||
// no longer pending.
|
||||
if (!Pending()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mTimeline && !mTimeline->IsMonotonicallyIncreasing());
|
||||
|
||||
// It's possible that the primary frame or the scrollable frame is not ready
|
||||
// when setting up this animation. So we don't finish pending right now. In
|
||||
// this case, the timeline is inactive so it is still pending. The caller
|
||||
// should handle this case by trying this later once the scrollable frame is
|
||||
// ready.
|
||||
const auto currentTime = mTimeline->GetCurrentTimeAsDuration();
|
||||
if (currentTime.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FinishPendingAt(currentTime.Value());
|
||||
return true;
|
||||
}
|
||||
|
||||
Nullable<TimeDuration> Animation::GetCurrentOrPendingStartTime() const {
|
||||
Nullable<TimeDuration> result;
|
||||
|
||||
@ -1442,10 +1468,14 @@ void Animation::PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior) {
|
||||
// animations if it applies.
|
||||
mSyncWithGeometricAnimations = false;
|
||||
|
||||
// If the animation use finite timeline, e.g. scroll timeline, we don't use
|
||||
// pending animation tracker. Instead, we let it play immediately.
|
||||
if (HasFiniteTimeline()) {
|
||||
TriggerNow();
|
||||
// Always schedule a task even if we would like to let this animation
|
||||
// immedidately ready, per spec.
|
||||
// https://drafts.csswg.org/web-animations/#playing-an-animation-section
|
||||
if (Document* doc = GetRenderedDocument()) {
|
||||
doc->GetOrCreateScrollTimelineAnimationTracker()->AddPending(*this);
|
||||
} // else: we fail to track this animation, so let the scroll frame to
|
||||
// trigger it when ticking.
|
||||
} else {
|
||||
if (Document* doc = GetRenderedDocument()) {
|
||||
PendingAnimationTracker* tracker =
|
||||
@ -1504,10 +1534,14 @@ void Animation::Pause(ErrorResult& aRv) {
|
||||
|
||||
mPendingState = PendingState::PausePending;
|
||||
|
||||
// If the animation use finite timeline, e.g. scroll timeline, we don't use
|
||||
// pending animation tracker. Instead, we let it pause immediately.
|
||||
if (HasFiniteTimeline()) {
|
||||
TriggerNow();
|
||||
// Always schedule a task even if we would like to let this animation
|
||||
// immedidately ready, per spec.
|
||||
// https://drafts.csswg.org/web-animations/#playing-an-animation-section
|
||||
if (Document* doc = GetRenderedDocument()) {
|
||||
doc->GetOrCreateScrollTimelineAnimationTracker()->AddPending(*this);
|
||||
} // else: we fail to track this animation, so let the scroll frame to
|
||||
// trigger it when ticking.
|
||||
} else {
|
||||
if (Document* doc = GetRenderedDocument()) {
|
||||
PendingAnimationTracker* tracker =
|
||||
@ -1804,10 +1838,13 @@ bool Animation::IsPossiblyOrphanedPendingAnimation() const {
|
||||
// * We started playing but our timeline became inactive.
|
||||
// In this case the pending animation tracker will drop us from its hashmap
|
||||
// when we have been painted.
|
||||
// * When we started playing we couldn't find a PendingAnimationTracker to
|
||||
// register with (perhaps the effect had no document) so we simply
|
||||
// set mPendingState in PlayNoUpdate and relied on this method to catch us
|
||||
// on the next tick.
|
||||
// * When we started playing we couldn't find a
|
||||
// PendingAnimationTracker/ScrollTimelineAnimationTracker to register with
|
||||
// (perhaps the effect had no document) so we may
|
||||
// 1. simply set mPendingState in PlayNoUpdate and relied on this method to
|
||||
// catch us on the next tick, or
|
||||
// 2. rely on the scroll frame to tick this animation and catch us in this
|
||||
// method.
|
||||
|
||||
// If we're not pending we're ok.
|
||||
if (mPendingState == PendingState::NotPending) {
|
||||
@ -1862,6 +1899,50 @@ Document* Animation::GetTimelineDocument() const {
|
||||
return mTimeline ? mTimeline->GetDocument() : nullptr;
|
||||
}
|
||||
|
||||
void Animation::UpdatePendingAnimationTracker(AnimationTimeline* aOldTimeline,
|
||||
AnimationTimeline* aNewTimeline) {
|
||||
// If we are still in pending, we may have to move this animation into the
|
||||
// correct animation tracker.
|
||||
Document* doc = GetRenderedDocument();
|
||||
if (!doc || !Pending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool fromFiniteTimeline =
|
||||
aOldTimeline && !aOldTimeline->IsMonotonicallyIncreasing();
|
||||
const bool toFiniteTimeline =
|
||||
aNewTimeline && !aNewTimeline->IsMonotonicallyIncreasing();
|
||||
if (fromFiniteTimeline == toFiniteTimeline) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isPlayPending = mPendingState == PendingState::PlayPending;
|
||||
if (toFiniteTimeline) {
|
||||
// From null/document-timeline to scroll-timeline
|
||||
if (auto* tracker = doc->GetPendingAnimationTracker()) {
|
||||
if (isPlayPending) {
|
||||
tracker->RemovePlayPending(*this);
|
||||
} else {
|
||||
tracker->RemovePausePending(*this);
|
||||
}
|
||||
}
|
||||
|
||||
doc->GetOrCreateScrollTimelineAnimationTracker()->AddPending(*this);
|
||||
} else {
|
||||
// From scroll-timeline to null/document-timeline
|
||||
if (auto* tracker = doc->GetScrollTimelineAnimationTracker()) {
|
||||
tracker->RemovePending(*this);
|
||||
}
|
||||
|
||||
auto* tracker = doc->GetOrCreatePendingAnimationTracker();
|
||||
if (isPlayPending) {
|
||||
tracker->AddPlayPending(*this);
|
||||
} else {
|
||||
tracker->AddPausePending(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncFinishNotification : public MicroTaskRunnable {
|
||||
public:
|
||||
explicit AsyncFinishNotification(Animation* aAnimation)
|
||||
|
@ -226,11 +226,6 @@ class Animation : public DOMEventTargetHelper,
|
||||
*/
|
||||
void TriggerOnNextTick(const Nullable<TimeDuration>& aReadyTime);
|
||||
/**
|
||||
* For the non-monotonically increasing timeline (e.g. ScrollTimeline), this
|
||||
* is used when playing or pausing because we don't put the animations into
|
||||
* PendingAnimationTracker, and we would like to use the current scroll
|
||||
* position as the ready time.
|
||||
*
|
||||
* For the monotonically increasing timeline, we use this only for testing:
|
||||
* Start or pause a pending animation using the current timeline time. This
|
||||
* is used to support existing tests that expect animations to begin
|
||||
@ -242,6 +237,13 @@ class Animation : public DOMEventTargetHelper,
|
||||
* added to.
|
||||
*/
|
||||
void TriggerNow();
|
||||
/**
|
||||
* For the non-monotonically increasing timeline (e.g. ScrollTimeline), we try
|
||||
* to trigger it in ScrollTimelineAnimationTracker by this method. This uses
|
||||
* the current scroll position as the ready time. Return true if we don't need
|
||||
* to trigger it or we trigger it successfully.
|
||||
*/
|
||||
bool TryTriggerNowForFiniteTimeline();
|
||||
/**
|
||||
* When TriggerOnNextTick is called, we store the ready time but we don't
|
||||
* apply it until the next tick. In the meantime, GetStartTime() will return
|
||||
@ -595,6 +597,9 @@ class Animation : public DOMEventTargetHelper,
|
||||
return mTimeline && !mTimeline->IsMonotonicallyIncreasing();
|
||||
}
|
||||
|
||||
void UpdatePendingAnimationTracker(AnimationTimeline* aOldTimeline,
|
||||
AnimationTimeline* aNewTimeline);
|
||||
|
||||
RefPtr<AnimationTimeline> mTimeline;
|
||||
RefPtr<AnimationEffect> mEffect;
|
||||
// The beginning of the delay period.
|
||||
|
@ -64,8 +64,8 @@ void PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(
|
||||
}
|
||||
|
||||
MOZ_ASSERT(timeline->IsMonotonicallyIncreasing(),
|
||||
"Don't put non-MonotoniciallyIncreasing timeline into "
|
||||
"PendingAnimationTracker");
|
||||
"The non-monotonicially-increasing timeline should be in "
|
||||
"ScrollTimelineAnimationTracker");
|
||||
|
||||
// When the timeline's refresh driver is under test control, its values
|
||||
// have no correspondance to wallclock times so we shouldn't try to
|
||||
|
@ -4,8 +4,8 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PendingAnimationTracker_h
|
||||
#define mozilla_dom_PendingAnimationTracker_h
|
||||
#ifndef mozilla_PendingAnimationTracker_h
|
||||
#define mozilla_PendingAnimationTracker_h
|
||||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/TypedEnumBits.h"
|
||||
@ -20,6 +20,10 @@ namespace dom {
|
||||
class Document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the pending animations which use document-timeline or null-timeline
|
||||
* while playing or pausing.
|
||||
*/
|
||||
class PendingAnimationTracker final {
|
||||
public:
|
||||
explicit PendingAnimationTracker(dom::Document* aDocument);
|
||||
@ -106,4 +110,4 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PendingAnimationTracker::CheckState)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PendingAnimationTracker_h
|
||||
#endif // mozilla_PendingAnimationTracker_h
|
||||
|
51
dom/animation/ScrollTimelineAnimationTracker.cpp
Normal file
51
dom/animation/ScrollTimelineAnimationTracker.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ScrollTimelineAnimationTracker.h"
|
||||
|
||||
#include "mozilla/dom/Document.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(ScrollTimelineAnimationTracker, mPendingSet, mDocument)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ScrollTimelineAnimationTracker, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScrollTimelineAnimationTracker, Release)
|
||||
|
||||
void ScrollTimelineAnimationTracker::TriggerPendingAnimations() {
|
||||
for (auto iter = mPendingSet.begin(), end = mPendingSet.end(); iter != end;
|
||||
++iter) {
|
||||
dom::Animation* animation = *iter;
|
||||
|
||||
MOZ_ASSERT(animation->GetTimeline() &&
|
||||
!animation->GetTimeline()->IsMonotonicallyIncreasing());
|
||||
|
||||
// FIXME: Trigger now may not be correct because the spec says:
|
||||
// If a user agent determines that animation is immediately ready, it may
|
||||
// schedule the task (i.e. ResumeAt()) as a microtask such that it runs at
|
||||
// the next microtask checkpoint, but it must not perform the task
|
||||
// synchronously.
|
||||
// Note: So, for now, we put the animation into the tracker, and trigger
|
||||
// them immediately until the frames are ready. Using TriggerOnNextTick()
|
||||
// for scroll-linked animations may have issues because we don't tick if
|
||||
// no one does scroll.
|
||||
if (!animation->TryTriggerNowForFiniteTimeline()) {
|
||||
// Note: We keep this animation pending even if its timeline is always
|
||||
// inactive. It's pretty hard to tell its future status, for example, it's
|
||||
// possible that the scroll container is in display:none subtree but the
|
||||
// animating element isn't the subtree, then we need to keep tracking the
|
||||
// situation until the scroll container gets framed. so in general we make
|
||||
// this animation be pending (i.e. not ready) if its scroll-timeline is
|
||||
// inactive, and this also matches the current spec definition.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: Remove() is legitimately called once per entry during the loop.
|
||||
mPendingSet.Remove(iter);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
58
dom/animation/ScrollTimelineAnimationTracker.h
Normal file
58
dom/animation/ScrollTimelineAnimationTracker.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_ScrollTimelineAnimationTracker_h
|
||||
#define mozilla_ScrollTimelineAnimationTracker_h
|
||||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsTHashSet.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the pending animations which use scroll timeline while playing or
|
||||
* pausing.
|
||||
*/
|
||||
class ScrollTimelineAnimationTracker final {
|
||||
public:
|
||||
explicit ScrollTimelineAnimationTracker(dom::Document* aDocument)
|
||||
: mDocument(aDocument) {}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(
|
||||
ScrollTimelineAnimationTracker)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ScrollTimelineAnimationTracker)
|
||||
|
||||
void AddPending(dom::Animation& aAnimation) {
|
||||
mPendingSet.Insert(&aAnimation);
|
||||
}
|
||||
|
||||
void RemovePending(dom::Animation& aAnimation) {
|
||||
mPendingSet.Remove(&aAnimation);
|
||||
}
|
||||
|
||||
bool HasPendingAnimations() const { return mPendingSet.Count() > 0; }
|
||||
|
||||
bool IsWaiting(const dom::Animation& aAnimation) const {
|
||||
return mPendingSet.Contains(const_cast<dom::Animation*>(&aAnimation));
|
||||
}
|
||||
|
||||
void TriggerPendingAnimations();
|
||||
|
||||
private:
|
||||
~ScrollTimelineAnimationTracker() = default;
|
||||
|
||||
nsTHashSet<nsRefPtrHashKey<dom::Animation>> mPendingSet;
|
||||
RefPtr<dom::Document> mDocument;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ScrollTimelineAnimationTracker_h
|
@ -38,6 +38,7 @@ EXPORTS.mozilla += [
|
||||
"PendingAnimationTracker.h",
|
||||
"PostRestyleMode.h",
|
||||
"PseudoElementHashEntry.h",
|
||||
"ScrollTimelineAnimationTracker.h",
|
||||
"TimingParams.h",
|
||||
]
|
||||
|
||||
@ -58,6 +59,7 @@ UNIFIED_SOURCES += [
|
||||
"KeyframeUtils.cpp",
|
||||
"PendingAnimationTracker.cpp",
|
||||
"ScrollTimeline.cpp",
|
||||
"ScrollTimelineAnimationTracker.cpp",
|
||||
"TimingParams.cpp",
|
||||
]
|
||||
|
||||
|
@ -100,6 +100,7 @@
|
||||
#include "mozilla/RelativeTo.h"
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
#include "mozilla/ScrollTimelineAnimationTracker.h"
|
||||
#include "mozilla/SMILAnimationController.h"
|
||||
#include "mozilla/SMILTimeContainer.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
@ -2500,6 +2501,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollTimelineAnimationTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
|
||||
@ -2625,6 +2627,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollTimelineAnimationTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
|
||||
@ -9274,6 +9277,15 @@ PendingAnimationTracker* Document::GetOrCreatePendingAnimationTracker() {
|
||||
return mPendingAnimationTracker;
|
||||
}
|
||||
|
||||
ScrollTimelineAnimationTracker*
|
||||
Document::GetOrCreateScrollTimelineAnimationTracker() {
|
||||
if (!mScrollTimelineAnimationTracker) {
|
||||
mScrollTimelineAnimationTracker = new ScrollTimelineAnimationTracker(this);
|
||||
}
|
||||
|
||||
return mScrollTimelineAnimationTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the "direction" property of the document.
|
||||
*
|
||||
|
@ -204,6 +204,7 @@ struct LangGroupFontPrefs;
|
||||
class PendingAnimationTracker;
|
||||
class PermissionDelegateHandler;
|
||||
class PresShell;
|
||||
class ScrollTimelineAnimationTracker;
|
||||
class ServoStyleSet;
|
||||
enum class StyleOrigin : uint8_t;
|
||||
class SMILAnimationController;
|
||||
@ -2765,6 +2766,19 @@ class Document : public nsINode,
|
||||
// will never be nullptr.
|
||||
PendingAnimationTracker* GetOrCreatePendingAnimationTracker();
|
||||
|
||||
// Gets the tracker for scroll-linked animations that are waiting to start.
|
||||
// Returns nullptr if there is no scroll-linked animation tracker for this
|
||||
// document which will be the case if there have never been any scroll-linked
|
||||
// animations in the document.
|
||||
ScrollTimelineAnimationTracker* GetScrollTimelineAnimationTracker() {
|
||||
return mScrollTimelineAnimationTracker;
|
||||
}
|
||||
|
||||
// Gets the tracker for scroll-linked animations that are waiting to start and
|
||||
// creates it if it doesn't already exist. As a result, the return value
|
||||
// will never be nullptr.
|
||||
ScrollTimelineAnimationTracker* GetOrCreateScrollTimelineAnimationTracker();
|
||||
|
||||
/**
|
||||
* Prevents user initiated events from being dispatched to the document and
|
||||
* subdocuments.
|
||||
@ -5140,6 +5154,10 @@ class Document : public nsINode,
|
||||
// nullptr until GetOrCreatePendingAnimationTracker is called.
|
||||
RefPtr<PendingAnimationTracker> mPendingAnimationTracker;
|
||||
|
||||
// Tracker for scroll-linked animations that are waiting to start.
|
||||
// nullptr until GetOrCreateScrollTimelineAnimationTracker is called.
|
||||
RefPtr<ScrollTimelineAnimationTracker> mScrollTimelineAnimationTracker;
|
||||
|
||||
// A document "without a browsing context" that owns the content of
|
||||
// HTMLTemplateElement.
|
||||
RefPtr<Document> mTemplateContentsOwner;
|
||||
|
@ -193,6 +193,7 @@
|
||||
#include "mozilla/layers/APZPublicUtils.h"
|
||||
#include "mozilla/ProfilerLabels.h"
|
||||
#include "mozilla/ProfilerMarkers.h"
|
||||
#include "mozilla/ScrollTimelineAnimationTracker.h"
|
||||
#include "mozilla/ScrollTypes.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
@ -4241,6 +4242,14 @@ static inline void AssertFrameTreeIsSane(const PresShell& aPresShell) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void TriggerPendingScrollTimelineAnimations(Document* aDocument) {
|
||||
auto* tracker = aDocument->GetScrollTimelineAnimationTracker();
|
||||
if (!tracker || !tracker->HasPendingAnimations()) {
|
||||
return;
|
||||
}
|
||||
tracker->TriggerPendingAnimations();
|
||||
}
|
||||
|
||||
void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
|
||||
// FIXME(emilio, bug 1530177): Turn into a release assert when bug 1530188 and
|
||||
// bug 1530190 are fixed.
|
||||
@ -4414,6 +4423,17 @@ void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
|
||||
|
||||
FlushPendingScrollResnap();
|
||||
|
||||
if (MOZ_LIKELY(!mIsDestroying)) {
|
||||
// Try to trigger pending scroll-linked animations after we flush
|
||||
// style and layout (if any). If we try to trigger them after flushing
|
||||
// style but the frame tree is not ready, we will check them again after
|
||||
// we flush layout because the requirement to trigger scroll-linked
|
||||
// animations is that the associated scroll containers are ready (i.e. the
|
||||
// scroll-timeline is active), and this depends on the readiness of the
|
||||
// scrollable frame and the primary frame of the scroll container.
|
||||
TriggerPendingScrollTimelineAnimations(mDocument);
|
||||
}
|
||||
|
||||
if (flushType >= FlushType::Layout) {
|
||||
if (!mIsDestroying) {
|
||||
viewManager->UpdateWidgetGeometry();
|
||||
|
@ -1,12 +1,16 @@
|
||||
[scroll-timeline-dynamic.tentative.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT, CRASH]
|
||||
[Switching between document and scroll timelines [immediate\]]
|
||||
expected: FAIL
|
||||
|
||||
[Switching between document and scroll timelines [scroll\]]
|
||||
expected: FAIL
|
||||
|
||||
[Switching pending animation from document to scroll timelines [immediate\]]
|
||||
expected: FAIL
|
||||
|
||||
[Switching pending animation from document to scroll timelines [scroll\]]
|
||||
expected: FAIL
|
||||
|
||||
[Changing computed value of animation-timeline changes effective timeline [immediate\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -83,14 +83,6 @@ promise_test(async t => {
|
||||
assert_equals(getComputedStyle(target).translate, '100px');
|
||||
}, 'animation-duration: 0s');
|
||||
|
||||
promise_test(async t => {
|
||||
let [target, scroller] = createTargetAndScroller(t);
|
||||
target.style.animation = 'infinite linear anim scroll(nearest)';
|
||||
|
||||
await scrollTop(scroller, 25); // [0, 100].
|
||||
assert_equals(getComputedStyle(target).translate, '100px');
|
||||
}, 'animation-duration: infinite');
|
||||
|
||||
|
||||
// ------------------------------
|
||||
// Test animation-iteration-count
|
||||
|
@ -1,4 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
|
||||
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
@ -106,6 +106,25 @@
|
||||
await assert_width(element, '120px');
|
||||
}, 'Switching between document and scroll timelines');
|
||||
|
||||
dynamic_rule_test(async (t, assert_width) => {
|
||||
let element = insertElement();
|
||||
|
||||
// Flush style and create the animation with play pending.
|
||||
getComputedStyle(element).animation;
|
||||
|
||||
let anim = element.getAnimations()[0];
|
||||
assert_true(anim.pending, "The animation is in play pending");
|
||||
|
||||
// Switch to scroll timeline for a pending animation.
|
||||
scroller1.style.scrollTimelineName = 'timeline';
|
||||
element.style.animationTimeline = 'timeline';
|
||||
|
||||
await anim.ready;
|
||||
assert_false(anim.pending, "The animation is not pending");
|
||||
|
||||
await assert_width(element, '120px');
|
||||
}, 'Switching pending animation from document to scroll timelines');
|
||||
|
||||
dynamic_rule_test(async (t, assert_width) => {
|
||||
let element = insertElement();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user