--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -311,16 +311,17 @@ public:
* if any.
* Any properties already contained in |aSetProperties| are not changed. Any
* properties that are changed are added to |aSetProperties|.
*/
void ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
nsCSSPropertySet& aSetProperties);
void NotifyEffectTimingUpdated();
+ StickyTimeDuration EffectEnd() const;
protected:
void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
void SilentlySetPlaybackRate(double aPlaybackRate);
void CancelNoUpdate();
void PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior);
void PauseNoUpdate(ErrorResult& aRv);
void ResumeAt(const TimeDuration& aReadyTime);
@@ -366,17 +367,16 @@ protected:
/**
* Remove this animation from the pending animation tracker and reset
* mPendingState as necessary. The caller is responsible for resolving or
* aborting the mReady promise as necessary.
*/
void CancelPendingTasks();
bool IsPossiblyOrphanedPendingAnimation() const;
- StickyTimeDuration EffectEnd() const;
nsIDocument* GetRenderedDocument() const;
nsPresContext* GetPresContext() const;
RefPtr<AnimationTimeline> mTimeline;
RefPtr<KeyframeEffectReadOnly> mEffect;
// The beginning of the delay period.
Nullable<TimeDuration> mStartTime; // Timeline timescale
new file mode 100644
--- /dev/null
+++ b/dom/animation/ScrollTimeline.cpp
@@ -0,0 +1,261 @@
+/* -*- 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 "ScrollTimeline.h"
+#include "mozilla/dom/ScrollTimelineBinding.h"
+#include "AnimationUtils.h"
+#include "nsContentUtils.h"
+#include "nsCSSPseudoElements.h" // For CSSPseudoElementType
+#include "nsDOMMutationObserver.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "nsRefreshDriver.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ScrollTimeline, AnimationTimeline,
+ mDocument)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ScrollTimeline,
+ AnimationTimeline)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ScrollTimeline)
+NS_INTERFACE_MAP_END_INHERITING(AnimationTimeline)
+
+NS_IMPL_ADDREF_INHERITED(ScrollTimeline, AnimationTimeline)
+NS_IMPL_RELEASE_INHERITED(ScrollTimeline, AnimationTimeline)
+
+JSObject*
+ScrollTimeline::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return ScrollTimelineBinding::Wrap(aCx, this, aGivenProto);
+}
+
+/* static */ already_AddRefed<ScrollTimeline>
+ScrollTimeline::Constructor(const GlobalObject& aGlobal,
+ Element& scrollElement,
+ const Orientation orientation,
+ const Optional<double>& maxTime,
+ ErrorResult& aRv)
+{
+ nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
+ if (!doc) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<ScrollTimeline> timeline = new ScrollTimeline(doc,
+ &scrollElement,
+ 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;
+ }
+ }
+
+ 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
+ if (aTimeStamp.IsNull()) {
+ return result;
+ }
+
+ RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
+ if (MOZ_UNLIKELY(!timing)) {
+ return result;
+ }
+
+ result.SetValue(aTimeStamp
+ - timing->GetNavigationStartTimeStamp());
+ return result;
+}
+
+// memo: This function is member function of AnimationTimeline.
+void
+ScrollTimeline::NotifyAnimationUpdated(Animation& aAnimation)
+{
+ // MEMO: Should I pause animation when setting animation?
+ AnimationTimeline::NotifyAnimationUpdated(aAnimation);
+
+ if (!mIsObservingRefreshDriver) {
+ nsRefreshDriver* refreshDriver = GetRefreshDriver();
+ if (refreshDriver) {
+ refreshDriver->AddRefreshObserver(this, Flush_Style);
+ mIsObservingRefreshDriver = true;
+ }
+ }
+}
+
+void
+ScrollTimeline::WillRefresh(mozilla::TimeStamp aTime)
+{
+ MOZ_ASSERT(mIsObservingRefreshDriver);
+ MOZ_ASSERT(GetRefreshDriver(),
+ "Should be able to reach refresh driver from within WillRefresh");
+
+ bool needsTicks = false;
+ nsTArray<Animation*> animationsToRemove(mAnimations.Count());
+
+ nsAutoAnimationMutationBatch mb(mDocument);
+
+ for (Animation* animation = mAnimationOrder.getFirst(); animation;
+ animation = animation->getNext()) {
+ // Skip any animations that are longer need associated with this timeline.
+ if (animation->GetTimeline() != this) {
+ // If animation has some other timeline, it better not be also in the
+ // animation list of this timeline object!
+ MOZ_ASSERT(!animation->GetTimeline());
+ animationsToRemove.AppendElement(animation);
+ continue;
+ }
+
+ needsTicks |= animation->NeedsTicks();
+
+ // Even if |animation| doesn't need future ticks, we should still
+ // Tick it this time around since it might just need a one-off tick in
+ // order to dispatch events.
+ animation->Tick();
+
+ if (!animation->IsRelevant() && !animation->NeedsTicks()) {
+ animationsToRemove.AppendElement(animation);
+ }
+ }
+
+ for (Animation* animation : animationsToRemove) {
+ RemoveAnimation(animation);
+ }
+
+ if (!needsTicks) {
+ // We already assert that GetRefreshDriver() is non-null at the beginning
+ // of this function but we check it again here to be sure that ticking
+ // animations does not have any side effects that cause us to lose the
+ // connection with the refresh driver, such as triggering the destruction
+ // of mDocument's PresShell.
+ MOZ_ASSERT(GetRefreshDriver(),
+ "Refresh driver should still be valid at end of WillRefresh");
+ GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
+ mIsObservingRefreshDriver = false;
+ }
+}
+
+void
+ScrollTimeline::NotifyRefreshDriverCreated(nsRefreshDriver* aDriver)
+{
+ MOZ_ASSERT(!mIsObservingRefreshDriver,
+ "Timeline should not be observing the refresh driver before"
+ " it is created");
+
+ if (!mAnimationOrder.isEmpty()) {
+ aDriver->AddRefreshObserver(this, Flush_Style);
+ mIsObservingRefreshDriver = true;
+ }
+}
+
+void
+ScrollTimeline::NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver)
+{
+ if (!mIsObservingRefreshDriver) {
+ return;
+ }
+
+ aDriver->RemoveRefreshObserver(this, Flush_Style);
+ mIsObservingRefreshDriver = false;
+}
+
+void
+ScrollTimeline::RemoveAnimation(Animation* aAnimation)
+{
+ AnimationTimeline::RemoveAnimation(aAnimation);
+
+ if (mIsObservingRefreshDriver && mAnimations.IsEmpty()) {
+ MOZ_ASSERT(GetRefreshDriver(),
+ "Refresh driver should still be valid when "
+ "mIsObservingRefreshDriver is true");
+ GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
+ mIsObservingRefreshDriver = false;
+ }
+}
+
+TimeStamp
+ScrollTimeline::ToTimeStamp(const TimeDuration& aTimeDuration) const
+{
+ TimeStamp result;
+ RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
+ if (MOZ_UNLIKELY(!timing)) {
+ return result;
+ }
+
+ result =
+ timing->GetNavigationStartTimeStamp() + aTimeDuration;
+ return result;
+}
+
+nsRefreshDriver*
+ScrollTimeline::GetRefreshDriver() const
+{
+ nsIPresShell* presShell = mDocument->GetShell();
+ if (MOZ_UNLIKELY(!presShell)) {
+ return nullptr;
+ }
+
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (MOZ_UNLIKELY(!presContext)) {
+ return nullptr;
+ }
+
+ return presContext->RefreshDriver();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/animation/ScrollTimeline.h
@@ -0,0 +1,144 @@
+/* -*- 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_dom_ScrollTimeline_h
+#define mozilla_dom_ScrollTimeline_h
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/AnimationTarget.h"
+#include "mozilla/dom/ScrollTimelineBinding.h"
+#include "AnimationTimeline.h"
+#include "nsIDocument.h"
+#include "nsRefreshDriver.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Animation.h"
+#include "mozilla/LinkedList.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace dom {
+class ScrollObserver;
+
+class ScrollObserverImpl : public ScrollObserver {
+public:
+ ScrollObserverImpl(Element* elem, LinkedList<dom::Animation>* animationOrder) {
+ mElement = elem;
+ mAnimationOrder = animationOrder;
+ }
+
+ void notify() override {
+ if (mElement) {
+ mCurrent = mElement->ScrollLeft();
+ mMin = mElement->ScrollLeftMin();
+ mMax = mElement->ScrollLeftMax();
+ }
+
+ // 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);
+ }
+ }
+private:
+ double mCurrent, mMin, mMax;
+ Element* mElement;
+ LinkedList<dom::Animation>* mAnimationOrder;
+};
+
+class ScrollTimeline final
+ : public AnimationTimeline
+ , public nsARefreshObserver
+{
+public:
+ ScrollTimeline(nsIDocument* aDocument,
+ Element* aTarget,
+ Orientation orientation,
+ const Optional<double>& maxTime)
+ : AnimationTimeline(aDocument->GetParentObject())
+ , mDocument(aDocument)
+ , mIsObservingRefreshDriver(false)
+ , mElement(aTarget)
+ {
+ mScrollObserver = new ScrollObserverImpl(aTarget, &mAnimationOrder);
+ aTarget->RegistScrollTimelineObserver(mScrollObserver);
+ }
+
+protected:
+ virtual ~ScrollTimeline()
+ {
+ MOZ_ASSERT(!mIsObservingRefreshDriver, "Timeline should have disassociated"
+ " from the refresh driver before being destroyed");
+ if (mScrollObserver) {
+ mElement.get()->UnregistScrollTimelineObserver();
+ }
+ }
+
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ScrollTimeline,
+ AnimationTimeline)
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ static already_AddRefed<ScrollTimeline>
+ Constructor(const GlobalObject& aGlobal,
+ Element& scrollElement,
+ const Orientation orientation,
+ const Optional<double>& maxTime,
+ ErrorResult& aRv);
+
+ virtual Nullable<TimeDuration> GetCurrentTime() const override;
+
+ bool TracksWallclockTime() const override
+ {
+ nsRefreshDriver* refreshDriver = GetRefreshDriver();
+ return !refreshDriver ||
+ !refreshDriver->IsTestControllingRefreshesEnabled();
+ }
+ Nullable<TimeDuration> ToTimelineTime(const TimeStamp& aTimeStamp) const
+ override;
+ TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const override;
+
+ void NotifyAnimationUpdated(Animation& aAnimation) override;
+
+ void RemoveAnimation(Animation* aAnimation) override;
+
+ // nsARefreshObserver methods
+ void WillRefresh(TimeStamp aTime) override;
+
+ void NotifyRefreshDriverCreated(nsRefreshDriver* aDriver);
+ void NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver);
+
+protected:
+ TimeStamp GetCurrentTimeStamp() const;
+ nsRefreshDriver* GetRefreshDriver() const;
+
+ 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;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ScrollTimeline_h
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -11,16 +11,17 @@ EXPORTS.mozilla.dom += [
'Animation.h',
'AnimationEffectReadOnly.h',
'AnimationEffectTiming.h',
'AnimationEffectTimingReadOnly.h',
'AnimationTimeline.h',
'CSSPseudoElement.h',
'DocumentTimeline.h',
'KeyframeEffect.h',
+ 'ScrollTimeline.h',
]
EXPORTS.mozilla += [
'AnimationComparator.h',
'AnimationPerformanceWarning.h',
'AnimationTarget.h',
'AnimationUtils.h',
'AnimValuesStyleRule.h',
@@ -48,16 +49,17 @@ UNIFIED_SOURCES += [
'CSSPseudoElement.cpp',
'DocumentTimeline.cpp',
'EffectCompositor.cpp',
'EffectSet.cpp',
'KeyframeEffect.cpp',
'KeyframeEffectParams.cpp',
'KeyframeUtils.cpp',
'PendingAnimationTracker.cpp',
+ 'ScrollTimeline.cpp',
'TimingParams.cpp',
]
LOCAL_INCLUDES += [
'/dom/base',
'/layout/base',
'/layout/style',
]
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -136,25 +136,73 @@ namespace dom {
class Animation;
class Link;
class UndoManager;
class DOMRect;
class DOMRectList;
class DestinationInsertionPointList;
class Grid;
+class ScrollHandler;
// IID for the dom::Element interface
#define NS_ELEMENT_IID \
{ 0xc67ed254, 0xfd3b, 0x4b10, \
{ 0x96, 0xa2, 0xc5, 0x8b, 0x7b, 0x64, 0x97, 0xd1 } }
+class ScrollObserver {
+public:
+ ScrollObserver() {};
+ virtual void notify() { };
+};
+
+class ScrollHandlerImpl : public ScrollHandler{
+public:
+ ScrollHandlerImpl(ScrollObserver* observer)
+ : ScrollHandler()
+ , scrollObserver(observer) {
+ };
+
+ // Implemented after Element definitions.
+ void notify() override {
+ scrollObserver->notify();
+ }
+
+private:
+ ScrollObserver* scrollObserver;
+};
+
class Element : public FragmentOrElement
{
public:
+
+ ScrollObserver* scrollObserver;
+ ScrollHandlerImpl* mScrollHandler;
+ void RegistScrollTimelineObserver(ScrollObserver* obs) {
+ scrollObserver = obs;
+ nsIScrollableFrame* sf = GetScrollFrame(nullptr, false);
+ if (!sf) {
+ printf("Element:%s, ScrollableFrame is not found.\n", __func__);
+ return;
+ }
+ mScrollHandler = new ScrollHandlerImpl(scrollObserver);
+ sf->AddScrollHandler(mScrollHandler);
+ }
+
+ void UnregistScrollTimelineObserver() {
+ scrollObserver = nullptr;
+
+ nsIScrollableFrame* sf = GetScrollFrame(nullptr, false);
+ if (!sf) {
+ printf("Element:%s, ScrollableFrame is not found.\n", __func__);
+ return;
+ }
+ sf->RemoveScrollHandler();
+ }
+
#ifdef MOZILLA_INTERNAL_API
explicit Element(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) :
FragmentOrElement(aNodeInfo),
mState(NS_EVENT_STATE_MOZ_READONLY)
{
MOZ_ASSERT(mNodeInfo->NodeType() == nsIDOMNode::ELEMENT_NODE,
"Bad NodeType in aNodeInfo");
SetIsElement();
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ScrollTimeline.webidl
@@ -0,0 +1,20 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/web-animations/#documenttimeline
+ *
+ * Copyright © 2016 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+enum Orientation { "vertical", "horizontal" };
+
+[Func="nsDocument::IsWebAnimationsEnabled",
+ Constructor (Element scrollSource,
+ Orientation orientation,
+ optional double maxTime)]
+interface ScrollTimeline : AnimationTimeline {
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -400,16 +400,17 @@ WEBIDL_FILES = [
'Response.webidl',
'RGBColor.webidl',
'RTCStatsReport.webidl',
'Screen.webidl',
'ScreenOrientation.webidl',
'ScriptProcessorNode.webidl',
'ScrollAreaEvent.webidl',
'ScrollBoxObject.webidl',
+ 'ScrollTimeline.webidl',
'Selection.webidl',
'ServiceWorker.webidl',
'ServiceWorkerContainer.webidl',
'ServiceWorkerGlobalScope.webidl',
'ServiceWorkerMessageEvent.webidl',
'SettingChangeNotification.webidl',
'SettingsManager.webidl',
'ShadowRoot.webidl',
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2815,16 +2815,20 @@ ScrollFrameHelper::ScrollToImpl(nsPoint
UpdateScrollbarPosition();
if (!weakFrame.IsAlive()) {
return;
}
}
PostScrollEvent();
+ // Notify the scroll volume to ScrollTimeline.
+ nsIScrollableFrame *scrollable = do_QueryFrame(mOuter);
+ scrollable->NotifyScroll();
+
// notify the listeners.
for (uint32_t i = 0; i < mListeners.Length(); i++) {
mListeners[i]->ScrollPositionDidChange(pt.x, pt.y);
}
nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
if (docShell) {
docShell->NotifyScrollObservers();
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -31,25 +31,46 @@ class nsRenderingContext;
class nsIAtom;
class nsDisplayListBuilder;
namespace mozilla {
struct ContainerLayerParameters;
namespace layers {
class Layer;
} // namespace layers
+namespace dom {
+class ScrollHandler
+{
+public:
+ ScrollHandler() {};
+ virtual void notify() { };
+};
+} // namespace dom
} // namespace mozilla
/**
* Interface for frames that are scrollable. This interface exposes
* APIs for examining scroll state, observing changes to scroll state,
* and triggering scrolling.
*/
class nsIScrollableFrame : public nsIScrollbarMediator {
public:
+ mozilla::dom::ScrollHandler* scrollHandler = nullptr;
+ void AddScrollHandler(mozilla::dom::ScrollHandler* handler) {
+ scrollHandler = handler;
+ }
+ void RemoveScrollHandler() {
+ scrollHandler = nullptr;
+ }
+ void NotifyScroll() {
+ if (scrollHandler) {
+ scrollHandler->notify();
+ }
+ }
+
typedef mozilla::CSSIntPoint CSSIntPoint;
typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
typedef mozilla::layers::FrameMetrics FrameMetrics;
typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame)
/**
@@ -463,9 +484,10 @@ public:
/**
* Returns information required to determine where to snap to after a scroll.
*/
virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
};
+
#endif