summaryrefslogtreecommitdiffstats
path: root/layout/style/AnimationCommon.h
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/AnimationCommon.h')
-rw-r--r--layout/style/AnimationCommon.h225
1 files changed, 225 insertions, 0 deletions
diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h
new file mode 100644
index 0000000000..d7a54c9751
--- /dev/null
+++ b/layout/style/AnimationCommon.h
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_css_AnimationCommon_h
+#define mozilla_css_AnimationCommon_h
+
+#include "mozilla/AnimationCollection.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/dom/Animation.h"
+#include "mozilla/dom/BaseKeyframeTypesBinding.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/TimingParams.h"
+#include "mozilla/dom/Nullable.h"
+#include "nsContentUtils.h"
+#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
+
+class nsPresContext;
+
+namespace mozilla {
+enum class PseudoStyleType : uint8_t;
+
+namespace dom {
+class Element;
+}
+
+template <class AnimationType>
+class CommonAnimationManager {
+ public:
+ explicit CommonAnimationManager(nsPresContext* aPresContext)
+ : mPresContext(aPresContext) {}
+
+ // NOTE: This can return null after Disconnect().
+ nsPresContext* PresContext() const { return mPresContext; }
+
+ /**
+ * Notify the manager that the pres context is going away.
+ */
+ void Disconnect() {
+ // Content nodes might outlive the transition or animation manager.
+ RemoveAllElementCollections();
+
+ mPresContext = nullptr;
+ }
+
+ /**
+ * Stop animations on the element. This method takes the real element
+ * rather than the element for the generated content for animations on
+ * ::before, ::after and ::marker.
+ */
+ void StopAnimationsForElement(dom::Element* aElement,
+ PseudoStyleType aPseudoType) {
+ MOZ_ASSERT(aElement);
+ auto* collection =
+ AnimationCollection<AnimationType>::Get(aElement, aPseudoType);
+ if (!collection) {
+ return;
+ }
+
+ nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());
+ collection->Destroy();
+ }
+
+ protected:
+ virtual ~CommonAnimationManager() {
+ MOZ_ASSERT(!mPresContext, "Disconnect should have been called");
+ }
+
+ void AddElementCollection(AnimationCollection<AnimationType>* aCollection) {
+ mElementCollections.insertBack(aCollection);
+ }
+ void RemoveAllElementCollections() {
+ while (AnimationCollection<AnimationType>* head =
+ mElementCollections.getFirst()) {
+ head->Destroy(); // Note: this removes 'head' from mElementCollections.
+ }
+ }
+
+ LinkedList<AnimationCollection<AnimationType>> mElementCollections;
+ nsPresContext* mPresContext; // weak (non-null from ctor to Disconnect)
+};
+
+/**
+ * Utility class for referencing the element that created a CSS animation or
+ * transition. It is non-owning (i.e. it uses a raw pointer) since it is only
+ * expected to be set by the owned animation while it actually being managed
+ * by the owning element.
+ *
+ * This class also abstracts the comparison of an element/pseudo-class pair
+ * for the sake of composite ordering since this logic is common to both CSS
+ * animations and transitions.
+ *
+ * (We call this OwningElementRef instead of just OwningElement so that we can
+ * call the getter on CSSAnimation/CSSTransition OwningElement() without
+ * clashing with this object's contructor.)
+ */
+class OwningElementRef final {
+ public:
+ OwningElementRef() = default;
+
+ explicit OwningElementRef(const NonOwningAnimationTarget& aTarget)
+ : mTarget(aTarget) {}
+
+ OwningElementRef(dom::Element& aElement, PseudoStyleType aPseudoType)
+ : mTarget(&aElement, aPseudoType) {}
+
+ bool Equals(const OwningElementRef& aOther) const {
+ return mTarget == aOther.mTarget;
+ }
+
+ bool LessThan(Maybe<uint32_t>& aChildIndex, const OwningElementRef& aOther,
+ Maybe<uint32_t>& aOtherChildIndex) const {
+ MOZ_ASSERT(mTarget.mElement && aOther.mTarget.mElement,
+ "Elements to compare should not be null");
+
+ if (mTarget.mElement != aOther.mTarget.mElement) {
+ return nsContentUtils::PositionIsBefore(mTarget.mElement,
+ aOther.mTarget.mElement,
+ &aChildIndex, &aOtherChildIndex);
+ }
+
+ return mTarget.mPseudoType == PseudoStyleType::NotPseudo ||
+ (mTarget.mPseudoType == PseudoStyleType::before &&
+ aOther.mTarget.mPseudoType == PseudoStyleType::after) ||
+ (mTarget.mPseudoType == PseudoStyleType::marker &&
+ aOther.mTarget.mPseudoType == PseudoStyleType::before) ||
+ (mTarget.mPseudoType == PseudoStyleType::marker &&
+ aOther.mTarget.mPseudoType == PseudoStyleType::after);
+ }
+
+ bool IsSet() const { return !!mTarget.mElement; }
+
+ void GetElement(dom::Element*& aElement, PseudoStyleType& aPseudoType) const {
+ aElement = mTarget.mElement;
+ aPseudoType = mTarget.mPseudoType;
+ }
+
+ const NonOwningAnimationTarget& Target() const { return mTarget; }
+
+ nsPresContext* GetPresContext() const {
+ return nsContentUtils::GetContextForContent(mTarget.mElement);
+ }
+
+ private:
+ NonOwningAnimationTarget mTarget;
+};
+
+// Return the TransitionPhase or AnimationPhase to use when the animation
+// doesn't have a target effect.
+template <typename PhaseType>
+PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation) {
+ MOZ_ASSERT(!aAnimation.GetEffect(),
+ "Should only be called when we do not have an effect");
+
+ dom::Nullable<TimeDuration> currentTime =
+ aAnimation.GetCurrentTimeAsDuration();
+ if (currentTime.IsNull()) {
+ return PhaseType::Idle;
+ }
+
+ // If we don't have a target effect, the duration will be zero so the phase is
+ // 'before' if the current time is less than zero.
+ return currentTime.Value() < TimeDuration() ? PhaseType::Before
+ : PhaseType::After;
+};
+
+inline dom::PlaybackDirection StyleToDom(StyleAnimationDirection aDirection) {
+ switch (aDirection) {
+ case StyleAnimationDirection::Normal:
+ return dom::PlaybackDirection::Normal;
+ case StyleAnimationDirection::Reverse:
+ return dom::PlaybackDirection::Reverse;
+ case StyleAnimationDirection::Alternate:
+ return dom::PlaybackDirection::Alternate;
+ case StyleAnimationDirection::AlternateReverse:
+ return dom::PlaybackDirection::Alternate_reverse;
+ }
+ MOZ_ASSERT_UNREACHABLE("Wrong style value?");
+ return dom::PlaybackDirection::Normal;
+}
+
+inline dom::FillMode StyleToDom(StyleAnimationFillMode aFillMode) {
+ switch (aFillMode) {
+ case StyleAnimationFillMode::None:
+ return dom::FillMode::None;
+ case StyleAnimationFillMode::Both:
+ return dom::FillMode::Both;
+ case StyleAnimationFillMode::Forwards:
+ return dom::FillMode::Forwards;
+ case StyleAnimationFillMode::Backwards:
+ return dom::FillMode::Backwards;
+ }
+ MOZ_ASSERT_UNREACHABLE("Wrong style value?");
+ return dom::FillMode::None;
+}
+
+inline dom::CompositeOperation StyleToDom(StyleAnimationComposition aStyle) {
+ switch (aStyle) {
+ case StyleAnimationComposition::Replace:
+ return dom::CompositeOperation::Replace;
+ case StyleAnimationComposition::Add:
+ return dom::CompositeOperation::Add;
+ case StyleAnimationComposition::Accumulate:
+ return dom::CompositeOperation::Accumulate;
+ }
+ MOZ_ASSERT_UNREACHABLE("Invalid style composite operation?");
+ return dom::CompositeOperation::Replace;
+}
+
+inline TimingParams TimingParamsFromCSSParams(
+ float aDuration, float aDelay, float aIterationCount,
+ StyleAnimationDirection aDirection, StyleAnimationFillMode aFillMode) {
+ MOZ_ASSERT(aIterationCount >= 0.0 && !std::isnan(aIterationCount),
+ "aIterations should be nonnegative & finite, as ensured by "
+ "CSSParser");
+ return TimingParams{aDuration, aDelay, aIterationCount,
+ StyleToDom(aDirection), StyleToDom(aFillMode)};
+}
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_css_AnimationCommon_h) */