Bug 1846516 - [css-properties-values-api] Introduce AnimatedPropertyID/AnimatedPropertIDSet. r=firefox-animation-reviewers,firefox-style-system-reviewers,layout-reviewers,emilio,hiro

This will make possible to animate custom properties on Gecko side. For now, the
animation code keeps only dealing with nsCSSPropertyID, so behavior is unchanged.

Co-authored-by: Frederic Wang <fred.wang@free.fr>

Depends on D190816

Differential Revision: https://phabricator.services.mozilla.com/D191059
This commit is contained in:
Zach Hoffman
2023-12-18 09:24:08 +00:00
parent ab51c31bba
commit 1e7649831a
23 changed files with 503 additions and 176 deletions

View File

@@ -868,10 +868,11 @@ void Animation::CommitStyles(ErrorResult& aRv) {
// Set the animated styles
bool changed = false;
nsCSSPropertyIDSet properties = keyframeEffect->GetPropertySet();
for (nsCSSPropertyID property : properties) {
const AnimatedPropertyIDSet& properties = keyframeEffect->GetPropertySet();
for (const AnimatedPropertyID& property : properties) {
// TODO(zrhoffman, bug 1846516): Handle custom properties
RefPtr<StyleAnimationValue> computedValue =
Servo_AnimationValueMap_GetValue(animationValues.get(), property)
Servo_AnimationValueMap_GetValue(animationValues.get(), property.mID)
.Consume();
if (computedValue) {
changed |= Servo_DeclarationBlock_SetPropertyToAnimationValue(

View File

@@ -92,7 +92,7 @@ void AnimationEventInfo::MaybeAddMarker() const {
if (dom::KeyframeEffect* keyFrameEffect = effect->AsKeyframeEffect()) {
keyFrameEffect->GetTarget()->Describe(target, true);
for (const AnimationProperty& property : keyFrameEffect->Properties()) {
propertySet.AddProperty(property.mProperty);
propertySet.AddProperty(property.mProperty.mID);
}
}
}
@@ -136,7 +136,7 @@ void AnimationEventInfo::MaybeAddMarker() const {
mAnimation->GetOwner()
? MarkerInnerWindowId(mAnimation->GetOwner()->WindowID())
: MarkerInnerWindowId::NoId()),
CSSTransitionMarker, NS_ConvertUTF16toUTF8(target), data.mPropertyId,
CSSTransitionMarker, NS_ConvertUTF16toUTF8(target), data.mProperty.mID,
message == eTransitionCancel);
}

View File

@@ -124,7 +124,7 @@ struct AnimationEventInfo {
struct CssTransitionData : public CssAnimationOrTransitionData {
// For transition events only.
const nsCSSPropertyID mPropertyId = eCSSProperty_UNKNOWN;
const AnimatedPropertyID mProperty;
};
struct WebAnimationData {
@@ -150,7 +150,7 @@ struct AnimationEventInfo {
void MaybeAddMarker() const;
// For CSS animation events
AnimationEventInfo(nsAtom* aAnimationName,
AnimationEventInfo(RefPtr<nsAtom> aAnimationName,
const NonOwningAnimationTarget& aTarget,
EventMessage aMessage, double aElapsedTime,
const TimeStamp& aScheduledEventTimeStamp,
@@ -160,14 +160,14 @@ struct AnimationEventInfo {
mData(CssAnimationData{
{OwningAnimationTarget(aTarget.mElement, aTarget.mPseudoType),
aMessage, aElapsedTime},
aAnimationName}) {
std::move(aAnimationName)}) {
if (profiler_thread_is_being_profiled_for_markers()) {
MaybeAddMarker();
}
}
// For CSS transition events
AnimationEventInfo(nsCSSPropertyID aProperty,
AnimationEventInfo(const AnimatedPropertyID& aProperty,
const NonOwningAnimationTarget& aTarget,
EventMessage aMessage, double aElapsedTime,
const TimeStamp& aScheduledEventTimeStamp,
@@ -242,8 +242,7 @@ struct AnimationEventInfo {
}
InternalTransitionEvent event(true, data.mMessage);
CopyUTF8toUTF16(nsCSSProps::GetStringValue(data.mPropertyId),
event.mPropertyName);
data.mProperty.ToString(event.mPropertyName);
event.mElapsedTime = data.mElapsedTime;
event.mPseudoElement =
nsCSSPseudoElements::PseudoTypeAsString(data.mTarget.mPseudoType);

View File

@@ -21,10 +21,9 @@ JSObject* CSSTransition::WrapObject(JSContext* aCx,
}
void CSSTransition::GetTransitionProperty(nsString& aRetVal) const {
MOZ_ASSERT(eCSSProperty_UNKNOWN != mTransitionProperty,
MOZ_ASSERT(mTransitionProperty.IsValid(),
"Transition Property should be initialized");
aRetVal =
NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(mTransitionProperty));
mTransitionProperty.ToString(aRetVal);
}
AnimationPlayState CSSTransition::PlayStateFromJS() const {
@@ -194,8 +193,8 @@ void CSSTransition::Tick(TickState& aState) {
QueueEvents();
}
nsCSSPropertyID CSSTransition::TransitionProperty() const {
MOZ_ASSERT(eCSSProperty_UNKNOWN != mTransitionProperty,
const AnimatedPropertyID& CSSTransition::TransitionProperty() const {
MOZ_ASSERT(mTransitionProperty.IsValid(),
"Transition property should be initialized");
return mTransitionProperty;
}
@@ -231,8 +230,10 @@ bool CSSTransition::HasLowerCompositeOrderThan(
}
// 3. (Same transition generation): Sort by transition property
return nsCSSProps::GetStringValue(TransitionProperty()) <
nsCSSProps::GetStringValue(aOther.TransitionProperty());
nsAutoString name, otherName;
GetTransitionProperty(name);
aOther.GetTransitionProperty(otherName);
return name < otherName;
}
/* static */

View File

@@ -9,6 +9,7 @@
#include "mozilla/ComputedTiming.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/AnimatedPropertyID.h"
#include "mozilla/StyleAnimationValue.h"
#include "AnimationCommon.h"
@@ -19,11 +20,12 @@ namespace dom {
class CSSTransition final : public Animation {
public:
explicit CSSTransition(nsIGlobalObject* aGlobal)
explicit CSSTransition(nsIGlobalObject* aGlobal,
const AnimatedPropertyID& aProperty)
: Animation(aGlobal),
mPreviousTransitionPhase(TransitionPhase::Idle),
mNeedsNewAnimationIndexWhenRun(false),
mTransitionProperty(eCSSProperty_UNKNOWN) {}
mTransitionProperty(aProperty) {}
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@@ -72,7 +74,7 @@ class CSSTransition final : public Animation {
void Tick(TickState&) override;
nsCSSPropertyID TransitionProperty() const;
const AnimatedPropertyID& TransitionProperty() const;
AnimationValue ToValue() const;
bool HasLowerCompositeOrderThan(const CSSTransition& aOther) const;
@@ -197,7 +199,7 @@ class CSSTransition final : public Animation {
// information in order to determine if there is an existing transition
// for a given style change. We can't store that information on the
// effect however since it can be replaced using the Web Animations API.
nsCSSPropertyID mTransitionProperty;
AnimatedPropertyID mTransitionProperty;
AnimationValue mTransitionToValue;
// This is the start value to be used for a check for whether a

View File

@@ -608,11 +608,11 @@ nsCSSPropertyIDSet EffectCompositor::GetOverriddenProperties(
nsCSSPropertyIDSet propertiesToTrackAsSet;
for (KeyframeEffect* effect : aEffectSet) {
for (const AnimationProperty& property : effect->Properties()) {
if (nsCSSProps::PropHasFlags(property.mProperty,
if (nsCSSProps::PropHasFlags(property.mProperty.mID,
CSSPropFlags::CanAnimateOnCompositor) &&
!propertiesToTrackAsSet.HasProperty(property.mProperty)) {
propertiesToTrackAsSet.AddProperty(property.mProperty);
propertiesToTrack.AppendElement(property.mProperty);
!propertiesToTrackAsSet.HasProperty(property.mProperty.mID)) {
propertiesToTrackAsSet.AddProperty(property.mProperty.mID);
propertiesToTrack.AppendElement(property.mProperty.mID);
}
}
// Skip iterating over the rest of the effects if we've already
@@ -684,16 +684,16 @@ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
CascadeLevel cascadeLevel = effect->GetAnimation()->CascadeLevel();
for (const AnimationProperty& prop : effect->Properties()) {
if (overriddenProperties.HasProperty(prop.mProperty)) {
propertiesWithImportantRules.AddProperty(prop.mProperty);
if (overriddenProperties.HasProperty(prop.mProperty.mID)) {
propertiesWithImportantRules.AddProperty(prop.mProperty.mID);
}
switch (cascadeLevel) {
case EffectCompositor::CascadeLevel::Animations:
propertiesForAnimationsLevel.AddProperty(prop.mProperty);
propertiesForAnimationsLevel.AddProperty(prop.mProperty.mID);
break;
case EffectCompositor::CascadeLevel::Transitions:
propertiesForTransitionsLevel.AddProperty(prop.mProperty);
propertiesForTransitionsLevel.AddProperty(prop.mProperty.mID);
break;
}
}
@@ -912,7 +912,7 @@ static void ReduceEffectSet(EffectSet& aEffectSet) {
}
sortedEffectList.Sort(EffectCompositeOrderComparator());
nsCSSPropertyIDSet setProperties;
AnimatedPropertyIDSet setProperties;
// Iterate in reverse
for (auto iter = sortedEffectList.rbegin(); iter != sortedEffectList.rend();
@@ -925,7 +925,7 @@ static void ReduceEffectSet(EffectSet& aEffectSet) {
effect.GetPropertySet().IsSubsetOf(setProperties)) {
animation.Remove();
} else if (animation.IsReplaceable()) {
setProperties |= effect.GetPropertySet();
setProperties.AddProperties(effect.GetPropertySet());
}
}
}

View File

@@ -288,7 +288,7 @@ void KeyframeEffect::ReplaceTransitionStartValue(AnimationValue&& aStartValue) {
// Check that the value we are about to substitute in is actually for the
// same property.
if (Servo_AnimationValue_GetPropertyId(aStartValue.mServo) !=
mProperties[0].mProperty) {
mProperties[0].mProperty.mID) {
return;
}
@@ -309,14 +309,14 @@ const AnimationProperty* KeyframeEffect::GetEffectiveAnimationOfProperty(
mTarget.mPseudoType));
for (const AnimationProperty& property : mProperties) {
if (aProperty != property.mProperty) {
if (aProperty != property.mProperty.mID) {
continue;
}
const AnimationProperty* result = nullptr;
// Only include the property if it is not overridden by !important rules in
// the transitions level.
if (IsEffectiveProperty(aEffects, property.mProperty)) {
if (IsEffectiveProperty(aEffects, property.mProperty.mID)) {
result = &property;
}
return result;
@@ -327,8 +327,8 @@ const AnimationProperty* KeyframeEffect::GetEffectiveAnimationOfProperty(
bool KeyframeEffect::HasEffectiveAnimationOfPropertySet(
const nsCSSPropertyIDSet& aPropertySet, const EffectSet& aEffectSet) const {
for (const AnimationProperty& property : mProperties) {
if (aPropertySet.HasProperty(property.mProperty) &&
IsEffectiveProperty(aEffectSet, property.mProperty)) {
if (aPropertySet.HasProperty(property.mProperty.mID) &&
IsEffectiveProperty(aEffectSet, property.mProperty.mID)) {
return true;
}
}
@@ -355,26 +355,27 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
AnimationPerformanceWarning::Type dummyWarning;
for (const AnimationProperty& property : mProperties) {
if (!compositorAnimatables.HasProperty(property.mProperty)) {
if (!compositorAnimatables.HasProperty(property.mProperty.mID)) {
continue;
}
// Transform-like properties are combined together on the compositor so we
// need to evaluate them as a group. We build up a separate set here then
// evaluate it as a separate step below.
if (transformLikeProperties.HasProperty(property.mProperty)) {
transformSet.AddProperty(property.mProperty);
if (transformLikeProperties.HasProperty(property.mProperty.mID)) {
transformSet.AddProperty(property.mProperty.mID);
continue;
}
KeyframeEffect::MatchForCompositor matchResult = IsMatchForCompositor(
nsCSSPropertyIDSet{property.mProperty}, aFrame, aEffects, dummyWarning);
KeyframeEffect::MatchForCompositor matchResult =
IsMatchForCompositor(nsCSSPropertyIDSet{property.mProperty.mID}, aFrame,
aEffects, dummyWarning);
if (matchResult ==
KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty ||
matchResult == KeyframeEffect::MatchForCompositor::No) {
continue;
}
properties.AddProperty(property.mProperty);
properties.AddProperty(property.mProperty.mID);
}
if (!transformSet.IsEmpty()) {
@@ -389,8 +390,8 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
return properties;
}
nsCSSPropertyIDSet KeyframeEffect::GetPropertySet() const {
nsCSSPropertyIDSet result;
AnimatedPropertyIDSet KeyframeEffect::GetPropertySet() const {
AnimatedPropertyIDSet result;
for (const AnimationProperty& property : mProperties) {
result.AddProperty(property.mProperty);
@@ -455,7 +456,7 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle,
for (const AnimationProperty& property : mProperties) {
if (property.mIsRunningOnCompositor) {
runningOnCompositorProperties.AddProperty(property.mProperty);
runningOnCompositorProperties.AddProperty(property.mProperty.mID);
}
}
@@ -465,7 +466,7 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle,
mCumulativeChanges = {};
for (AnimationProperty& property : mProperties) {
property.mIsRunningOnCompositor =
runningOnCompositorProperties.HasProperty(property.mProperty);
runningOnCompositorProperties.HasProperty(property.mProperty.mID);
CalculateCumulativeChangesForProperty(property);
}
@@ -551,7 +552,7 @@ void KeyframeEffect::EnsureBaseStyle(
// delay on the compositor.
return aTimeline && aTimeline->IsScrollTimeline() &&
nsCSSPropertyIDSet::CompositorAnimatables().HasProperty(
aProperty.mProperty) &&
aProperty.mProperty.mID) &&
(timing.Delay() > zeroDuration ||
timing.EndDelay() > zeroDuration);
};
@@ -586,9 +587,9 @@ void KeyframeEffect::EnsureBaseStyle(
}
RefPtr<StyleAnimationValue> baseValue =
Servo_ComputedValues_ExtractAnimationValue(aBaseComputedStyle,
aProperty.mProperty)
aProperty.mProperty.mID)
.Consume();
mBaseValues.InsertOrUpdate(aProperty.mProperty, std::move(baseValue));
mBaseValues.InsertOrUpdate(aProperty.mProperty.mID, std::move(baseValue));
}
void KeyframeEffect::WillComposeStyle() {
@@ -603,9 +604,10 @@ void KeyframeEffect::ComposeStyleRule(StyleAnimationValueMap& aAnimationValues,
const ComputedTiming& aComputedTiming) {
auto* opaqueTable =
reinterpret_cast<RawServoAnimationValueTable*>(&mBaseValues);
Servo_AnimationCompose(&aAnimationValues, opaqueTable, aProperty.mProperty,
&aSegment, &aProperty.mSegments.LastElement(),
&aComputedTiming, mEffectOptions.mIterationComposite);
Servo_AnimationCompose(&aAnimationValues, opaqueTable,
aProperty.mProperty.mID, &aSegment,
&aProperty.mSegments.LastElement(), &aComputedTiming,
mEffectOptions.mIterationComposite);
}
void KeyframeEffect::ComposeStyle(StyleAnimationValueMap& aComposeResult,
@@ -626,7 +628,7 @@ void KeyframeEffect::ComposeStyle(StyleAnimationValueMap& aComposeResult,
MOZ_ASSERT(prop.mSegments[prop.mSegments.Length() - 1].mToKey == 1.0,
"incorrect last to key");
if (aPropertiesToSkip.HasProperty(prop.mProperty)) {
if (aPropertiesToSkip.HasProperty(prop.mProperty.mID)) {
continue;
}
@@ -690,7 +692,7 @@ void KeyframeEffect::SetIsRunningOnCompositor(nsCSSPropertyID aProperty,
"Property being animated on compositor is a recognized "
"compositor-animatable property");
for (AnimationProperty& property : mProperties) {
if (property.mProperty == aProperty) {
if (property.mProperty.mID == aProperty) {
property.mIsRunningOnCompositor = aIsRunning;
// We currently only set a performance warning message when animations
// cannot be run on the compositor, so if this animation is running
@@ -708,8 +710,8 @@ void KeyframeEffect::SetIsRunningOnCompositor(nsCSSPropertyID aProperty,
void KeyframeEffect::SetIsRunningOnCompositor(
const nsCSSPropertyIDSet& aPropertySet, bool aIsRunning) {
for (AnimationProperty& property : mProperties) {
if (aPropertySet.HasProperty(property.mProperty)) {
MOZ_ASSERT(nsCSSProps::PropHasFlags(property.mProperty,
if (aPropertySet.HasProperty(property.mProperty.mID)) {
MOZ_ASSERT(nsCSSProps::PropHasFlags(property.mProperty.mID,
CSSPropFlags::CanAnimateOnCompositor),
"Property being animated on compositor is a recognized "
"compositor-animatable property");
@@ -1036,7 +1038,8 @@ void DumpAnimationProperties(
const StylePerDocumentStyleData* aRawData,
nsTArray<AnimationProperty>& aAnimationProperties) {
for (auto& p : aAnimationProperties) {
printf("%s\n", nsCString(nsCSSProps::GetStringValue(p.mProperty)).get());
printf("%s\n",
nsCString(nsCSSProps::GetStringValue(p.mProperty.mID)).get());
for (auto& s : p.mSegments) {
nsAutoCString fromValue, toValue;
s.mFromValue.SerializeSpecifiedValue(p.mProperty, aRawData, fromValue);
@@ -1120,7 +1123,7 @@ void KeyframeEffect::SetPseudoElement(const nsAString& aPseudoElement,
}
static void CreatePropertyValue(
nsCSSPropertyID aProperty, float aOffset,
const AnimatedPropertyID& aProperty, float aOffset,
const Maybe<StyleComputedTimingFunction>& aTimingFunction,
const AnimationValue& aValue, dom::CompositeOperation aComposite,
const StylePerDocumentStyleData* aRawData,
@@ -1150,8 +1153,7 @@ void KeyframeEffect::GetProperties(
for (const AnimationProperty& property : mProperties) {
AnimationPropertyDetails propertyDetails;
propertyDetails.mProperty =
NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(property.mProperty));
property.mProperty.ToString(propertyDetails.mProperty);
propertyDetails.mRunningOnCompositor = property.mIsRunningOnCompositor;
nsAutoString localizedString;
@@ -1475,7 +1477,7 @@ bool KeyframeEffect::CanThrottle() const {
}
MOZ_ASSERT(nsCSSPropertyIDSet::CompositorAnimatables().HasProperty(
property.mProperty),
property.mProperty.mID),
"The property should be able to run on the compositor");
if (!effectSet) {
effectSet = EffectSet::Get(mTarget.mElement, mTarget.mPseudoType);
@@ -1483,12 +1485,14 @@ bool KeyframeEffect::CanThrottle() const {
"CanThrottle should be called on an effect "
"associated with a target element");
}
MOZ_ASSERT(HasEffectiveAnimationOfProperty(property.mProperty, *effectSet),
"There should be an effective animation of the property while "
"it is marked as being run on the compositor");
MOZ_ASSERT(
HasEffectiveAnimationOfProperty(property.mProperty.mID, *effectSet),
"There should be an effective animation of the property while "
"it is marked as being run on the compositor");
DisplayItemType displayItemType =
LayerAnimationInfo::GetDisplayItemTypeForProperty(property.mProperty);
LayerAnimationInfo::GetDisplayItemTypeForProperty(
property.mProperty.mID);
// Note that AnimationInfo::GetGenarationFromFrame() is supposed to work
// with the primary frame instead of the style frame.
@@ -1714,14 +1718,14 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
// !important rules.
if (effectSet &&
effectSet->PropertiesWithImportantRules().HasProperty(
property.mProperty) &&
property.mProperty.mID) &&
effectSet->PropertiesForAnimationsLevel().HasProperty(
property.mProperty)) {
property.mProperty.mID)) {
continue;
}
// Check for geometric properties
if (enableMainthreadSynchronizationWithGeometricAnimations &&
IsGeometricProperty(property.mProperty)) {
IsGeometricProperty(property.mProperty.mID)) {
aPerformanceWarning =
AnimationPerformanceWarning::Type::TransformWithGeometricProperties;
return true;
@@ -1729,7 +1733,7 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
// Check for unsupported transform animations
if (LayerAnimationInfo::GetCSSPropertiesFor(DisplayItemType::TYPE_TRANSFORM)
.HasProperty(property.mProperty)) {
.HasProperty(property.mProperty.mID)) {
if (!CanAnimateTransformOnCompositor(aFrame, aPerformanceWarning)) {
return true;
}
@@ -1737,7 +1741,7 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
// If there are any offset-path animations whose animation values are url(),
// we have to sync with the main thread when resolving it.
if (property.mProperty == eCSSProperty_offset_path) {
if (property.mProperty.mID == eCSSProperty_offset_path) {
for (const auto& seg : property.mSegments) {
if (seg.mFromValue.IsOffsetPathUrl() ||
seg.mToValue.IsOffsetPathUrl()) {
@@ -1752,7 +1756,7 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
bool KeyframeEffect::HasGeometricProperties() const {
for (const AnimationProperty& property : mProperties) {
if (IsGeometricProperty(property.mProperty)) {
if (IsGeometricProperty(property.mProperty.mID)) {
return true;
}
}
@@ -1765,11 +1769,11 @@ void KeyframeEffect::SetPerformanceWarning(
const AnimationPerformanceWarning& aWarning) {
nsCSSPropertyIDSet curr = aPropertySet;
for (AnimationProperty& property : mProperties) {
if (!curr.HasProperty(property.mProperty)) {
if (!curr.HasProperty(property.mProperty.mID)) {
continue;
}
property.SetPerformanceWarning(aWarning, mTarget.mElement);
curr.RemoveProperty(property.mProperty);
curr.RemoveProperty(property.mProperty.mID);
if (curr.IsEmpty()) {
return;
}
@@ -1778,19 +1782,24 @@ void KeyframeEffect::SetPerformanceWarning(
void KeyframeEffect::CalculateCumulativeChangesForProperty(
const AnimationProperty& aProperty) {
if (aProperty.mProperty.IsCustom()) {
// Custom properties don't affect rendering on their own.
return;
}
constexpr auto kInterestingFlags =
CSSPropFlags::AffectsLayout | CSSPropFlags::AffectsOverflow;
if (aProperty.mProperty == eCSSProperty_opacity) {
if (aProperty.mProperty.mID == eCSSProperty_opacity) {
mCumulativeChanges.mOpacity = true;
return; // We know opacity is visual-only.
}
if (aProperty.mProperty == eCSSProperty_visibility) {
if (aProperty.mProperty.mID == eCSSProperty_visibility) {
mCumulativeChanges.mVisibility = true;
return; // We know visibility is visual-only.
}
if (aProperty.mProperty == eCSSProperty_background_color) {
if (aProperty.mProperty.mID == eCSSProperty_background_color) {
if (!mCumulativeChanges.mHasBackgroundColorCurrentColor) {
mCumulativeChanges.mHasBackgroundColorCurrentColor =
HasCurrentColor(aProperty.mSegments);
@@ -1798,7 +1807,7 @@ void KeyframeEffect::CalculateCumulativeChangesForProperty(
return; // We know background-color is visual-only.
}
auto flags = nsCSSProps::PropFlags(aProperty.mProperty);
auto flags = nsCSSProps::PropFlags(aProperty.mProperty.mID);
if (!(flags & kInterestingFlags)) {
return; // Property is visual-only.
}
@@ -1906,9 +1915,9 @@ bool KeyframeEffect::ContainsAnimatedScale(const nsIFrame* aFrame) const {
}
for (const AnimationProperty& prop : mProperties) {
if (prop.mProperty != eCSSProperty_transform &&
prop.mProperty != eCSSProperty_scale &&
prop.mProperty != eCSSProperty_rotate) {
if (prop.mProperty.mID != eCSSProperty_transform &&
prop.mProperty.mID != eCSSProperty_scale &&
prop.mProperty.mID != eCSSProperty_rotate) {
continue;
}

View File

@@ -15,6 +15,8 @@
#include "nsRefPtrHashtable.h"
#include "nsTArray.h"
#include "nsWrapperCache.h"
#include "mozilla/AnimatedPropertyID.h"
#include "mozilla/AnimatedPropertyIDSet.h"
#include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/AnimationPropertySegment.h"
#include "mozilla/AnimationTarget.h"
@@ -55,7 +57,7 @@ struct AnimationPropertyDetails;
} // namespace dom
struct AnimationProperty {
nsCSSPropertyID mProperty = eCSSProperty_UNKNOWN;
AnimatedPropertyID mProperty;
// If true, the propery is currently being animated on the compositor.
//
@@ -74,7 +76,7 @@ struct AnimationProperty {
// The copy constructor/assignment doesn't copy mIsRunningOnCompositor and
// mPerformanceWarning.
AnimationProperty() = default;
AnimationProperty() : mProperty(eCSSProperty_UNKNOWN){};
AnimationProperty(const AnimationProperty& aOther)
: mProperty(aOther.mProperty), mSegments(aOther.mSegments.Clone()) {}
AnimationProperty& operator=(const AnimationProperty& aOther) {
@@ -199,7 +201,7 @@ class KeyframeEffect : public AnimationEffect {
// Returns the set of properties affected by this effect regardless of
// whether any of these properties is overridden by an !important rule.
nsCSSPropertyIDSet GetPropertySet() const;
AnimatedPropertyIDSet GetPropertySet() const;
// Returns true if the effect includes a property in |aPropertySet| regardless
// of whether any property in the set is overridden by an !important rule.
@@ -325,13 +327,13 @@ class KeyframeEffect : public AnimationEffect {
// |aFrame| is used for calculation of scale values.
bool ContainsAnimatedScale(const nsIFrame* aFrame) const;
AnimationValue BaseStyle(nsCSSPropertyID aProperty) const {
AnimationValue BaseStyle(const AnimatedPropertyID& aProperty) const {
AnimationValue result;
bool hasProperty = false;
// We cannot use getters_AddRefs on StyleAnimationValue because it is
// an incomplete type, so Get() doesn't work. Instead, use GetWeak, and
// then assign the raw pointer to a RefPtr.
result.mServo = mBaseValues.GetWeak(aProperty, &hasProperty);
result.mServo = mBaseValues.GetWeak(aProperty.mID, &hasProperty);
MOZ_ASSERT(hasProperty || result.IsNull());
return result;
}

View File

@@ -12,6 +12,7 @@
#include "jsapi.h" // For most JSAPI
#include "js/ForOfIterator.h" // For JS::ForOfIterator
#include "js/PropertyAndElement.h" // JS_Enumerate, JS_GetProperty, JS_GetPropertyById
#include "mozilla/AnimatedPropertyID.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/RangedArray.h"
@@ -34,6 +35,7 @@
#include "nsContentUtils.h" // For GetContextForContent
#include "nsIScriptError.h"
#include "nsPresContextInlines.h"
#include "nsString.h"
#include "nsTArray.h"
using mozilla::dom::Nullable;
@@ -59,7 +61,9 @@ enum class ListAllowance { eDisallow, eAllow };
* mValues.
*/
struct PropertyValuesPair {
nsCSSPropertyID mProperty;
PropertyValuesPair() : mProperty(eCSSProperty_UNKNOWN) {}
AnimatedPropertyID mProperty;
nsTArray<nsCString> mValues;
};
@@ -68,7 +72,9 @@ struct PropertyValuesPair {
* BaseKeyframe or BasePropertyIndexedKeyframe object.
*/
struct AdditionalProperty {
nsCSSPropertyID mProperty;
AdditionalProperty() : mProperty(eCSSProperty_UNKNOWN), mJsidIndex() {}
AnimatedPropertyID mProperty;
size_t mJsidIndex; // Index into |ids| in GetPropertyValuesPairs.
struct PropertyComparator {
@@ -78,8 +84,24 @@ struct AdditionalProperty {
}
bool LessThan(const AdditionalProperty& aLhs,
const AdditionalProperty& aRhs) const {
return nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) <
nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty);
bool customLhs =
aLhs.mProperty.mID == nsCSSPropertyID::eCSSPropertyExtra_variable;
bool customRhs =
aRhs.mProperty.mID == nsCSSPropertyID::eCSSPropertyExtra_variable;
if (!customLhs && !customRhs) {
// Compare by IDL names.
return nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty.mID) <
nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty.mID);
}
if (customLhs && customRhs) {
// Compare by custom property names.
return nsDependentAtomString(aLhs.mProperty.mCustomName) <
nsDependentAtomString(aRhs.mProperty.mCustomName);
}
// Custom properties should be ordered before normal CSS properties, as if
// the custom property name starts with `--`.
// <https://drafts.csswg.org/web-animations-1/#idl-attribute-name-to-animation-property-name>
return !customLhs && customRhs;
}
};
};
@@ -92,7 +114,10 @@ struct AdditionalProperty {
* to gather data for each individual segment.
*/
struct KeyframeValueEntry {
nsCSSPropertyID mProperty;
KeyframeValueEntry()
: mProperty(eCSSProperty_UNKNOWN), mOffset(), mComposite() {}
AnimatedPropertyID mProperty;
AnimationValue mValue;
float mOffset;
@@ -106,11 +131,28 @@ struct KeyframeValueEntry {
}
static bool LessThan(const KeyframeValueEntry& aLhs,
const KeyframeValueEntry& aRhs) {
// First, sort by property IDL name.
int32_t order = nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) -
nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty);
if (order != 0) {
return order < 0;
// First, sort by property name.
bool customLhs =
aLhs.mProperty.mID == nsCSSPropertyID::eCSSPropertyExtra_variable;
bool customRhs =
aRhs.mProperty.mID == nsCSSPropertyID::eCSSPropertyExtra_variable;
if (!customLhs && !customRhs) {
// Compare by IDL names.
int32_t order =
nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty.mID) -
nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty.mID);
if (order != 0) {
return order < 0;
}
} else if (customLhs && customRhs) {
// Compare by custom property names.
int order = Compare(nsDependentAtomString(aLhs.mProperty.mCustomName),
nsDependentAtomString(aRhs.mProperty.mCustomName));
if (order != 0) {
return order < 0;
}
} else {
return !customLhs && customRhs;
}
// Then, by offset.
@@ -159,7 +201,7 @@ static bool AppendValueAsString(JSContext* aCx, nsTArray<nsCString>& aValues,
JS::Handle<JS::Value> aValue);
static Maybe<PropertyValuePair> MakePropertyValuePair(
nsCSSPropertyID aProperty, const nsACString& aStringValue,
const AnimatedPropertyID& aProperty, const nsACString& aStringValue,
dom::Document* aDocument);
static bool HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
@@ -292,7 +334,7 @@ nsTArray<AnimationProperty> KeyframeUtils::GetAnimationPropertiesFromKeyframes(
"Invalid computed offset");
KeyframeValueEntry* entry = entries.AppendElement();
entry->mOffset = frame.mComputedOffset;
entry->mProperty = value.mProperty;
entry->mProperty.mID = value.mProperty;
entry->mValue = value.mValue;
entry->mTimingFunction = frame.mTimingFunction;
// The following assumes that CompositeOperation is a strict subset of
@@ -309,14 +351,18 @@ nsTArray<AnimationProperty> KeyframeUtils::GetAnimationPropertiesFromKeyframes(
}
/* static */
bool KeyframeUtils::IsAnimatableProperty(nsCSSPropertyID aProperty) {
bool KeyframeUtils::IsAnimatableProperty(const AnimatedPropertyID& aProperty) {
// Regardless of the backend type, treat the 'display' property as not
// animatable. (Servo will report it as being animatable, since it is
// in fact animatable by SMIL.)
if (aProperty == eCSSProperty_display) {
if (aProperty.mID == eCSSProperty_display) {
return false;
}
return Servo_Property_IsAnimatable(aProperty);
// TODO(bug 1846516): handle custom property.
if (!aProperty.IsCustom()) {
return false;
}
return Servo_Property_IsAnimatable(aProperty.mID);
}
// ------------------------------------------------------------------
@@ -461,7 +507,7 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
// includes a chrome-only member that can be set to indicate that
// ComputeValues should fail for shorthand property values on that
// keyframe.
if (nsCSSProps::IsShorthand(pair.mProperty) &&
if (nsCSSProps::IsShorthand(pair.mProperty.mID) &&
keyframeDict.mSimulateComputeValuesFailure) {
MarkAsComputeValuesFailureKey(keyframe->mPropertyValues.LastElement());
}
@@ -517,20 +563,32 @@ static bool GetPropertyValuesPairs(JSContext* aCx,
// This means if the attribute is the string "cssOffset"/"cssFloat", we use
// CSS "offset"/"float" property.
// https://drafts.csswg.org/web-animations/#property-name-conversion
nsCSSPropertyID property = nsCSSPropertyID::eCSSProperty_UNKNOWN;
if (propName.EqualsLiteral("cssOffset")) {
property = nsCSSPropertyID::eCSSProperty_offset;
nsCSSPropertyID propertyID = nsCSSPropertyID::eCSSProperty_UNKNOWN;
if (nsCSSProps::IsCustomPropertyName(propName)) {
propertyID = eCSSPropertyExtra_variable;
} else if (propName.EqualsLiteral("cssOffset")) {
propertyID = nsCSSPropertyID::eCSSProperty_offset;
} else if (propName.EqualsLiteral("cssFloat")) {
property = nsCSSPropertyID::eCSSProperty_float;
propertyID = nsCSSPropertyID::eCSSProperty_float;
} else if (!propName.EqualsLiteral("offset") &&
!propName.EqualsLiteral("float")) {
property = nsCSSProps::LookupPropertyByIDLName(
propertyID = nsCSSProps::LookupPropertyByIDLName(
propName, CSSEnabledState::ForAllContent);
}
if (KeyframeUtils::IsAnimatableProperty(property)) {
AnimatedPropertyID* property;
if (propertyID == eCSSPropertyExtra_variable) {
// TODO(zrhoffman, bug 1811897) Add test coverage for removing the `--`
// prefix here.
property = new AnimatedPropertyID(
NS_Atomize(Substring(propName, 2, propName.Length() - 2)));
} else {
property = new AnimatedPropertyID(propertyID);
}
if (KeyframeUtils::IsAnimatableProperty(*property)) {
AdditionalProperty* p = properties.AppendElement();
p->mProperty = property;
p->mProperty = *property;
p->mJsidIndex = i;
}
}
@@ -611,12 +669,11 @@ static bool AppendValueAsString(JSContext* aCx, nsTArray<nsCString>& aValues,
}
static void ReportInvalidPropertyValueToConsole(
nsCSSPropertyID aProperty, const nsACString& aInvalidPropertyValue,
dom::Document* aDoc) {
const AnimatedPropertyID& aProperty,
const nsACString& aInvalidPropertyValue, dom::Document* aDoc) {
AutoTArray<nsString, 2> params;
params.AppendElement(NS_ConvertUTF8toUTF16(aInvalidPropertyValue));
CopyASCIItoUTF16(nsCSSProps::GetStringValue(aProperty),
*params.AppendElement());
aProperty.ToString(*params.AppendElement());
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Animation"_ns,
aDoc, nsContentUtils::eDOM_PROPERTIES,
"InvalidKeyframePropertyValue", params);
@@ -633,7 +690,7 @@ static void ReportInvalidPropertyValueToConsole(
* an invalid property value.
*/
static Maybe<PropertyValuePair> MakePropertyValuePair(
nsCSSPropertyID aProperty, const nsACString& aStringValue,
const AnimatedPropertyID& aProperty, const nsACString& aStringValue,
dom::Document* aDocument) {
MOZ_ASSERT(aDocument);
Maybe<PropertyValuePair> result;
@@ -641,11 +698,11 @@ static Maybe<PropertyValuePair> MakePropertyValuePair(
ServoCSSParser::ParsingEnvironment env =
ServoCSSParser::GetParsingEnvironment(aDocument);
RefPtr<StyleLockedDeclarationBlock> servoDeclarationBlock =
ServoCSSParser::ParseProperty(aProperty, aStringValue, env,
ServoCSSParser::ParseProperty(aProperty.mID, aStringValue, env,
StyleParsingMode::DEFAULT);
if (servoDeclarationBlock) {
result.emplace(aProperty, std::move(servoDeclarationBlock));
result.emplace(aProperty.mID, std::move(servoDeclarationBlock));
} else {
ReportInvalidPropertyValueToConsole(aProperty, aStringValue, aDocument);
}
@@ -814,7 +871,7 @@ static void BuildSegmentsFromValueEntries(
// care to identify properties that lack a value at offset 0.0/1.0 and drops
// those properties from |aResult|.
nsCSSPropertyID lastProperty = eCSSProperty_UNKNOWN;
AnimatedPropertyID lastProperty(eCSSProperty_UNKNOWN);
AnimationProperty* animationProperty = nullptr;
size_t i = 0, n = aEntries.Length();
@@ -835,9 +892,9 @@ static void BuildSegmentsFromValueEntries(
break;
}
MOZ_ASSERT(aEntries[i].mProperty != eCSSProperty_UNKNOWN &&
aEntries[i + 1].mProperty != eCSSProperty_UNKNOWN,
"Each entry should specify a valid property");
MOZ_ASSERT(
aEntries[i].mProperty.IsValid() && aEntries[i + 1].mProperty.IsValid(),
"Each entry should specify a valid property");
// No keyframe for this property at offset 0.
if (aEntries[i].mProperty != lastProperty && aEntries[i].mOffset != 0.0f) {

View File

@@ -16,6 +16,7 @@ struct JSContext;
class JSObject;
namespace mozilla {
struct AnimatedPropertyID;
struct AnimationProperty;
class ComputedStyle;
@@ -102,7 +103,7 @@ class KeyframeUtils {
* if the property is animatable or not.
* @return true if |aProperty| is animatable.
*/
static bool IsAnimatableProperty(nsCSSPropertyID aProperty);
static bool IsAnimatableProperty(const AnimatedPropertyID& aProperty);
};
} // namespace mozilla

View File

@@ -3206,6 +3206,7 @@ nsDOMWindowUtils::GetUnanimatedComputedStyle(Element* aElement,
nsCSSProps::IsShorthand(propertyID)) {
return NS_ERROR_INVALID_ARG;
}
// TODO(bug 1846516): Handle custom properties.
switch (aFlushType) {
case FLUSH_NONE:

View File

@@ -453,7 +453,9 @@ void AnimationInfo::AddAnimationForProperty(
static_cast<float>(computedTiming.mIterationStart);
animation->direction() = static_cast<uint8_t>(timing.Direction());
animation->fillMode() = static_cast<uint8_t>(computedTiming.mFill);
animation->property() = aProperty.mProperty;
MOZ_ASSERT(!aProperty.mProperty.IsCustom(),
"We don't animate custom properties in the compositor");
animation->property() = aProperty.mProperty.mID;
animation->playbackRate() =
static_cast<float>(aAnimation->CurrentOrPendingPlaybackRate());
animation->previousPlaybackRate() =
@@ -478,7 +480,7 @@ void AnimationInfo::AddAnimationForProperty(
aAnimation->GetEffect()->AsKeyframeEffect()->BaseStyle(
aProperty.mProperty);
if (!baseStyle.IsNull()) {
SetAnimatable(aProperty.mProperty, baseStyle, aFrame, refBox,
SetAnimatable(aProperty.mProperty.mID, baseStyle, aFrame, refBox,
animation->baseStyle());
} else {
animation->baseStyle() = null_t();
@@ -488,9 +490,9 @@ void AnimationInfo::AddAnimationForProperty(
const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
AnimationSegment* animSegment = animation->segments().AppendElement();
SetAnimatable(aProperty.mProperty, segment.mFromValue, aFrame, refBox,
SetAnimatable(aProperty.mProperty.mID, segment.mFromValue, aFrame, refBox,
animSegment->startState());
SetAnimatable(aProperty.mProperty, segment.mToValue, aFrame, refBox,
SetAnimatable(aProperty.mProperty.mID, segment.mToValue, aFrame, refBox,
animSegment->endState());
animSegment->startPortion() = segment.mFromKey;
@@ -541,14 +543,16 @@ GroupAnimationsByProperty(const nsTArray<RefPtr<dom::Animation>>& aAnimations,
const dom::KeyframeEffect* effect = anim->GetEffect()->AsKeyframeEffect();
MOZ_ASSERT(effect);
for (const AnimationProperty& property : effect->Properties()) {
if (!aPropertySet.HasProperty(property.mProperty)) {
// TODO(zrhoffman, bug 1869475): Handle custom properties
if (!aPropertySet.HasProperty(property.mProperty.mID)) {
continue;
}
auto animsForPropertyPtr = groupedAnims.lookupForAdd(property.mProperty);
auto animsForPropertyPtr =
groupedAnims.lookupForAdd(property.mProperty.mID);
if (!animsForPropertyPtr) {
DebugOnly<bool> rv =
groupedAnims.add(animsForPropertyPtr, property.mProperty,
groupedAnims.add(animsForPropertyPtr, property.mProperty.mID,
nsTArray<RefPtr<dom::Animation>>());
MOZ_ASSERT(rv, "Should have enough memory");
}

View File

@@ -416,15 +416,15 @@ static Array<MinAndMaxScale, 2> GetMinAndMaxScaleForAnimationProperty(
anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
MOZ_ASSERT(effect, "A playing animation should have a keyframe effect");
for (const AnimationProperty& prop : effect->Properties()) {
if (prop.mProperty != eCSSProperty_transform &&
prop.mProperty != eCSSProperty_scale) {
if (prop.mProperty.mID != eCSSProperty_transform &&
prop.mProperty.mID != eCSSProperty_scale) {
continue;
}
// 0: eCSSProperty_transform.
// 1: eCSSProperty_scale.
MinAndMaxScale& scales =
minAndMaxScales[prop.mProperty == eCSSProperty_transform ? 0 : 1];
minAndMaxScales[prop.mProperty.mID == eCSSProperty_transform ? 0 : 1];
// We need to factor in the scale of the base style if the base style
// will be used on the compositor.

View File

@@ -0,0 +1,63 @@
/* -*- 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_AnimatedPropertyID_h
#define mozilla_AnimatedPropertyID_h
#include "nsCSSPropertyID.h"
#include "nsCSSProps.h"
#include "nsString.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/ServoBindings.h"
namespace mozilla {
struct AnimatedPropertyID {
explicit AnimatedPropertyID(nsCSSPropertyID aProperty) : mID(aProperty) {
MOZ_ASSERT(aProperty != eCSSPropertyExtra_variable,
"Cannot create an AnimatedPropertyID from only a "
"eCSSPropertyExtra_variable.");
}
explicit AnimatedPropertyID(RefPtr<nsAtom> aCustomName)
: mID(eCSSPropertyExtra_variable), mCustomName(std::move(aCustomName)) {}
nsCSSPropertyID mID = eCSSProperty_UNKNOWN;
RefPtr<nsAtom> mCustomName;
bool IsCustom() const { return mID == eCSSPropertyExtra_variable; }
bool operator==(const AnimatedPropertyID& aOther) const {
return mID == aOther.mID &&
(!IsCustom() || mCustomName == aOther.mCustomName);
}
bool operator!=(const AnimatedPropertyID& aOther) const {
return !(*this == aOther);
}
bool IsValid() const {
if (mID == eCSSProperty_UNKNOWN) {
return false;
}
return IsCustom() == !!mCustomName;
}
void ToString(nsAString& aString) const {
if (IsCustom()) {
MOZ_ASSERT(mCustomName, "Custom property should have a name");
mCustomName->ToString(aString);
} else {
aString.Assign(NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(mID)));
}
}
HashNumber Hash() const {
HashNumber hash = mCustomName ? mCustomName->hash() : 0;
return AddToHash(hash, mID);
}
};
} // namespace mozilla
#endif // mozilla_AnimatedPropertyID_h

View File

@@ -0,0 +1,150 @@
/* -*- 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_AnimatedPropertyIDSet_h
#define mozilla_AnimatedPropertyIDSet_h
#include "mozilla/ServoBindings.h"
#include "AnimatedPropertyID.h"
#include "nsTHashSet.h"
namespace mozilla {
class AnimatedPropertyIDSet {
public:
void AddProperty(const AnimatedPropertyID& aProperty) {
if (aProperty.IsCustom()) {
mCustomNames.Insert(aProperty.mCustomName);
} else {
mIDs.AddProperty(aProperty.mID);
}
}
void RemoveProperty(const AnimatedPropertyID& aProperty) {
if (aProperty.IsCustom()) {
mCustomNames.Remove(aProperty.mCustomName);
} else {
mIDs.RemoveProperty(aProperty.mID);
}
}
bool HasProperty(const AnimatedPropertyID& aProperty) const {
if (aProperty.IsCustom()) {
return mCustomNames.Contains(aProperty.mCustomName);
}
return mIDs.HasProperty(aProperty.mID);
}
bool Intersects(const nsCSSPropertyIDSet& aIDs) const {
return mIDs.Intersects(aIDs);
}
bool IsSubsetOf(const AnimatedPropertyIDSet& aOther) const {
if (!mIDs.IsSubsetOf(aOther.mIDs) ||
mCustomNames.Count() > aOther.mCustomNames.Count()) {
return false;
}
for (const auto& name : mCustomNames) {
if (!aOther.mCustomNames.Contains(name)) {
return false;
}
}
return true;
}
void AddProperties(const AnimatedPropertyIDSet& aOther) {
mIDs |= aOther.mIDs;
for (const auto& name : aOther.mCustomNames) {
mCustomNames.Insert(name);
}
}
private:
using CustomNameSet = nsTHashSet<RefPtr<nsAtom>>;
public:
// Iterator for use in range-based for loops
class Iterator {
public:
Iterator(Iterator&& aOther)
: mPropertySet(aOther.mPropertySet),
mIDIterator(std::move(aOther.mIDIterator)),
mCustomNameIterator(std::move(aOther.mCustomNameIterator)),
mPropertyID(eCSSProperty_UNKNOWN) {}
Iterator() = delete;
Iterator(const Iterator&) = delete;
Iterator& operator=(const Iterator&) = delete;
Iterator& operator=(const Iterator&&) = delete;
static Iterator BeginIterator(const AnimatedPropertyIDSet& aPropertySet) {
return Iterator(aPropertySet, aPropertySet.mIDs.begin(),
aPropertySet.mCustomNames.begin());
}
static Iterator EndIterator(const AnimatedPropertyIDSet& aPropertySet) {
return Iterator(aPropertySet, aPropertySet.mIDs.end(),
aPropertySet.mCustomNames.end());
}
bool operator!=(const Iterator& aOther) const {
return mIDIterator != aOther.mIDIterator ||
mCustomNameIterator != aOther.mCustomNameIterator;
}
Iterator& operator++() {
MOZ_ASSERT(mIDIterator != mPropertySet.mIDs.end() ||
mCustomNameIterator != mPropertySet.mCustomNames.end(),
"Should not iterate beyond end");
if (mIDIterator != mPropertySet.mIDs.end()) {
++mIDIterator;
} else {
++mCustomNameIterator;
}
return *this;
}
AnimatedPropertyID operator*() {
if (mIDIterator != mPropertySet.mIDs.end()) {
mPropertyID.mID = *mIDIterator;
mPropertyID.mCustomName = nullptr;
} else if (mCustomNameIterator != mPropertySet.mCustomNames.end()) {
mPropertyID.mID = eCSSPropertyExtra_variable;
mPropertyID.mCustomName = *mCustomNameIterator;
} else {
MOZ_ASSERT_UNREACHABLE("Should not dereference beyond end");
mPropertyID.mID = eCSSProperty_UNKNOWN;
mPropertyID.mCustomName = nullptr;
}
return mPropertyID;
}
private:
explicit Iterator(const AnimatedPropertyIDSet& aPropertySet,
nsCSSPropertyIDSet::Iterator aIDIterator,
CustomNameSet::const_iterator aCustomNameIterator)
: mPropertySet(aPropertySet),
mIDIterator(std::move(aIDIterator)),
mCustomNameIterator(std::move(aCustomNameIterator)),
mPropertyID(eCSSProperty_UNKNOWN) {}
const AnimatedPropertyIDSet& mPropertySet;
nsCSSPropertyIDSet::Iterator mIDIterator;
CustomNameSet::const_iterator mCustomNameIterator;
AnimatedPropertyID mPropertyID;
};
Iterator begin() const { return Iterator::BeginIterator(*this); }
Iterator end() const { return Iterator::EndIterator(*this); }
private:
nsCSSPropertyIDSet mIDs;
CustomNameSet mCustomNames;
};
} // namespace mozilla
#endif // mozilla_AnimatedPropertyIDSet_h

View File

@@ -651,7 +651,7 @@ static CSSTransition* GetCurrentTransitionAt(const Element* aElement,
nsCSSPropertyID Gecko_ElementTransitions_PropertyAt(const Element* aElement,
size_t aIndex) {
CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex);
return transition ? transition->TransitionProperty()
return transition ? transition->TransitionProperty().mID
: nsCSSPropertyID::eCSSProperty_UNKNOWN;
}

View File

@@ -17,6 +17,7 @@
#include "mozilla/UniquePtr.h"
#include "nsCOMArray.h"
#include "nsString.h"
#include "mozilla/AnimatedPropertyID.h"
#include "mozilla/ComputedStyle.h"
#include "nsComputedDOMStyle.h"
#include "nsCSSPseudoElements.h"
@@ -155,13 +156,13 @@ MatrixScales AnimationValue::GetScaleValue(const nsIFrame* aFrame) const {
}
void AnimationValue::SerializeSpecifiedValue(
nsCSSPropertyID aProperty, const StylePerDocumentStyleData* aRawData,
nsACString& aString) const {
const AnimatedPropertyID& aProperty,
const StylePerDocumentStyleData* aRawData, nsACString& aString) const {
MOZ_ASSERT(mServo);
Servo_AnimationValue_Serialize(mServo, aProperty, aRawData, &aString);
Servo_AnimationValue_Serialize(mServo, aProperty.mID, aRawData, &aString);
}
bool AnimationValue::IsInterpolableWith(nsCSSPropertyID aProperty,
bool AnimationValue::IsInterpolableWith(const AnimatedPropertyID& aProperty,
const AnimationValue& aToValue) const {
if (IsNull() || aToValue.IsNull()) {
return false;

View File

@@ -43,6 +43,7 @@ class Animatable;
enum class PseudoStyleType : uint8_t;
struct PropertyStyleAnimationValuePair;
struct AnimatedPropertyID;
struct AnimationValue {
explicit AnimationValue(const RefPtr<StyleAnimationValue>& aValue)
@@ -91,12 +92,12 @@ struct AnimationValue {
mozilla::gfx::MatrixScales GetScaleValue(const nsIFrame* aFrame) const;
// Uncompute this AnimationValue and then serialize it.
void SerializeSpecifiedValue(nsCSSPropertyID aProperty,
void SerializeSpecifiedValue(const AnimatedPropertyID& aProperty,
const StylePerDocumentStyleData* aRawData,
nsACString& aString) const;
// Check if |*this| and |aToValue| can be interpolated.
bool IsInterpolableWith(nsCSSPropertyID aProperty,
bool IsInterpolableWith(const AnimatedPropertyID& aProperty,
const AnimationValue& aToValue) const;
// Compute the distance between *this and aOther.

View File

@@ -22,6 +22,9 @@ with Files("nsDOM*"):
with Files("AnimationCollection.*"):
BUG_COMPONENT = ("Core", "CSS Transitions and Animations")
with Files("AnimatedPropertyID*"):
BUG_COMPONENT = ("Core", "CSS Transitions and Animations")
with Files("AnimationCommon.*"):
BUG_COMPONENT = ("Core", "CSS Transitions and Animations")
@@ -67,6 +70,8 @@ EXPORTS += [
EXPORTS.mozilla += [
"!ServoCSSPropList.h",
"AnimatedPropertyID.h",
"AnimatedPropertyIDSet.h",
"AnimationCollection.h",
"AttributeStyles.h",
"CachedInheritingStyles.h",

View File

@@ -67,9 +67,12 @@ class nsCSSProps {
static bool IsCustomPropertyName(const nsACString& aProperty);
static bool IsShorthand(nsCSSPropertyID aProperty) {
if (aProperty == eCSSPropertyExtra_variable) {
return false;
}
MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT,
"out of range");
return (aProperty >= eCSSProperty_COUNT_no_shorthands);
return aProperty >= eCSSProperty_COUNT_no_shorthands;
}
// Same but for @font-face descriptors

View File

@@ -1661,6 +1661,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
nsCSSPropertyID GetTransitionProperty(uint32_t aIndex) const {
return mTransitions[aIndex % mTransitionPropertyCount].GetProperty();
}
nsAtom* GetTransitionUnknownProperty(uint32_t aIndex) const {
return mTransitions[aIndex % mTransitionPropertyCount].GetUnknownProperty();
}
const mozilla::StyleTime& GetTransitionDelay(uint32_t aIndex) const {
return mTransitions[aIndex % mTransitionDelayCount].GetDelay();
}

View File

@@ -11,6 +11,8 @@
#include "nsAnimationManager.h"
#include "nsIContent.h"
#include "AnimatedPropertyID.h"
#include "AnimatedPropertyIDSet.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/MemoryReporting.h"
#include "nsCSSPropertyIDSet.h"
@@ -60,14 +62,19 @@ bool nsTransitionManager::UpdateTransitions(dom::Element* aElement,
// transition-property, and then execute |aHandler| on the expanded longhand.
// |aHandler| should be a lamda function which accepts nsCSSPropertyID.
template <typename T>
static void ExpandTransitionProperty(nsCSSPropertyID aProperty, T aHandler) {
static void ExpandTransitionProperty(nsCSSPropertyID aProperty,
nsAtom* aCustomName, T aHandler) {
if (aProperty == eCSSPropertyExtra_no_properties ||
aProperty == eCSSPropertyExtra_variable ||
aProperty == eCSSProperty_UNKNOWN) {
// Nothing to do.
return;
}
if (aProperty == eCSSPropertyExtra_variable) {
AnimatedPropertyID property(aCustomName);
return;
}
// FIXME(emilio): This should probably just use the "all" shorthand id, and we
// should probably remove eCSSPropertyExtra_all_properties.
if (aProperty == eCSSPropertyExtra_all_properties) {
@@ -76,15 +83,18 @@ static void ExpandTransitionProperty(nsCSSPropertyID aProperty, T aHandler) {
if (!nsCSSProps::IsEnabled(p, CSSEnabledState::ForAllContent)) {
continue;
}
aHandler(p);
AnimatedPropertyID property(p);
aHandler(property);
}
} else if (nsCSSProps::IsShorthand(aProperty)) {
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, aProperty,
CSSEnabledState::ForAllContent) {
aHandler(*subprop);
AnimatedPropertyID property(*subprop);
aHandler(property);
}
} else {
aHandler(aProperty);
AnimatedPropertyID property(aProperty);
aHandler(property);
}
}
@@ -100,7 +110,7 @@ bool nsTransitionManager::DoUpdateTransitions(
// 'transition-property' on down, and later ones will override earlier
// ones (tracked using |propertiesChecked|).
bool startedAny = false;
nsCSSPropertyIDSet propertiesChecked;
AnimatedPropertyIDSet propertiesChecked;
for (uint32_t i = aStyle.mTransitionPropertyCount; i--;) {
// We're not going to look at any further transitions, so we can just avoid
// looking at this if we know it will not start any transitions.
@@ -109,7 +119,8 @@ bool nsTransitionManager::DoUpdateTransitions(
}
ExpandTransitionProperty(
aStyle.GetTransitionProperty(i), [&](nsCSSPropertyID aProperty) {
aStyle.GetTransitionProperty(i), aStyle.GetTransitionUnknownProperty(i),
[&](const AnimatedPropertyID& aProperty) {
// We might have something to transition. See if any of the
// properties in question changed and are animatable.
startedAny |= ConsiderInitiatingTransition(
@@ -128,13 +139,16 @@ bool nsTransitionManager::DoUpdateTransitions(
if (aElementTransitions) {
bool checkProperties =
aStyle.GetTransitionProperty(0) != eCSSPropertyExtra_all_properties;
nsCSSPropertyIDSet allTransitionProperties;
AnimatedPropertyIDSet allTransitionProperties;
if (checkProperties) {
for (uint32_t i = aStyle.mTransitionPropertyCount; i-- != 0;) {
ExpandTransitionProperty(
aStyle.GetTransitionProperty(i), [&](nsCSSPropertyID aProperty) {
allTransitionProperties.AddProperty(
nsCSSProps::Physicalize(aProperty, aNewStyle));
aStyle.GetTransitionProperty(i),
aStyle.GetTransitionUnknownProperty(i),
[&](const AnimatedPropertyID& aProperty) {
AnimatedPropertyID property = aProperty;
property.mID = nsCSSProps::Physicalize(aProperty.mID, aNewStyle);
allTransitionProperties.AddProperty(aProperty);
});
}
}
@@ -146,7 +160,7 @@ bool nsTransitionManager::DoUpdateTransitions(
do {
--i;
CSSTransition* anim = animations[i];
const nsCSSPropertyID property = anim->TransitionProperty();
const AnimatedPropertyID& property = anim->TransitionProperty();
if (
// Properties no longer in `transition-property`.
(checkProperties && !allTransitionProperties.HasProperty(property)) ||
@@ -155,7 +169,7 @@ bool nsTransitionManager::DoUpdateTransitions(
// or because the new value is not interpolable); a new transition
// would have anim->ToValue() matching currentValue.
!Servo_ComputedValues_TransitionValueMatches(
&aNewStyle, property, anim->ToValue().mServo.get())) {
&aNewStyle, property.mID, anim->ToValue().mServo.get())) {
// Stop the transition.
DoCancelTransition(aElement, aPseudoType, aElementTransitions, i);
}
@@ -165,7 +179,8 @@ bool nsTransitionManager::DoUpdateTransitions(
return startedAny;
}
static Keyframe& AppendKeyframe(double aOffset, nsCSSPropertyID aProperty,
static Keyframe& AppendKeyframe(double aOffset,
const AnimatedPropertyID& aProperty,
AnimationValue&& aValue,
nsTArray<Keyframe>& aKeyframes) {
Keyframe& frame = *aKeyframes.AppendElement();
@@ -174,13 +189,13 @@ static Keyframe& AppendKeyframe(double aOffset, nsCSSPropertyID aProperty,
RefPtr<StyleLockedDeclarationBlock> decl =
Servo_AnimationValue_Uncompute(aValue.mServo).Consume();
frame.mPropertyValues.AppendElement(
PropertyValuePair(aProperty, std::move(decl)));
PropertyValuePair(aProperty.mID, std::move(decl)));
return frame;
}
static nsTArray<Keyframe> GetTransitionKeyframes(nsCSSPropertyID aProperty,
AnimationValue&& aStartValue,
AnimationValue&& aEndValue) {
static nsTArray<Keyframe> GetTransitionKeyframes(
const AnimatedPropertyID& aProperty, AnimationValue&& aStartValue,
AnimationValue&& aEndValue) {
nsTArray<Keyframe> keyframes(2);
AppendKeyframe(0.0, aProperty, std::move(aStartValue), keyframes);
@@ -189,8 +204,12 @@ static nsTArray<Keyframe> GetTransitionKeyframes(nsCSSPropertyID aProperty,
return keyframes;
}
static bool IsTransitionable(nsCSSPropertyID aProperty) {
return Servo_Property_IsTransitionable(aProperty);
static bool IsTransitionable(const AnimatedPropertyID& aProperty) {
// TODO(bug 1846516): handle custom property.
if (aProperty.IsCustom()) {
return false;
}
return Servo_Property_IsTransitionable(aProperty.mID);
}
static Maybe<CSSTransition::ReplacedTransitionProperties>
@@ -240,30 +259,34 @@ GetReplacedTransitionProperties(const CSSTransition* aTransition,
}
bool nsTransitionManager::ConsiderInitiatingTransition(
nsCSSPropertyID aProperty, const nsStyleUIReset& aStyle,
const AnimatedPropertyID& aProperty, const nsStyleUIReset& aStyle,
uint32_t transitionIdx, dom::Element* aElement, PseudoStyleType aPseudoType,
CSSTransitionCollection*& aElementTransitions,
const ComputedStyle& aOldStyle, const ComputedStyle& aNewStyle,
nsCSSPropertyIDSet& aPropertiesChecked) {
AnimatedPropertyIDSet& aPropertiesChecked) {
// IsShorthand itself will assert if aProperty is not a property.
MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), "property out of range");
MOZ_ASSERT(aProperty.IsCustom() || !nsCSSProps::IsShorthand(aProperty.mID),
"property out of range");
NS_ASSERTION(
!aElementTransitions || &aElementTransitions->mElement == aElement,
"Element mismatch");
aProperty = nsCSSProps::Physicalize(aProperty, aNewStyle);
AnimatedPropertyID property = aProperty;
if (!property.IsCustom()) {
property.mID = nsCSSProps::Physicalize(property.mID, aNewStyle);
}
// A later item in transition-property already specified a transition for
// this property, so we ignore this one.
//
// See http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html .
if (aPropertiesChecked.HasProperty(aProperty)) {
if (aPropertiesChecked.HasProperty(property)) {
return false;
}
aPropertiesChecked.AddProperty(aProperty);
aPropertiesChecked.AddProperty(property);
if (!IsTransitionable(aProperty)) {
if (!IsTransitionable(property)) {
return false;
}
@@ -287,7 +310,7 @@ bool nsTransitionManager::ConsiderInitiatingTransition(
const OwningCSSTransitionPtrArray& animations =
aElementTransitions->mAnimations;
for (size_t i = 0, i_end = animations.Length(); i < i_end; ++i) {
if (animations[i]->TransitionProperty() == aProperty) {
if (animations[i]->TransitionProperty() == property) {
currentIndex = i;
return animations[i];
}
@@ -298,7 +321,7 @@ bool nsTransitionManager::ConsiderInitiatingTransition(
AnimationValue startValue, endValue;
const StyleShouldTransitionResult result =
Servo_ComputedValues_ShouldTransition(
&aOldStyle, &aNewStyle, aProperty,
&aOldStyle, &aNewStyle, aProperty.mID,
oldTransition ? oldTransition->ToValue().mServo.get() : nullptr,
&startValue.mServo, &endValue.mServo);
@@ -389,7 +412,7 @@ bool nsTransitionManager::ConsiderInitiatingTransition(
}
RefPtr<CSSTransition> transition = DoCreateTransition(
aProperty, aElement, aPseudoType, aNewStyle, aElementTransitions,
property, aElement, aPseudoType, aNewStyle, aElementTransitions,
std::move(timing), std::move(startValue), std::move(endValue),
std::move(startForReversingTest), reversePortion);
if (!transition) {
@@ -400,7 +423,7 @@ bool nsTransitionManager::ConsiderInitiatingTransition(
#ifdef DEBUG
for (size_t i = 0, i_end = transitions.Length(); i < i_end; ++i) {
MOZ_ASSERT(
i == currentIndex || transitions[i]->TransitionProperty() != aProperty,
i == currentIndex || transitions[i]->TransitionProperty() != property,
"duplicate transitions for property");
}
#endif
@@ -436,7 +459,7 @@ bool nsTransitionManager::ConsiderInitiatingTransition(
}
already_AddRefed<CSSTransition> nsTransitionManager::DoCreateTransition(
nsCSSPropertyID aProperty, dom::Element* aElement,
const AnimatedPropertyID& aProperty, dom::Element* aElement,
PseudoStyleType aPseudoType, const mozilla::ComputedStyle& aNewStyle,
CSSTransitionCollection*& aElementTransitions, TimingParams&& aTiming,
AnimationValue&& aStartValue, AnimationValue&& aEndValue,
@@ -457,7 +480,7 @@ already_AddRefed<CSSTransition> nsTransitionManager::DoCreateTransition(
}
RefPtr<CSSTransition> animation =
new CSSTransition(mPresContext->Document()->GetScopeObject());
new CSSTransition(mPresContext->Document()->GetScopeObject(), aProperty);
animation->SetOwningElement(OwningElementRef(*aElement, aPseudoType));
animation->SetTimelineNoUpdate(timeline);
animation->SetCreationSequence(

View File

@@ -18,6 +18,7 @@ class nsCSSPropertyIDSet;
struct nsStyleUIReset;
namespace mozilla {
class AnimatedPropertyIDSet;
class ComputedStyle;
enum class PseudoStyleType : uint8_t;
} // namespace mozilla
@@ -60,17 +61,17 @@ class nsTransitionManager final
// Returns whether the transition actually started.
bool ConsiderInitiatingTransition(
nsCSSPropertyID aProperty, const nsStyleUIReset& aStyle,
const mozilla::AnimatedPropertyID&, const nsStyleUIReset& aStyle,
uint32_t transitionIdx, mozilla::dom::Element* aElement,
mozilla::PseudoStyleType aPseudoType,
CSSTransitionCollection*& aElementTransitions,
const mozilla::ComputedStyle& aOldStyle,
const mozilla::ComputedStyle& aNewStyle,
nsCSSPropertyIDSet& aPropertiesChecked);
mozilla::AnimatedPropertyIDSet& aPropertiesChecked);
already_AddRefed<mozilla::dom::CSSTransition> DoCreateTransition(
nsCSSPropertyID aProperty, mozilla::dom::Element* aElement,
mozilla::PseudoStyleType aPseudoType,
const mozilla::AnimatedPropertyID& aProperty,
mozilla::dom::Element* aElement, mozilla::PseudoStyleType aPseudoType,
const mozilla::ComputedStyle& aNewStyle,
CSSTransitionCollection*& aElementTransitions,
mozilla::TimingParams&& aTiming, mozilla::AnimationValue&& aStartValue,