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 #if ENABLE(SVG_ANIMATION) 28 #include "SVGAnimationElement.h" 29 30 #include "Attribute.h" 31 #include "CSSComputedStyleDeclaration.h" 32 #include "CSSParser.h" 33 #include "CSSPropertyNames.h" 34 #include "Color.h" 35 #include "Document.h" 36 #include "Event.h" 37 #include "EventListener.h" 38 #include "FloatConversion.h" 39 #include "HTMLNames.h" 40 #include "PlatformString.h" 41 #include "SVGElementInstance.h" 42 #include "SVGNames.h" 43 #include "SVGParserUtilities.h" 44 #include "SVGStyledElement.h" 45 #include "SVGURIReference.h" 46 #include "SVGUseElement.h" 47 #include "XLinkNames.h" 48 #include <wtf/StdLibExtras.h> 49 50 using namespace std; 51 52 namespace WebCore { 53 54 // Animated property definitions 55 DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 56 57 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* document) 58 : SVGSMILElement(tagName, document) 59 , m_animationValid(false) 60 { 61 } 62 63 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder) 64 { 65 result.clear(); 66 Vector<String> parseList; 67 parse.split(';', parseList); 68 for (unsigned n = 0; n < parseList.size(); ++n) { 69 String timeString = parseList[n]; 70 bool ok; 71 float time = timeString.toFloat(&ok); 72 if (!ok || time < 0 || time > 1) 73 goto fail; 74 if (verifyOrder) { 75 if (!n) { 76 if (time) 77 goto fail; 78 } else if (time < result.last()) 79 goto fail; 80 } 81 result.append(time); 82 } 83 return; 84 fail: 85 result.clear(); 86 } 87 88 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result) 89 { 90 result.clear(); 91 if (parse.isEmpty()) 92 return; 93 const UChar* cur = parse.characters(); 94 const UChar* end = cur + parse.length(); 95 96 skipOptionalSpaces(cur, end); 97 98 bool delimParsed = false; 99 while (cur < end) { 100 delimParsed = false; 101 float posA = 0; 102 if (!parseNumber(cur, end, posA)) { 103 result.clear(); 104 return; 105 } 106 107 float posB = 0; 108 if (!parseNumber(cur, end, posB)) { 109 result.clear(); 110 return; 111 } 112 113 float posC = 0; 114 if (!parseNumber(cur, end, posC)) { 115 result.clear(); 116 return; 117 } 118 119 float posD = 0; 120 if (!parseNumber(cur, end, posD, false)) { 121 result.clear(); 122 return; 123 } 124 125 skipOptionalSpaces(cur, end); 126 127 if (cur < end && *cur == ';') { 128 delimParsed = true; 129 cur++; 130 } 131 skipOptionalSpaces(cur, end); 132 133 result.append(UnitBezier(posA, posB, posC, posD)); 134 } 135 if (!(cur == end && !delimParsed)) 136 result.clear(); 137 } 138 139 void SVGAnimationElement::parseMappedAttribute(Attribute* attr) 140 { 141 if (attr->name() == SVGNames::valuesAttr) 142 attr->value().string().split(';', m_values); 143 else if (attr->name() == SVGNames::keyTimesAttr) 144 parseKeyTimes(attr->value(), m_keyTimes, true); 145 else if (attr->name() == SVGNames::keyPointsAttr && hasTagName(SVGNames::animateMotionTag)) { 146 // This is specified to be an animateMotion attribute only but it is simpler to put it here 147 // where the other timing calculatations are. 148 parseKeyTimes(attr->value(), m_keyPoints, false); 149 } else if (attr->name() == SVGNames::keySplinesAttr) 150 parseKeySplines(attr->value(), m_keySplines); 151 else { 152 if (SVGTests::parseMappedAttribute(attr)) 153 return; 154 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) 155 return; 156 SVGSMILElement::parseMappedAttribute(attr); 157 } 158 } 159 160 void SVGAnimationElement::attributeChanged(Attribute* attr, bool preserveDecls) 161 { 162 // Assumptions may not hold after an attribute change. 163 m_animationValid = false; 164 SVGSMILElement::attributeChanged(attr, preserveDecls); 165 } 166 167 void SVGAnimationElement::synchronizeProperty(const QualifiedName& attrName) 168 { 169 SVGSMILElement::synchronizeProperty(attrName); 170 171 if (attrName == anyQName()) { 172 synchronizeExternalResourcesRequired(); 173 SVGTests::synchronizeProperties(this, attrName); 174 return; 175 } 176 177 if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) 178 synchronizeExternalResourcesRequired(); 179 else if (SVGTests::isKnownAttribute(attrName)) 180 SVGTests::synchronizeProperties(this, attrName); 181 } 182 183 float SVGAnimationElement::getStartTime() const 184 { 185 return narrowPrecisionToFloat(intervalBegin().value()); 186 } 187 188 float SVGAnimationElement::getCurrentTime() const 189 { 190 return narrowPrecisionToFloat(elapsed().value()); 191 } 192 193 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const 194 { 195 return narrowPrecisionToFloat(simpleDuration().value()); 196 } 197 198 void SVGAnimationElement::beginElement() 199 { 200 beginElementAt(0); 201 } 202 203 void SVGAnimationElement::beginElementAt(float offset) 204 { 205 addBeginTime(elapsed() + offset); 206 } 207 208 void SVGAnimationElement::endElement() 209 { 210 endElementAt(0); 211 } 212 213 void SVGAnimationElement::endElementAt(float offset) 214 { 215 addEndTime(elapsed() + offset); 216 } 217 218 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode() const 219 { 220 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues 221 if (hasTagName(SVGNames::setTag)) 222 return ToAnimation; 223 if (!animationPath().isEmpty()) 224 return PathAnimation; 225 if (hasAttribute(SVGNames::valuesAttr)) 226 return ValuesAnimation; 227 if (!toValue().isEmpty()) 228 return fromValue().isEmpty() ? ToAnimation : FromToAnimation; 229 if (!byValue().isEmpty()) 230 return fromValue().isEmpty() ? ByAnimation : FromByAnimation; 231 return NoAnimation; 232 } 233 234 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode() const 235 { 236 DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete")); 237 DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear")); 238 DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced")); 239 DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline")); 240 const AtomicString& value = getAttribute(SVGNames::calcModeAttr); 241 if (value == discrete) 242 return CalcModeDiscrete; 243 if (value == linear) 244 return CalcModeLinear; 245 if (value == paced) 246 return CalcModePaced; 247 if (value == spline) 248 return CalcModeSpline; 249 return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear; 250 } 251 252 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const 253 { 254 DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS")); 255 DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML")); 256 const AtomicString& value = getAttribute(SVGNames::attributeTypeAttr); 257 if (value == css) 258 return AttributeTypeCSS; 259 if (value == xml) 260 return AttributeTypeXML; 261 return AttributeTypeAuto; 262 } 263 264 String SVGAnimationElement::toValue() const 265 { 266 return getAttribute(SVGNames::toAttr); 267 } 268 269 String SVGAnimationElement::byValue() const 270 { 271 return getAttribute(SVGNames::byAttr); 272 } 273 274 String SVGAnimationElement::fromValue() const 275 { 276 return getAttribute(SVGNames::fromAttr); 277 } 278 279 bool SVGAnimationElement::isAdditive() const 280 { 281 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum")); 282 const AtomicString& value = getAttribute(SVGNames::additiveAttr); 283 return value == sum || animationMode() == ByAnimation; 284 } 285 286 bool SVGAnimationElement::isAccumulated() const 287 { 288 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum")); 289 const AtomicString& value = getAttribute(SVGNames::accumulateAttr); 290 return value == sum && animationMode() != ToAnimation; 291 } 292 293 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName) 294 { 295 ASSERT(targetElement); 296 if (!targetElement->isStyled()) 297 return false; 298 299 return SVGStyledElement::isAnimatableCSSProperty(attributeName); 300 } 301 302 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value) 303 { 304 if (!hasValidAttributeType()) 305 return; 306 SVGElement* targetElement = this->targetElement(); 307 QualifiedName attributeName = this->attributeName(); 308 if (!targetElement || attributeName == anyQName() || value.isNull()) 309 return; 310 311 // We don't want the instance tree to get rebuild. Instances are updated in the loop below. 312 if (targetElement->isStyled()) 313 static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(true); 314 315 bool attributeIsCSSProperty = isTargetAttributeCSSProperty(targetElement, attributeName); 316 // Stop animation, if attributeType is set to CSS by the user, but the attribute itself is not a CSS property. 317 if (!attributeIsCSSProperty && attributeType() == AttributeTypeCSS) 318 return; 319 320 ExceptionCode ec; 321 if (attributeIsCSSProperty) { 322 // FIXME: This should set the override style, not the inline style. 323 // Sadly override styles are not yet implemented. 324 targetElement->style()->setProperty(attributeName.localName(), value, "", ec); 325 } else { 326 // FIXME: This should set the 'presentation' value, not the actual 327 // attribute value. Whatever that means in practice. 328 targetElement->setAttribute(attributeName, value, ec); 329 } 330 331 if (targetElement->isStyled()) 332 static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(false); 333 334 // If the target element is used in an <use> instance tree, update that as well. 335 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); 336 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 337 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 338 SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); 339 if (!shadowTreeElement) 340 continue; 341 if (attributeIsCSSProperty) 342 shadowTreeElement->style()->setProperty(attributeName.localName(), value, "", ec); 343 else 344 shadowTreeElement->setAttribute(attributeName, value, ec); 345 (*it)->correspondingUseElement()->setNeedsStyleRecalc(); 346 } 347 } 348 349 void SVGAnimationElement::calculateKeyTimesForCalcModePaced() 350 { 351 ASSERT(calcMode() == CalcModePaced); 352 ASSERT(animationMode() == ValuesAnimation); 353 354 unsigned valuesCount = m_values.size(); 355 ASSERT(valuesCount > 1); 356 Vector<float> keyTimesForPaced; 357 float totalDistance = 0; 358 keyTimesForPaced.append(0); 359 for (unsigned n = 0; n < valuesCount - 1; ++n) { 360 // Distance in any units 361 float distance = calculateDistance(m_values[n], m_values[n + 1]); 362 if (distance < 0) 363 return; 364 totalDistance += distance; 365 keyTimesForPaced.append(distance); 366 } 367 if (!totalDistance) 368 return; 369 370 // Normalize. 371 for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n) 372 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance; 373 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1; 374 375 // Use key times calculated based on pacing instead of the user provided ones. 376 m_keyTimes.swap(keyTimesForPaced); 377 } 378 379 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); } 380 381 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const 382 { 383 unsigned index; 384 unsigned keyTimesCount = m_keyTimes.size(); 385 for (index = 1; index < keyTimesCount; ++index) { 386 if (m_keyTimes[index] >= percent) 387 break; 388 } 389 return --index; 390 } 391 392 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const 393 { 394 ASSERT(calcMode() == CalcModeSpline); 395 ASSERT(splineIndex < m_keySplines.size()); 396 UnitBezier bezier = m_keySplines[splineIndex]; 397 SMILTime duration = simpleDuration(); 398 if (!duration.isFinite()) 399 duration = 100.0; 400 return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value()))); 401 } 402 403 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const 404 { 405 ASSERT(!m_keyPoints.isEmpty()); 406 ASSERT(calcMode() != CalcModePaced); 407 ASSERT(m_keyTimes.size() > 1); 408 ASSERT(m_keyPoints.size() == m_keyTimes.size()); 409 410 unsigned index = calculateKeyTimesIndex(percent); 411 float fromPercent = m_keyTimes[index]; 412 float toPercent = m_keyTimes[index + 1]; 413 float fromKeyPoint = m_keyPoints[index]; 414 float toKeyPoint = m_keyPoints[index + 1]; 415 416 if (calcMode() == CalcModeDiscrete) 417 return percent == 1 ? toKeyPoint : fromKeyPoint; 418 419 float keyPointPercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent); 420 421 if (calcMode() == CalcModeSpline) { 422 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1); 423 keyPointPercent = calculatePercentForSpline(keyPointPercent, index); 424 } 425 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint; 426 } 427 428 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const 429 { 430 ASSERT(!m_keyPoints.isEmpty()); 431 ASSERT(m_keyPoints.size() == m_keyTimes.size()); 432 ASSERT(calcMode() != CalcModePaced); 433 effectivePercent = calculatePercentFromKeyPoints(percent); 434 unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1)); 435 from = m_values[index]; 436 to = m_values[index + 1]; 437 } 438 439 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) const 440 { 441 unsigned valuesCount = m_values.size(); 442 ASSERT(m_animationValid); 443 ASSERT(valuesCount > 1); 444 445 CalcMode calcMode = this->calcMode(); 446 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced) 447 return currentValuesFromKeyPoints(percent, effectivePercent, from, to); 448 449 unsigned keyTimesCount = m_keyTimes.size(); 450 ASSERT(!keyTimesCount || valuesCount == keyTimesCount); 451 ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0])); 452 453 unsigned index = calculateKeyTimesIndex(percent); 454 if (calcMode == CalcModeDiscrete) { 455 if (!keyTimesCount) 456 index = percent == 1 ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount); 457 from = m_values[index]; 458 to = m_values[index]; 459 effectivePercent = 0; 460 return; 461 } 462 463 float fromPercent; 464 float toPercent; 465 if (keyTimesCount) { 466 fromPercent = m_keyTimes[index]; 467 toPercent = m_keyTimes[index + 1]; 468 } else { 469 index = static_cast<unsigned>(percent * (valuesCount - 1)); 470 fromPercent = static_cast<float>(index) / (valuesCount - 1); 471 toPercent = static_cast<float>(index + 1) / (valuesCount - 1); 472 } 473 474 if (index == valuesCount - 1) 475 --index; 476 from = m_values[index]; 477 to = m_values[index + 1]; 478 ASSERT(toPercent > fromPercent); 479 effectivePercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent); 480 481 if (calcMode == CalcModeSpline) { 482 ASSERT(m_keySplines.size() == m_values.size() - 1); 483 effectivePercent = calculatePercentForSpline(effectivePercent, index); 484 } 485 } 486 487 void SVGAnimationElement::startedActiveInterval() 488 { 489 m_animationValid = false; 490 491 if (!hasValidAttributeType()) 492 return; 493 494 // These validations are appropriate for all animation modes. 495 if (hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size()) 496 return; 497 498 AnimationMode animationMode = this->animationMode(); 499 CalcMode calcMode = this->calcMode(); 500 if (calcMode == CalcModeSpline) { 501 unsigned splinesCount = m_keySplines.size() + 1; 502 if ((hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount) 503 || (animationMode == ValuesAnimation && m_values.size() != splinesCount)) 504 return; 505 } 506 507 String from = fromValue(); 508 String to = toValue(); 509 String by = byValue(); 510 if (animationMode == NoAnimation) 511 return; 512 if (animationMode == FromToAnimation) 513 m_animationValid = calculateFromAndToValues(from, to); 514 else if (animationMode == ToAnimation) { 515 // For to-animations the from value is the current accumulated value from lower priority animations. 516 // The value is not static and is determined during the animation. 517 m_animationValid = calculateFromAndToValues(String(), to); 518 } else if (animationMode == FromByAnimation) 519 m_animationValid = calculateFromAndByValues(from, by); 520 else if (animationMode == ByAnimation) 521 m_animationValid = calculateFromAndByValues(String(), by); 522 else if (animationMode == ValuesAnimation) { 523 m_animationValid = m_values.size() > 1 524 && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size())) 525 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1) 526 && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1)) 527 && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size())); 528 if (calcMode == CalcModePaced && m_animationValid) 529 calculateKeyTimesForCalcModePaced(); 530 } else if (animationMode == PathAnimation) 531 m_animationValid = calcMode == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()); 532 } 533 534 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement) 535 { 536 if (!m_animationValid) 537 return; 538 539 float effectivePercent; 540 CalcMode mode = calcMode(); 541 if (animationMode() == ValuesAnimation) { 542 String from; 543 String to; 544 currentValuesForValuesAnimation(percent, effectivePercent, from, to); 545 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) { 546 m_animationValid = calculateFromAndToValues(from, to); 547 if (!m_animationValid) 548 return; 549 m_lastValuesAnimationFrom = from; 550 m_lastValuesAnimationTo = to; 551 } 552 } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced) 553 effectivePercent = calculatePercentFromKeyPoints(percent); 554 else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1) 555 effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent)); 556 else 557 effectivePercent = percent; 558 559 calculateAnimatedValue(effectivePercent, repeat, resultElement); 560 } 561 562 void SVGAnimationElement::endedActiveInterval() 563 { 564 } 565 566 } 567 #endif // ENABLE(SVG_ANIMATION) 568 569