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