diff options
Diffstat (limited to 'dom/animation/KeyframeEffect.cpp')
-rw-r--r-- | dom/animation/KeyframeEffect.cpp | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp new file mode 100644 index 000000000..decbf6305 --- /dev/null +++ b/dom/animation/KeyframeEffect.cpp @@ -0,0 +1,211 @@ +/* -*- 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/. */ + +#include "mozilla/dom/KeyframeEffect.h" + +#include "mozilla/dom/KeyframeAnimationOptionsBinding.h" + // For UnrestrictedDoubleOrKeyframeAnimationOptions +#include "mozilla/dom/AnimationEffectTiming.h" +#include "mozilla/dom/KeyframeEffectBinding.h" +#include "mozilla/KeyframeUtils.h" +#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch +#include "nsIScriptError.h" + +namespace mozilla { +namespace dom { + +KeyframeEffect::KeyframeEffect(nsIDocument* aDocument, + const Maybe<OwningAnimationTarget>& aTarget, + const TimingParams& aTiming, + const KeyframeEffectParams& aOptions) + : KeyframeEffectReadOnly(aDocument, aTarget, + new AnimationEffectTiming(aDocument, aTiming, this), + aOptions) +{ +} + +JSObject* +KeyframeEffect::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) +{ + return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto); +} + +/* static */ already_AddRefed<KeyframeEffect> +KeyframeEffect::Constructor( + const GlobalObject& aGlobal, + const Nullable<ElementOrCSSPseudoElement>& aTarget, + JS::Handle<JSObject*> aKeyframes, + const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aKeyframes, + aOptions, aRv); +} + +/* static */ already_AddRefed<KeyframeEffect> +KeyframeEffect::Constructor(const GlobalObject& aGlobal, + KeyframeEffectReadOnly& aSource, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aSource, aRv); +} + +/* static */ already_AddRefed<KeyframeEffect> +KeyframeEffect::Constructor( + const GlobalObject& aGlobal, + const Nullable<ElementOrCSSPseudoElement>& aTarget, + JS::Handle<JSObject*> aKeyframes, + const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aKeyframes, + aOptions, aRv); +} + +void +KeyframeEffect::NotifySpecifiedTimingUpdated() +{ + // Use the same document for a pseudo element and its parent element. + // Use nullptr if we don't have mTarget, so disable the mutation batch. + nsAutoAnimationMutationBatch mb(mTarget ? mTarget->mElement->OwnerDoc() + : nullptr); + + if (mAnimation) { + mAnimation->NotifyEffectTimingUpdated(); + + if (mAnimation->IsRelevant()) { + nsNodeUtils::AnimationChanged(mAnimation); + } + + RequestRestyle(EffectCompositor::RestyleType::Layer); + } +} + +void +KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget) +{ + Maybe<OwningAnimationTarget> newTarget = ConvertTarget(aTarget); + if (mTarget == newTarget) { + // Assign the same target, skip it. + return; + } + + if (mTarget) { + UnregisterTarget(); + ResetIsRunningOnCompositor(); + + RequestRestyle(EffectCompositor::RestyleType::Layer); + + nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc()); + if (mAnimation) { + nsNodeUtils::AnimationRemoved(mAnimation); + } + } + + mTarget = newTarget; + + if (mTarget) { + UpdateTargetRegistration(); + RefPtr<nsStyleContext> styleContext = GetTargetStyleContext(); + if (styleContext) { + UpdateProperties(styleContext); + } else if (mEffectOptions.mSpacingMode == SpacingMode::paced) { + KeyframeUtils::ApplyDistributeSpacing(mKeyframes); + } + + MaybeUpdateFrameForCompositor(); + + RequestRestyle(EffectCompositor::RestyleType::Layer); + + nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc()); + if (mAnimation) { + nsNodeUtils::AnimationAdded(mAnimation); + } + } else if (mEffectOptions.mSpacingMode == SpacingMode::paced) { + // New target is null, so fall back to distribute spacing. + KeyframeUtils::ApplyDistributeSpacing(mKeyframes); + } +} + +void +KeyframeEffect::SetIterationComposite( + const IterationCompositeOperation& aIterationComposite) +{ + // Ignore iterationComposite if the Web Animations API is not enabled, + // then the default value 'Replace' will be used. + if (!AnimationUtils::IsCoreAPIEnabledForCaller()) { + return; + } + + if (mEffectOptions.mIterationComposite == aIterationComposite) { + return; + } + + if (mAnimation && mAnimation->IsRelevant()) { + nsNodeUtils::AnimationChanged(mAnimation); + } + + mEffectOptions.mIterationComposite = aIterationComposite; + RequestRestyle(EffectCompositor::RestyleType::Layer); +} + +void +KeyframeEffect::SetSpacing(JSContext* aCx, + const nsAString& aSpacing, + ErrorResult& aRv) +{ + SpacingMode spacingMode = SpacingMode::distribute; + nsCSSPropertyID pacedProperty = eCSSProperty_UNKNOWN; + nsAutoString invalidPacedProperty; + KeyframeEffectParams::ParseSpacing(aSpacing, + spacingMode, + pacedProperty, + invalidPacedProperty, + aRv); + if (aRv.Failed()) { + return; + } + + if (!invalidPacedProperty.IsEmpty()) { + const char16_t* params[] = { invalidPacedProperty.get() }; + nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aCx); + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Animation"), + doc, + nsContentUtils::eDOM_PROPERTIES, + "UnanimatablePacedProperty", + params, ArrayLength(params)); + } + + if (mEffectOptions.mSpacingMode == spacingMode && + mEffectOptions.mPacedProperty == pacedProperty) { + return; + } + + mEffectOptions.mSpacingMode = spacingMode; + mEffectOptions.mPacedProperty = pacedProperty; + + // Apply spacing. We apply distribute here. If the new spacing is paced, + // UpdateProperties() will apply it. + if (mEffectOptions.mSpacingMode == SpacingMode::distribute) { + KeyframeUtils::ApplyDistributeSpacing(mKeyframes); + } + + if (mAnimation && mAnimation->IsRelevant()) { + nsNodeUtils::AnimationChanged(mAnimation); + } + + if (mTarget) { + RefPtr<nsStyleContext> styleContext = GetTargetStyleContext(); + if (styleContext) { + UpdateProperties(styleContext); + } + } +} + +} // namespace dom +} // namespace mozilla |