summaryrefslogtreecommitdiff
path: root/Source/WebCore/svg/SVGAnimateElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/svg/SVGAnimateElement.cpp')
-rw-r--r--Source/WebCore/svg/SVGAnimateElement.cpp470
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