diff options
Diffstat (limited to 'Source/WebCore/svg/SVGAnimateElement.cpp')
-rw-r--r-- | Source/WebCore/svg/SVGAnimateElement.cpp | 470 |
1 files changed, 25 insertions, 445 deletions
diff --git a/Source/WebCore/svg/SVGAnimateElement.cpp b/Source/WebCore/svg/SVGAnimateElement.cpp index 1789ff3e9..110284e9a 100644 --- a/Source/WebCore/svg/SVGAnimateElement.cpp +++ b/Source/WebCore/svg/SVGAnimateElement.cpp @@ -1,462 +1,42 @@ /* - * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org> - * Copyright (C) 2008 Apple Inc. All rights reserved. - * Copyright (C) Research In Motion Limited 2011. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" - -#if ENABLE(SVG) #include "SVGAnimateElement.h" -#include "CSSParser.h" -#include "CSSPropertyNames.h" -#include "QualifiedName.h" -#include "RenderObject.h" -#include "SVGAnimatorFactory.h" -#include "SVGElement.h" -#include "SVGNames.h" -#include "StyleProperties.h" - namespace WebCore { SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document) - : SVGAnimationElement(tagName, document) - , m_animatedPropertyType(AnimatedString) -{ - ASSERT(hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::setTag) || hasTagName(SVGNames::animateColorTag) || hasTagName(SVGNames::animateTransformTag)); -} - -PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document& document) -{ - return adoptRef(new SVGAnimateElement(tagName, document)); -} - -SVGAnimateElement::~SVGAnimateElement() -{ -} - -bool SVGAnimateElement::hasValidAttributeType() -{ - SVGElement* targetElement = this->targetElement(); - if (!targetElement) - return false; - - return m_animatedPropertyType != AnimatedUnknown && !hasInvalidCSSAttributeType(); -} - -AnimatedPropertyType SVGAnimateElement::determineAnimatedPropertyType(SVGElement* targetElement) const -{ - ASSERT(targetElement); - - Vector<AnimatedPropertyType> propertyTypes; - targetElement->animatedPropertyTypeForAttribute(attributeName(), propertyTypes); - if (propertyTypes.isEmpty()) - return AnimatedUnknown; - - ASSERT(propertyTypes.size() <= 2); - AnimatedPropertyType type = propertyTypes[0]; - if (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor) - return AnimatedUnknown; - - // Animations of transform lists are not allowed for <animate> or <set> - // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties - if (type == AnimatedTransformList && !hasTagName(SVGNames::animateTransformTag)) - return AnimatedUnknown; - - // Fortunately there's just one special case needed here: SVGMarkerElements orientAttr, which - // corresponds to SVGAnimatedAngle orientAngle and SVGAnimatedEnumeration orientType. We have to - // figure out whose value to change here. - if (targetElement->hasTagName(SVGNames::markerTag) && type == AnimatedAngle) { - ASSERT(propertyTypes.size() == 2); - ASSERT(propertyTypes[0] == AnimatedAngle); - ASSERT(propertyTypes[1] == AnimatedEnumeration); - } else if (propertyTypes.size() == 2) - ASSERT(propertyTypes[0] == propertyTypes[1]); - - return type; -} - -void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement* resultElement) -{ - ASSERT(resultElement); - SVGElement* targetElement = this->targetElement(); - if (!targetElement) - return; - - ASSERT(m_animatedPropertyType == determineAnimatedPropertyType(targetElement)); - - ASSERT(percentage >= 0 && percentage <= 1); - ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGNames::animateTransformTag)); - ASSERT(m_animatedPropertyType != AnimatedUnknown); - ASSERT(m_animator); - ASSERT(m_animator->type() == m_animatedPropertyType); - ASSERT(m_fromType); - ASSERT(m_fromType->type() == m_animatedPropertyType); - ASSERT(m_toType); - - SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElement); - ASSERT(resultAnimationElement->m_animatedType); - ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyType); - - if (hasTagName(SVGNames::setTag)) - percentage = 1; - - if (calcMode() == CalcModeDiscrete) - percentage = percentage < 0.5 ? 0 : 1; - - // Target element might have changed. - m_animator->setContextElement(targetElement); - - // Be sure to detach list wrappers before we modfiy their underlying value. If we'd do - // if after calculateAnimatedValue() ran the cached pointers in the list propery tear - // offs would point nowhere, and we couldn't create copies of those values anymore, - // while detaching. This is covered by assertions, moving this down would fire them. - if (!m_animatedProperties.isEmpty()) - m_animator->animValWillChange(m_animatedProperties); - - // Values-animation accumulates using the last values entry corresponding to the end of duration time. - SVGAnimatedType* toAtEndOfDurationType = m_toAtEndOfDurationType ? m_toAtEndOfDurationType.get() : m_toType.get(); - m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromType.get(), m_toType.get(), toAtEndOfDurationType, resultAnimationElement->m_animatedType.get()); -} - -bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString) -{ - if (toAtEndOfDurationString.isEmpty()) - return false; - m_toAtEndOfDurationType = ensureAnimator()->constructFromString(toAtEndOfDurationString); - return true; -} - -bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString) -{ - SVGElement* targetElement = this->targetElement(); - if (!targetElement) - return false; - - determinePropertyValueTypes(fromString, toString); - ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString, toString); - ASSERT(m_animatedPropertyType == m_animator->type()); - return true; -} - -bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString) -{ - SVGElement* targetElement = this->targetElement(); - if (!targetElement) - return false; - - if (animationMode() == ByAnimation && !isAdditive()) - return false; - - // from-by animation may only be used with attributes that support addition (e.g. most numeric attributes). - if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddition()) - return false; - - ASSERT(!hasTagName(SVGNames::setTag)); - - determinePropertyValueTypes(fromString, byString); - ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString, byString); - ASSERT(m_animatedPropertyType == m_animator->type()); - return true; -} - -#ifndef NDEBUG -static inline bool propertyTypesAreConsistent(AnimatedPropertyType expectedPropertyType, const SVGElementAnimatedPropertyList& animatedTypes) -{ - SVGElementAnimatedPropertyList::const_iterator end = animatedTypes.end(); - for (SVGElementAnimatedPropertyList::const_iterator it = animatedTypes.begin(); it != end; ++it) { - for (size_t i = 0; i < it->properties.size(); ++i) { - if (expectedPropertyType != it->properties[i]->animatedPropertyType()) { - // This is the only allowed inconsistency. SVGAnimatedAngleAnimator handles both SVGAnimatedAngle & SVGAnimatedEnumeration for markers orient attribute. - if (expectedPropertyType == AnimatedAngle && it->properties[i]->animatedPropertyType() == AnimatedEnumeration) - return true; - return false; - } - } - } - - return true; -} -#endif - -void SVGAnimateElement::resetAnimatedType() -{ - SVGAnimatedTypeAnimator* animator = ensureAnimator(); - ASSERT(m_animatedPropertyType == animator->type()); - - SVGElement* targetElement = this->targetElement(); - const QualifiedName& attributeName = this->attributeName(); - ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName); - - if (shouldApply == DontApplyAnimation) - return; - - if (shouldApply == ApplyXMLAnimation) { - // SVG DOM animVal animation code-path. - m_animatedProperties = animator->findAnimatedPropertiesForAttributeName(targetElement, attributeName); - ASSERT(!m_animatedProperties.isEmpty()); - - ASSERT(propertyTypesAreConsistent(m_animatedPropertyType, m_animatedProperties)); - if (!m_animatedType) - m_animatedType = animator->startAnimValAnimation(m_animatedProperties); - else { - animator->resetAnimValToBaseVal(m_animatedProperties, m_animatedType.get()); - animator->animValDidChange(m_animatedProperties); - } - return; - } - - // CSS properties animation code-path. - ASSERT(m_animatedProperties.isEmpty()); - String baseValue; - - if (shouldApply == ApplyCSSAnimation) { - ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName)); - computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue); - } - - if (!m_animatedType) - m_animatedType = animator->constructFromString(baseValue); - else - m_animatedType->setValueAsString(attributeName, baseValue); -} - -static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value) + : SVGAnimateElementBase(tagName, document) { - ASSERT(!targetElement->m_deletionHasBegun); - - if (!targetElement->ensureAnimatedSMILStyleProperties().setProperty(id, value, false, 0)) - return; - - targetElement->setNeedsStyleRecalc(SyntheticStyleChange); -} - -static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id) -{ - ASSERT(!targetElement->m_deletionHasBegun); - targetElement->ensureAnimatedSMILStyleProperties().removeProperty(id); - targetElement->setNeedsStyleRecalc(SyntheticStyleChange); -} - -static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString) -{ - ASSERT(targetElement); - if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode()) - return; - - CSSPropertyID id = cssPropertyID(attributeName.localName()); - - SVGElementInstance::InstanceUpdateBlocker blocker(targetElement); - applyCSSPropertyToTarget(targetElement, id, valueAsString); - - // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt. - const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); - const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { - if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement()) - applyCSSPropertyToTarget(shadowTreeElement, id, valueAsString); - } -} - -static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName) -{ - ASSERT(targetElement); - if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode()) - return; - - CSSPropertyID id = cssPropertyID(attributeName.localName()); - - SVGElementInstance::InstanceUpdateBlocker blocker(targetElement); - removeCSSPropertyFromTarget(targetElement, id); - - // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt. - const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); - const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { - if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement()) - removeCSSPropertyFromTarget(shadowTreeElement, id); - } -} - -static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName) -{ - ASSERT(!targetElement->m_deletionHasBegun); - targetElement->svgAttributeChanged(attributeName); -} - -static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName) -{ - ASSERT(targetElement); - if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode()) - return; - - SVGElementInstance::InstanceUpdateBlocker blocker(targetElement); - notifyTargetAboutAnimValChange(targetElement, attributeName); - - // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt. - const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); - const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { - if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement()) - notifyTargetAboutAnimValChange(shadowTreeElement, attributeName); - } -} - -void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement) -{ - if (!m_animatedType) - return; - - if (!targetElement) { - m_animatedType = nullptr; - return; - } - - if (m_animatedProperties.isEmpty()) { - // CSS properties animation code-path. - removeCSSPropertyFromTargetAndInstances(targetElement, attributeName()); - m_animatedType = nullptr; - return; - } - - // SVG DOM animVal animation code-path. - if (m_animator) { - m_animator->stopAnimValAnimation(m_animatedProperties); - notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName()); - } - - m_animatedProperties.clear(); - m_animatedType = nullptr; + ASSERT(hasTagName(SVGNames::animateTag)); } -void SVGAnimateElement::applyResultsToTarget() +Ref<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document& document) { - ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGNames::animateTransformTag)); - ASSERT(m_animatedPropertyType != AnimatedUnknown); - ASSERT(m_animator); - - // Early exit if our animated type got destructed by a previous endedActiveInterval(). - if (!m_animatedType) - return; - - if (m_animatedProperties.isEmpty()) { - // CSS properties animation code-path. - // Convert the result of the animation to a String and apply it as CSS property on the target & all instances. - applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m_animatedType->valueAsString()); - return; - } - - // SVG DOM animVal animation code-path. - // At this point the SVG DOM values are already changed, unlike for CSS. - // We only have to trigger update notifications here. - m_animator->animValDidChange(m_animatedProperties); - notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName()); -} - -bool SVGAnimateElement::animatedPropertyTypeSupportsAddition() const -{ - // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties. - switch (m_animatedPropertyType) { - case AnimatedBoolean: - case AnimatedEnumeration: - case AnimatedPreserveAspectRatio: - case AnimatedString: - case AnimatedUnknown: - return false; - case AnimatedAngle: - case AnimatedColor: - case AnimatedInteger: - case AnimatedIntegerOptionalInteger: - case AnimatedLength: - case AnimatedLengthList: - case AnimatedNumber: - case AnimatedNumberList: - case AnimatedNumberOptionalNumber: - case AnimatedPath: - case AnimatedPoints: - case AnimatedRect: - case AnimatedTransformList: - return true; - default: - RELEASE_ASSERT_NOT_REACHED(); - return true; - } -} - -bool SVGAnimateElement::isAdditive() const -{ - if (animationMode() == ByAnimation || animationMode() == FromByAnimation) - if (!animatedPropertyTypeSupportsAddition()) - return false; - - return SVGAnimationElement::isAdditive(); -} - -float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString) -{ - // FIXME: A return value of float is not enough to support paced animations on lists. - SVGElement* targetElement = this->targetElement(); - if (!targetElement) - return -1; - - return ensureAnimator()->calculateDistance(fromString, toString); -} - -void SVGAnimateElement::setTargetElement(SVGElement* target) -{ - SVGAnimationElement::setTargetElement(target); - resetAnimatedPropertyType(); -} - -void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName) -{ - SVGAnimationElement::setAttributeName(attributeName); - resetAnimatedPropertyType(); -} - -void SVGAnimateElement::resetAnimatedPropertyType() -{ - ASSERT(!m_animatedType); - m_fromType = nullptr; - m_toType = nullptr; - m_toAtEndOfDurationType = nullptr; - m_animator = nullptr; - m_animatedPropertyType = targetElement() ? determineAnimatedPropertyType(targetElement()) : AnimatedString; -} - -SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator() -{ - if (!m_animator) - m_animator = SVGAnimatorFactory::create(this, targetElement(), m_animatedPropertyType); - ASSERT(m_animatedPropertyType == m_animator->type()); - return m_animator.get(); -} - -bool isSVGAnimateElement(const Node& node) -{ - return node.hasTagName(SVGNames::animateTag) - || node.hasTagName(SVGNames::animateColorTag) - || node.hasTagName(SVGNames::animateTransformTag) - || node.hasTagName(SVGNames::setTag); -} - + return adoptRef(*new SVGAnimateElement(tagName, document)); } -#endif // ENABLE(SVG) +} // namespace WebCore |