Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2008 Apple Inc. All rights reserved.
      5  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 
     25 #include "core/svg/SVGAnimateElement.h"
     26 
     27 #include "core/CSSPropertyNames.h"
     28 #include "core/css/StylePropertySet.h"
     29 #include "core/css/parser/CSSParser.h"
     30 #include "core/dom/Document.h"
     31 #include "core/dom/QualifiedName.h"
     32 #include "core/svg/SVGAnimatedTypeAnimator.h"
     33 #include "core/svg/SVGDocumentExtensions.h"
     34 
     35 namespace blink {
     36 
     37 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document)
     38     : SVGAnimationElement(tagName, document)
     39 {
     40 }
     41 
     42 PassRefPtrWillBeRawPtr<SVGAnimateElement> SVGAnimateElement::create(Document& document)
     43 {
     44     return adoptRefWillBeNoop(new SVGAnimateElement(SVGNames::animateTag, document));
     45 }
     46 
     47 SVGAnimateElement::~SVGAnimateElement()
     48 {
     49 }
     50 
     51 AnimatedPropertyType SVGAnimateElement::animatedPropertyType()
     52 {
     53     return ensureAnimator()->type();
     54 }
     55 
     56 bool SVGAnimateElement::hasValidAttributeType()
     57 {
     58     SVGElement* targetElement = this->targetElement();
     59     if (!targetElement)
     60         return false;
     61 
     62     return animatedPropertyType() != AnimatedUnknown && !hasInvalidCSSAttributeType();
     63 }
     64 
     65 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement* resultElement)
     66 {
     67     ASSERT(resultElement);
     68     SVGElement* targetElement = this->targetElement();
     69     if (!targetElement || !isSVGAnimateElement(*resultElement))
     70         return;
     71 
     72     ASSERT(percentage >= 0 && percentage <= 1);
     73     ASSERT(m_animator);
     74     ASSERT(animatedPropertyType() != AnimatedTransformList || isSVGAnimateTransformElement(*this));
     75     ASSERT(animatedPropertyType() != AnimatedUnknown);
     76     ASSERT(m_fromProperty);
     77     ASSERT(m_fromProperty->type() == animatedPropertyType());
     78     ASSERT(m_toProperty);
     79 
     80     SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElement);
     81     ASSERT(resultAnimationElement->m_animatedProperty);
     82     ASSERT(resultAnimationElement->animatedPropertyType() == animatedPropertyType());
     83 
     84     if (isSVGSetElement(*this))
     85         percentage = 1;
     86 
     87     if (calcMode() == CalcModeDiscrete)
     88         percentage = percentage < 0.5 ? 0 : 1;
     89 
     90     // Target element might have changed.
     91     m_animator->setContextElement(targetElement);
     92 
     93     // Values-animation accumulates using the last values entry corresponding to the end of duration time.
     94     SVGPropertyBase* toAtEndOfDurationProperty = m_toAtEndOfDurationProperty ? m_toAtEndOfDurationProperty.get() : m_toProperty.get();
     95     m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromProperty.get(), m_toProperty.get(), toAtEndOfDurationProperty, resultAnimationElement->m_animatedProperty.get());
     96 }
     97 
     98 bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
     99 {
    100     if (toAtEndOfDurationString.isEmpty())
    101         return false;
    102     m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndOfDurationString);
    103     return true;
    104 }
    105 
    106 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
    107 {
    108     SVGElement* targetElement = this->targetElement();
    109     if (!targetElement)
    110         return false;
    111 
    112     determinePropertyValueTypes(fromString, toString);
    113     ensureAnimator()->calculateFromAndToValues(m_fromProperty, m_toProperty, fromString, toString);
    114     return true;
    115 }
    116 
    117 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
    118 {
    119     SVGElement* targetElement = this->targetElement();
    120     if (!targetElement)
    121         return false;
    122 
    123     if (animationMode() == ByAnimation && !isAdditive())
    124         return false;
    125 
    126     // from-by animation may only be used with attributes that support addition (e.g. most numeric attributes).
    127     if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddition())
    128         return false;
    129 
    130     ASSERT(!isSVGSetElement(*this));
    131 
    132     determinePropertyValueTypes(fromString, byString);
    133     ensureAnimator()->calculateFromAndByValues(m_fromProperty, m_toProperty, fromString, byString);
    134     return true;
    135 }
    136 
    137 namespace {
    138 
    139 WillBeHeapVector<RawPtrWillBeMember<SVGElement> > findElementInstances(SVGElement* targetElement)
    140 {
    141     ASSERT(targetElement);
    142     WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements;
    143 
    144     animatedElements.append(targetElement);
    145 
    146     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
    147     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
    148     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
    149         if (SVGElement* shadowTreeElement = *it)
    150             animatedElements.append(shadowTreeElement);
    151     }
    152 
    153     return animatedElements;
    154 }
    155 
    156 }
    157 
    158 void SVGAnimateElement::resetAnimatedType()
    159 {
    160     SVGAnimatedTypeAnimator* animator = ensureAnimator();
    161 
    162     SVGElement* targetElement = this->targetElement();
    163     const QualifiedName& attributeName = this->attributeName();
    164     ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
    165 
    166     if (shouldApply == DontApplyAnimation)
    167         return;
    168 
    169     if (shouldApply == ApplyXMLAnimation) {
    170         // SVG DOM animVal animation code-path.
    171         WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements = findElementInstances(targetElement);
    172         ASSERT(!animatedElements.isEmpty());
    173 
    174         WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator end = animatedElements.end();
    175         for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator it = animatedElements.begin(); it != end; ++it)
    176             addReferenceTo(*it);
    177 
    178         if (!m_animatedProperty)
    179             m_animatedProperty = animator->startAnimValAnimation(animatedElements);
    180         else
    181             m_animatedProperty = animator->resetAnimValToBaseVal(animatedElements);
    182 
    183         return;
    184     }
    185 
    186     // CSS properties animation code-path.
    187     String baseValue;
    188 
    189     if (shouldApply == ApplyCSSAnimation) {
    190         ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
    191         computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
    192     }
    193 
    194     m_animatedProperty = animator->constructFromString(baseValue);
    195 }
    196 
    197 static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value)
    198 {
    199 #if !ENABLE(OILPAN)
    200     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
    201 #endif
    202 
    203     MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
    204     if (!propertySet->setProperty(id, value, false, 0))
    205         return;
    206 
    207     targetElement->setNeedsStyleRecalc(LocalStyleChange);
    208 }
    209 
    210 static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id)
    211 {
    212 #if !ENABLE(OILPAN)
    213     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
    214 #endif
    215     targetElement->ensureAnimatedSMILStyleProperties()->removeProperty(id);
    216     targetElement->setNeedsStyleRecalc(LocalStyleChange);
    217 }
    218 
    219 static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString)
    220 {
    221     ASSERT(targetElement);
    222     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
    223         return;
    224 
    225     CSSPropertyID id = cssPropertyID(attributeName.localName());
    226 
    227     SVGElement::InstanceUpdateBlocker blocker(targetElement);
    228     applyCSSPropertyToTarget(targetElement, id, valueAsString);
    229 
    230     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    231     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
    232     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
    233     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
    234         if (SVGElement* shadowTreeElement = *it)
    235             applyCSSPropertyToTarget(shadowTreeElement, id, valueAsString);
    236     }
    237 }
    238 
    239 static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName)
    240 {
    241     ASSERT(targetElement);
    242     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
    243         return;
    244 
    245     CSSPropertyID id = cssPropertyID(attributeName.localName());
    246 
    247     SVGElement::InstanceUpdateBlocker blocker(targetElement);
    248     removeCSSPropertyFromTarget(targetElement, id);
    249 
    250     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    251     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
    252     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
    253     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
    254         if (SVGElement* shadowTreeElement = *it)
    255             removeCSSPropertyFromTarget(shadowTreeElement, id);
    256     }
    257 }
    258 
    259 static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
    260 {
    261 #if !ENABLE(OILPAN)
    262     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
    263 #endif
    264     targetElement->invalidateSVGAttributes();
    265     targetElement->svgAttributeChanged(attributeName);
    266 }
    267 
    268 static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
    269 {
    270     ASSERT(targetElement);
    271     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
    272         return;
    273 
    274     SVGElement::InstanceUpdateBlocker blocker(targetElement);
    275     notifyTargetAboutAnimValChange(targetElement, attributeName);
    276 
    277     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    278     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
    279     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
    280     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
    281         notifyTargetAboutAnimValChange(*it, attributeName);
    282     }
    283 }
    284 
    285 void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement)
    286 {
    287     if (!m_animatedProperty)
    288         return;
    289 
    290     if (!targetElement) {
    291         m_animatedProperty.clear();
    292         return;
    293     }
    294 
    295     if (ensureAnimator()->isAnimatingCSSProperty()) {
    296         // CSS properties animation code-path.
    297         removeCSSPropertyFromTargetAndInstances(targetElement, attributeName());
    298         m_animatedProperty.clear();
    299         return;
    300     }
    301 
    302     // SVG DOM animVal animation code-path.
    303     if (m_animator) {
    304         WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements = findElementInstances(targetElement);
    305         m_animator->stopAnimValAnimation(animatedElements);
    306         notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName());
    307     }
    308 
    309     m_animatedProperty.clear();
    310 }
    311 
    312 void SVGAnimateElement::applyResultsToTarget()
    313 {
    314     ASSERT(m_animator);
    315     ASSERT(animatedPropertyType() != AnimatedTransformList || isSVGAnimateTransformElement(*this));
    316     ASSERT(animatedPropertyType() != AnimatedUnknown);
    317 
    318     // Early exit if our animated type got destructed by a previous endedActiveInterval().
    319     if (!m_animatedProperty)
    320         return;
    321 
    322     if (m_animator->isAnimatingCSSProperty()) {
    323         // CSS properties animation code-path.
    324         // Convert the result of the animation to a String and apply it as CSS property on the target & all instances.
    325         applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m_animatedProperty->valueAsString());
    326         return;
    327     }
    328 
    329     // SVG DOM animVal animation code-path.
    330     // At this point the SVG DOM values are already changed, unlike for CSS.
    331     // We only have to trigger update notifications here.
    332     notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName());
    333 }
    334 
    335 bool SVGAnimateElement::animatedPropertyTypeSupportsAddition()
    336 {
    337     // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
    338     switch (animatedPropertyType()) {
    339     case AnimatedBoolean:
    340     case AnimatedEnumeration:
    341     case AnimatedPreserveAspectRatio:
    342     case AnimatedString:
    343     case AnimatedUnknown:
    344         return false;
    345     default:
    346         return true;
    347     }
    348 }
    349 
    350 bool SVGAnimateElement::isAdditive()
    351 {
    352     if (animationMode() == ByAnimation || animationMode() == FromByAnimation)
    353         if (!animatedPropertyTypeSupportsAddition())
    354             return false;
    355 
    356     return SVGAnimationElement::isAdditive();
    357 }
    358 
    359 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
    360 {
    361     // FIXME: A return value of float is not enough to support paced animations on lists.
    362     SVGElement* targetElement = this->targetElement();
    363     if (!targetElement)
    364         return -1;
    365 
    366     return ensureAnimator()->calculateDistance(fromString, toString);
    367 }
    368 
    369 void SVGAnimateElement::setTargetElement(SVGElement* target)
    370 {
    371     SVGAnimationElement::setTargetElement(target);
    372     resetAnimatedPropertyType();
    373 }
    374 
    375 void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName)
    376 {
    377     SVGAnimationElement::setAttributeName(attributeName);
    378     resetAnimatedPropertyType();
    379 }
    380 
    381 void SVGAnimateElement::resetAnimatedPropertyType()
    382 {
    383     ASSERT(!m_animatedProperty);
    384     m_fromProperty.clear();
    385     m_toProperty.clear();
    386     m_toAtEndOfDurationProperty.clear();
    387     m_animator.clear();
    388 }
    389 
    390 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
    391 {
    392     if (!m_animator)
    393         m_animator = SVGAnimatedTypeAnimator::create(this, targetElement());
    394     return m_animator.get();
    395 }
    396 
    397 void SVGAnimateElement::trace(Visitor* visitor)
    398 {
    399     visitor->trace(m_animator);
    400     SVGAnimationElement::trace(visitor);
    401 }
    402 
    403 }
    404