Bug 1945643 - Update to mozilla-nimbus-schemas 2025.1.1 r=chumphreys
[gecko.git] / dom / animation / DocumentTimeline.cpp
blob14e86cb9d1057636d819fa7da4c6311d3197b008
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DocumentTimeline.h"
8 #include "mozilla/dom/DocumentInlines.h"
9 #include "mozilla/dom/DocumentTimelineBinding.h"
10 #include "AnimationUtils.h"
11 #include "nsContentUtils.h"
12 #include "nsDOMMutationObserver.h"
13 #include "nsDOMNavigationTiming.h"
14 #include "nsPresContext.h"
15 #include "nsRefreshDriver.h"
17 namespace mozilla::dom {
19 NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentTimeline)
20 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocumentTimeline,
21 AnimationTimeline)
22 if (tmp->isInList()) {
23 tmp->remove();
25 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
27 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocumentTimeline,
28 AnimationTimeline)
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
32 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DocumentTimeline,
33 AnimationTimeline)
34 NS_IMPL_CYCLE_COLLECTION_TRACE_END
36 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentTimeline)
37 NS_INTERFACE_MAP_END_INHERITING(AnimationTimeline)
39 NS_IMPL_ADDREF_INHERITED(DocumentTimeline, AnimationTimeline)
40 NS_IMPL_RELEASE_INHERITED(DocumentTimeline, AnimationTimeline)
42 DocumentTimeline::DocumentTimeline(Document* aDocument,
43 const TimeDuration& aOriginTime)
44 : AnimationTimeline(aDocument->GetParentObject(),
45 aDocument->GetScopeObject()->GetRTPCallerType()),
46 mDocument(aDocument),
47 mOriginTime(aOriginTime) {
48 if (mDocument) {
49 mDocument->Timelines().insertBack(this);
51 // Ensure mLastRefreshDriverTime is valid.
52 UpdateLastRefreshDriverTime();
55 DocumentTimeline::~DocumentTimeline() {
56 if (isInList()) {
57 remove();
61 JSObject* DocumentTimeline::WrapObject(JSContext* aCx,
62 JS::Handle<JSObject*> aGivenProto) {
63 return DocumentTimeline_Binding::Wrap(aCx, this, aGivenProto);
66 /* static */
67 already_AddRefed<DocumentTimeline> DocumentTimeline::Constructor(
68 const GlobalObject& aGlobal, const DocumentTimelineOptions& aOptions,
69 ErrorResult& aRv) {
70 Document* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
71 if (!doc) {
72 aRv.Throw(NS_ERROR_FAILURE);
73 return nullptr;
75 TimeDuration originTime =
76 TimeDuration::FromMilliseconds(aOptions.mOriginTime);
78 if (originTime == TimeDuration::Forever() ||
79 originTime == -TimeDuration::Forever()) {
80 aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>("Origin time");
81 return nullptr;
83 RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime);
85 return timeline.forget();
88 Nullable<TimeDuration> DocumentTimeline::GetCurrentTimeAsDuration() const {
89 return ToTimelineTime(GetCurrentTimeStamp());
92 bool DocumentTimeline::TracksWallclockTime() const {
93 nsRefreshDriver* refreshDriver = GetRefreshDriver();
94 return !refreshDriver || !refreshDriver->IsTestControllingRefreshesEnabled();
97 TimeStamp DocumentTimeline::GetCurrentTimeStamp() const {
98 nsRefreshDriver* refreshDriver = GetRefreshDriver();
99 return refreshDriver ? refreshDriver->MostRecentRefresh()
100 : mLastRefreshDriverTime;
103 void DocumentTimeline::UpdateLastRefreshDriverTime() {
104 TimeStamp result = [&] {
105 if (auto* rd = GetRefreshDriver()) {
106 return rd->MostRecentRefresh();
108 return mLastRefreshDriverTime;
109 }();
111 if (nsDOMNavigationTiming* timing = mDocument->GetNavigationTiming()) {
112 // If we don't have a refresh driver and we've never had one use the
113 // timeline's zero time.
114 // In addition, it's possible that our refresh driver's timestamp is behind
115 // from the navigation start time because the refresh driver timestamp is
116 // sent through an IPC call whereas the navigation time is set by calling
117 // TimeStamp::Now() directly. In such cases we also use the timeline's zero
118 // time.
119 // Also, let this time represent the current refresh time. This way we'll
120 // save it as the last refresh time and skip looking up navigation start
121 // time each time.
122 if (result.IsNull() || result < timing->GetNavigationStartTimeStamp()) {
123 result = timing->GetNavigationStartTimeStamp();
127 if (!result.IsNull()) {
128 mLastRefreshDriverTime = result;
132 Nullable<TimeDuration> DocumentTimeline::ToTimelineTime(
133 const TimeStamp& aTimeStamp) const {
134 Nullable<TimeDuration> result; // Initializes to null
135 if (aTimeStamp.IsNull()) {
136 return result;
139 nsDOMNavigationTiming* timing = mDocument->GetNavigationTiming();
140 if (MOZ_UNLIKELY(!timing)) {
141 return result;
144 result.SetValue(aTimeStamp - timing->GetNavigationStartTimeStamp() -
145 mOriginTime);
146 return result;
149 void DocumentTimeline::NotifyAnimationUpdated(Animation& aAnimation) {
150 AnimationTimeline::NotifyAnimationUpdated(aAnimation);
152 if (!mAnimationOrder.isEmpty()) {
153 if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
154 MOZ_ASSERT(isInList(),
155 "We should not register with the refresh driver if we are not"
156 " in the document's list of timelines");
157 refreshDriver->EnsureAnimationUpdate();
162 void DocumentTimeline::TriggerAllPendingAnimationsNow() {
163 for (Animation* animation :
164 ToTArray<AutoTArray<RefPtr<Animation>, 32>>(mAnimationOrder)) {
165 animation->TryTriggerNow();
169 void DocumentTimeline::WillRefresh() {
170 if (!mDocument->GetPresShell()) {
171 // If we're not displayed, don't tick animations.
172 return;
174 UpdateLastRefreshDriverTime();
175 if (mAnimationOrder.isEmpty()) {
176 return;
178 nsAutoAnimationMutationBatch mb(mDocument);
180 TickState state;
181 bool ticked = Tick(state);
182 if (!ticked) {
183 return;
185 // We already assert that GetRefreshDriver() is non-null at the beginning
186 // of this function but we check it again here to be sure that ticking
187 // animations does not have any side effects that cause us to lose the
188 // connection with the refresh driver, such as triggering the destruction
189 // of mDocument's PresShell.
190 if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
191 refreshDriver->EnsureAnimationUpdate();
195 void DocumentTimeline::NotifyAnimationContentVisibilityChanged(
196 Animation* aAnimation, bool aIsVisible) {
197 AnimationTimeline::NotifyAnimationContentVisibilityChanged(aAnimation,
198 aIsVisible);
200 if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
201 MOZ_ASSERT(isInList(),
202 "We should not register with the refresh driver if we are not"
203 " in the document's list of timelines");
204 refreshDriver->EnsureAnimationUpdate();
208 TimeStamp DocumentTimeline::ToTimeStamp(
209 const TimeDuration& aTimeDuration) const {
210 TimeStamp result;
211 nsDOMNavigationTiming* timing = mDocument->GetNavigationTiming();
212 if (MOZ_UNLIKELY(!timing)) {
213 return result;
216 result =
217 timing->GetNavigationStartTimeStamp() + (aTimeDuration + mOriginTime);
218 return result;
221 nsRefreshDriver* DocumentTimeline::GetRefreshDriver() const {
222 nsPresContext* presContext = mDocument->GetPresContext();
223 if (MOZ_UNLIKELY(!presContext)) {
224 return nullptr;
226 return presContext->RefreshDriver();
229 } // namespace mozilla::dom