103 static inline bool compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(Element* lhsOwningElement, Element* rhsOwningElement)
104 {
105 // With regard to pseudo-elements, the sort order is as follows:
106 // - element
107 // - ::before
108 // - ::after
109 // - element children
110
111 // We could be comparing two pseudo-elements that are hosted on the same element.
112 if (is<PseudoElement>(lhsOwningElement) && is<PseudoElement>(rhsOwningElement)) {
113 auto* lhsPseudoElement = downcast<PseudoElement>(lhsOwningElement);
114 auto* rhsPseudoElement = downcast<PseudoElement>(rhsOwningElement);
115 if (lhsPseudoElement->hostElement() == rhsPseudoElement->hostElement())
116 return lhsPseudoElement->isBeforePseudoElement();
117 }
118
119 // Or comparing a pseudo-element that is compared to another non-pseudo element, in which case
120 // we want to see if it's hosted on that other element, and if not use its host element to compare.
121 if (is<PseudoElement>(lhsOwningElement)) {
122 auto* lhsHostElement = downcast<PseudoElement>(lhsOwningElement)->hostElement();
123 if (rhsOwningElement == lhsHostElement)
124 return false;
125 lhsOwningElement = lhsHostElement;
126 }
127
128 if (is<PseudoElement>(rhsOwningElement)) {
129 auto* rhsHostElement = downcast<PseudoElement>(rhsOwningElement)->hostElement();
130 if (lhsOwningElement == rhsHostElement)
131 return true;
132 rhsOwningElement = rhsHostElement;
133 }
134
135 return lhsOwningElement->compareDocumentPosition(*rhsOwningElement) & Node::DOCUMENT_POSITION_FOLLOWING;
136 }
137
138 Vector<RefPtr<WebAnimation>> DocumentTimeline::getAnimations() const
139 {
140 ASSERT(m_document);
141
142 Vector<RefPtr<WebAnimation>> cssTransitions;
143 Vector<RefPtr<WebAnimation>> cssAnimations;
144 Vector<RefPtr<WebAnimation>> webAnimations;
145
146 // First, let's get all qualifying animations in their right group.
147 for (const auto& animation : m_animations) {
148 if (!animation || !animation->isRelevant() || animation->timeline() != this || !is<KeyframeEffect>(animation->effect()))
149 continue;
150
151 auto* target = downcast<KeyframeEffect>(animation->effect())->target();
152 if (!target || !target->isDescendantOf(*m_document))
153 continue;
154
155 if (is<CSSTransition>(animation.get()) && downcast<CSSTransition>(animation.get())->owningElement())
156 cssTransitions.append(animation);
157 else if (is<CSSAnimation>(animation.get()) && downcast<CSSAnimation>(animation.get())->owningElement())
158 cssAnimations.append(animation);
159 else
160 webAnimations.append(animation);
161 }
162
163 // Now sort CSS Transitions by their composite order.
164 std::stable_sort(cssTransitions.begin(), cssTransitions.end(), [](auto& lhs, auto& rhs) {
165 // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
166 auto* lhsTransition = downcast<CSSTransition>(lhs.get());
167 auto* rhsTransition = downcast<CSSTransition>(rhs.get());
168
169 auto* lhsOwningElement = lhsTransition->owningElement();
170 auto* rhsOwningElement = rhsTransition->owningElement();
171
172 // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements.
173 if (lhsOwningElement != rhsOwningElement)
174 return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(lhsOwningElement, rhsOwningElement);
175
176 // Otherwise, if A and B have different transition generation values, sort by their corresponding transition generation in ascending order.
177 if (lhsTransition->generationTime() != rhsTransition->generationTime())
178 return lhsTransition->generationTime() < rhsTransition->generationTime();
179
180 // Otherwise, sort A and B in ascending order by the Unicode codepoints that make up the expanded transition property name of each transition
181 // (i.e. without attempting case conversion and such that ‘-moz-column-width’ sorts before ‘column-width’).
182 return lhsTransition->transitionProperty().utf8() < rhsTransition->transitionProperty().utf8();
183 });
184
185 // Now sort CSS Animations by their composite order.
186 std::stable_sort(cssAnimations.begin(), cssAnimations.end(), [](auto& lhs, auto& rhs) {
187 // https://drafts.csswg.org/css-animations-2/#animation-composite-order
188 auto* lhsOwningElement = downcast<CSSAnimation>(lhs.get())->owningElement();
189 auto* rhsOwningElement = downcast<CSSAnimation>(rhs.get())->owningElement();
190
191 // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements.
192 if (lhsOwningElement != rhsOwningElement)
193 return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(lhsOwningElement, rhsOwningElement);
194
195 // Otherwise, sort A and B based on their position in the computed value of the animation-name property of the (common) owning element.
196 return compareAnimationsByCompositeOrder(*lhs, *rhs, lhsOwningElement->ensureKeyframeEffectStack().cssAnimationList());
197 });
198
199 std::stable_sort(webAnimations.begin(), webAnimations.end(), [](auto& lhs, auto& rhs) {
200 return lhs->globalPosition() < rhs->globalPosition();
201 });
202
203 // Finally, we can concatenate the sorted CSS Transitions, CSS Animations and Web Animations in their relative composite order.
204 Vector<RefPtr<WebAnimation>> animations;
205 animations.appendRange(cssTransitions.begin(), cssTransitions.end());
206 animations.appendRange(cssAnimations.begin(), cssAnimations.end());
207 animations.appendRange(webAnimations.begin(), webAnimations.end());
208 return animations;
209 }
210