Derive a scroll timeline's current time from the scroll progress
MozReview-Commit-ID: IYfwG5kaVxu
--- a/dom/animation/ScrollTimeline.cpp
+++ b/dom/animation/ScrollTimeline.cpp
@@ -55,55 +55,22 @@ ScrollTimeline::Constructor(const Global
orientation,
maxTime);
return timeline.forget();
}
Nullable<TimeDuration>
ScrollTimeline::GetCurrentTime() const
{
- // Memo: We should retrun current time which related with scroll value.
- return ToTimelineTime(GetCurrentTimeStamp());
-}
-
-TimeStamp
-ScrollTimeline::GetCurrentTimeStamp() const
-{
- // Memo: We should return current timestamp wich related with scroll value.
- // The following implementation is DocumentTimeline. So we should remove
- // this implementation, and return new concept time.
- // Maybe, we don't need to use RefreshDriver.
- // Idea: Perhaps, We will return
- nsRefreshDriver* refreshDriver = GetRefreshDriver();
- TimeStamp refreshTime = refreshDriver
- ? refreshDriver->MostRecentRefresh()
- : TimeStamp();
-
- // Always return the same object to benefit from return-value optimization.
- TimeStamp result = !refreshTime.IsNull()
- ? refreshTime
- : mLastRefreshDriverTime;
-
- // If we don't have a refresh driver and we've never had one use the
- // timeline's zero time.
- if (result.IsNull()) {
- RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
- if (timing) {
- result = timing->GetNavigationStartTimeStamp();
- // Also, let this time represent the current refresh time. This way
- // we'll save it as the last refresh time and skip looking up
- // navigation timing each time.
- refreshTime = result;
- }
+ Nullable<TimeDuration> result; // Initializes to null
+ double scrollRange = mMaxScroll - mMinScroll;
+ if (scrollRange != 0 && !mTimeRange.IsZero()) {
+ result.SetValue(
+ TimeDuration(mTimeRange.MultDouble(mCurrentScroll / scrollRange)));
}
-
- if (!refreshTime.IsNull()) {
- mLastRefreshDriverTime = refreshTime;
- }
-
return result;
}
Nullable<TimeDuration>
ScrollTimeline::ToTimelineTime(const TimeStamp& aTimeStamp) const
{
// Memo: We don't concern about clock time and zero time.
Nullable<TimeDuration> result; // Initializes to null
@@ -122,16 +89,17 @@ ScrollTimeline::ToTimelineTime(const Tim
}
// memo: This function is member function of AnimationTimeline.
void
ScrollTimeline::NotifyAnimationUpdated(Animation& aAnimation)
{
// MEMO: Should I pause animation when setting animation?
AnimationTimeline::NotifyAnimationUpdated(aAnimation);
+ CalculateTimeRange();
if (!mIsObservingRefreshDriver) {
nsRefreshDriver* refreshDriver = GetRefreshDriver();
if (refreshDriver) {
refreshDriver->AddRefreshObserver(this, Flush_Style);
mIsObservingRefreshDriver = true;
}
}
@@ -212,16 +180,17 @@ ScrollTimeline::NotifyRefreshDriverDestr
aDriver->RemoveRefreshObserver(this, Flush_Style);
mIsObservingRefreshDriver = false;
}
void
ScrollTimeline::RemoveAnimation(Animation* aAnimation)
{
AnimationTimeline::RemoveAnimation(aAnimation);
+ CalculateTimeRange();
if (mIsObservingRefreshDriver && mAnimations.IsEmpty()) {
MOZ_ASSERT(GetRefreshDriver(),
"Refresh driver should still be valid when "
"mIsObservingRefreshDriver is true");
GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
mIsObservingRefreshDriver = false;
}
@@ -252,10 +221,42 @@ ScrollTimeline::GetRefreshDriver() const
nsPresContext* presContext = presShell->GetPresContext();
if (MOZ_UNLIKELY(!presContext)) {
return nullptr;
}
return presContext->RefreshDriver();
}
+void
+ScrollTimeline::CalculateTimeRange()
+{
+ mTimeRange = 0;
+ for (Animation* animation : mAnimationOrder) {
+ StickyTimeDuration endTime = animation->EffectEnd();
+ mTimeRange = StickyTimeDuration::Max(endTime, mTimeRange);
+ }
+}
+
+void
+ScrollTimeline::SetScrollValues()
+{
+ if (mElement) {
+ if (mOrientation == Orientation::Horizontal) {
+ mCurrentScroll = mElement->ScrollLeft();
+ mMinScroll = mElement->ScrollLeftMin();
+ mMaxScroll = mElement->ScrollLeftMax();
+ } else {
+ mCurrentScroll = mElement->ScrollTop();
+ mMinScroll = mElement->ScrollTopMin();
+ mMaxScroll = mElement->ScrollTopMax();
+ }
+ }
+}
+
+void
+ScrollObserverImpl::notify()
+{
+ mTimeline->SetScrollValues();
+}
+
} // namespace dom
} // namespace mozilla
--- a/dom/animation/ScrollTimeline.h
+++ b/dom/animation/ScrollTimeline.h
@@ -20,71 +20,47 @@
struct JSContext;
namespace mozilla {
namespace dom {
class ScrollObserver;
class ScrollObserverImpl : public ScrollObserver {
public:
- ScrollObserverImpl(Element* elem, LinkedList<dom::Animation>* animationOrder,
- Orientation aOrientation) {
- mElement = elem;
- mAnimationOrder = animationOrder;
- mOrientation = aOrientation;
+ ScrollObserverImpl(ScrollTimeline* aTimeline) {
+ mTimeline = aTimeline;
}
- void notify() override {
- if (mElement) {
- if (mOrientation == Orientation::Horizontal) {
- mCurrent = mElement->ScrollLeft();
- mMin = mElement->ScrollLeftMin();
- mMax = mElement->ScrollLeftMax();
- } else {
- mCurrent = mElement->ScrollTop();
- mMin = mElement->ScrollTopMin();
- mMax = mElement->ScrollTopMax();
- }
- }
-
- // We will change the currentTime according to scroll volumes.
- for (Animation* animation = mAnimationOrder->getFirst(); animation;
- animation = animation->getNext()) {
- StickyTimeDuration endTime = animation->EffectEnd();
- TimeDuration time =
- TimeDuration(endTime.MultDouble(mCurrent /(mMax - mMin)));
-
- ErrorResult rv;
- animation->SetCurrentTimeAsDouble(Nullable<double>(time.ToMilliseconds()), rv);
- }
- }
+ void notify() override;
private:
- double mCurrent, mMin, mMax;
- Element* mElement;
- LinkedList<dom::Animation>* mAnimationOrder;
- Orientation mOrientation;
+ ScrollTimeline* mTimeline;
};
class ScrollTimeline final
: public AnimationTimeline
, public nsARefreshObserver
{
public:
ScrollTimeline(nsIDocument* aDocument,
Element* aTarget,
Orientation aOrientation,
const Optional<double>& maxTime)
: AnimationTimeline(aDocument->GetParentObject())
, mDocument(aDocument)
, mIsObservingRefreshDriver(false)
, mOrientation(aOrientation)
, mElement(aTarget)
+ , mCurrentScroll(0)
+ , mMinScroll(0)
+ , mMaxScroll(0)
{
- mScrollObserver = new ScrollObserverImpl(aTarget, &mAnimationOrder, mOrientation);
+ mScrollObserver = new ScrollObserverImpl(this);
aTarget->RegistScrollTimelineObserver(mScrollObserver);
+ CalculateTimeRange();
+ SetScrollValues();
}
protected:
virtual ~ScrollTimeline()
{
MOZ_ASSERT(!mIsObservingRefreshDriver, "Timeline should have disassociated"
" from the refresh driver before being destroyed");
if (mScrollObserver) {
@@ -124,31 +100,35 @@ public:
void RemoveAnimation(Animation* aAnimation) override;
// nsARefreshObserver methods
void WillRefresh(TimeStamp aTime) override;
void NotifyRefreshDriverCreated(nsRefreshDriver* aDriver);
void NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver);
+ void SetScrollValues();
+
protected:
- TimeStamp GetCurrentTimeStamp() const;
nsRefreshDriver* GetRefreshDriver() const;
+ void CalculateTimeRange();
nsCOMPtr<nsIDocument> mDocument;
// The most recently used refresh driver time. This is used in cases where
// we don't have a refresh driver (e.g. because we are in a display:none
// iframe).
mutable TimeStamp mLastRefreshDriverTime;
bool mIsObservingRefreshDriver;
Orientation mOrientation;
double mMaxTime;
ScrollObserverImpl *mScrollObserver;
RefPtr<Element> mElement;
+ StickyTimeDuration mTimeRange;
+ double mCurrentScroll, mMinScroll, mMaxScroll;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ScrollTimeline_h