blob: cc8d3497c4ea139854d3dea77c297c219b17493f [file] [log] [blame]
/*
* 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.
*
* 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.
*
* 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.
*/
#include "config.h"
#if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
#include "SVGAnimateElement.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSParser.h"
#include "CSSPropertyNames.h"
#include "QualifiedName.h"
#include "RenderObject.h"
#include "SVGAnimatorFactory.h"
#include "SVGNames.h"
#include "SVGStyledElement.h"
using namespace std;
namespace WebCore {
SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* document)
: SVGAnimationElement(tagName, document)
, m_animatedAttributeType(AnimatedString)
, m_fromPropertyValueType(RegularPropertyValue)
, m_toPropertyValueType(RegularPropertyValue)
{
ASSERT(hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::setTag) || hasTagName(SVGNames::animateColorTag));
}
PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document* document)
{
return adoptRef(new SVGAnimateElement(tagName, document));
}
SVGAnimateElement::~SVGAnimateElement()
{
}
void SVGAnimateElement::adjustForCurrentColor(SVGElement* targetElement, Color& color)
{
ASSERT(targetElement);
if (RenderObject* targetRenderer = targetElement->renderer())
color = targetRenderer->style()->visitedDependentColor(CSSPropertyColor);
else
color = Color();
}
static inline void getPropertyValue(SVGElement* svgParent, const QualifiedName& attributeName, String& value)
{
ASSERT(svgParent->isStyled());
value = computedStyle(svgParent)->getPropertyValue(cssPropertyID(attributeName.localName()));
}
void SVGAnimateElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
{
// FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
// In the future we might want to work with the value type directly to avoid the String parsing.
ASSERT(targetElement);
Element* parent = targetElement->parentElement();
if (!parent || !parent->isSVGElement())
return;
SVGElement* svgParent = static_cast<SVGElement*>(parent);
if (svgParent->isStyled())
getPropertyValue(svgParent, attributeName, value);
}
bool SVGAnimateElement::hasValidAttributeType() const
{
SVGElement* targetElement = this->targetElement();
if (!targetElement)
return false;
return determineAnimatedAttributeType(targetElement) != AnimatedUnknown;
}
AnimatedAttributeType SVGAnimateElement::determineAnimatedAttributeType(SVGElement* targetElement) const
{
ASSERT(targetElement);
AnimatedAttributeType type = targetElement->animatedPropertyTypeForAttribute(attributeName());
if (type == AnimatedUnknown || (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor))
return AnimatedUnknown;
// FIXME: Animator for AnimatedBoolean and AnimatedEnumeration missing.
switch (type) {
case AnimatedAngle:
return AnimatedAngle;
case AnimatedBoolean:
case AnimatedEnumeration:
case AnimatedString:
return AnimatedString;
case AnimatedColor:
return AnimatedColor;
case AnimatedLength:
return AnimatedLength;
case AnimatedLengthList:
return AnimatedLengthList;
case AnimatedInteger:
case AnimatedNumber:
return AnimatedNumber;
case AnimatedNumberList:
return AnimatedNumberList;
case AnimatedNumberOptionalNumber:
return AnimatedNumberOptionalNumber;
case AnimatedPath:
return AnimatedPath;
case AnimatedPoints:
return AnimatedPoints;
case AnimatedPreserveAspectRatio:
return AnimatedPreserveAspectRatio;
case AnimatedRect:
return AnimatedRect;
case AnimatedTransformList:
case AnimatedUnknown:
// Animations of transform lists are not allowed for <animate> or <set>
// http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
return AnimatedUnknown;
}
ASSERT_NOT_REACHED();
return AnimatedUnknown;
}
void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement)
{
ASSERT(percentage >= 0 && percentage <= 1);
ASSERT(resultElement);
SVGElement* targetElement = this->targetElement();
if (!targetElement)
return;
if (hasTagName(SVGNames::setTag))
percentage = 1;
if (!resultElement->hasTagName(SVGNames::animateTag) && !resultElement->hasTagName(SVGNames::animateColorTag)
&& !resultElement->hasTagName(SVGNames::setTag))
return;
SVGAnimateElement* results = static_cast<SVGAnimateElement*>(resultElement);
// Can't accumulate over a string property.
if (results->m_animatedAttributeType == AnimatedString && m_animatedAttributeType != AnimatedString)
return;
switch (m_animatedAttributeType) {
case AnimatedAngle:
case AnimatedColor:
case AnimatedLength:
case AnimatedLengthList:
case AnimatedNumber:
case AnimatedNumberList:
case AnimatedNumberOptionalNumber:
case AnimatedPath:
case AnimatedPoints:
case AnimatedPreserveAspectRatio:
case AnimatedRect:
case AnimatedString: {
ASSERT(m_animator);
ASSERT(results->m_animatedType);
// Target element might have changed.
m_animator->setContextElement(targetElement);
m_animator->calculateAnimatedValue(percentage, repeat, m_fromType, m_toType, results->m_animatedType);
return;
}
default:
break;
}
ASSERT_NOT_REACHED();
}
static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
{
ASSERT(targetElement);
DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit"));
if (value.isEmpty() || value != inherit || !targetElement->isStyled())
return false;
return SVGStyledElement::isAnimatableCSSProperty(attributeName);
}
static bool attributeValueIsCurrentColor(const String& value)
{
DEFINE_STATIC_LOCAL(const AtomicString, currentColor, ("currentColor"));
return value == currentColor;
}
void SVGAnimateElement::determinePropertyValueTypes(const String& from, const String& to)
{
SVGElement* targetElement = this->targetElement();
ASSERT(targetElement);
if (inheritsFromProperty(targetElement, attributeName(), from))
m_fromPropertyValueType = InheritValue;
if (inheritsFromProperty(targetElement, attributeName(), to))
m_toPropertyValueType = InheritValue;
if (m_animatedAttributeType != AnimatedColor)
return;
if (attributeValueIsCurrentColor(from))
m_fromPropertyValueType = CurrentColorValue;
if (attributeValueIsCurrentColor(to))
m_toPropertyValueType = CurrentColorValue;
}
bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
{
SVGElement* targetElement = this->targetElement();
if (!targetElement)
return false;
// FIXME: Needs more solid way determine target attribute type.
m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
switch (m_animatedAttributeType) {
case AnimatedAngle:
case AnimatedColor:
case AnimatedLength:
case AnimatedLengthList:
case AnimatedNumber:
case AnimatedNumberList:
case AnimatedNumberOptionalNumber:
case AnimatedPath:
case AnimatedPoints:
case AnimatedPreserveAspectRatio:
case AnimatedRect:
case AnimatedString:
ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString, toString);
return true;
default:
break;
}
ASSERT_NOT_REACHED();
return false;
}
bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
{
SVGElement* targetElement = this->targetElement();
if (!targetElement)
return false;
ASSERT(!hasTagName(SVGNames::setTag));
m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
switch (m_animatedAttributeType) {
case AnimatedAngle:
case AnimatedColor:
case AnimatedLength:
case AnimatedLengthList:
case AnimatedNumber:
case AnimatedNumberList:
case AnimatedNumberOptionalNumber:
case AnimatedPoints:
case AnimatedPreserveAspectRatio:
case AnimatedRect:
case AnimatedString:
ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString, byString);
return true;
case AnimatedPath:
ASSERT_NOT_REACHED(); // This state is not reachable for now.
break;
default:
break;
}
ASSERT_NOT_REACHED();
return false;
}
void SVGAnimateElement::resetToBaseValue(const String& baseString)
{
SVGElement* targetElement = this->targetElement();
ASSERT(targetElement);
m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
switch (m_animatedAttributeType) {
case AnimatedAngle:
case AnimatedColor:
case AnimatedLength:
case AnimatedLengthList:
case AnimatedNumber:
case AnimatedNumberList:
case AnimatedNumberOptionalNumber:
case AnimatedPath:
case AnimatedPoints:
case AnimatedPreserveAspectRatio:
case AnimatedRect:
case AnimatedString: {
if (!m_animatedType)
m_animatedType = ensureAnimator()->constructFromString(baseString);
else
m_animatedType->setValueAsString(attributeName(), baseString);
return;
}
default:
break;
}
ASSERT_NOT_REACHED();
}
void SVGAnimateElement::applyResultsToTarget()
{
String valueToApply;
switch (m_animatedAttributeType) {
case AnimatedAngle:
case AnimatedColor:
case AnimatedLength:
case AnimatedLengthList:
case AnimatedNumber:
case AnimatedNumberList:
case AnimatedNumberOptionalNumber:
case AnimatedPath:
case AnimatedPoints:
case AnimatedPreserveAspectRatio:
case AnimatedRect:
case AnimatedString:
valueToApply = m_animatedType->valueAsString();
break;
default:
ASSERT_NOT_REACHED();
}
setTargetAttributeAnimatedValue(valueToApply);
}
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;
m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
switch (m_animatedAttributeType) {
case AnimatedAngle:
case AnimatedColor:
case AnimatedLength:
case AnimatedLengthList:
case AnimatedNumber:
case AnimatedNumberList:
case AnimatedNumberOptionalNumber:
case AnimatedPath:
case AnimatedPoints:
case AnimatedPreserveAspectRatio:
case AnimatedRect:
case AnimatedString:
return ensureAnimator()->calculateDistance(fromString, toString);
default:
break;
}
ASSERT_NOT_REACHED();
return -1;
}
SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
{
if (!m_animator)
m_animator = SVGAnimatorFactory::create(this, targetElement(), m_animatedAttributeType);
return m_animator.get();
}
}
#endif // ENABLE(SVG)