1/* 2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2008 Apple Inc. All rights reserved. 6 * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> 7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25#include "config.h" 26 27#if ENABLE(SVG) 28#include "SVGAnimationElement.h" 29 30#include "Attribute.h" 31#include "CSSComputedStyleDeclaration.h" 32#include "CSSParser.h" 33#include "CSSPropertyNames.h" 34#include "Document.h" 35#include "FloatConversion.h" 36#include "RenderObject.h" 37#include "SVGAnimateElement.h" 38#include "SVGElementInstance.h" 39#include "SVGNames.h" 40#include "SVGParserUtilities.h" 41#include "SVGStyledElement.h" 42#include <wtf/MathExtras.h> 43 44namespace WebCore { 45 46// Animated property definitions 47DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 48 49BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAnimationElement) 50 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) 51 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests) 52END_REGISTER_ANIMATED_PROPERTIES 53 54SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* document) 55 : SVGSMILElement(tagName, document) 56 , m_fromPropertyValueType(RegularPropertyValue) 57 , m_toPropertyValueType(RegularPropertyValue) 58 , m_animationValid(false) 59 , m_attributeType(AttributeTypeAuto) 60 , m_hasInvalidCSSAttributeType(false) 61 , m_calcMode(CalcModeLinear) 62 , m_animationMode(NoAnimation) 63{ 64 registerAnimatedPropertiesForSVGAnimationElement(); 65} 66 67static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder) 68{ 69 result.clear(); 70 Vector<String> parseList; 71 parse.split(';', parseList); 72 for (unsigned n = 0; n < parseList.size(); ++n) { 73 String timeString = parseList[n]; 74 bool ok; 75 float time = timeString.toFloat(&ok); 76 if (!ok || time < 0 || time > 1) 77 goto fail; 78 if (verifyOrder) { 79 if (!n) { 80 if (time) 81 goto fail; 82 } else if (time < result.last()) 83 goto fail; 84 } 85 result.append(time); 86 } 87 return; 88fail: 89 result.clear(); 90} 91 92static void parseKeySplines(const String& parse, Vector<UnitBezier>& result) 93{ 94 result.clear(); 95 if (parse.isEmpty()) 96 return; 97 const UChar* cur = parse.characters(); 98 const UChar* end = cur + parse.length(); 99 100 skipOptionalSVGSpaces(cur, end); 101 102 bool delimParsed = false; 103 while (cur < end) { 104 delimParsed = false; 105 float posA = 0; 106 if (!parseNumber(cur, end, posA)) { 107 result.clear(); 108 return; 109 } 110 111 float posB = 0; 112 if (!parseNumber(cur, end, posB)) { 113 result.clear(); 114 return; 115 } 116 117 float posC = 0; 118 if (!parseNumber(cur, end, posC)) { 119 result.clear(); 120 return; 121 } 122 123 float posD = 0; 124 if (!parseNumber(cur, end, posD, false)) { 125 result.clear(); 126 return; 127 } 128 129 skipOptionalSVGSpaces(cur, end); 130 131 if (cur < end && *cur == ';') { 132 delimParsed = true; 133 cur++; 134 } 135 skipOptionalSVGSpaces(cur, end); 136 137 result.append(UnitBezier(posA, posB, posC, posD)); 138 } 139 if (!(cur == end && !delimParsed)) 140 result.clear(); 141} 142 143bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName) 144{ 145 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 146 if (supportedAttributes.isEmpty()) { 147 SVGTests::addSupportedAttributes(supportedAttributes); 148 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes); 149 supportedAttributes.add(SVGNames::valuesAttr); 150 supportedAttributes.add(SVGNames::keyTimesAttr); 151 supportedAttributes.add(SVGNames::keyPointsAttr); 152 supportedAttributes.add(SVGNames::keySplinesAttr); 153 supportedAttributes.add(SVGNames::attributeTypeAttr); 154 supportedAttributes.add(SVGNames::calcModeAttr); 155 supportedAttributes.add(SVGNames::fromAttr); 156 supportedAttributes.add(SVGNames::toAttr); 157 supportedAttributes.add(SVGNames::byAttr); 158 } 159 return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName); 160} 161 162void SVGAnimationElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 163{ 164 if (!isSupportedAttribute(name)) { 165 SVGSMILElement::parseAttribute(name, value); 166 return; 167 } 168 169 if (name == SVGNames::valuesAttr) { 170 // Per the SMIL specification, leading and trailing white space, 171 // and white space before and after semicolon separators, is allowed and will be ignored. 172 // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute 173 value.string().split(';', m_values); 174 for (unsigned i = 0; i < m_values.size(); ++i) 175 m_values[i] = m_values[i].stripWhiteSpace(); 176 177 updateAnimationMode(); 178 return; 179 } 180 181 if (name == SVGNames::keyTimesAttr) { 182 parseKeyTimes(value, m_keyTimes, true); 183 return; 184 } 185 186 if (name == SVGNames::keyPointsAttr) { 187 if (hasTagName(SVGNames::animateMotionTag)) { 188 // This is specified to be an animateMotion attribute only but it is simpler to put it here 189 // where the other timing calculatations are. 190 parseKeyTimes(value, m_keyPoints, false); 191 } 192 return; 193 } 194 195 if (name == SVGNames::keySplinesAttr) { 196 parseKeySplines(value, m_keySplines); 197 return; 198 } 199 200 if (name == SVGNames::attributeTypeAttr) { 201 setAttributeType(value); 202 return; 203 } 204 205 if (name == SVGNames::calcModeAttr) { 206 setCalcMode(value); 207 return; 208 } 209 210 if (name == SVGNames::fromAttr || name == SVGNames::toAttr || name == SVGNames::byAttr) { 211 updateAnimationMode(); 212 return; 213 } 214 215 if (SVGTests::parseAttribute(name, value)) 216 return; 217 if (SVGExternalResourcesRequired::parseAttribute(name, value)) 218 return; 219 220 ASSERT_NOT_REACHED(); 221} 222 223void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName) 224{ 225 if (!isSupportedAttribute(attrName)) { 226 SVGSMILElement::svgAttributeChanged(attrName); 227 return; 228 } 229 230 animationAttributeChanged(); 231} 232 233void SVGAnimationElement::animationAttributeChanged() 234{ 235 // Assumptions may not hold after an attribute change. 236 m_animationValid = false; 237 setInactive(); 238} 239 240float SVGAnimationElement::getStartTime() const 241{ 242 return narrowPrecisionToFloat(intervalBegin().value()); 243} 244 245float SVGAnimationElement::getCurrentTime() const 246{ 247 return narrowPrecisionToFloat(elapsed().value()); 248} 249 250float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const 251{ 252 return narrowPrecisionToFloat(simpleDuration().value()); 253} 254 255void SVGAnimationElement::beginElement() 256{ 257 beginElementAt(0); 258} 259 260void SVGAnimationElement::beginElementAt(float offset) 261{ 262 if (std::isnan(offset)) 263 return; 264 SMILTime elapsed = this->elapsed(); 265 addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin); 266} 267 268void SVGAnimationElement::endElement() 269{ 270 endElementAt(0); 271} 272 273void SVGAnimationElement::endElementAt(float offset) 274{ 275 if (std::isnan(offset)) 276 return; 277 SMILTime elapsed = this->elapsed(); 278 addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin); 279} 280 281void SVGAnimationElement::updateAnimationMode() 282{ 283 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues 284 if (hasAttribute(SVGNames::valuesAttr)) 285 setAnimationMode(ValuesAnimation); 286 else if (!toValue().isEmpty()) 287 setAnimationMode(fromValue().isEmpty() ? ToAnimation : FromToAnimation); 288 else if (!byValue().isEmpty()) 289 setAnimationMode(fromValue().isEmpty() ? ByAnimation : FromByAnimation); 290 else 291 setAnimationMode(NoAnimation); 292} 293 294void SVGAnimationElement::setCalcMode(const AtomicString& calcMode) 295{ 296 DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete", AtomicString::ConstructFromLiteral)); 297 DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear", AtomicString::ConstructFromLiteral)); 298 DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced", AtomicString::ConstructFromLiteral)); 299 DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline", AtomicString::ConstructFromLiteral)); 300 if (calcMode == discrete) 301 setCalcMode(CalcModeDiscrete); 302 else if (calcMode == linear) 303 setCalcMode(CalcModeLinear); 304 else if (calcMode == paced) 305 setCalcMode(CalcModePaced); 306 else if (calcMode == spline) 307 setCalcMode(CalcModeSpline); 308 else 309 setCalcMode(hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear); 310} 311 312void SVGAnimationElement::setAttributeType(const AtomicString& attributeType) 313{ 314 DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS", AtomicString::ConstructFromLiteral)); 315 DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML", AtomicString::ConstructFromLiteral)); 316 if (attributeType == css) 317 m_attributeType = AttributeTypeCSS; 318 else if (attributeType == xml) 319 m_attributeType = AttributeTypeXML; 320 else 321 m_attributeType = AttributeTypeAuto; 322 checkInvalidCSSAttributeType(targetElement()); 323} 324 325String SVGAnimationElement::toValue() const 326{ 327 return fastGetAttribute(SVGNames::toAttr); 328} 329 330String SVGAnimationElement::byValue() const 331{ 332 return fastGetAttribute(SVGNames::byAttr); 333} 334 335String SVGAnimationElement::fromValue() const 336{ 337 return fastGetAttribute(SVGNames::fromAttr); 338} 339 340bool SVGAnimationElement::isAdditive() const 341{ 342 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral)); 343 const AtomicString& value = fastGetAttribute(SVGNames::additiveAttr); 344 return value == sum || animationMode() == ByAnimation; 345} 346 347bool SVGAnimationElement::isAccumulated() const 348{ 349 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral)); 350 const AtomicString& value = fastGetAttribute(SVGNames::accumulateAttr); 351 return value == sum && animationMode() != ToAnimation; 352} 353 354bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName) 355{ 356 ASSERT(targetElement); 357 if (!targetElement->isSVGStyledElement()) 358 return false; 359 360 return SVGStyledElement::isAnimatableCSSProperty(attributeName); 361} 362 363SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName) 364{ 365 if (!hasValidAttributeType() || !targetElement || attributeName == anyQName()) 366 return DontApplyAnimation; 367 368 // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value. 369 if (isTargetAttributeCSSProperty(targetElement, attributeName)) 370 return ApplyCSSAnimation; 371 372 // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation. 373 if (attributeType() == AttributeTypeCSS) 374 return DontApplyAnimation; 375 376 return ApplyXMLAnimation; 377} 378 379void SVGAnimationElement::calculateKeyTimesForCalcModePaced() 380{ 381 ASSERT(calcMode() == CalcModePaced); 382 ASSERT(animationMode() == ValuesAnimation); 383 384 unsigned valuesCount = m_values.size(); 385 ASSERT(valuesCount >= 1); 386 if (valuesCount == 1) 387 return; 388 389 // FIXME, webkit.org/b/109010: m_keyTimes should not be modified in this function. 390 m_keyTimes.clear(); 391 392 Vector<float> keyTimesForPaced; 393 float totalDistance = 0; 394 keyTimesForPaced.append(0); 395 for (unsigned n = 0; n < valuesCount - 1; ++n) { 396 // Distance in any units 397 float distance = calculateDistance(m_values[n], m_values[n + 1]); 398 if (distance < 0) 399 return; 400 totalDistance += distance; 401 keyTimesForPaced.append(distance); 402 } 403 if (!totalDistance) 404 return; 405 406 // Normalize. 407 for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n) 408 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance; 409 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1; 410 411 // Use key times calculated based on pacing instead of the user provided ones. 412 m_keyTimes = keyTimesForPaced; 413} 414 415static inline double solveEpsilon(double duration) { return 1 / (200 * duration); } 416 417unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const 418{ 419 unsigned index; 420 unsigned keyTimesCount = m_keyTimes.size(); 421 // Compare index + 1 to keyTimesCount because the last keyTimes entry is 422 // required to be 1, and percent can never exceed 1; i.e., the second last 423 // keyTimes entry defines the beginning of the final interval 424 for (index = 1; index + 1 < keyTimesCount; ++index) { 425 if (m_keyTimes[index] > percent) 426 break; 427 } 428 return --index; 429} 430 431float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const 432{ 433 ASSERT(calcMode() == CalcModeSpline); 434 ASSERT_WITH_SECURITY_IMPLICATION(splineIndex < m_keySplines.size()); 435 UnitBezier bezier = m_keySplines[splineIndex]; 436 SMILTime duration = simpleDuration(); 437 if (!duration.isFinite()) 438 duration = 100.0; 439 return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value()))); 440} 441 442float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const 443{ 444 ASSERT(!m_keyPoints.isEmpty()); 445 ASSERT(calcMode() != CalcModePaced); 446 ASSERT(m_keyTimes.size() > 1); 447 ASSERT(m_keyPoints.size() == m_keyTimes.size()); 448 449 if (percent == 1) 450 return m_keyPoints[m_keyPoints.size() - 1]; 451 452 unsigned index = calculateKeyTimesIndex(percent); 453 float fromPercent = m_keyTimes[index]; 454 float toPercent = m_keyTimes[index + 1]; 455 float fromKeyPoint = m_keyPoints[index]; 456 float toKeyPoint = m_keyPoints[index + 1]; 457 458 if (calcMode() == CalcModeDiscrete) 459 return fromKeyPoint; 460 461 float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent); 462 463 if (calcMode() == CalcModeSpline) { 464 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1); 465 keyPointPercent = calculatePercentForSpline(keyPointPercent, index); 466 } 467 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint; 468} 469 470float SVGAnimationElement::calculatePercentForFromTo(float percent) const 471{ 472 if (calcMode() == CalcModeDiscrete && m_keyTimes.size() == 2) 473 return percent > m_keyTimes[1] ? 1 : 0; 474 475 return percent; 476} 477 478void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const 479{ 480 ASSERT(!m_keyPoints.isEmpty()); 481 ASSERT(m_keyPoints.size() == m_keyTimes.size()); 482 ASSERT(calcMode() != CalcModePaced); 483 effectivePercent = calculatePercentFromKeyPoints(percent); 484 unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1)); 485 from = m_values[index]; 486 to = m_values[index + 1]; 487} 488 489void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) 490{ 491 unsigned valuesCount = m_values.size(); 492 ASSERT(m_animationValid); 493 ASSERT(valuesCount >= 1); 494 495 if (percent == 1 || valuesCount == 1) { 496 from = m_values[valuesCount - 1]; 497 to = m_values[valuesCount - 1]; 498 effectivePercent = 1; 499 return; 500 } 501 502 CalcMode calcMode = this->calcMode(); 503 if (hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::animateColorTag)) { 504 SVGAnimateElement* animateElement = static_cast<SVGAnimateElement*>(this); 505 AnimatedPropertyType attributeType = animateElement->determineAnimatedPropertyType(targetElement()); 506 // Fall back to discrete animations for Strings. 507 if (attributeType == AnimatedBoolean 508 || attributeType == AnimatedEnumeration 509 || attributeType == AnimatedPreserveAspectRatio 510 || attributeType == AnimatedString) 511 calcMode = CalcModeDiscrete; 512 } 513 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced) 514 return currentValuesFromKeyPoints(percent, effectivePercent, from, to); 515 516 unsigned keyTimesCount = m_keyTimes.size(); 517 ASSERT(!keyTimesCount || valuesCount == keyTimesCount); 518 ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0])); 519 520 unsigned index = calculateKeyTimesIndex(percent); 521 if (calcMode == CalcModeDiscrete) { 522 if (!keyTimesCount) 523 index = static_cast<unsigned>(percent * valuesCount); 524 from = m_values[index]; 525 to = m_values[index]; 526 effectivePercent = 0; 527 return; 528 } 529 530 float fromPercent; 531 float toPercent; 532 if (keyTimesCount) { 533 fromPercent = m_keyTimes[index]; 534 toPercent = m_keyTimes[index + 1]; 535 } else { 536 index = static_cast<unsigned>(floorf(percent * (valuesCount - 1))); 537 fromPercent = static_cast<float>(index) / (valuesCount - 1); 538 toPercent = static_cast<float>(index + 1) / (valuesCount - 1); 539 } 540 541 if (index == valuesCount - 1) 542 --index; 543 from = m_values[index]; 544 to = m_values[index + 1]; 545 ASSERT(toPercent > fromPercent); 546 effectivePercent = (percent - fromPercent) / (toPercent - fromPercent); 547 548 if (calcMode == CalcModeSpline) { 549 ASSERT(m_keySplines.size() == m_values.size() - 1); 550 effectivePercent = calculatePercentForSpline(effectivePercent, index); 551 } 552} 553 554void SVGAnimationElement::startedActiveInterval() 555{ 556 m_animationValid = false; 557 558 if (!hasValidAttributeType()) 559 return; 560 561 // These validations are appropriate for all animation modes. 562 if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size()) 563 return; 564 565 AnimationMode animationMode = this->animationMode(); 566 CalcMode calcMode = this->calcMode(); 567 if (calcMode == CalcModeSpline) { 568 unsigned splinesCount = m_keySplines.size(); 569 if (!splinesCount 570 || (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() - 1 != splinesCount) 571 || (animationMode == ValuesAnimation && m_values.size() - 1 != splinesCount) 572 || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() - 1 != splinesCount)) 573 return; 574 } 575 576 String from = fromValue(); 577 String to = toValue(); 578 String by = byValue(); 579 if (animationMode == NoAnimation) 580 return; 581 if (animationMode == FromToAnimation) 582 m_animationValid = calculateFromAndToValues(from, to); 583 else if (animationMode == ToAnimation) { 584 // For to-animations the from value is the current accumulated value from lower priority animations. 585 // The value is not static and is determined during the animation. 586 m_animationValid = calculateFromAndToValues(emptyString(), to); 587 } else if (animationMode == FromByAnimation) 588 m_animationValid = calculateFromAndByValues(from, by); 589 else if (animationMode == ByAnimation) 590 m_animationValid = calculateFromAndByValues(emptyString(), by); 591 else if (animationMode == ValuesAnimation) { 592 m_animationValid = m_values.size() >= 1 593 && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size())) 594 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1) 595 && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1)) 596 && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size())); 597 if (m_animationValid) 598 m_animationValid = calculateToAtEndOfDurationValue(m_values.last()); 599 if (calcMode == CalcModePaced && m_animationValid) 600 calculateKeyTimesForCalcModePaced(); 601 } else if (animationMode == PathAnimation) 602 m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()); 603} 604 605void SVGAnimationElement::updateAnimation(float percent, unsigned repeatCount, SVGSMILElement* resultElement) 606{ 607 if (!m_animationValid) 608 return; 609 610 float effectivePercent; 611 CalcMode calcMode = this->calcMode(); 612 AnimationMode animationMode = this->animationMode(); 613 if (animationMode == ValuesAnimation) { 614 String from; 615 String to; 616 currentValuesForValuesAnimation(percent, effectivePercent, from, to); 617 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) { 618 m_animationValid = calculateFromAndToValues(from, to); 619 if (!m_animationValid) 620 return; 621 m_lastValuesAnimationFrom = from; 622 m_lastValuesAnimationTo = to; 623 } 624 } else if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced) 625 effectivePercent = calculatePercentFromKeyPoints(percent); 626 else if (m_keyPoints.isEmpty() && calcMode == CalcModeSpline && m_keyTimes.size() > 1) 627 effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent)); 628 else if (animationMode == FromToAnimation || animationMode == ToAnimation) 629 effectivePercent = calculatePercentForFromTo(percent); 630 else 631 effectivePercent = percent; 632 633 calculateAnimatedValue(effectivePercent, repeatCount, resultElement); 634} 635 636void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& valueString) 637{ 638 ASSERT(element); 639 ASSERT(element->isSVGStyledElement()); 640 641 // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value". 642 element->setUseOverrideComputedStyle(true); 643 RefPtr<CSSValue> value = ComputedStyleExtractor(element).propertyValue(id); 644 valueString = value ? value->cssText() : String(); 645 element->setUseOverrideComputedStyle(false); 646} 647 648void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value) 649{ 650 // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again. 651 // In the future we might want to work with the value type directly to avoid the String parsing. 652 ASSERT(targetElement); 653 654 Element* parent = targetElement->parentElement(); 655 if (!parent || !parent->isSVGElement()) 656 return; 657 658 SVGElement* svgParent = toSVGElement(parent); 659 if (!svgParent->isSVGStyledElement()) 660 return; 661 computeCSSPropertyValue(svgParent, cssPropertyID(attributeName.localName()), value); 662} 663 664static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value) 665{ 666 ASSERT(targetElement); 667 DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit", AtomicString::ConstructFromLiteral)); 668 669 if (value.isEmpty() || value != inherit || !targetElement->isSVGStyledElement()) 670 return false; 671 return SVGStyledElement::isAnimatableCSSProperty(attributeName); 672} 673 674void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to) 675{ 676 SVGElement* targetElement = this->targetElement(); 677 ASSERT(targetElement); 678 679 const QualifiedName& attributeName = this->attributeName(); 680 if (inheritsFromProperty(targetElement, attributeName, from)) 681 m_fromPropertyValueType = InheritValue; 682 if (inheritsFromProperty(targetElement, attributeName, to)) 683 m_toPropertyValueType = InheritValue; 684} 685 686void SVGAnimationElement::setTargetElement(SVGElement* target) 687{ 688 SVGSMILElement::setTargetElement(target); 689 checkInvalidCSSAttributeType(target); 690} 691 692void SVGAnimationElement::setAttributeName(const QualifiedName& attributeName) 693{ 694 SVGSMILElement::setAttributeName(attributeName); 695 checkInvalidCSSAttributeType(targetElement()); 696} 697 698void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target) 699{ 700 m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeTypeCSS && !isTargetAttributeCSSProperty(target, attributeName()); 701} 702 703} 704 705#endif // ENABLE(SVG) 706