Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / TimelineManager.cpp
blob78db0765e6fdd2c4a23587c01d40005765f32a6d
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "TimelineManager.h"
9 #include "mozilla/ElementAnimationData.h"
10 #include "mozilla/dom/Element.h"
11 #include "mozilla/dom/ScrollTimeline.h"
12 #include "mozilla/dom/ViewTimeline.h"
13 #include "nsPresContext.h"
15 namespace mozilla {
16 using dom::Element;
17 using dom::ScrollTimeline;
18 using dom::ViewTimeline;
20 template <typename TimelineType>
21 void TryDestroyTimeline(Element* aElement, PseudoStyleType aPseudoType) {
22 auto* collection =
23 TimelineCollection<TimelineType>::Get(aElement, aPseudoType);
24 if (!collection) {
25 return;
27 collection->Destroy();
30 void TimelineManager::UpdateTimelines(Element* aElement,
31 PseudoStyleType aPseudoType,
32 const ComputedStyle* aComputedStyle,
33 ProgressTimelineType aType) {
34 MOZ_ASSERT(
35 aElement->IsInComposedDoc(),
36 "No need to update timelines that are not attached to the document tree");
38 // If we are in a display:none subtree we will have no computed values.
39 // However, if we are on the root of display:none subtree, the computed values
40 // might not have been cleared yet. In either case, since CSS animations
41 // should not run in display:none subtrees, so we don't need timeline, either.
42 const bool shouldDestroyTimelines =
43 !aComputedStyle ||
44 aComputedStyle->StyleDisplay()->mDisplay == StyleDisplay::None;
46 switch (aType) {
47 case ProgressTimelineType::Scroll:
48 if (shouldDestroyTimelines) {
49 TryDestroyTimeline<ScrollTimeline>(aElement, aPseudoType);
50 return;
52 DoUpdateTimelines<StyleScrollTimeline, ScrollTimeline>(
53 mPresContext, aElement, aPseudoType,
54 aComputedStyle->StyleUIReset()->mScrollTimelines,
55 aComputedStyle->StyleUIReset()->mScrollTimelineNameCount);
56 break;
58 case ProgressTimelineType::View:
59 if (shouldDestroyTimelines) {
60 TryDestroyTimeline<ViewTimeline>(aElement, aPseudoType);
61 return;
63 DoUpdateTimelines<StyleViewTimeline, ViewTimeline>(
64 mPresContext, aElement, aPseudoType,
65 aComputedStyle->StyleUIReset()->mViewTimelines,
66 aComputedStyle->StyleUIReset()->mViewTimelineNameCount);
67 break;
71 template <typename TimelineType>
72 static already_AddRefed<TimelineType> PopExistingTimeline(
73 nsAtom* aName, TimelineCollection<TimelineType>* aCollection) {
74 if (!aCollection) {
75 return nullptr;
77 return aCollection->Extract(aName);
80 template <typename StyleType, typename TimelineType>
81 static auto BuildTimelines(nsPresContext* aPresContext, Element* aElement,
82 PseudoStyleType aPseudoType,
83 const nsStyleAutoArray<StyleType>& aTimelines,
84 size_t aTimelineCount,
85 TimelineCollection<TimelineType>* aCollection) {
86 typename TimelineCollection<TimelineType>::TimelineMap result;
87 // If multiple timelines are attempting to modify the same property, then the
88 // timeline closest to the end of the list of names wins.
89 // The spec doesn't mention this specifically for scroll-timeline-name and
90 // view-timeline-name, so we follow the same rule with animation-name.
91 for (size_t idx = 0; idx < aTimelineCount; ++idx) {
92 const StyleType& timeline = aTimelines[idx];
93 if (timeline.GetName() == nsGkAtoms::_empty) {
94 continue;
97 RefPtr<TimelineType> dest =
98 PopExistingTimeline(timeline.GetName(), aCollection);
99 if (dest) {
100 dest->ReplacePropertiesWith(aElement, aPseudoType, timeline);
101 } else {
102 dest = TimelineType::MakeNamed(aPresContext->Document(), aElement,
103 aPseudoType, timeline);
105 MOZ_ASSERT(dest);
107 // Override the previous one if it is duplicated.
108 Unused << result.InsertOrUpdate(timeline.GetName(), dest);
110 return result;
113 template <typename TimelineType>
114 static TimelineCollection<TimelineType>& EnsureTimelineCollection(
115 Element& aElement, PseudoStyleType aPseudoType);
117 template <>
118 ScrollTimelineCollection& EnsureTimelineCollection<ScrollTimeline>(
119 Element& aElement, PseudoStyleType aPseudoType) {
120 return aElement.EnsureAnimationData().EnsureScrollTimelineCollection(
121 aElement, aPseudoType);
124 template <>
125 ViewTimelineCollection& EnsureTimelineCollection<ViewTimeline>(
126 Element& aElement, PseudoStyleType aPseudoType) {
127 return aElement.EnsureAnimationData().EnsureViewTimelineCollection(
128 aElement, aPseudoType);
131 template <typename StyleType, typename TimelineType>
132 void TimelineManager::DoUpdateTimelines(
133 nsPresContext* aPresContext, Element* aElement, PseudoStyleType aPseudoType,
134 const nsStyleAutoArray<StyleType>& aStyleTimelines, size_t aTimelineCount) {
135 auto* collection =
136 TimelineCollection<TimelineType>::Get(aElement, aPseudoType);
137 if (!collection && aTimelineCount == 1 &&
138 aStyleTimelines[0].GetName() == nsGkAtoms::_empty) {
139 return;
142 // We create a new timeline list based on its computed style and the existing
143 // timelines.
144 auto newTimelines = BuildTimelines<StyleType, TimelineType>(
145 aPresContext, aElement, aPseudoType, aStyleTimelines, aTimelineCount,
146 collection);
148 if (newTimelines.IsEmpty()) {
149 if (collection) {
150 collection->Destroy();
152 return;
155 if (!collection) {
156 collection =
157 &EnsureTimelineCollection<TimelineType>(*aElement, aPseudoType);
158 if (!collection->isInList()) {
159 AddTimelineCollection(collection);
163 // Replace unused timeline with new ones.
164 collection->Swap(newTimelines);
166 // FIXME: Bug 1774060. We may have to restyle the animations which use the
167 // dropped timelines. Or rely on restyling the subtree and the following
168 // siblings when mutating {scroll|view}-timeline-name.
171 } // namespace mozilla