File indexing completed on 2024-05-12 11:51:52
0001 /* 0002 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 0003 * 0004 * Redistribution and use in source and binary forms, with or without 0005 * modification, are permitted provided that the following conditions 0006 * are met: 0007 * 1. Redistributions of source code must retain the above copyright 0008 * notice, this list of conditions and the following disclaimer. 0009 * 2. Redistributions in binary form must reproduce the above copyright 0010 * notice, this list of conditions and the following disclaimer in the 0011 * documentation and/or other materials provided with the distribution. 0012 * 0013 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 0014 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 0015 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 0016 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 0017 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 0018 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 0019 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 0020 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 0021 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0022 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 0023 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0024 */ 0025 0026 #include "SMILTimeContainer.h" 0027 0028 #include "CSSComputedStyleDeclaration.h" 0029 #include "CSSParser.h" 0030 #include "Document.h" 0031 #include "SVGAnimationElement.h" 0032 #include "SVGSMILElement.h" 0033 #include "SVGSVGElement.h" 0034 #include "SystemTime.h" 0035 0036 using namespace std; 0037 0038 namespace WebCore 0039 { 0040 0041 static const double animationFrameDelay = 0.025; 0042 0043 SMILTimeContainer::SMILTimeContainer(SVGSVGElement *owner) 0044 : m_beginTime(0) 0045 , m_pauseTime(0) 0046 , m_accumulatedPauseTime(0) 0047 , m_documentOrderIndexesDirty(false) 0048 , m_timer(this, &SMILTimeContainer::timerFired) 0049 , m_ownerSVGElement(owner) 0050 { 0051 } 0052 0053 #if !ENABLE(SVG_ANIMATION) 0054 void SMILTimeContainer::begin() {} 0055 void SMILTimeContainer::pause() {} 0056 void SMILTimeContainer::resume() {} 0057 SMILTime SMILTimeContainer::elapsed() const 0058 { 0059 return 0; 0060 } 0061 bool SMILTimeContainer::isPaused() const 0062 { 0063 return false; 0064 } 0065 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer> *) {} 0066 #else 0067 0068 void SMILTimeContainer::schedule(SVGSMILElement *animation) 0069 { 0070 ASSERT(animation->timeContainer() == this); 0071 SMILTime nextFireTime = animation->nextProgressTime(); 0072 if (!nextFireTime.isFinite()) { 0073 return; 0074 } 0075 m_scheduledAnimations.add(animation); 0076 startTimer(0); 0077 } 0078 0079 void SMILTimeContainer::unschedule(SVGSMILElement *animation) 0080 { 0081 ASSERT(animation->timeContainer() == this); 0082 0083 m_scheduledAnimations.remove(animation); 0084 } 0085 0086 SMILTime SMILTimeContainer::elapsed() const 0087 { 0088 if (!m_beginTime) { 0089 return 0; 0090 } 0091 return currentTime() - m_beginTime - m_accumulatedPauseTime; 0092 } 0093 0094 bool SMILTimeContainer::isActive() const 0095 { 0096 return m_beginTime && !isPaused(); 0097 } 0098 0099 bool SMILTimeContainer::isPaused() const 0100 { 0101 return m_pauseTime; 0102 } 0103 0104 void SMILTimeContainer::begin() 0105 { 0106 ASSERT(!m_beginTime); 0107 m_beginTime = currentTime(); 0108 updateAnimations(0); 0109 } 0110 0111 void SMILTimeContainer::pause() 0112 { 0113 if (!m_beginTime) { 0114 return; 0115 } 0116 ASSERT(!isPaused()); 0117 m_pauseTime = currentTime(); 0118 m_timer.stop(); 0119 } 0120 0121 void SMILTimeContainer::resume() 0122 { 0123 if (!m_beginTime) { 0124 return; 0125 } 0126 ASSERT(isPaused()); 0127 m_accumulatedPauseTime += currentTime() - m_pauseTime; 0128 m_pauseTime = 0; 0129 startTimer(0); 0130 } 0131 0132 void SMILTimeContainer::startTimer(SMILTime fireTime, SMILTime minimumDelay) 0133 { 0134 if (!m_beginTime || isPaused()) { 0135 return; 0136 } 0137 0138 if (!fireTime.isFinite()) { 0139 return; 0140 } 0141 0142 SMILTime delay = max(fireTime - elapsed(), minimumDelay); 0143 m_timer.startOneShot(delay.value()); 0144 } 0145 0146 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer> *) 0147 { 0148 ASSERT(m_beginTime); 0149 ASSERT(!m_pauseTime); 0150 SMILTime elapsed = this->elapsed(); 0151 updateAnimations(elapsed); 0152 } 0153 0154 void SMILTimeContainer::updateDocumentOrderIndexes() 0155 { 0156 unsigned timingElementCount = 0; 0157 for (Node *node = m_ownerSVGElement; node; node = node->traverseNextNode(m_ownerSVGElement)) { 0158 if (SVGSMILElement::isSMILElement(node)) { 0159 static_cast<SVGSMILElement *>(node)->setDocumentOrderIndex(timingElementCount++); 0160 } 0161 } 0162 m_documentOrderIndexesDirty = false; 0163 } 0164 0165 struct PriorityCompare { 0166 PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {} 0167 bool operator()(SVGSMILElement *a, SVGSMILElement *b) 0168 { 0169 // FIXME: This should also consider possible timing relations between the elements. 0170 SMILTime aBegin = a->intervalBegin(); 0171 SMILTime bBegin = b->intervalBegin(); 0172 // Frozen elements need to be prioritized based on their previous interval. 0173 aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin() : aBegin; 0174 bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin() : bBegin; 0175 if (aBegin == bBegin) { 0176 return a->documentOrderIndex() < b->documentOrderIndex(); 0177 } 0178 return aBegin < bBegin; 0179 } 0180 SMILTime m_elapsed; 0181 }; 0182 0183 void SMILTimeContainer::sortByPriority(Vector<SVGSMILElement *> &smilElements, SMILTime elapsed) 0184 { 0185 if (m_documentOrderIndexesDirty) { 0186 updateDocumentOrderIndexes(); 0187 } 0188 std::sort(smilElements.begin(), smilElements.end(), PriorityCompare(elapsed)); 0189 } 0190 0191 static bool applyOrderSortFunction(SVGSMILElement *a, SVGSMILElement *b) 0192 { 0193 if (!a->hasTagName(SVGNames::animateTransformTag) && b->hasTagName(SVGNames::animateTransformTag)) { 0194 return true; 0195 } 0196 return false; 0197 } 0198 0199 static void sortByApplyOrder(Vector<SVGSMILElement *> &smilElements) 0200 { 0201 std::sort(smilElements.begin(), smilElements.end(), applyOrderSortFunction); 0202 } 0203 0204 String SMILTimeContainer::baseValueFor(ElementAttributePair key) 0205 { 0206 // FIXME: We wouldn't need to do this if we were keeping base values around properly in DOM. 0207 // Currently animation overwrites them so we need to save them somewhere. 0208 BaseValueMap::iterator it = m_savedBaseValues.find(key); 0209 if (it != m_savedBaseValues.end()) { 0210 return it->second; 0211 } 0212 0213 SVGElement *target = key.first; 0214 String attributeName = key.second; 0215 ASSERT(target); 0216 ASSERT(!attributeName.isEmpty()); 0217 String baseValue; 0218 if (SVGAnimationElement::attributeIsCSS(attributeName)) { 0219 CSSComputedStyleDeclaration computedStyle(target); 0220 baseValue = computedStyle.getPropertyValue(cssPropertyID(attributeName)); 0221 } else { 0222 baseValue = target->getAttribute(attributeName); 0223 } 0224 m_savedBaseValues.add(key, baseValue); 0225 return baseValue; 0226 } 0227 0228 void SMILTimeContainer::updateAnimations(SMILTime elapsed) 0229 { 0230 SMILTime earliersFireTime = SMILTime::unresolved(); 0231 0232 Vector<SVGSMILElement *> toAnimate; 0233 copyToVector(m_scheduledAnimations, toAnimate); 0234 0235 // Sort according to priority. Elements with later begin time have higher priority. 0236 // In case of a tie, document order decides. 0237 // FIXME: This should also consider timing relationships between the elements. Dependents 0238 // have higher priority. 0239 sortByPriority(toAnimate, elapsed); 0240 0241 // Calculate animation contributions. 0242 typedef HashMap<ElementAttributePair, SVGSMILElement *> ResultElementMap; 0243 ResultElementMap resultsElements; 0244 for (unsigned n = 0; n < toAnimate.size(); ++n) { 0245 SVGSMILElement *animation = toAnimate[n]; 0246 ASSERT(animation->timeContainer() == this); 0247 0248 SVGElement *targetElement = animation->targetElement(); 0249 if (!targetElement) { 0250 continue; 0251 } 0252 String attributeName = animation->attributeName(); 0253 if (attributeName.isEmpty()) { 0254 if (animation->hasTagName(SVGNames::animateMotionTag)) { 0255 attributeName = SVGNames::animateMotionTag.localName(); 0256 } else { 0257 continue; 0258 } 0259 } 0260 0261 // Results are accumulated to the first animation that animates a particular element/attribute pair. 0262 ElementAttributePair key(targetElement, attributeName); 0263 SVGSMILElement *resultElement = resultsElements.get(key); 0264 if (!resultElement) { 0265 resultElement = animation; 0266 resultElement->resetToBaseValue(baseValueFor(key)); 0267 resultsElements.add(key, resultElement); 0268 } 0269 0270 // This will calculate the contribution from the animation and add it to the resultsElement. 0271 animation->progress(elapsed, resultElement); 0272 0273 SMILTime nextFireTime = animation->nextProgressTime(); 0274 if (nextFireTime.isFinite()) { 0275 earliersFireTime = min(nextFireTime, earliersFireTime); 0276 } else if (!animation->isContributing(elapsed)) { 0277 m_scheduledAnimations.remove(animation); 0278 if (m_scheduledAnimations.isEmpty()) { 0279 m_savedBaseValues.clear(); 0280 } 0281 } 0282 } 0283 0284 Vector<SVGSMILElement *> animationsToApply; 0285 ResultElementMap::iterator end = resultsElements.end(); 0286 for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) { 0287 animationsToApply.append(it->second); 0288 } 0289 0290 // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as 0291 // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly. 0292 sortByApplyOrder(animationsToApply); 0293 0294 // Apply results to target elements. 0295 for (unsigned n = 0; n < animationsToApply.size(); ++n) { 0296 animationsToApply[n]->applyResultsToTarget(); 0297 } 0298 0299 startTimer(earliersFireTime, animationFrameDelay); 0300 0301 Document::updateDocumentsRendering(); 0302 } 0303 0304 #endif 0305 } 0306