Files
UXP/dom/animation/KeyframeEffect.cpp
T
Moonchild fdeb68f0b8 Issue #1689 - Part 3: Add a preference for animation composite modes.
This feature should not be shipped until the various definitions of addition for
each additive property are properly specified and then implemented accordingly.

Unlike other patches in this series, compositing is not frequently used
internally so there is no need to enable this by default for chrome callers.
2021-01-21 14:31:22 +08:00

213 lines
6.7 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/Preferences.h"
#include "nsContentUtils.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 API is not enabled,
// then the default value 'Replace' will be used.
if (!Preferences::GetBool("dom.animations-api.compositing.enabled")) {
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