Derive a scroll timeline's current time from the scroll progress draft
authorBotond Ballo <botond@mozilla.com>
Wed, 27 Jul 2016 15:46:26 -0400
changeset 394553 2830bdb39f5c9fa7446b30fe2a0b626c9ff1e8b7
parent 394552 bac442a609767fbb21b266dbd719d6eb968fbf3d
child 394554 46d99f451017530ba4d493fb164213b9c89c39da
push id24603
push userbballo@mozilla.com
push dateFri, 29 Jul 2016 23:28:33 +0000
milestone50.0a1
Derive a scroll timeline's current time from the scroll progress MozReview-Commit-ID: IYfwG5kaVxu
dom/animation/ScrollTimeline.cpp
dom/animation/ScrollTimeline.h
--- 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