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