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