void SVGSMILElement::connectConditions() { if (m_conditionsConnected) disconnectConditions(); m_conditionsConnected = true; for (unsigned n = 0; n < m_conditions.size(); ++n) { Condition& condition = m_conditions[n]; if (condition.m_type == Condition::EventBase) { ASSERT(!condition.m_syncbase); Element* eventBase = eventBaseFor(condition); if (!eventBase) continue; ASSERT(!condition.m_eventListener); condition.m_eventListener = ConditionEventListener::create(this, &condition); 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 (!isSMILElement(condition.m_syncbase.get())) { condition.m_syncbase = 0; continue; } SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get()); syncbase->addTimeDependent(this); } } }
void SVGAElement::defaultEventHandler(Event* evt) { if (isLink() && (evt->type() == clickEvent || (evt->type() == keydownEvent && focused()))) { MouseEvent* e = 0; if (evt->type() == clickEvent && evt->isMouseEvent()) e = static_cast<MouseEvent*>(evt); KeyboardEvent* k = 0; if (evt->type() == keydownEvent && evt->isKeyboardEvent()) k = static_cast<KeyboardEvent*>(evt); if (e && e->button() == RightButton) { SVGStyledTransformableElement::defaultEventHandler(evt); return; } if (k) { if (k->keyIdentifier() != "Enter") { SVGStyledTransformableElement::defaultEventHandler(evt); return; } evt->setDefaultHandled(); dispatchSimulatedClick(evt); return; } String target = this->target(); if (e && e->button() == MiddleButton) target = "_blank"; else if (target.isEmpty()) // if target is empty, default to "_self" or use xlink:target if set target = (getAttribute(XLinkNames::showAttr) == "new") ? "_blank" : "_self"; if (!evt->defaultPrevented()) { String url = parseURL(href()); #if ENABLE(SVG_ANIMATION) if (url.startsWith("#")) { Element* targetElement = document()->getElementById(url.substring(1)); if (SVGSMILElement::isSMILElement(targetElement)) { SVGSMILElement* timed = static_cast<SVGSMILElement*>(targetElement); timed->beginByLinkActivation(); evt->setDefaultHandled(); SVGStyledTransformableElement::defaultEventHandler(evt); return; } } #endif if (document()->frame()) document()->frame()->loader()->urlSelected(document()->completeURL(url), target, evt, false, true); } evt->setDefaultHandled(); } SVGStyledTransformableElement::defaultEventHandler(evt); }
void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting) { ASSERT(m_intervalBegin.isFinite()); DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ()); if (loopBreaker.contains(this)) return; loopBreaker.add(this); TimeDependentSet::iterator end = m_timeDependents.end(); for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) { SVGSMILElement* dependent = *it; dependent->createInstanceTimesFromSyncbase(this, newOrExisting); } loopBreaker.remove(this); }
void SVGSMILElement::connectSyncBaseConditions() { if (m_syncBaseConditionsConnected) disconnectSyncBaseConditions(); m_syncBaseConditionsConnected = true; for (unsigned n = 0; n < m_conditions.size(); ++n) { Condition* condition = m_conditions[n].get(); if (condition->getType() == Condition::Syncbase) { ASSERT(!condition->baseID().isEmpty()); Element* element = treeScope().getElementById(AtomicString(condition->baseID())); if (!element || !isSVGSMILElement(*element)) { condition->setSyncBase(0); continue; } SVGSMILElement* svgSMILElement = toSVGSMILElement(element); condition->setSyncBase(svgSMILElement); svgSMILElement->addSyncBaseDependent(this); } } }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliestFireTime = SMILTime::unresolved(); #ifndef NDEBUG // 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 AnimationsVector animationsToApply; GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { AnimationsVector* scheduled = it->second; // 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. sortByPriority(*scheduled, elapsed); SVGSMILElement* resultElement = 0; unsigned size = scheduled->size(); for (unsigned n = 0; n < size; n++) { SVGSMILElement* animation = scheduled->at(n); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; } // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) resultElement = 0; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = min(nextFireTime, earliestFireTime); } if (resultElement) animationsToApply.append(resultElement); } unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif startTimer(earliestFireTime, animationFrameDelay); return; } // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif startTimer(earliestFireTime, animationFrameDelay); Document::updateStyleForAllDocuments(); }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, double nextManualSampleTime, const String& nextSamplingTarget) { SMILTime earliersFireTime = SMILTime::unresolved(); Vector<SVGSMILElement*> toAnimate; copyToVector(m_scheduledAnimations, toAnimate); if (nextManualSampleTime) { SMILTime samplingDiff; for (unsigned n = 0; n < toAnimate.size(); ++n) { SVGSMILElement* animation = toAnimate[n]; ASSERT(animation->timeContainer() == this); SVGElement* targetElement = animation->targetElement(); // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. if (!targetElement || !targetElement->hasID() || targetElement->idForStyleResolution() != nextSamplingTarget) continue; samplingDiff = animation->intervalBegin(); break; } elapsed = SMILTime(nextManualSampleTime) + samplingDiff; } // 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. sortByPriority(toAnimate, elapsed); // Calculate animation contributions. typedef HashMap<ElementAttributePair, RefPtr<SVGSMILElement> > ResultElementMap; ResultElementMap resultsElements; for (unsigned n = 0; n < toAnimate.size(); ++n) { SVGSMILElement* animation = toAnimate[n]; ASSERT(animation->timeContainer() == this); SVGElement* targetElement = animation->targetElement(); if (!targetElement) continue; QualifiedName attributeName = animation->attributeName(); if (attributeName == anyQName()) { if (animation->hasTagName(SVGNames::animateMotionTag)) attributeName = SVGNames::animateMotionTag; else continue; } // Results are accumulated to the first animation that animates a particular element/attribute pair. ElementAttributePair key(targetElement, attributeName); SVGSMILElement* resultElement = resultsElements.get(key).get(); if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; resultElement->resetToBaseValue(baseValueFor(key)); resultsElements.add(key, resultElement); } // This will calculate the contribution from the animation and add it to the resultsElement. animation->progress(elapsed, resultElement); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliersFireTime = min(nextFireTime, earliersFireTime); } Vector<SVGSMILElement*> animationsToApply; ResultElementMap::iterator end = resultsElements.end(); for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) animationsToApply.append(it->second.get()); // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly. sortByApplyOrder(animationsToApply); // Apply results to target elements. for (unsigned n = 0; n < animationsToApply.size(); ++n) animationsToApply[n]->applyResultsToTarget(); startTimer(earliersFireTime, animationFrameDelay); Document::updateStyleForAllDocuments(); }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliersFireTime = SMILTime::unresolved(); Vector<SVGSMILElement*> toAnimate; copyToVector(m_scheduledAnimations, toAnimate); // 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. sortByPriority(toAnimate, elapsed); // Calculate animation contributions. typedef pair<SVGElement*, QualifiedName> ElementAttributePair; typedef HashMap<ElementAttributePair, RefPtr<SVGSMILElement> > ResultElementMap; ResultElementMap resultsElements; HashSet<SVGSMILElement*> contributingElements; for (unsigned n = 0; n < toAnimate.size(); ++n) { SVGSMILElement* animation = toAnimate[n]; ASSERT(animation->timeContainer() == this); SVGElement* targetElement = animation->targetElement(); if (!targetElement) continue; QualifiedName attributeName = animation->attributeName(); if (attributeName == anyQName()) { if (animation->hasTagName(SVGNames::animateMotionTag)) attributeName = SVGNames::animateMotionTag; else continue; } // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. ElementAttributePair key(targetElement, attributeName); SVGSMILElement* resultElement = resultsElements.get(key).get(); bool accumulatedResultElement = false; if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; resultsElements.add(key, resultElement); accumulatedResultElement = true; } // This will calculate the contribution from the animation and add it to the resultsElement. if (animation->progress(elapsed, resultElement, seekToTime)) contributingElements.add(resultElement); else if (accumulatedResultElement) resultsElements.remove(key); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliersFireTime = min(nextFireTime, earliersFireTime); } Vector<SVGSMILElement*> animationsToApply; ResultElementMap::iterator end = resultsElements.end(); for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) { SVGSMILElement* animation = it->second.get(); if (contributingElements.contains(animation)) animationsToApply.append(animation); } unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { startTimer(earliersFireTime, animationFrameDelay); return; } // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly. sortByApplyOrder(animationsToApply); // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); startTimer(earliersFireTime, animationFrameDelay); Document::updateStyleForAllDocuments(); }
void ConditionEventListener::handleEvent(ExecutionContext*, Event* event) { if (!m_animation) return; m_animation->handleConditionEvent(event, m_condition); }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliestFireTime = SMILTime::unresolved(); #ifndef NDEBUG // 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(); Vector<RefPtr<SVGSMILElement> > animationsToApply; GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { 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. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. std::sort(scheduled->begin(), scheduled->end(), PriorityCompare(elapsed)); SVGSMILElement* resultElement = 0; unsigned size = scheduled->size(); for (unsigned n = 0; n < size; n++) { SVGSMILElement* animation = scheduled->at(n); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. // FIXME: we should ensure that resultElement is of an appropriate type. if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; } // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) resultElement = 0; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = min(nextFireTime, earliestFireTime); } if (resultElement) animationsToApply.append(resultElement); } std::sort(animationsToApply.begin(), animationsToApply.end(), PriorityCompare(elapsed)); unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif scheduleAnimationFrame(earliestFireTime); return; } // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif scheduleAnimationFrame(earliestFireTime); for (unsigned i = 0; i < animationsToApplySize; ++i) { if (animationsToApply[i]->inDocument() && animationsToApply[i]->isSVGDiscardElement()) { RefPtr<SVGSMILElement> animDiscard = animationsToApply[i]; RefPtr<SVGElement> targetElement = animDiscard->targetElement(); if (targetElement && targetElement->inDocument()) { targetElement->remove(IGNORE_EXCEPTION); ASSERT(!targetElement->inDocument()); } if (animDiscard->inDocument()) { animDiscard->remove(IGNORE_EXCEPTION); ASSERT(!animDiscard->inDocument()); } } } }
SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { 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(); WillBeHeapHashSet<ElementAttributePair> invalidKeys; using AnimationsVector = WillBeHeapVector<RefPtrWillBeMember<SVGSMILElement>>; AnimationsVector animationsToApply; for (const auto& entry : m_scheduledAnimations) { if (!entry.key.first || entry.value->isEmpty()) { invalidKeys.add(entry.key); continue; } AnimationsLinkedHashSet* scheduled = entry.value.get(); // 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. AnimationsVector scheduledAnimations; copyToVector(*scheduled, scheduledAnimations); std::sort(scheduledAnimations.begin(), scheduledAnimations.end(), PriorityCompare(elapsed)); SVGSMILElement* resultElement = nullptr; for (const auto& itAnimation : scheduledAnimations) { SVGSMILElement* animation = itAnimation.get(); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. // FIXME: we should ensure that resultElement is of an appropriate type. if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; } // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) resultElement = nullptr; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = std::min(nextFireTime, earliestFireTime); } if (resultElement) animationsToApply.append(resultElement); } 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]->inDocument() && animationsToApply[i]->isSVGDiscardElement()) { RefPtrWillBeRawPtr<SVGSMILElement> animDiscard = animationsToApply[i]; RefPtrWillBeRawPtr<SVGElement> targetElement = animDiscard->targetElement(); if (targetElement && targetElement->inDocument()) { targetElement->remove(IGNORE_EXCEPTION); ASSERT(!targetElement->inDocument()); } if (animDiscard->inDocument()) { animDiscard->remove(IGNORE_EXCEPTION); ASSERT(!animDiscard->inDocument()); } } } return earliestFireTime; }