void SVGSMILElement::buildPendingResource() { clearResourceAndEventBaseReferences(); if (!inShadowIncludingDocument()) { // Reset the target element if we are no longer in the document. setTargetElement(nullptr); return; } AtomicString id; const AtomicString& href = SVGURIReference::legacyHrefString(*this); Element* target; if (href.isEmpty()) target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : nullptr; else target = SVGURIReference::targetElementFromIRIString(href, treeScope(), &id); SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : nullptr; if (svgTarget && !svgTarget->inShadowIncludingDocument()) svgTarget = nullptr; if (svgTarget != targetElement()) setTargetElement(svgTarget); if (!svgTarget) { // Do not register as pending if we are already pending this resource. if (document().accessSVGExtensions().isElementPendingResource(this, id)) return; if (!id.isEmpty()) { 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. addReferenceTo(svgTarget); } connectEventBaseConditions(); }
SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { ASSERT(document().isActive()); SMILTime earliestFireTime = SMILTime::unresolved(); #if ENABLE(ASSERT) // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section. // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. m_preventScheduledAnimationsChanges = true; #endif if (m_documentOrderIndexesDirty) updateDocumentOrderIndexes(); HeapHashSet<ElementAttributePair> invalidKeys; using AnimationsVector = HeapVector<Member<SVGSMILElement>>; AnimationsVector animationsToApply; AnimationsVector scheduledAnimationsInSameGroup; for (const auto& entry : m_scheduledAnimations) { if (!entry.key.first || entry.value->isEmpty()) { invalidKeys.add(entry.key); continue; } // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. copyToVector(*entry.value, scheduledAnimationsInSameGroup); std::sort(scheduledAnimationsInSameGroup.begin(), scheduledAnimationsInSameGroup.end(), PriorityCompare(elapsed)); SVGSMILElement* resultElement = nullptr; for (const auto& itAnimation : scheduledAnimationsInSameGroup) { SVGSMILElement* animation = itAnimation.get(); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); ASSERT(animation->hasValidAttributeType()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. if (!resultElement) { resultElement = animation; resultElement->lockAnimatedType(); } // This will calculate the contribution from the animation and add it to the resultElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) { resultElement->unlockAnimatedType(); resultElement->clearAnimatedType(); resultElement = nullptr; } SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = std::min(nextFireTime, earliestFireTime); } if (resultElement) { animationsToApply.append(resultElement); resultElement->unlockAnimatedType(); } } m_scheduledAnimations.removeAll(invalidKeys); std::sort(animationsToApply.begin(), animationsToApply.end(), PriorityCompare(elapsed)); unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { #if ENABLE(ASSERT) m_preventScheduledAnimationsChanges = false; #endif return earliestFireTime; } // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); #if ENABLE(ASSERT) m_preventScheduledAnimationsChanges = false; #endif for (unsigned i = 0; i < animationsToApplySize; ++i) { if (animationsToApply[i]->inShadowIncludingDocument() && animationsToApply[i]->isSVGDiscardElement()) { SVGSMILElement* animDiscard = animationsToApply[i]; SVGElement* targetElement = animDiscard->targetElement(); if (targetElement && targetElement->inShadowIncludingDocument()) { targetElement->remove(IGNORE_EXCEPTION); ASSERT(!targetElement->inShadowIncludingDocument()); } if (animDiscard->inShadowIncludingDocument()) { animDiscard->remove(IGNORE_EXCEPTION); ASSERT(!animDiscard->inShadowIncludingDocument()); } } } return earliestFireTime; }