diff options
Diffstat (limited to 'Source/WebCore/svg/animation')
-rw-r--r-- | Source/WebCore/svg/animation/SMILTime.cpp | 3 | ||||
-rw-r--r-- | Source/WebCore/svg/animation/SMILTime.h | 14 | ||||
-rw-r--r-- | Source/WebCore/svg/animation/SMILTimeContainer.cpp | 64 | ||||
-rw-r--r-- | Source/WebCore/svg/animation/SMILTimeContainer.h | 20 | ||||
-rw-r--r-- | Source/WebCore/svg/animation/SVGSMILElement.cpp | 266 | ||||
-rw-r--r-- | Source/WebCore/svg/animation/SVGSMILElement.h | 102 |
6 files changed, 233 insertions, 236 deletions
diff --git a/Source/WebCore/svg/animation/SMILTime.cpp b/Source/WebCore/svg/animation/SMILTime.cpp index 2b1f19e5d..3e3d70771 100644 --- a/Source/WebCore/svg/animation/SMILTime.cpp +++ b/Source/WebCore/svg/animation/SMILTime.cpp @@ -24,7 +24,6 @@ */ #include "config.h" -#if ENABLE(SVG) #include "SMILTime.h" #include <float.h> @@ -63,5 +62,3 @@ SMILTime WebCore::operator*(const SMILTime& a, const SMILTime& b) return SMILTime::indefinite(); return a.value() * b.value(); } -#endif - diff --git a/Source/WebCore/svg/animation/SMILTime.h b/Source/WebCore/svg/animation/SMILTime.h index 4e7f5e263..e79b5b34f 100644 --- a/Source/WebCore/svg/animation/SMILTime.h +++ b/Source/WebCore/svg/animation/SMILTime.h @@ -23,16 +23,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SMILTime_h -#define SMILTime_h - -#if ENABLE(SVG) +#pragma once #include <algorithm> -#include <wtf/MathExtras.h> +#include <cmath> namespace WebCore { +const double SMILAnimationFrameDelay = 1.0 / 60; + class SMILTime { public: SMILTime() : m_time(0) { } @@ -96,7 +95,4 @@ SMILTime operator-(const SMILTime&, const SMILTime&); // So multiplying times does not make too much sense but SMIL defines it for duration * repeatCount SMILTime operator*(const SMILTime&, const SMILTime&); -} - -#endif // ENABLE(SVG) -#endif // SMILTime_h +} // namespace WebCore diff --git a/Source/WebCore/svg/animation/SMILTimeContainer.cpp b/Source/WebCore/svg/animation/SMILTimeContainer.cpp index 25529d692..333f7a17d 100644 --- a/Source/WebCore/svg/animation/SMILTimeContainer.cpp +++ b/Source/WebCore/svg/animation/SMILTimeContainer.cpp @@ -26,18 +26,14 @@ #include "config.h" #include "SMILTimeContainer.h" -#if ENABLE(SVG) #include "Document.h" #include "ElementIterator.h" -#include "SVGNames.h" #include "SVGSMILElement.h" #include "SVGSVGElement.h" #include <wtf/CurrentTime.h> namespace WebCore { -static const double animationFrameDelay = 0.025; - SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) : m_beginTime(0) , m_pauseTime(0) @@ -45,7 +41,7 @@ SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) , m_resumeTime(0) , m_presetStartTime(0) , m_documentOrderIndexesDirty(false) - , m_timer(this, &SMILTimeContainer::timerFired) + , m_timer(*this, &SMILTimeContainer::timerFired) , m_ownerSVGElement(owner) #ifndef NDEBUG , m_preventScheduledAnimationsChanges(false) @@ -93,16 +89,15 @@ void SMILTimeContainer::unschedule(SVGSMILElement* animation, SVGElement* target ElementAttributePair key(target, attributeName); AnimationsVector* scheduled = m_scheduledAnimations.get(key); ASSERT(scheduled); - size_t idx = scheduled->find(animation); - ASSERT(idx != notFound); - scheduled->remove(idx); + bool removed = scheduled->removeFirst(animation); + ASSERT_UNUSED(removed, removed); } void SMILTimeContainer::notifyIntervalsChanged() { // Schedule updateAnimations() to be called asynchronously so multiple intervals // can change with updateAnimations() only called once at the end. - startTimer(0); + startTimer(elapsed(), 0); } SMILTime SMILTimeContainer::elapsed() const @@ -163,7 +158,7 @@ void SMILTimeContainer::resume() m_resumeTime = monotonicallyIncreasingTime(); m_pauseTime = 0; - startTimer(0); + startTimer(elapsed(), 0); } void SMILTimeContainer::setElapsed(SMILTime time) @@ -189,12 +184,9 @@ void SMILTimeContainer::setElapsed(SMILTime time) #ifndef NDEBUG m_preventScheduledAnimationsChanges = true; #endif - GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); - for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { - AnimationsVector* scheduled = it->value.get(); - unsigned size = scheduled->size(); - for (unsigned n = 0; n < size; n++) - scheduled->at(n)->reset(); + for (auto& animation : m_scheduledAnimations.values()) { + for (auto& element : *animation) + element->reset(); } #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; @@ -203,7 +195,7 @@ void SMILTimeContainer::setElapsed(SMILTime time) updateAnimations(time, true); } -void SMILTimeContainer::startTimer(SMILTime fireTime, SMILTime minimumDelay) +void SMILTimeContainer::startTimer(SMILTime elapsed, SMILTime fireTime, SMILTime minimumDelay) { if (!m_beginTime || isPaused()) return; @@ -211,11 +203,11 @@ void SMILTimeContainer::startTimer(SMILTime fireTime, SMILTime minimumDelay) if (!fireTime.isFinite()) return; - SMILTime delay = std::max(fireTime - elapsed(), minimumDelay); + SMILTime delay = std::max(fireTime - elapsed, minimumDelay); m_timer.startOneShot(delay.value()); } -void SMILTimeContainer::timerFired(Timer<SMILTimeContainer>*) +void SMILTimeContainer::timerFired() { ASSERT(m_beginTime); ASSERT(!m_pauseTime); @@ -267,9 +259,16 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) #endif AnimationsVector animationsToApply; - GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); - for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { - AnimationsVector* scheduled = it->value.get(); + for (auto& it : m_scheduledAnimations) { + AnimationsVector* scheduled = it.value.get(); + for (auto* animation : *scheduled) { + if (!animation->hasConditionsConnected()) + animation->connectConditions(); + } + } + + for (auto& it : m_scheduledAnimations) { + AnimationsVector* scheduled = it.value.get(); // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. @@ -277,10 +276,8 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) // have higher priority. sortByPriority(*scheduled, elapsed); - SVGSMILElement* resultElement = 0; - unsigned size = scheduled->size(); - for (unsigned n = 0; n < size; n++) { - SVGSMILElement* animation = scheduled->at(n); + SVGSMILElement* resultElement = nullptr; + for (auto& animation : *scheduled) { ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); @@ -294,7 +291,7 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) - resultElement = 0; + resultElement = nullptr; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) @@ -305,26 +302,23 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) animationsToApply.append(resultElement); } - unsigned animationsToApplySize = animationsToApply.size(); - if (!animationsToApplySize) { + if (animationsToApply.isEmpty()) { #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif - startTimer(earliestFireTime, animationFrameDelay); + startTimer(elapsed, earliestFireTime, SMILAnimationFrameDelay); return; } // Apply results to target elements. - for (unsigned i = 0; i < animationsToApplySize; ++i) - animationsToApply[i]->applyResultsToTarget(); + for (auto& animation : animationsToApply) + animation->applyResultsToTarget(); #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif - startTimer(earliestFireTime, animationFrameDelay); + startTimer(elapsed, earliestFireTime, SMILAnimationFrameDelay); } } - -#endif // ENABLE(SVG) diff --git a/Source/WebCore/svg/animation/SMILTimeContainer.h b/Source/WebCore/svg/animation/SMILTimeContainer.h index 4a3efa096..861665efa 100644 --- a/Source/WebCore/svg/animation/SMILTimeContainer.h +++ b/Source/WebCore/svg/animation/SMILTimeContainer.h @@ -23,17 +23,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SMILTimeContainer_h -#define SMILTimeContainer_h - -#if ENABLE(SVG) +#pragma once #include "QualifiedName.h" #include "SMILTime.h" #include "Timer.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/text/StringHash.h> #include <wtf/text/WTFString.h> @@ -44,9 +40,9 @@ class SVGElement; class SVGSMILElement; class SVGSVGElement; -class SMILTimeContainer : public RefCounted<SMILTimeContainer> { +class SMILTimeContainer final : public RefCounted<SMILTimeContainer> { public: - static PassRefPtr<SMILTimeContainer> create(SVGSVGElement* owner) { return adoptRef(new SMILTimeContainer(owner)); } + static Ref<SMILTimeContainer> create(SVGSVGElement* owner) { return adoptRef(*new SMILTimeContainer(owner)); } ~SMILTimeContainer(); void schedule(SVGSMILElement*, SVGElement*, const QualifiedName&); @@ -69,8 +65,8 @@ public: private: SMILTimeContainer(SVGSVGElement* owner); - void timerFired(Timer<SMILTimeContainer>*); - void startTimer(SMILTime fireTime, SMILTime minimumDelay = 0); + void timerFired(); + void startTimer(SMILTime elapsed, SMILTime fireTime, SMILTime minimumDelay = 0); void updateAnimations(SMILTime elapsed, bool seekToTime = false); void updateDocumentOrderIndexes(); @@ -84,7 +80,7 @@ private: bool m_documentOrderIndexesDirty; - Timer<SMILTimeContainer> m_timer; + Timer m_timer; typedef std::pair<SVGElement*, QualifiedName> ElementAttributePair; typedef Vector<SVGSMILElement*> AnimationsVector; @@ -97,7 +93,5 @@ private: bool m_preventScheduledAnimationsChanges; #endif }; -} -#endif // ENABLE(SVG) -#endif // SMILTimeContainer_h +} // namespace WebCore diff --git a/Source/WebCore/svg/animation/SVGSMILElement.cpp b/Source/WebCore/svg/animation/SVGSMILElement.cpp index a1df1f81e..2551af600 100644 --- a/Source/WebCore/svg/animation/SVGSMILElement.cpp +++ b/Source/WebCore/svg/animation/SVGSMILElement.cpp @@ -24,18 +24,16 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGSMILElement.h" -#include "Attribute.h" #include "CSSPropertyNames.h" #include "Document.h" #include "Event.h" #include "EventListener.h" +#include "EventNames.h" +#include "EventSender.h" #include "FloatConversion.h" #include "FrameView.h" -#include "HTMLNames.h" #include "SMILTimeContainer.h" #include "SVGDocumentExtensions.h" #include "SVGNames.h" @@ -48,29 +46,41 @@ #include <wtf/Vector.h> namespace WebCore { - + +static SMILEventSender& smilBeginEventSender() +{ + static NeverDestroyed<SMILEventSender> sender(eventNames().beginEventEvent); + return sender; +} + +static SMILEventSender& smilEndEventSender() +{ + static NeverDestroyed<SMILEventSender> sender(eventNames().endEventEvent); + return sender; +} + // This is used for duration type time values that can't be negative. static const double invalidCachedTime = -1.; -class ConditionEventListener : public EventListener { +class ConditionEventListener final : public EventListener { public: - static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition) + static Ref<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition) { - return adoptRef(new ConditionEventListener(animation, condition)); + return adoptRef(*new ConditionEventListener(animation, condition)); } static const ConditionEventListener* cast(const EventListener* listener) { return listener->type() == ConditionEventListenerType ? static_cast<const ConditionEventListener*>(listener) - : 0; + : nullptr; } - virtual bool operator==(const EventListener& other) override; + bool operator==(const EventListener& other) const final; void disconnectAnimation() { - m_animation = 0; + m_animation = nullptr; } private: @@ -81,13 +91,13 @@ private: { } - virtual void handleEvent(ScriptExecutionContext*, Event*) override; + void handleEvent(ScriptExecutionContext*, Event*) final; SVGSMILElement* m_animation; SVGSMILElement::Condition* m_condition; }; -bool ConditionEventListener::operator==(const EventListener& listener) +bool ConditionEventListener::operator==(const EventListener& listener) const { if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener)) return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition; @@ -114,7 +124,7 @@ SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const Str SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document& doc) : SVGElement(tagName, doc) , m_attributeName(anyQName()) - , m_targetElement(0) + , m_targetElement(nullptr) , m_conditionsConnected(false) , m_hasEndEventConditions(false) , m_isWaitingForFirstInterval(true) @@ -138,6 +148,8 @@ SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document& doc) SVGSMILElement::~SVGSMILElement() { clearResourceReferences(); + smilBeginEventSender().cancelEvent(*this); + smilEndEventSender().cancelEvent(*this); disconnectConditions(); if (m_timeContainer && m_targetElement && hasValidAttributeName()) m_timeContainer->unschedule(this, m_targetElement, m_attributeName); @@ -145,16 +157,21 @@ SVGSMILElement::~SVGSMILElement() void SVGSMILElement::clearResourceReferences() { - document().accessSVGExtensions()->removeAllTargetReferencesForElement(this); + document().accessSVGExtensions().removeAllTargetReferencesForElement(this); +} + +void SVGSMILElement::clearTarget() +{ + setTargetElement(nullptr); } void SVGSMILElement::buildPendingResource() { clearResourceReferences(); - if (!inDocument()) { + if (!isConnected()) { // Reset the target element if we are no longer in the document. - setTargetElement(0); + setTargetElement(nullptr); return; } @@ -162,59 +179,62 @@ void SVGSMILElement::buildPendingResource() String href = getAttribute(XLinkNames::hrefAttr); Element* target; if (href.isEmpty()) - target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : 0; + target = is<Element>(parentNode()) ? downcast<Element>(parentNode()) : nullptr; else target = SVGURIReference::targetElementFromIRIString(href, document(), &id); - SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : 0; + SVGElement* svgTarget = is<SVGElement>(target) ? downcast<SVGElement>(target) : nullptr; - if (svgTarget && !svgTarget->inDocument()) - svgTarget = 0; + if (svgTarget && !svgTarget->isConnected()) + svgTarget = nullptr; if (svgTarget != targetElement()) setTargetElement(svgTarget); if (!svgTarget) { // Do not register as pending if we are already pending this resource. - if (document().accessSVGExtensions()->isPendingResource(this, id)) + if (document().accessSVGExtensions().isPendingResource(this, id)) return; if (!id.isEmpty()) { - document().accessSVGExtensions()->addPendingResource(id, this); + document().accessSVGExtensions().addPendingResource(id, this); ASSERT(hasPendingResources()); } } else { // Register us with the target in the dependencies map. Any change of hrefElement // that leads to relayout/repainting now informs us, so we can react to it. - document().accessSVGExtensions()->addElementReferencingTarget(this, svgTarget); + document().accessSVGExtensions().addElementReferencingTarget(this, svgTarget); } } -static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const String& attributeName) +inline QualifiedName SVGSMILElement::constructAttributeName() const { - ASSERT(svgElement); - if (attributeName.isEmpty()) + auto parseResult = Document::parseQualifiedName(attributeWithoutSynchronization(SVGNames::attributeNameAttr)); + if (parseResult.hasException()) return anyQName(); - if (!attributeName.contains(':')) - return QualifiedName(nullAtom, attributeName, nullAtom); - - String prefix; - String localName; - if (!Document::parseQualifiedName(attributeName, prefix, localName, ASSERT_NO_EXCEPTION)) - return anyQName(); - - String namespaceURI = svgElement->lookupNamespaceURI(prefix); + + AtomicString prefix, localName; + std::tie(prefix, localName) = parseResult.releaseReturnValue(); + + if (prefix.isNull()) + return { nullAtom, localName, nullAtom }; + + auto namespaceURI = lookupNamespaceURI(prefix); if (namespaceURI.isEmpty()) return anyQName(); - - return QualifiedName(nullAtom, localName, namespaceURI); + + return { nullAtom, localName, namespaceURI }; +} + +inline void SVGSMILElement::updateAttributeName() +{ + setAttributeName(constructAttributeName()); } static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList) { - for (int i = timeList.size() - 1; i >= 0; --i) { - if (timeList[i].originIsScript()) - timeList.remove(i); - } + timeList.removeAllMatching([] (const SMILTimeWithOrigin& time) { + return time.originIsScript(); + }); } void SVGSMILElement::reset() @@ -235,23 +255,23 @@ void SVGSMILElement::reset() Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode& rootParent) { SVGElement::insertedInto(rootParent); - if (!rootParent.inDocument()) + if (!rootParent.isConnected()) return InsertionDone; // Verify we are not in <use> instance tree. ASSERT(!isInShadowTree()); - setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr))); + updateAttributeName(); + SVGSVGElement* owner = ownerSVGElement(); if (!owner) return InsertionDone; - m_timeContainer = owner->timeContainer(); - ASSERT(m_timeContainer); + m_timeContainer = &owner->timeContainer(); m_timeContainer->setDocumentOrderIndexesDirty(); // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated." - if (!fastHasAttribute(SVGNames::beginAttr)) + if (!hasAttributeWithoutSynchronization(SVGNames::beginAttr)) m_beginTimes.append(SMILTimeWithOrigin()); if (m_isWaitingForFirstInterval) @@ -260,20 +280,23 @@ Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode& r if (m_timeContainer) m_timeContainer->notifyIntervalsChanged(); - buildPendingResource(); + return InsertionShouldCallFinishedInsertingSubtree; +} - return InsertionDone; +void SVGSMILElement::finishedInsertingSubtree() +{ + buildPendingResource(); } void SVGSMILElement::removedFrom(ContainerNode& rootParent) { - if (rootParent.inDocument()) { + if (rootParent.isConnected()) { clearResourceReferences(); disconnectConditions(); - setTargetElement(0); + setTargetElement(nullptr); setAttributeName(anyQName()); animationAttributeChanged(); - m_timeContainer = 0; + m_timeContainer = nullptr; } SVGElement::removedFrom(rootParent); @@ -299,7 +322,7 @@ SMILTime SVGSMILElement::parseOffsetValue(const String& data) result = parse.left(parse.length() - 1).toDouble(&ok); else result = parse.toDouble(&ok); - if (!ok) + if (!ok || !SMILTime(result).isFinite()) return SMILTime::unresolved(); return result; } @@ -311,7 +334,7 @@ SMILTime SVGSMILElement::parseClockValue(const String& data) String parse = data.stripWhiteSpace(); - DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> indefiniteValue("indefinite", AtomicString::ConstructFromLiteral); if (parse == indefiniteValue) return SMILTime::indefinite(); @@ -335,7 +358,7 @@ SMILTime SVGSMILElement::parseClockValue(const String& data) } else return parseOffsetValue(parse); - if (!ok) + if (!ok || !SMILTime(result).isFinite()) return SMILTime::unresolved(); return result; } @@ -418,14 +441,14 @@ void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd begin if (beginOrEnd == End) m_hasEndEventConditions = false; HashSet<double> existing; - for (unsigned n = 0; n < timeList.size(); ++n) - existing.add(timeList[n].time().value()); + for (auto& time : timeList) + existing.add(time.time().value()); Vector<String> splitString; parseString.split(';', splitString); - for (unsigned n = 0; n < splitString.size(); ++n) { - SMILTime value = parseClockValue(splitString[n]); + for (auto& string : splitString) { + SMILTime value = parseClockValue(string); if (value.isUnresolved()) - parseCondition(splitString[n], beginOrEnd); + parseCondition(string, beginOrEnd); else if (!existing.contains(value.value())) timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin)); } @@ -434,19 +457,19 @@ void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd begin bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName) { - DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); - if (supportedAttributes.isEmpty()) { - supportedAttributes.add(SVGNames::beginAttr); - supportedAttributes.add(SVGNames::endAttr); - supportedAttributes.add(SVGNames::durAttr); - supportedAttributes.add(SVGNames::repeatDurAttr); - supportedAttributes.add(SVGNames::repeatCountAttr); - supportedAttributes.add(SVGNames::minAttr); - supportedAttributes.add(SVGNames::maxAttr); - supportedAttributes.add(SVGNames::attributeNameAttr); - supportedAttributes.add(XLinkNames::hrefAttr); + static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes; + if (supportedAttributes.get().isEmpty()) { + supportedAttributes.get().add(SVGNames::beginAttr); + supportedAttributes.get().add(SVGNames::endAttr); + supportedAttributes.get().add(SVGNames::durAttr); + supportedAttributes.get().add(SVGNames::repeatDurAttr); + supportedAttributes.get().add(SVGNames::repeatCountAttr); + supportedAttributes.get().add(SVGNames::minAttr); + supportedAttributes.get().add(SVGNames::maxAttr); + supportedAttributes.get().add(SVGNames::attributeNameAttr); + supportedAttributes.get().add(XLinkNames::hrefAttr); } - return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); + return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName); } void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value) @@ -455,21 +478,25 @@ void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicStrin if (!m_conditions.isEmpty()) { disconnectConditions(); m_conditions.clear(); - parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End); + parseBeginOrEnd(attributeWithoutSynchronization(SVGNames::endAttr), End); } parseBeginOrEnd(value.string(), Begin); - if (inDocument()) + if (isConnected()) connectConditions(); } else if (name == SVGNames::endAttr) { if (!m_conditions.isEmpty()) { disconnectConditions(); m_conditions.clear(); - parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin); + parseBeginOrEnd(attributeWithoutSynchronization(SVGNames::beginAttr), Begin); } parseBeginOrEnd(value.string(), End); - if (inDocument()) + if (isConnected()) connectConditions(); - } else + } else if (name == SVGNames::onendAttr) + setAttributeEventListener(eventNames().endEventEvent, name, value); + else if (name == SVGNames::onbeginAttr) + setAttributeEventListener(eventNames().beginEventEvent, name, value); + else SVGElement::parseAttribute(name, value); } @@ -491,11 +518,11 @@ void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName) else if (attrName == SVGNames::maxAttr) m_cachedMax = invalidCachedTime; else if (attrName == SVGNames::attributeNameAttr) - setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr))); + updateAttributeName(); else if (attrName.matches(XLinkNames::hrefAttr)) { - SVGElementInstance::InvalidationGuard invalidationGuard(this); + InstanceInvalidationGuard guard(*this); buildPendingResource(); - } else if (inDocument()) { + } else if (isConnected()) { if (attrName == SVGNames::beginAttr) beginListChanged(elapsed()); else if (attrName == SVGNames::endAttr) @@ -515,8 +542,7 @@ void SVGSMILElement::connectConditions() if (m_conditionsConnected) disconnectConditions(); m_conditionsConnected = true; - for (unsigned n = 0; n < m_conditions.size(); ++n) { - Condition& condition = m_conditions[n]; + for (auto& condition : m_conditions) { if (condition.m_type == Condition::EventBase) { ASSERT(!condition.m_syncbase); Element* eventBase = eventBaseFor(condition); @@ -524,17 +550,17 @@ void SVGSMILElement::connectConditions() continue; ASSERT(!condition.m_eventListener); condition.m_eventListener = ConditionEventListener::create(this, &condition); - eventBase->addEventListener(condition.m_name, condition.m_eventListener, false); + eventBase->addEventListener(condition.m_name, *condition.m_eventListener, false); } else if (condition.m_type == Condition::Syncbase) { ASSERT(!condition.m_baseID.isEmpty()); condition.m_syncbase = treeScope().getElementById(condition.m_baseID); if (!condition.m_syncbase) continue; - if (!isSVGSMILElement(*condition.m_syncbase)) { + if (!is<SVGSMILElement>(*condition.m_syncbase)) { condition.m_syncbase = nullptr; continue; } - toSVGSMILElement(*condition.m_syncbase).addTimeDependent(this); + downcast<SVGSMILElement>(*condition.m_syncbase).addTimeDependent(this); } } } @@ -544,8 +570,7 @@ void SVGSMILElement::disconnectConditions() if (!m_conditionsConnected) return; m_conditionsConnected = false; - for (unsigned n = 0; n < m_conditions.size(); ++n) { - Condition& condition = m_conditions[n]; + for (auto& condition : m_conditions) { if (condition.m_type == Condition::EventBase) { ASSERT(!condition.m_syncbase); if (!condition.m_eventListener) @@ -557,14 +582,14 @@ void SVGSMILElement::disconnectConditions() // our condition event listener, in case it later fires. Element* eventBase = eventBaseFor(condition); if (eventBase) - eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false); + eventBase->removeEventListener(condition.m_name, *condition.m_eventListener, false); condition.m_eventListener->disconnectAnimation(); - condition.m_eventListener = 0; + condition.m_eventListener = nullptr; } else if (condition.m_type == Condition::Syncbase) { if (condition.m_syncbase) - toSVGSMILElement(condition.m_syncbase.get())->removeTimeDependent(this); + downcast<SVGSMILElement>(condition.m_syncbase.get())->removeTimeDependent(this); } - condition.m_syncbase = 0; + condition.m_syncbase = nullptr; } } @@ -623,9 +648,9 @@ bool SVGSMILElement::isFrozen() const SVGSMILElement::Restart SVGSMILElement::restart() const { - DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral)); - const AtomicString& value = fastGetAttribute(SVGNames::restartAttr); + static NeverDestroyed<const AtomicString> never("never", AtomicString::ConstructFromLiteral); + static NeverDestroyed<const AtomicString> whenNotActive("whenNotActive", AtomicString::ConstructFromLiteral); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::restartAttr); if (value == never) return RestartNever; if (value == whenNotActive) @@ -635,8 +660,8 @@ SVGSMILElement::Restart SVGSMILElement::restart() const SVGSMILElement::FillMode SVGSMILElement::fill() const { - DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral)); - const AtomicString& value = fastGetAttribute(SVGNames::fillAttr); + static NeverDestroyed<const AtomicString> freeze("freeze", AtomicString::ConstructFromLiteral); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::fillAttr); return value == freeze ? FillFreeze : FillRemove; } @@ -644,7 +669,7 @@ SMILTime SVGSMILElement::dur() const { if (m_cachedDur != invalidCachedTime) return m_cachedDur; - const AtomicString& value = fastGetAttribute(SVGNames::durAttr); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::durAttr); SMILTime clockValue = parseClockValue(value); return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue; } @@ -653,7 +678,7 @@ SMILTime SVGSMILElement::repeatDur() const { if (m_cachedRepeatDur != invalidCachedTime) return m_cachedRepeatDur; - const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::repeatDurAttr); SMILTime clockValue = parseClockValue(value); m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue; return m_cachedRepeatDur; @@ -664,11 +689,11 @@ SMILTime SVGSMILElement::repeatCount() const { if (m_cachedRepeatCount != invalidCachedTime) return m_cachedRepeatCount; - const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::repeatCountAttr); if (value.isNull()) return SMILTime::unresolved(); - DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> indefiniteValue("indefinite", AtomicString::ConstructFromLiteral); if (value == indefiniteValue) return SMILTime::indefinite(); bool ok; @@ -680,16 +705,16 @@ SMILTime SVGSMILElement::maxValue() const { if (m_cachedMax != invalidCachedTime) return m_cachedMax; - const AtomicString& value = fastGetAttribute(SVGNames::maxAttr); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::maxAttr); SMILTime result = parseClockValue(value); - return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result; + return m_cachedMax = (result.isUnresolved() || result <= 0) ? SMILTime::indefinite() : result; } SMILTime SVGSMILElement::minValue() const { if (m_cachedMin != invalidCachedTime) return m_cachedMin; - const AtomicString& value = fastGetAttribute(SVGNames::minAttr); + const AtomicString& value = attributeWithoutSynchronization(SVGNames::minAttr); SMILTime result = parseClockValue(value); return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result; } @@ -1022,7 +1047,7 @@ SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const return repeatingDurationEnd; return m_intervalEnd; } - return elapsed + 0.025; + return elapsed + SMILAnimationFrameDelay; } return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved(); } @@ -1047,9 +1072,6 @@ bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, b ASSERT(m_timeContainer); ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite()); - if (!m_conditionsConnected) - connectConditions(); - if (!m_intervalBegin.isFinite()) { ASSERT(m_activeState == Inactive); m_nextProgressTime = SMILTime::unresolved(); @@ -1106,9 +1128,17 @@ bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, b } if (oldActiveState == Active && m_activeState != Active) { + smilEndEventSender().dispatchEventSoon(*this); endedActiveInterval(); if (m_activeState != Frozen) clearAnimatedType(m_targetElement); + } else if (oldActiveState != Active && m_activeState == Active) + smilBeginEventSender().dispatchEventSoon(*this); + + // Triggering all the pending events if the animation timeline is changed. + if (seekToTime) { + if (m_activeState == Inactive || m_activeState == Frozen) + smilEndEventSender().dispatchEventSoon(*this); } m_nextProgressTime = calculateNextProgressTime(elapsed); @@ -1118,26 +1148,23 @@ bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, b void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting) { ASSERT(m_intervalBegin.isFinite()); - DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ()); - if (loopBreaker.contains(this)) + static NeverDestroyed<HashSet<SVGSMILElement*>> loopBreaker; + if (loopBreaker.get().contains(this)) return; - loopBreaker.add(this); + loopBreaker.get().add(this); - TimeDependentSet::iterator end = m_timeDependents.end(); - for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) { - SVGSMILElement* dependent = *it; + for (auto& dependent : m_timeDependents) { dependent->createInstanceTimesFromSyncbase(this, newOrExisting); } - loopBreaker.remove(this); + loopBreaker.get().remove(this); } void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval) { // FIXME: To be really correct, this should handle updating exising interval by changing // the associated times instead of creating new ones. - for (unsigned n = 0; n < m_conditions.size(); ++n) { - Condition& condition = m_conditions[n]; + for (auto& condition : m_conditions) { if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) { ASSERT(condition.m_name == "begin" || condition.m_name == "end"); // No nested time containers in SVG, no need for crazy time space conversions. Phew! @@ -1189,6 +1216,11 @@ void SVGSMILElement::endedActiveInterval() clearTimesWithDynamicOrigins(m_endTimes); } +void SVGSMILElement::dispatchPendingEvent(SMILEventSender* eventSender) +{ + ASSERT(eventSender == &smilBeginEventSender() || eventSender == &smilEndEventSender()); + const AtomicString& eventType = eventSender->eventType(); + dispatchEvent(Event::create(eventType, false, false)); } -#endif +} diff --git a/Source/WebCore/svg/animation/SVGSMILElement.h b/Source/WebCore/svg/animation/SVGSMILElement.h index b8d38236b..cb7bd159e 100644 --- a/Source/WebCore/svg/animation/SVGSMILElement.h +++ b/Source/WebCore/svg/animation/SVGSMILElement.h @@ -23,18 +23,21 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SVGSMILElement_h -#define SVGSMILElement_h -#if ENABLE(SVG) +#pragma once + #include "SMILTime.h" #include "SVGElement.h" - -#include <wtf/HashMap.h> +#include <wtf/HashSet.h> namespace WebCore { class ConditionEventListener; class SMILTimeContainer; +class SVGSMILElement; + +template<typename T> class EventSender; + +using SMILEventSender = EventSender<SVGSMILElement>; // This class implements SMIL interval timing model as needed for SVG animation. class SVGSMILElement : public SVGElement { @@ -42,36 +45,26 @@ public: SVGSMILElement(const QualifiedName&, Document&); virtual ~SVGSMILElement(); - bool isSupportedAttribute(const QualifiedName&); - virtual void parseAttribute(const QualifiedName&, const AtomicString&) override; - virtual void svgAttributeChanged(const QualifiedName&) override; - virtual InsertionNotificationRequest insertedInto(ContainerNode&) override; - virtual void removedFrom(ContainerNode&) override; + void parseAttribute(const QualifiedName&, const AtomicString&) override; + void svgAttributeChanged(const QualifiedName&) override; + InsertionNotificationRequest insertedInto(ContainerNode&) override; + void removedFrom(ContainerNode&) override; virtual bool hasValidAttributeType() = 0; virtual bool hasValidAttributeName(); virtual void animationAttributeChanged() = 0; - SMILTimeContainer* timeContainer() const { return m_timeContainer.get(); } + SMILTimeContainer* timeContainer() { return m_timeContainer.get(); } SVGElement* targetElement() const { return m_targetElement; } const QualifiedName& attributeName() const { return m_attributeName; } void beginByLinkActivation(); - enum Restart { - RestartAlways, - RestartWhenNotActive, - RestartNever - }; - + enum Restart { RestartAlways, RestartWhenNotActive, RestartNever }; Restart restart() const; - enum FillMode { - FillRemove, - FillFreeze - }; - + enum FillMode { FillRemove, FillFreeze }; FillMode fill() const; SMILTime dur() const; @@ -108,28 +101,40 @@ public: virtual void clearAnimatedType(SVGElement* targetElement) = 0; virtual void applyResultsToTarget() = 0; + void connectConditions(); + bool hasConditionsConnected() const { return m_conditionsConnected; } + + void dispatchPendingEvent(SMILEventSender*); + protected: void addBeginTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin = SMILTimeWithOrigin::ParserOrigin); void addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin = SMILTimeWithOrigin::ParserOrigin); void setInactive() { m_activeState = Inactive; } - virtual bool rendererIsNeeded(const RenderStyle&) override { return false; } + bool rendererIsNeeded(const RenderStyle&) override { return false; } // Sub-classes may need to take action when the target is changed. virtual void setTargetElement(SVGElement*); virtual void setAttributeName(const QualifiedName&); + void finishedInsertingSubtree() override; + private: void buildPendingResource() override; void clearResourceReferences(); + void clearTarget() override; + virtual void startedActiveInterval() = 0; void endedActiveInterval(); virtual void updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement) = 0; + static bool isSupportedAttribute(const QualifiedName&); + QualifiedName constructAttributeName() const; + void updateAttributeName(); + enum BeginOrEnd { Begin, End }; - SMILTime findInstanceTime(BeginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const; void resolveFirstInterval(); void resolveNextInterval(bool notifyDependents); @@ -143,19 +148,14 @@ private: // This represents conditions on elements begin or end list that need to be resolved on runtime // for example <animate begin="otherElement.begin + 8s; button.click" ... /> struct Condition { - enum Type { - EventBase, - Syncbase, - AccessKey - }; - + enum Type { EventBase, Syncbase, AccessKey }; Condition(Type, BeginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats = -1); Type m_type; BeginOrEnd m_beginOrEnd; String m_baseID; String m_name; SMILTime m_offset; - int m_repeats; + int m_repeats { -1 }; RefPtr<Element> m_syncbase; RefPtr<ConditionEventListener> m_eventListener; }; @@ -163,38 +163,28 @@ private: void parseBeginOrEnd(const String&, BeginOrEnd beginOrEnd); Element* eventBaseFor(const Condition&); - void connectConditions(); void disconnectConditions(); // Event base timing void handleConditionEvent(Event*, Condition*); // Syncbase timing - enum NewOrExistingInterval { - NewInterval, - ExistingInterval - }; - + enum NewOrExistingInterval { NewInterval, ExistingInterval }; void notifyDependentsIntervalChanged(NewOrExistingInterval); void createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval); void addTimeDependent(SVGSMILElement*); void removeTimeDependent(SVGSMILElement*); - enum ActiveState { - Inactive, - Active, - Frozen - }; - - QualifiedName m_attributeName; - + enum ActiveState { Inactive, Active, Frozen }; ActiveState determineActiveState(SMILTime elapsed) const; float calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const; SMILTime calculateNextProgressTime(SMILTime elapsed) const; - virtual bool isSMILElement() const override final { return true; } + bool isSMILElement() const final { return true; } + + QualifiedName m_attributeName; - mutable SVGElement* m_targetElement; + SVGElement* m_targetElement; Vector<Condition> m_conditions; bool m_conditionsConnected; @@ -202,8 +192,7 @@ private: bool m_isWaitingForFirstInterval; - typedef HashSet<SVGSMILElement*> TimeDependentSet; - TimeDependentSet m_timeDependents; + HashSet<SVGSMILElement*> m_timeDependents; // Instance time lists Vector<SMILTimeWithOrigin> m_beginTimes; @@ -233,14 +222,9 @@ private: friend class ConditionEventListener; }; -void isSVGSMILElement(const SVGSMILElement&); // Catch unnecessary runtime check of type known at compile time. -inline bool isSVGSMILElement(const SVGElement& element) { return element.isSMILElement(); } -inline bool isSVGSMILElement(const Node& node) { return node.isSVGElement() && toSVGElement(node).isSMILElement(); } -template <> inline bool isElementOfType<const SVGSMILElement>(const Element& element) { return isSVGSMILElement(element); } - -NODE_TYPE_CASTS(SVGSMILElement) - -} +} // namespace WebCore -#endif // ENABLE(SVG) -#endif // SVGSMILElement_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::SVGSMILElement) + static bool isType(const WebCore::SVGElement& element) { return element.isSMILElement(); } + static bool isType(const WebCore::Node& node) { return is<WebCore::SVGElement>(node) && isType(downcast<WebCore::SVGElement>(node)); } +SPECIALIZE_TYPE_TRAITS_END() |