|  | /* | 
|  | * Copyright (C) 2019 Apple Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
|  | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
|  | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
|  | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
|  | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
|  | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "WebAnimationUtilities.h" | 
|  |  | 
|  | #include "Animation.h" | 
|  | #include "AnimationList.h" | 
|  | #include "CSSAnimation.h" | 
|  | #include "CSSTransition.h" | 
|  | #include "DeclarativeAnimation.h" | 
|  | #include "Element.h" | 
|  | #include "KeyframeEffectStack.h" | 
|  | #include "PseudoElement.h" | 
|  | #include "WebAnimation.h" | 
|  |  | 
|  | namespace WebCore { | 
|  |  | 
|  | static bool compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(Element& a, Element& b) | 
|  | { | 
|  | // We should not ever be calling this function with two Elements that are the same. If that were the case, | 
|  | // then comparing objects of this kind would yield inconsistent results when comparing A == B and B == A. | 
|  | // As such, this function should be called with std::stable_sort(). | 
|  | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&a != &b); | 
|  |  | 
|  | // With regard to pseudo-elements, the sort order is as follows: | 
|  | //     - element | 
|  | //     - ::before | 
|  | //     - ::after | 
|  | //     - element children | 
|  |  | 
|  | enum SortingIndex : uint8_t { NotPseudo, Before, After }; | 
|  |  | 
|  | int aSortingIndex = NotPseudo; | 
|  | Element* aReferenceElement = &a; | 
|  | if (is<PseudoElement>(a)) { | 
|  | auto& aPseudo = downcast<PseudoElement>(a); | 
|  | aSortingIndex = aPseudo.isBeforePseudoElement() ? Before : After; | 
|  | aReferenceElement = aPseudo.hostElement(); | 
|  | ASSERT(aReferenceElement); | 
|  | } | 
|  |  | 
|  | int bSortingIndex = NotPseudo; | 
|  | Element* bReferenceElement = &b; | 
|  | if (is<PseudoElement>(b)) { | 
|  | auto& bPseudo = downcast<PseudoElement>(b); | 
|  | bSortingIndex = bPseudo.isBeforePseudoElement() ? Before : After; | 
|  | bReferenceElement = bPseudo.hostElement(); | 
|  | ASSERT(bReferenceElement); | 
|  | } | 
|  |  | 
|  | if (aReferenceElement == bReferenceElement) | 
|  | return aSortingIndex < bSortingIndex; | 
|  | return aReferenceElement->compareDocumentPosition(*bReferenceElement) & Node::DOCUMENT_POSITION_FOLLOWING; | 
|  | } | 
|  |  | 
|  | static bool compareCSSTransitions(const CSSTransition& a, const CSSTransition& b) | 
|  | { | 
|  | ASSERT(a.owningElement()); | 
|  | ASSERT(b.owningElement()); | 
|  | auto& aOwningElement = *a.owningElement(); | 
|  | auto& bOwningElement = *b.owningElement(); | 
|  |  | 
|  | // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements. | 
|  | if (&aOwningElement != &bOwningElement) | 
|  | return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(aOwningElement, bOwningElement); | 
|  |  | 
|  | // Otherwise, if A and B have different transition generation values, sort by their corresponding transition generation in ascending order. | 
|  | if (a.generationTime() != b.generationTime()) | 
|  | return a.generationTime() < b.generationTime(); | 
|  |  | 
|  | // Otherwise, sort A and B in ascending order by the Unicode codepoints that make up the expanded transition property name of each transition | 
|  | // (i.e. without attempting case conversion and such that ‘-moz-column-width’ sorts before ‘column-width’). | 
|  | return a.transitionProperty().utf8() < b.transitionProperty().utf8(); | 
|  | } | 
|  |  | 
|  | static bool compareCSSAnimations(const CSSAnimation& a, const CSSAnimation& b) | 
|  | { | 
|  | // https://drafts.csswg.org/css-animations-2/#animation-composite-order | 
|  | ASSERT(a.owningElement()); | 
|  | ASSERT(b.owningElement()); | 
|  | auto& aOwningElement = *a.owningElement(); | 
|  | auto& bOwningElement = *b.owningElement(); | 
|  |  | 
|  | // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements. | 
|  | if (&aOwningElement != &bOwningElement) | 
|  | return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(aOwningElement, bOwningElement); | 
|  |  | 
|  | // Sort A and B based on their position in the computed value of the animation-name property of the (common) owning element. | 
|  | auto* cssAnimationList = aOwningElement.ensureKeyframeEffectStack().cssAnimationList(); | 
|  | ASSERT(cssAnimationList); | 
|  | ASSERT(!cssAnimationList->isEmpty()); | 
|  |  | 
|  | auto& aBackingAnimation = a.backingAnimation(); | 
|  | auto& bBackingAnimation = b.backingAnimation(); | 
|  | for (size_t i = 0; i < cssAnimationList->size(); ++i) { | 
|  | auto& animation = cssAnimationList->animation(i); | 
|  | if (animation == aBackingAnimation) | 
|  | return true; | 
|  | if (animation == bBackingAnimation) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // We should have found either of those CSS animations in the CSS animations list. | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | bool compareAnimationsByCompositeOrder(const WebAnimation& a, const WebAnimation& b) | 
|  | { | 
|  | // We should not ever be calling this function with two WebAnimation objects that are the same. If that were the case, | 
|  | // then comparing objects of this kind would yield inconsistent results when comparing A == B and B == A. As such, | 
|  | // this function should be called with std::stable_sort(). | 
|  | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&a != &b); | 
|  |  | 
|  | bool aHasOwningElement = is<DeclarativeAnimation>(a) && downcast<DeclarativeAnimation>(a).owningElement(); | 
|  | bool bHasOwningElement = is<DeclarativeAnimation>(b) && downcast<DeclarativeAnimation>(b).owningElement(); | 
|  |  | 
|  | // CSS Transitions sort first. | 
|  | bool aIsCSSTransition = aHasOwningElement && is<CSSTransition>(a); | 
|  | bool bIsCSSTransition = bHasOwningElement && is<CSSTransition>(b); | 
|  | if (aIsCSSTransition || bIsCSSTransition) { | 
|  | if (aIsCSSTransition == bIsCSSTransition) | 
|  | return compareCSSTransitions(downcast<CSSTransition>(a), downcast<CSSTransition>(b)); | 
|  | return !bIsCSSTransition; | 
|  | } | 
|  |  | 
|  | // CSS Animations sort next. | 
|  | bool aIsCSSAnimation = aHasOwningElement && is<CSSAnimation>(a); | 
|  | bool bIsCSSAnimation = bHasOwningElement && is<CSSAnimation>(b); | 
|  | if (aIsCSSAnimation || bIsCSSAnimation) { | 
|  | if (aIsCSSAnimation == bIsCSSAnimation) | 
|  | return compareCSSAnimations(downcast<CSSAnimation>(a), downcast<CSSAnimation>(b)); | 
|  | return !bIsCSSAnimation; | 
|  | } | 
|  |  | 
|  | // JS-originated animations sort last based on their position in the global animation list. | 
|  | // https://drafts.csswg.org/web-animations-1/#animation-composite-order | 
|  | RELEASE_ASSERT(a.globalPosition() != b.globalPosition()); | 
|  | return a.globalPosition() < b.globalPosition(); | 
|  | } | 
|  |  | 
|  | } // namespace WebCore |