WebKit Bugzilla
Attachment 357034 Details for
Bug 192334
: CSS Properties and Values API should support animating registered custom properties without any references
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-192334-20181210223415.patch (text/plain), 61.98 KB, created by
Justin Michaud
on 2018-12-10 22:34:16 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Justin Michaud
Created:
2018-12-10 22:34:16 PST
Size:
61.98 KB
patch
obsolete
>Subversion Revision: 239061 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 477c69df84175ebba3059b89a92aec96ae63eee2..cc5a9060c526989690c895bd90760d9ab015024c 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,59 @@ >+2018-12-10 Justin Michaud <justin_michaud@apple.com> >+ >+ CSS Properties and Values API should support animating registered custom properties without any references >+ https://bugs.webkit.org/show_bug.cgi?id=192334 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Tests: css-custom-properties-api/length-animation-multiple.html >+ css-custom-properties-api/length-animation-unregistered.html >+ css-custom-properties-api/length-animation.html >+ >+ * animation/AnimationTimeline.cpp: >+ (WebCore::removeCSSTransitionFromCustomMap): >+ (WebCore::AnimationTimeline::removeDeclarativeAnimationFromListsForOwningElement): >+ (WebCore::propertyInStyleMatchesValueForTransitionInMap): >+ (WebCore::AnimationTimeline::ensureRunningTransitionsByCustomProperty): >+ (WebCore::propertiesMatchPropertiesInMap): >+ (WebCore::ensureRunningTransitions): >+ (WebCore::AnimationTimeline::updateCustomPropertyCSSTransitionsForElement): >+ (WebCore::AnimationTimeline::updateCSSTransitionsForElement): >+ * animation/AnimationTimeline.h: >+ * animation/DocumentTimeline.cpp: >+ (WebCore::DocumentTimeline::transitionDidComplete): >+ * animation/KeyframeEffect.cpp: >+ (WebCore::KeyframeEffect::setAnimatedPropertiesInStyle): >+ * css/CSSToStyleMap.cpp: >+ (WebCore::CSSToStyleMap::mapAnimationProperty): >+ * css/StyleResolver.cpp: >+ (WebCore::StyleResolver::applyPropertyToCurrentStyle): >+ * css/StyleResolver.h: >+ * page/animation/CSSPropertyAnimation.cpp: >+ (WebCore::CSSPropertyAnimation::blendProperties): >+ (WebCore::CSSPropertyAnimation::blendCustomProperty): >+ (WebCore::CSSPropertyAnimation::propertiesEqual): >+ (WebCore::CSSPropertyAnimation::customPropertyEqual): >+ (WebCore::CSSPropertyAnimation::canPropertyBeInterpolated): >+ * page/animation/CSSPropertyAnimation.h: >+ * page/animation/CompositeAnimation.cpp: >+ (WebCore::CompositeAnimation::updateTransitions): >+ * page/animation/ImplicitAnimation.cpp: >+ (WebCore::ImplicitAnimation::animate): >+ (WebCore::ImplicitAnimation::isTargetPropertyEqual): >+ * page/animation/KeyframeAnimation.cpp: >+ (WebCore::KeyframeAnimation::animate): >+ * platform/animation/Animation.cpp: >+ (WebCore::Animation::Animation): >+ (WebCore::Animation::operator=): >+ (WebCore::Animation::animationsMatch const): >+ * platform/animation/Animation.h: >+ (WebCore::Animation::customProperty const): >+ (WebCore::Animation::setProperty): >+ (WebCore::Animation::setCustomProperty): >+ * rendering/style/RenderStyle.cpp: >+ (WebCore::RenderStyle::adjustTransitions): >+ (WebCore::RenderStyle::transitionForProperty const): >+ > 2018-12-10 Antti Koivisto <antti@apple.com> > > Rename "forced style recalc" to "full style rebuild" >diff --git a/Source/WebCore/animation/AnimationTimeline.cpp b/Source/WebCore/animation/AnimationTimeline.cpp >index 67a6f3f85b0d7a5107b39a9a8e27a7a498941a76..baa830aec6db1c79c47f7cfbb67b4959024713cb 100644 >--- a/Source/WebCore/animation/AnimationTimeline.cpp >+++ b/Source/WebCore/animation/AnimationTimeline.cpp >@@ -120,6 +120,26 @@ static inline bool removeCSSTransitionFromMap(CSSTransition& transition, Element > return true; > } > >+static inline bool removeCSSTransitionFromCustomMap(CSSTransition& transition, Element& element, HashMap<Element*, AnimationTimeline::CustomPropertyToTransitionMap>& map) >+{ >+ auto iterator = map.find(&element); >+ if (iterator == map.end()) >+ return false; >+ >+ auto& cssTransitionsByProperty = iterator->value; >+ >+ ASSERT(transition.property() == CSSPropertyCustom); >+ auto transitionIterator = cssTransitionsByProperty.find(transition.backingAnimation().customProperty()); >+ if (transitionIterator == cssTransitionsByProperty.end() || transitionIterator->value != &transition) >+ return false; >+ >+ cssTransitionsByProperty.remove(transitionIterator); >+ >+ if (cssTransitionsByProperty.isEmpty()) >+ map.remove(&element); >+ return true; >+} >+ > static inline void removeAnimationFromMapForElement(WebAnimation& animation, AnimationTimeline::ElementToAnimationsMap& map, Element& element) > { > auto iterator = map.find(&element); >@@ -159,8 +179,13 @@ void AnimationTimeline::removeDeclarativeAnimationFromListsForOwningElement(WebA > } > } else if (is<CSSTransition>(animation)) { > auto& transition = downcast<CSSTransition>(animation); >- if (!removeCSSTransitionFromMap(transition, element, m_elementToRunningCSSTransitionByCSSPropertyID)) >- removeCSSTransitionFromMap(transition, element, m_elementToCompletedCSSTransitionByCSSPropertyID); >+ if (UNLIKELY(transition.property() == CSSPropertyCustom)) { >+ if (!removeCSSTransitionFromCustomMap(transition, element, m_elementToRunningCSSTransitionByCustomPropertyName)) >+ removeCSSTransitionFromCustomMap(transition, element, m_elementToCompletedCSSTransitionByCustomPropertyName); >+ } else { >+ if (!removeCSSTransitionFromMap(transition, element, m_elementToRunningCSSTransitionByCSSPropertyID)) >+ removeCSSTransitionFromMap(transition, element, m_elementToCompletedCSSTransitionByCSSPropertyID); >+ } > } > } > >@@ -301,8 +326,9 @@ RefPtr<WebAnimation> AnimationTimeline::cssAnimationForElementAndProperty(Elemen > return matchingAnimation; > } > >-static bool propertyInStyleMatchesValueForTransitionInMap(CSSPropertyID property, const RenderStyle& style, AnimationTimeline::PropertyToTransitionMap& transitions) >+static bool propertyInStyleMatchesValueForTransitionInMap(CSSPropertyID property, const RenderStyle& style, const AnimationTimeline::PropertyToTransitionMap& transitions) > { >+ ASSERT(property != CSSPropertyCustom); > if (auto* transition = transitions.get(property)) { > if (CSSPropertyAnimation::propertiesEqual(property, &style, &transition->targetStyle())) > return true; >@@ -341,6 +367,186 @@ AnimationTimeline::PropertyToTransitionMap& AnimationTimeline::ensureRunningTran > }).iterator->value; > } > >+AnimationTimeline::CustomPropertyToTransitionMap& AnimationTimeline::ensureRunningTransitionsByCustomProperty(Element& element) >+{ >+ return m_elementToRunningCSSTransitionByCustomPropertyName.ensure(&element, [] { >+ return CustomPropertyToTransitionMap { }; >+ }).iterator->value; >+} >+ >+static bool propertiesMatchPropertiesInMap(const String& property, const RenderStyle& style, const AnimationTimeline::CustomPropertyToTransitionMap& transitions) >+{ >+ if (auto* transition = transitions.get(property)) { >+ if (CSSPropertyAnimation::customPropertyEqual(property, &style, &transition->targetStyle())) >+ return true; >+ } >+ return false; >+} >+ >+template<typename PropertyKey, >+ typename PropertyTransitionMap, >+ bool propertiesEqual(PropertyKey, const RenderStyle*, const RenderStyle*), >+ bool propertyInStyleMatchesValueForTransitionInMap(PropertyKey, const RenderStyle&, const PropertyTransitionMap&), >+ PropertyTransitionMap& (AnimationTimeline::*ensureRunningTransitions)(Element&)> >+inline void AnimationTimeline::updateCSSTransitionsForProperty(Element& element, >+ const RenderStyle& currentStyle, >+ const RenderStyle& afterChangeStyle, >+ CSSPropertyID propertyID, >+ PropertyKey property, >+ PropertyTransitionMap& runningTransitionsByProperty, >+ PropertyTransitionMap& completedTransitionsByProperty, >+ const Animation* matchingBackingAnimation, >+ WebAnimation* existingAnimation, >+ const MonotonicTime& generationTime) >+{ >+ // https://drafts.csswg.org/css-transitions-1/#before-change-style >+ // Define the before-change style as the computed values of all properties on the element as of the previous style change event, except with >+ // any styles derived from declarative animations such as CSS Transitions, CSS Animations, and SMIL Animations updated to the current time. >+ const auto& beforeChangeStyle = existingAnimation ? downcast<CSSAnimation>(existingAnimation)->unanimatedStyle() : currentStyle; >+ >+ if (!runningTransitionsByProperty.contains(property) >+ && !propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle) >+ && CSSPropertyAnimation::canPropertyBeInterpolated(propertyID, &beforeChangeStyle, &afterChangeStyle) >+ && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty) >+ && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) { >+ // 1. If all of the following are true: >+ // - the element does not have a running transition for the property, >+ // - the before-change style is different from and can be interpolated with the after-change style for that property, >+ // - the element does not have a completed transition for the property or the end value of the completed transition is different from the after-change style for the property, >+ // - there is a matching transition-property value, and >+ // - the combined duration is greater than 0s, >+ >+ // then implementations must remove the completed transition (if present) from the set of completed transitions >+ completedTransitionsByProperty.remove(property); >+ >+ // and start a transition whose: >+ // - start time is the time of the style change event plus the matching transition delay, >+ // - end time is the start time plus the matching transition duration, >+ // - start value is the value of the transitioning property in the before-change style, >+ // - end value is the value of the transitioning property in the after-change style, >+ // - reversing-adjusted start value is the same as the start value, and >+ // - reversing shortening factor is 1. >+ auto delay = Seconds(matchingBackingAnimation->delay()); >+ auto duration = Seconds(matchingBackingAnimation->duration()); >+ auto& reversingAdjustedStartStyle = beforeChangeStyle; >+ auto reversingShorteningFactor = 1; >+ runningTransitionsByProperty.set(property, CSSTransition::create(element, propertyID, generationTime, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor)); >+ } else if (completedTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)) { >+ // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from >+ // the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions. >+ completedTransitionsByProperty.remove(property); >+ } >+ >+ bool hasRunningTransition = runningTransitionsByProperty.contains(property); >+ if ((hasRunningTransition || completedTransitionsByProperty.contains(property)) && !matchingBackingAnimation) { >+ // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property >+ // value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions. >+ if (hasRunningTransition) >+ runningTransitionsByProperty.take(property)->cancel(); >+ else >+ completedTransitionsByProperty.remove(property); >+ } >+ >+ if (matchingBackingAnimation && runningTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, runningTransitionsByProperty)) { >+ auto previouslyRunningTransition = runningTransitionsByProperty.take(property); >+ auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle(); >+ // 4. If the element has a running transition for the property, there is a matching transition-property value, and the end value of the running >+ // transition is not equal to the value of the property in the after-change style, then: >+ if (propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(propertyID, ¤tStyle, &afterChangeStyle)) { >+ // 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style, >+ // or if these two values cannot be interpolated, then implementations must cancel the running transition. >+ cancelDeclarativeAnimation(*previouslyRunningTransition); >+ } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(propertyID, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) { >+ // 2. Otherwise, if the combined duration is less than or equal to 0s, or if the current value of the property in the running transition >+ // cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition. >+ cancelDeclarativeAnimation(*previouslyRunningTransition); >+ } else if (propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) { >+ // 3. Otherwise, if the reversing-adjusted start value of the running transition is the same as the value of the property in the after-change >+ // style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition >+ cancelDeclarativeAnimation(*previouslyRunningTransition); >+ >+ // and start a new transition whose: >+ // - reversing-adjusted start value is the end value of the running transition, >+ // - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of: >+ // 1. the output of the timing function of the old transition at the time of the style change event, times the reversing shortening factor of the old transition >+ // 2. 1 minus the reversing shortening factor of the old transition. >+ // - start time is the time of the style change event plus: >+ // 1. if the matching transition delay is nonnegative, the matching transition delay, or >+ // 2. if the matching transition delay is negative, the product of the new transitionâs reversing shortening factor and the matching transition delay, >+ // - end time is the start time plus the product of the matching transition duration and the new transitionâs reversing shortening factor, >+ // - start value is the current value of the property in the running transition, >+ // - end value is the value of the property in the after-change style >+ auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle(); >+ double transformedProgress = 1; >+ if (auto* effect = previouslyRunningTransition->effect()) >+ transformedProgress = effect->iterationProgress().value_or(transformedProgress); >+ auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0); >+ auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay()); >+ auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor; >+ >+ (this->*ensureRunningTransitions)(element).set(property, CSSTransition::create(element, propertyID, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor)); >+ } else { >+ // 4. Otherwise, implementations must cancel the running transition >+ cancelDeclarativeAnimation(*previouslyRunningTransition); >+ >+ // and start a new transition whose: >+ // - start time is the time of the style change event plus the matching transition delay, >+ // - end time is the start time plus the matching transition duration, >+ // - start value is the current value of the property in the running transition, >+ // - end value is the value of the property in the after-change style, >+ // - reversing-adjusted start value is the same as the start value, and >+ // - reversing shortening factor is 1. >+ auto delay = Seconds(matchingBackingAnimation->delay()); >+ auto duration = Seconds(matchingBackingAnimation->duration()); >+ auto& reversingAdjustedStartStyle = currentStyle; >+ auto reversingShorteningFactor = 1; >+ (this->*ensureRunningTransitions)(element).set(property, CSSTransition::create(element, propertyID, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor)); >+ } >+ } >+} >+ >+// The logic here should be identical to AnimationTimeline::updateCSSTransitionsForElement. >+void AnimationTimeline::updateCustomPropertyCSSTransitionsForElement(Element& element, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle) >+{ >+ // In case this element is newly getting a "display: none" we need to cancel all of its transitions and disregard new ones. >+ if (currentStyle.hasTransitions() && currentStyle.display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) { >+ if (m_elementToRunningCSSTransitionByCustomPropertyName.contains(&element)) { >+ for (const auto& cssTransitionsByCSSPropertyIDMapItem : m_elementToRunningCSSTransitionByCustomPropertyName.take(&element)) >+ cancelDeclarativeAnimation(*cssTransitionsByCSSPropertyIDMapItem.value); >+ } >+ return; >+ } >+ >+ // Section 3 "Starting of transitions" from the CSS Transitions Level 1 specification. >+ // https://drafts.csswg.org/css-transitions-1/#starting >+ >+ auto& runningTransitionsCustom = m_elementToRunningCSSTransitionByCustomPropertyName.ensure(&element, [] { >+ return CustomPropertyToTransitionMap { }; >+ }).iterator->value; >+ >+ auto& completedTransitionsCustom = m_elementToCompletedCSSTransitionByCustomPropertyName.ensure(&element, [] { >+ return CustomPropertyToTransitionMap { }; >+ }).iterator->value; >+ >+ auto generationTime = MonotonicTime::now(); >+ >+ if (!afterChangeStyle.transitions()) >+ return; >+ >+ for (size_t i = 0; i < afterChangeStyle.transitions()->size(); ++i) { >+ const Animation* matchingBackingAnimation = &afterChangeStyle.transitions()->animation(i); >+ if (matchingBackingAnimation->property() != CSSPropertyCustom) >+ continue; >+ const String& propertyName = matchingBackingAnimation->customProperty(); >+ >+ auto existingAnimation = cssAnimationForElementAndProperty(element, CSSPropertyCustom); >+ // FIXME: Not supported yet. >+ ASSERT(!existingAnimation); >+ >+ updateCSSTransitionsForProperty<const String&, CustomPropertyToTransitionMap, CSSPropertyAnimation::customPropertyEqual, propertiesMatchPropertiesInMap, &AnimationTimeline::ensureRunningTransitionsByCustomProperty>(element, currentStyle, afterChangeStyle, CSSPropertyCustom, propertyName, runningTransitionsCustom, completedTransitionsCustom, matchingBackingAnimation, existingAnimation.get(), generationTime); >+ } >+} >+ > void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle) > { > // In case this element is newly getting a "display: none" we need to cancel all of its transitions and disregard new ones. >@@ -363,12 +569,16 @@ void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const R > > auto generationTime = MonotonicTime::now(); > >+ updateCustomPropertyCSSTransitionsForElement(element, currentStyle, afterChangeStyle); >+ > auto numberOfProperties = CSSPropertyAnimation::getNumProperties(); > for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) { > std::optional<bool> isShorthand; > auto property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand); > if (isShorthand && *isShorthand) > continue; >+ if (property == CSSPropertyCustom) >+ continue; > > const Animation* matchingBackingAnimation = nullptr; > if (auto* transitions = afterChangeStyle.transitions()) { >@@ -379,111 +589,9 @@ void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const R > } > } > >- // https://drafts.csswg.org/css-transitions-1/#before-change-style >- // Define the before-change style as the computed values of all properties on the element as of the previous style change event, except with >- // any styles derived from declarative animations such as CSS Transitions, CSS Animations, and SMIL Animations updated to the current time. > auto existingAnimation = cssAnimationForElementAndProperty(element, property); >- const auto& beforeChangeStyle = existingAnimation ? downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : currentStyle; >- >- if (!runningTransitionsByProperty.contains(property) >- && !CSSPropertyAnimation::propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle) >- && CSSPropertyAnimation::canPropertyBeInterpolated(property, &beforeChangeStyle, &afterChangeStyle) >- && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty) >- && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) { >- // 1. If all of the following are true: >- // - the element does not have a running transition for the property, >- // - the before-change style is different from and can be interpolated with the after-change style for that property, >- // - the element does not have a completed transition for the property or the end value of the completed transition is different from the after-change style for the property, >- // - there is a matching transition-property value, and >- // - the combined duration is greater than 0s, >- >- // then implementations must remove the completed transition (if present) from the set of completed transitions >- completedTransitionsByProperty.remove(property); >- >- // and start a transition whose: >- // - start time is the time of the style change event plus the matching transition delay, >- // - end time is the start time plus the matching transition duration, >- // - start value is the value of the transitioning property in the before-change style, >- // - end value is the value of the transitioning property in the after-change style, >- // - reversing-adjusted start value is the same as the start value, and >- // - reversing shortening factor is 1. >- auto delay = Seconds(matchingBackingAnimation->delay()); >- auto duration = Seconds(matchingBackingAnimation->duration()); >- auto& reversingAdjustedStartStyle = beforeChangeStyle; >- auto reversingShorteningFactor = 1; >- runningTransitionsByProperty.set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor)); >- } else if (completedTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)) { >- // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from >- // the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions. >- completedTransitionsByProperty.remove(property); >- } >- >- bool hasRunningTransition = runningTransitionsByProperty.contains(property); >- if ((hasRunningTransition || completedTransitionsByProperty.contains(property)) && !matchingBackingAnimation) { >- // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property >- // value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions. >- if (hasRunningTransition) >- runningTransitionsByProperty.take(property)->cancel(); >- else >- completedTransitionsByProperty.remove(property); >- } > >- if (matchingBackingAnimation && runningTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, runningTransitionsByProperty)) { >- auto previouslyRunningTransition = runningTransitionsByProperty.take(property); >- auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle(); >- // 4. If the element has a running transition for the property, there is a matching transition-property value, and the end value of the running >- // transition is not equal to the value of the property in the after-change style, then: >- if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(property, ¤tStyle, &afterChangeStyle)) { >- // 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style, >- // or if these two values cannot be interpolated, then implementations must cancel the running transition. >- cancelDeclarativeAnimation(*previouslyRunningTransition); >- } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) { >- // 2. Otherwise, if the combined duration is less than or equal to 0s, or if the current value of the property in the running transition >- // cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition. >- cancelDeclarativeAnimation(*previouslyRunningTransition); >- } else if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) { >- // 3. Otherwise, if the reversing-adjusted start value of the running transition is the same as the value of the property in the after-change >- // style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition >- cancelDeclarativeAnimation(*previouslyRunningTransition); >- >- // and start a new transition whose: >- // - reversing-adjusted start value is the end value of the running transition, >- // - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of: >- // 1. the output of the timing function of the old transition at the time of the style change event, times the reversing shortening factor of the old transition >- // 2. 1 minus the reversing shortening factor of the old transition. >- // - start time is the time of the style change event plus: >- // 1. if the matching transition delay is nonnegative, the matching transition delay, or >- // 2. if the matching transition delay is negative, the product of the new transitionâs reversing shortening factor and the matching transition delay, >- // - end time is the start time plus the product of the matching transition duration and the new transitionâs reversing shortening factor, >- // - start value is the current value of the property in the running transition, >- // - end value is the value of the property in the after-change style >- auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle(); >- double transformedProgress = 1; >- if (auto* effect = previouslyRunningTransition->effect()) >- transformedProgress = effect->iterationProgress().value_or(transformedProgress); >- auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0); >- auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay()); >- auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor; >- >- ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor)); >- } else { >- // 4. Otherwise, implementations must cancel the running transition >- cancelDeclarativeAnimation(*previouslyRunningTransition); >- >- // and start a new transition whose: >- // - start time is the time of the style change event plus the matching transition delay, >- // - end time is the start time plus the matching transition duration, >- // - start value is the current value of the property in the running transition, >- // - end value is the value of the property in the after-change style, >- // - reversing-adjusted start value is the same as the start value, and >- // - reversing shortening factor is 1. >- auto delay = Seconds(matchingBackingAnimation->delay()); >- auto duration = Seconds(matchingBackingAnimation->duration()); >- auto& reversingAdjustedStartStyle = currentStyle; >- auto reversingShorteningFactor = 1; >- ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor)); >- } >- } >+ updateCSSTransitionsForProperty<CSSPropertyID, PropertyToTransitionMap, CSSPropertyAnimation::propertiesEqual, propertyInStyleMatchesValueForTransitionInMap, &AnimationTimeline::ensureRunningTransitionsByProperty>(element, currentStyle, afterChangeStyle, property, property, runningTransitionsByProperty, completedTransitionsByProperty, matchingBackingAnimation, existingAnimation.get(), generationTime); > } > } > >diff --git a/Source/WebCore/animation/AnimationTimeline.h b/Source/WebCore/animation/AnimationTimeline.h >index 896eb553dc62e30c430a772f59b35641b8948271..c55e5003bdf596f40c8a372f56788a6eb15c673c 100644 >--- a/Source/WebCore/animation/AnimationTimeline.h >+++ b/Source/WebCore/animation/AnimationTimeline.h >@@ -69,6 +69,7 @@ public: > > using ElementToAnimationsMap = HashMap<Element*, ListHashSet<RefPtr<WebAnimation>>>; > using PropertyToTransitionMap = HashMap<CSSPropertyID, RefPtr<CSSTransition>>; >+ using CustomPropertyToTransitionMap = HashMap<String, RefPtr<CSSTransition>>; > > virtual ~AnimationTimeline(); > >@@ -84,10 +85,29 @@ protected: > ListHashSet<WebAnimation*> m_allAnimations; > ListHashSet<RefPtr<WebAnimation>> m_animations; > HashMap<Element*, PropertyToTransitionMap> m_elementToCompletedCSSTransitionByCSSPropertyID; >+ HashMap<Element*, CustomPropertyToTransitionMap> m_elementToCompletedCSSTransitionByCustomPropertyName; > > private: >+ void updateCustomPropertyCSSTransitionsForElement(Element&, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle); >+ template<typename PropertyKey, >+ typename PropertyTransitionMap, >+ bool propertiesEqual(PropertyKey, const RenderStyle*, const RenderStyle*), >+ bool propertyInStyleMatchesValueForTransitionInMap(PropertyKey, const RenderStyle&, const PropertyTransitionMap&), >+ PropertyTransitionMap& (AnimationTimeline::*ensureRunningTransitions)(Element&)> >+ inline void updateCSSTransitionsForProperty(Element&, >+ const RenderStyle& currentStyle, >+ const RenderStyle& afterChangeStyle, >+ CSSPropertyID, >+ PropertyKey, >+ PropertyTransitionMap& runningTransitionsByProperty, >+ PropertyTransitionMap& completedTransitionsByProperty, >+ const Animation*, >+ WebAnimation*, >+ const MonotonicTime&); >+ > RefPtr<WebAnimation> cssAnimationForElementAndProperty(Element&, CSSPropertyID); > PropertyToTransitionMap& ensureRunningTransitionsByProperty(Element&); >+ CustomPropertyToTransitionMap& ensureRunningTransitionsByCustomProperty(Element&); > void cancelDeclarativeAnimation(DeclarativeAnimation&); > > ClassType m_classType; >@@ -97,6 +117,7 @@ private: > ElementToAnimationsMap m_elementToCSSTransitionsMap; > HashMap<Element*, HashMap<String, RefPtr<CSSAnimation>>> m_elementToCSSAnimationByName; > HashMap<Element*, PropertyToTransitionMap> m_elementToRunningCSSTransitionByCSSPropertyID; >+ HashMap<Element*, CustomPropertyToTransitionMap> m_elementToRunningCSSTransitionByCustomPropertyName; > }; > > } // namespace WebCore >diff --git a/Source/WebCore/animation/DocumentTimeline.cpp b/Source/WebCore/animation/DocumentTimeline.cpp >index 5381424ba55829353f25d6efb8700617b7c87c18..3d0d31618174b019e52b5240f6e780d16a4e2d05 100644 >--- a/Source/WebCore/animation/DocumentTimeline.cpp >+++ b/Source/WebCore/animation/DocumentTimeline.cpp >@@ -458,9 +458,15 @@ void DocumentTimeline::transitionDidComplete(RefPtr<CSSTransition> transition) > removeAnimation(*transition); > if (is<KeyframeEffect>(transition->effect())) { > if (auto* target = downcast<KeyframeEffect>(transition->effect())->target()) { >- m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(target, [] { >- return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { }; >- }).iterator->value.set(transition->property(), transition); >+ if (UNLIKELY(transition->property() == CSSPropertyCustom)) { >+ m_elementToCompletedCSSTransitionByCustomPropertyName.ensure(target, [] { >+ return HashMap<String, RefPtr<CSSTransition>> { }; >+ }).iterator->value.set(transition->backingAnimation().customProperty(), transition); >+ } else { >+ m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(target, [] { >+ return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { }; >+ }).iterator->value.set(transition->property(), transition); >+ } > } > } > } >diff --git a/Source/WebCore/animation/KeyframeEffect.cpp b/Source/WebCore/animation/KeyframeEffect.cpp >index 11c878aef6b20127829afb41a772c2c0cb77ec8d..ffabd43225b204e5a81acffc6c831f7b8ecb1d06 100644 >--- a/Source/WebCore/animation/KeyframeEffect.cpp >+++ b/Source/WebCore/animation/KeyframeEffect.cpp >@@ -1197,7 +1197,10 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub > // distance as the interpolation parameter p. > auto startStyle = !startKeyframeIndex ? &targetStyle : m_blendingKeyframes[startKeyframeIndex.value()].style(); > auto endStyle = !endKeyframeIndex ? &targetStyle : m_blendingKeyframes[endKeyframeIndex.value()].style(); >- CSSPropertyAnimation::blendProperties(this, cssPropertyId, &targetStyle, startStyle, endStyle, transformedDistance); >+ if (UNLIKELY(cssPropertyId == CSSPropertyCustom)) >+ CSSPropertyAnimation::blendCustomProperty(this, downcast<DeclarativeAnimation>(animation())->backingAnimation().customProperty(), &targetStyle, startStyle, endStyle, transformedDistance); >+ else >+ CSSPropertyAnimation::blendProperties(this, cssPropertyId, &targetStyle, startStyle, endStyle, transformedDistance); > } > } > >diff --git a/Source/WebCore/css/CSSToStyleMap.cpp b/Source/WebCore/css/CSSToStyleMap.cpp >index e93de9972d4bf389a3007bd87edc7a1673ceb691..09b573628e5be55eaa59393a706e0364c5b839e8 100644 >--- a/Source/WebCore/css/CSSToStyleMap.cpp >+++ b/Source/WebCore/css/CSSToStyleMap.cpp >@@ -448,6 +448,15 @@ void CSSToStyleMap::mapAnimationProperty(Animation& animation, const CSSValue& v > animation.setProperty(CSSPropertyInvalid); > return; > } >+ // String values correspond to <custom-ident>s for custom properties >+ if (primitiveValue.isString()) { >+ auto name = primitiveValue.getStringValue().returnValue().stripWhiteSpace(); >+ if (name.length() > 2 && name.characterAt(0) == '-' && name.characterAt(1) == '-') { >+ animation.setAnimationMode(Animation::AnimateSingleProperty); >+ animation.setCustomProperty(name); >+ return; >+ } >+ } > if (primitiveValue.propertyID() == CSSPropertyInvalid) { > animation.setAnimationMode(Animation::AnimateUnknownProperty); > animation.setProperty(CSSPropertyInvalid); >diff --git a/Source/WebCore/css/StyleResolver.cpp b/Source/WebCore/css/StyleResolver.cpp >index 6a1ce49d0c4079f990ab0f998be65572526e00e8..528411d46fd76d2a8ab59205c0441091f8d9709f 100644 >--- a/Source/WebCore/css/StyleResolver.cpp >+++ b/Source/WebCore/css/StyleResolver.cpp >@@ -1457,7 +1457,7 @@ void StyleResolver::applyPropertyToStyle(CSSPropertyID id, CSSValue* value, std: > > void StyleResolver::applyPropertyToCurrentStyle(CSSPropertyID id, CSSValue* value) > { >- ApplyCascadedPropertyState applyState { this, nullptr, nullptr }; >+ ApplyCascadedPropertyState applyState { this }; > if (value) > applyProperty(id, value, applyState); > } >diff --git a/Source/WebCore/css/StyleResolver.h b/Source/WebCore/css/StyleResolver.h >index 438e5c877fb43f44de572e6ede3d4c20c4738b01..1bd2eeee9326e5a3bb1826daf726a57f4b6cfb5d 100644 >--- a/Source/WebCore/css/StyleResolver.h >+++ b/Source/WebCore/css/StyleResolver.h >@@ -539,8 +539,8 @@ private: > // properties have been resolved. > struct ApplyCascadedPropertyState { > StyleResolver* styleResolver; >- StyleResolver::CascadedProperties* cascade; >- const StyleResolver::MatchResult* matchResult; >+ StyleResolver::CascadedProperties* cascade = nullptr; >+ const StyleResolver::MatchResult* matchResult = nullptr; > Bitmap<numCSSProperties> appliedProperties = { }; > HashSet<String> appliedCustomProperties = { }; > Bitmap<numCSSProperties> inProgressProperties = { }; >diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp >index b462b9f55fa63fb2b39338d2037769db2fd90f44..83f09d8bb15921b27f816ba790fd922fe2722d23 100644 >--- a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp >+++ b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp >@@ -32,6 +32,7 @@ > > #include "CSSComputedStyleDeclaration.h" > #include "CSSCrossfadeValue.h" >+#include "CSSCustomPropertyValue.h" > #include "CSSFilterImageValue.h" > #include "CSSImageGeneratorValue.h" > #include "CSSImageValue.h" >@@ -1797,6 +1798,7 @@ static bool gatherEnclosingShorthandProperties(CSSPropertyID property, Animation > bool CSSPropertyAnimation::blendProperties(const CSSPropertyBlendingClient* anim, CSSPropertyID prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) > { > ASSERT(prop != CSSPropertyInvalid); >+ ASSERT(prop != CSSPropertyCustom); > > AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); > if (wrapper) { >@@ -1809,6 +1811,29 @@ bool CSSPropertyAnimation::blendProperties(const CSSPropertyBlendingClient* anim > return false; > } > >+bool CSSPropertyAnimation::blendCustomProperty(const CSSPropertyBlendingClient* anim, const String& name, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) >+{ >+ if (!anim || !anim->renderer() || !anim->renderer()->element()) >+ return false; >+ >+ auto* aValue = a->getCustomProperty(name); >+ auto* bValue = b->getCustomProperty(name); >+ >+ if (!bValue) >+ return false; >+ >+ if (!WTF::holds_alternative<Length>(aValue->value()) || !WTF::holds_alternative<Length>(bValue->value())) >+ return false; >+ >+ auto newValue = blendFunc(anim, WTF::get<Length>(aValue->value()), WTF::get<Length>(bValue->value()), progress); >+ if (a->nonInheritedCustomProperties().get(name)) >+ dst->setNonInheritedCustomPropertyValue(name, CSSCustomPropertyValue::createSyntaxLength(name, newValue)); >+ else >+ dst->setInheritedCustomPropertyValue(name, CSSCustomPropertyValue::createSyntaxLength(name, newValue)); >+ >+ return true; >+} >+ > bool CSSPropertyAnimation::isPropertyAnimatable(CSSPropertyID prop) > { > return CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); >@@ -1834,14 +1859,22 @@ HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProper > > bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b) > { >+ ASSERT(prop != CSSPropertyCustom); > AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); > if (wrapper) > return wrapper->equals(a, b); > return true; > } > >+bool CSSPropertyAnimation::customPropertyEqual(const String& name, const RenderStyle* a, const RenderStyle* b) >+{ >+ return arePointingToEqualData(a->getCustomProperty(name), b->getCustomProperty(name)); >+} >+ > bool CSSPropertyAnimation::canPropertyBeInterpolated(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b) > { >+ if (UNLIKELY(prop == CSSPropertyCustom)) >+ return true; > AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); > if (wrapper) > return wrapper->canInterpolate(a, b); >diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.h b/Source/WebCore/page/animation/CSSPropertyAnimation.h >index acb24fe80029a8e325a4eb12901e6173aff7bc31..9986f7e85e619866bef9670ee9aa51ae3189b2ec 100644 >--- a/Source/WebCore/page/animation/CSSPropertyAnimation.h >+++ b/Source/WebCore/page/animation/CSSPropertyAnimation.h >@@ -41,6 +41,7 @@ public: > static bool isPropertyAnimatable(CSSPropertyID); > static bool animationOfPropertyIsAccelerated(CSSPropertyID); > static bool propertiesEqual(CSSPropertyID, const RenderStyle* a, const RenderStyle* b); >+ static bool customPropertyEqual(const String&, const RenderStyle* a, const RenderStyle* b); > static bool canPropertyBeInterpolated(CSSPropertyID, const RenderStyle* a, const RenderStyle* b); > static CSSPropertyID getPropertyAtIndex(int, std::optional<bool>& isShorthand); > static int getNumProperties(); >@@ -49,6 +50,7 @@ public: > > // Return true if we need to start software animation timers > static bool blendProperties(const CSSPropertyBlendingClient*, CSSPropertyID, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress); >+ static bool blendCustomProperty(const CSSPropertyBlendingClient*, const String&, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress); > }; > > } // namespace WebCore >diff --git a/Source/WebCore/page/animation/CompositeAnimation.cpp b/Source/WebCore/page/animation/CompositeAnimation.cpp >index b77a6c2fb3a3aa031f240646f7cf2913d44e050e..1b84c06fb445ac89074f9b7c46ac8253de7a1972 100644 >--- a/Source/WebCore/page/animation/CompositeAnimation.cpp >+++ b/Source/WebCore/page/animation/CompositeAnimation.cpp >@@ -101,6 +101,8 @@ void CompositeAnimation::updateTransitions(Element& element, const RenderStyle* > continue; > > CSSPropertyID prop = animation.property(); >+ // FIXME: Not supported yet. >+ ASSERT(prop != CSSPropertyCustom); > > bool all = mode == Animation::AnimateAll; > >diff --git a/Source/WebCore/page/animation/ImplicitAnimation.cpp b/Source/WebCore/page/animation/ImplicitAnimation.cpp >index c4125d5af5275ffd442358f61ed400b5e13146b4..9ad8d0cc01ed7b4a7098c58553ad0a9279d1aee8 100644 >--- a/Source/WebCore/page/animation/ImplicitAnimation.cpp >+++ b/Source/WebCore/page/animation/ImplicitAnimation.cpp >@@ -79,7 +79,10 @@ bool ImplicitAnimation::animate(CompositeAnimation& compositeAnimation, const Re > if (!animatedStyle) > animatedStyle = RenderStyle::clonePtr(targetStyle); > >- CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); >+ if (UNLIKELY(m_animatingProperty == CSSPropertyCustom)) >+ CSSPropertyAnimation::blendCustomProperty(this, animation().customProperty(), animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); >+ else >+ CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); > // FIXME: we also need to detect cases where we have to software animate for other reasons, > // such as a child using inheriting the transform. https://bugs.webkit.org/show_bug.cgi?id=23902 > >@@ -238,6 +241,8 @@ bool ImplicitAnimation::isTargetPropertyEqual(CSSPropertyID prop, const RenderSt > // So we check that here (see <https://bugs.webkit.org/show_bug.cgi?id=26706>) > if (!m_toStyle) > return false; >+ if (UNLIKELY(m_animatingProperty == CSSPropertyCustom)) >+ return CSSPropertyAnimation::customPropertyEqual(animation().customProperty(), m_toStyle.get(), targetStyle); > return CSSPropertyAnimation::propertiesEqual(prop, m_toStyle.get(), targetStyle); > } > >diff --git a/Source/WebCore/page/animation/KeyframeAnimation.cpp b/Source/WebCore/page/animation/KeyframeAnimation.cpp >index fefe0101150177b3c16e5db8c83804cb6063967e..0652fcc0268b841cf8da81b1ce926bcda5d671e0 100644 >--- a/Source/WebCore/page/animation/KeyframeAnimation.cpp >+++ b/Source/WebCore/page/animation/KeyframeAnimation.cpp >@@ -208,7 +208,10 @@ bool KeyframeAnimation::animate(CompositeAnimation& compositeAnimation, const Re > double progress = 0; > fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress); > >- CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); >+ if (UNLIKELY(propertyID == CSSPropertyCustom)) >+ CSSPropertyAnimation::blendCustomProperty(this, animation().customProperty(), animatedStyle.get(), fromStyle, toStyle, progress); >+ else >+ CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); > } > > didBlendStyle = true; >diff --git a/Source/WebCore/platform/animation/Animation.cpp b/Source/WebCore/platform/animation/Animation.cpp >index 1f8cad0efab1899534311ba3c4fd8546e300713c..be6c9c9d7d9d90eec40804f716fe5a36f4e4869b 100644 >--- a/Source/WebCore/platform/animation/Animation.cpp >+++ b/Source/WebCore/platform/animation/Animation.cpp >@@ -53,6 +53,7 @@ Animation::Animation(const Animation& o) > : RefCounted<Animation>() > , m_property(o.m_property) > , m_name(o.m_name) >+ , m_customProperty(o.m_customProperty) > , m_iterationCount(o.m_iterationCount) > , m_delay(o.m_delay) > , m_duration(o.m_duration) >@@ -84,6 +85,7 @@ Animation& Animation::operator=(const Animation& o) > m_timingFunction = o.m_timingFunction; > m_nameStyleScopeOrdinal = o.m_nameStyleScopeOrdinal; > m_property = o.m_property; >+ m_customProperty = o.m_customProperty; > m_mode = o.m_mode; > m_direction = o.m_direction; > m_fillMode = o.m_fillMode; >@@ -129,7 +131,7 @@ bool Animation::animationsMatch(const Animation& other, bool matchProperties) co > if (!result) > return false; > >- return !matchProperties || (m_mode == other.m_mode && m_property == other.m_property && m_propertySet == other.m_propertySet); >+ return !matchProperties || (m_mode == other.m_mode && m_property == other.m_property && m_propertySet == other.m_propertySet && m_customProperty == other.m_customProperty); > } > > const String& Animation::initialName() >diff --git a/Source/WebCore/platform/animation/Animation.h b/Source/WebCore/platform/animation/Animation.h >index 56bc87d872c8ef51095a98b026e8967c659bdbca..a1fbc63d47b22b6f1c46e8cdafadee078d913c89 100644 >--- a/Source/WebCore/platform/animation/Animation.h >+++ b/Source/WebCore/platform/animation/Animation.h >@@ -120,6 +120,7 @@ public: > AnimationPlayState playState() const { return static_cast<AnimationPlayState>(m_playState); } > CSSPropertyID property() const { return m_property; } > const String& unknownProperty() const { return m_unknownProperty; } >+ const String& customProperty() const { return m_customProperty; } > TimingFunction* timingFunction() const { return m_timingFunction.get(); } > AnimationMode animationMode() const { return static_cast<AnimationMode>(m_mode); } > >@@ -135,7 +136,8 @@ public: > m_nameSet = true; > } > void setPlayState(AnimationPlayState d) { m_playState = static_cast<unsigned>(d); m_playStateSet = true; } >- void setProperty(CSSPropertyID t) { m_property = t; m_propertySet = true; } >+ void setProperty(CSSPropertyID t) { ASSERT(t != CSSPropertyCustom); m_property = t; m_propertySet = true; } >+ void setCustomProperty(const String& property) { m_property = CSSPropertyCustom; m_propertySet = true; m_customProperty = property; } > void setUnknownProperty(const String& property) { m_unknownProperty = property; } > void setTimingFunction(RefPtr<TimingFunction>&& function) { m_timingFunction = WTFMove(function); m_timingFunctionSet = true; } > void setAnimationMode(AnimationMode mode) { m_mode = static_cast<unsigned>(mode); } >@@ -163,6 +165,7 @@ private: > > String m_name; > String m_unknownProperty; >+ String m_customProperty; > double m_iterationCount; > double m_delay; > double m_duration; >diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp >index df39cb23845e73bf51ac2b061cf79c935f35b94a..03969fd44515212a49e890335b4f6941aada9cf7 100644 >--- a/Source/WebCore/rendering/style/RenderStyle.cpp >+++ b/Source/WebCore/rendering/style/RenderStyle.cpp >@@ -1589,7 +1589,7 @@ void RenderStyle::adjustTransitions() > // This is an O(n^2) algorithm but the lists tend to be short, so it is probably OK. > for (size_t i = 0; i < transitionList->size(); ++i) { > for (size_t j = i + 1; j < transitionList->size(); ++j) { >- if (transitionList->animation(i).property() == transitionList->animation(j).property()) { >+ if (transitionList->animation(i).property() == transitionList->animation(j).property() && transitionList->animation(i).property() != CSSPropertyCustom) { > // toss i > transitionList->remove(i); > j = i; >@@ -1614,6 +1614,7 @@ AnimationList& RenderStyle::ensureTransitions() > > const Animation* RenderStyle::transitionForProperty(CSSPropertyID property) const > { >+ ASSERT(property != CSSPropertyCustom); > auto* transitions = this->transitions(); > if (!transitions) > return nullptr; >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 73e014f5feb9243020ca9d565f1b95f071244041..031bcf681e22fbb80210d9791a3be9797469cacc 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,17 @@ >+2018-12-10 Justin Michaud <justin_michaud@apple.com> >+ >+ CSS Properties and Values API should support animating registered custom properties without any references >+ https://bugs.webkit.org/show_bug.cgi?id=192334 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * css-custom-properties-api/length-animation-expected.txt: Added. >+ * css-custom-properties-api/length-animation-multiple-expected.txt: Added. >+ * css-custom-properties-api/length-animation-multiple.html: Added. >+ * css-custom-properties-api/length-animation-unregistered-expected.txt: Added. >+ * css-custom-properties-api/length-animation-unregistered.html: Added. >+ * css-custom-properties-api/length-animation.html: Added. >+ > 2018-12-10 Rob Buis <rbuis@igalia.com> > > XMLHttpRequest removes spaces from content-types before processing >diff --git a/LayoutTests/css-custom-properties-api/length-animation-expected.txt b/LayoutTests/css-custom-properties-api/length-animation-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..085fd1cd5de891446a2d6af5fc5eac69df4c72bc >--- /dev/null >+++ b/LayoutTests/css-custom-properties-api/length-animation-expected.txt >@@ -0,0 +1,4 @@ >+This text should transition from 50px to 500px over a period of 1 seconds. >+ >+PASS Verify CSS variable values during transition >+ >diff --git a/LayoutTests/css-custom-properties-api/length-animation-multiple-expected.txt b/LayoutTests/css-custom-properties-api/length-animation-multiple-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..085fd1cd5de891446a2d6af5fc5eac69df4c72bc >--- /dev/null >+++ b/LayoutTests/css-custom-properties-api/length-animation-multiple-expected.txt >@@ -0,0 +1,4 @@ >+This text should transition from 50px to 500px over a period of 1 seconds. >+ >+PASS Verify CSS variable values during transition >+ >diff --git a/LayoutTests/css-custom-properties-api/length-animation-multiple.html b/LayoutTests/css-custom-properties-api/length-animation-multiple.html >new file mode 100644 >index 0000000000000000000000000000000000000000..db32fc4ee1490268a812c914e901b62ab440df49 >--- /dev/null >+++ b/LayoutTests/css-custom-properties-api/length-animation-multiple.html >@@ -0,0 +1,88 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSCustomPropertiesAndValuesEnabled=true experimental:webAnimations=true experimental:webAnimationsWithCSSIntegration=true ] --> >+<html> >+<head> >+ <title>CSS Multiple Registered Property Interpolation</title> >+ >+ <script src="../resources/testharness.js"></script> >+ <script src="../resources/testharnessreport.js"></script> >+ <style> >+ #target >+ { >+ transition-property: --my-length, --my-length2; >+ transition-duration: 1s, 0.6s; >+ } >+ #target >+ { >+ --my-length: 50px; >+ --my-length2: 60px; >+ width: var(--my-length); >+ } >+ #target.changed >+ { >+ --my-length: 500px; >+ --my-length2: 600px; >+ } >+ </style> >+</head> >+<body> >+ >+ <div id="target">This text should transition from 50px to 500px over a period of 1 seconds.</div> >+ >+<script type="text/javascript"> >+ "use strict"; >+ >+ CSS.registerProperty({ >+ name: '--my-length', >+ syntax: '<length>', >+ inherits: false, >+ initialValue: '0px' >+ }); >+ CSS.registerProperty({ >+ name: '--my-length2', >+ syntax: '<length>', >+ inherits: false, >+ initialValue: '0px' >+ }); >+ >+ // Force an initial layout pass >+ document.documentElement.offsetHeight; >+ >+ test(function() { >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "50px"); >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--my-length2").trim(), "60px"); >+ >+ // Trigger transition >+ target.className = "changed"; >+ assert_equals(target.getAnimations().length, 2); >+ let anim = target.getAnimations()[0]; >+ let anim2 = target.getAnimations()[1]; >+ >+ if (anim.id == "--my-length2") { >+ [anim, anim2] = [anim2, anim]; >+ } >+ >+ anim.pause(); >+ anim2.pause(); >+ >+ anim.currentTime = 500; >+ anim2.currentTime = 500; >+ assert_not_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "500px"); >+ assert_not_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "50px"); >+ assert_not_equals(window.getComputedStyle(target).getPropertyValue("--my-length2").trim(), "600px"); >+ assert_not_equals(window.getComputedStyle(target).getPropertyValue("--my-length2").trim(), "60px"); >+ >+ anim.currentTime = 1000; >+ anim2.currentTime = 600; >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "500px"); >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--my-length2").trim(), "600px"); >+ >+ anim.currentTime = 0; >+ anim2.currentTime = 0; >+ anim.play(); >+ anim2.play(); >+ >+ }, "Verify CSS variable values during transition"); >+</script> >+ >+</body> >+</html> >diff --git a/LayoutTests/css-custom-properties-api/length-animation-unregistered-expected.txt b/LayoutTests/css-custom-properties-api/length-animation-unregistered-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..085fd1cd5de891446a2d6af5fc5eac69df4c72bc >--- /dev/null >+++ b/LayoutTests/css-custom-properties-api/length-animation-unregistered-expected.txt >@@ -0,0 +1,4 @@ >+This text should transition from 50px to 500px over a period of 1 seconds. >+ >+PASS Verify CSS variable values during transition >+ >diff --git a/LayoutTests/css-custom-properties-api/length-animation-unregistered.html b/LayoutTests/css-custom-properties-api/length-animation-unregistered.html >new file mode 100644 >index 0000000000000000000000000000000000000000..7385fb1eb1e319d42b6fdf28eb700458e349ed24 >--- /dev/null >+++ b/LayoutTests/css-custom-properties-api/length-animation-unregistered.html >@@ -0,0 +1,56 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSCustomPropertiesAndValuesEnabled=true experimental:webAnimations=true experimental:webAnimationsWithCSSIntegration=true ] --> >+<html> >+<head> >+ <title>CSS Unregistered Property Interpolation</title> >+ >+ <script src="../resources/testharness.js"></script> >+ <script src="../resources/testharnessreport.js"></script> >+ <style> >+ #target >+ { >+ transition-property: width; >+ transition-duration: 1s; >+ } >+ #target >+ { >+ --value: 50px; >+ width: var(--value); >+ } >+ #target.changed >+ { >+ --value: 500px; >+ } >+ </style> >+</head> >+<body> >+ >+ <div id="target">This text should transition from 50px to 500px over a period of 1 seconds.</div> >+ >+<script type="text/javascript"> >+ "use strict"; >+ >+ // Force an initial layout pass >+ document.documentElement.offsetHeight; >+ >+ test(function() { >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--value").trim(), "50px", "--value is 50px before transition runs"); >+ >+ // Trigger transition >+ target.className = "changed"; >+ assert_equals(target.getAnimations().length, 1); >+ const anim = target.getAnimations()[0]; >+ >+ anim.pause(); >+ >+ anim.currentTime = 501; >+ // We don't test for the 50% rule, only that there are no intermediate values >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--value").trim(), "500px"); >+ >+ anim.currentTime = 0; >+ anim.play(); >+ >+ }, "Verify CSS variable values during transition"); >+</script> >+ >+</body> >+</html> >diff --git a/LayoutTests/css-custom-properties-api/length-animation.html b/LayoutTests/css-custom-properties-api/length-animation.html >new file mode 100644 >index 0000000000000000000000000000000000000000..e95e30df78f68e33146ba2b4b898df8427748677 >--- /dev/null >+++ b/LayoutTests/css-custom-properties-api/length-animation.html >@@ -0,0 +1,66 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSCustomPropertiesAndValuesEnabled=true experimental:webAnimations=true experimental:webAnimationsWithCSSIntegration=true ] --> >+<html> >+<head> >+ <title>CSS Registered Property Interpolation</title> >+ >+ <script src="../resources/testharness.js"></script> >+ <script src="../resources/testharnessreport.js"></script> >+ <style> >+ #target >+ { >+ transition-property: --my-length; >+ transition-duration: 1s; >+ } >+ #target >+ { >+ --my-length: 50px; >+ width: var(--my-length); >+ } >+ #target.changed >+ { >+ --my-length: 500px; >+ } >+ </style> >+</head> >+<body> >+ >+ <div id="target">This text should transition from 50px to 500px over a period of 1 seconds.</div> >+ >+<script type="text/javascript"> >+ "use strict"; >+ >+ CSS.registerProperty({ >+ name: '--my-length', >+ syntax: '<length>', >+ inherits: false, >+ initialValue: '0px' >+ }); >+ >+ // Force an initial layout pass >+ document.documentElement.offsetHeight; >+ >+ test(function() { >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "50px"); >+ >+ // Trigger transition >+ target.className = "changed"; >+ assert_equals(target.getAnimations().length, 1); >+ const anim = target.getAnimations()[0]; >+ >+ anim.pause(); >+ >+ anim.currentTime = 500; >+ assert_not_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "500px"); >+ assert_not_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "50px"); >+ >+ anim.currentTime = 1000; >+ assert_equals(window.getComputedStyle(target).getPropertyValue("--my-length").trim(), "500px"); >+ >+ anim.currentTime = 0; >+ anim.play(); >+ >+ }, "Verify CSS variable values during transition"); >+</script> >+ >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 192334
:
356699
|
356703
|
356705
|
356707
|
356711
|
356720
| 357034