static v8::Handle<v8::Value> endElementCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGAnimationElement.endElement"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(args.Holder()); imp->endElement(); return v8::Handle<v8::Value>(); }
static v8::Handle<v8::Value> hasExtensionCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGAnimationElement.hasExtension"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(args.Holder()); STRING_TO_V8PARAMETER_EXCEPTION_BLOCK(V8Parameter<>, extension, args[0]); return v8Boolean(imp->hasExtension(extension)); }
SVGTimer::TargetAnimationMap SVGTimer::animationsByElement(double elapsedSeconds) { // Build a list of all animations which apply to each element // FIXME: This list should be sorted by animation priority ExceptionCode ec = 0; TargetAnimationMap targetMap; SVGNotifySet::const_iterator end = m_notifySet.end(); for (SVGNotifySet::const_iterator it = m_notifySet.begin(); it != end; ++it) { SVGAnimationElement* animation = *it; // If we're dealing with a disabled element with fill="freeze", // we have to take it into account for further calculations. if (!m_enabledNotifySet.contains(animation)) { if (!animation->isFrozen()) continue; if (elapsedSeconds <= (animation->getStartTime() + animation->getSimpleDuration(ec))) continue; } SVGElement* target = const_cast<SVGElement*>(animation->targetElement()); TargetAnimationMap::iterator i = targetMap.find(target); if (i != targetMap.end()) i->second.append(animation); else { Vector<SVGAnimationElement*> list; list.append(animation); targetMap.set(target, list); } } return targetMap; }
void nsSMILAnimationController::RewindElements() { bool rewindNeeded = false; for (auto iter = mChildContainerTable.Iter(); !iter.Done(); iter.Next()) { nsSMILTimeContainer* container = iter.Get()->GetKey(); if (container->NeedsRewind()) { rewindNeeded = true; break; } } if (!rewindNeeded) return; for (auto iter = mAnimationElementTable.Iter(); !iter.Done(); iter.Next()) { SVGAnimationElement* animElem = iter.Get()->GetKey(); nsSMILTimeContainer* timeContainer = animElem->GetTimeContainer(); if (timeContainer && timeContainer->NeedsRewind()) { animElem->TimedElement().Rewind(); } } for (auto iter = mChildContainerTable.Iter(); !iter.Done(); iter.Next()) { iter.Get()->GetKey()->ClearNeedsRewind(); } }
static v8::Handle<v8::Value> externalResourcesRequiredAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { INC_STATS("DOM.SVGAnimationElement.externalResourcesRequired._get"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(info.Holder()); SVGElement* context = imp; PassRefPtr<SVGAnimatedBoolean> resultAsPassRefPtr = V8Proxy::withSVGContext(imp->externalResourcesRequiredAnimated(), context); return toV8(resultAsPassRefPtr); }
static v8::Handle<v8::Value> systemLanguageAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { INC_STATS("DOM.SVGAnimationElement.systemLanguage._get"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(info.Holder()); SVGElement* context = imp; PassRefPtr<SVGStringList> resultAsPassRefPtr = V8Proxy::withSVGContext(imp->systemLanguage(), context); return toV8(resultAsPassRefPtr); }
static v8::Handle<v8::Value> endElementAtCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGAnimationElement.endElementAt"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(args.Holder()); EXCEPTION_BLOCK(float, offset, static_cast<float>(args[0]->NumberValue())); imp->endElementAt(offset); return v8::Handle<v8::Value>(); }
/*static*/ PLDHashOperator nsSMILAnimationController::RewindAnimation(AnimationElementPtrKey* aKey, void* aData) { SVGAnimationElement* animElem = aKey->GetKey(); nsSMILTimeContainer* timeContainer = animElem->GetTimeContainer(); if (timeContainer && timeContainer->NeedsRewind()) { animElem->TimedElement().Rewind(); } return PL_DHASH_NEXT; }
static v8::Handle<v8::Value> getSimpleDurationCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGAnimationElement.getSimpleDuration"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(args.Holder()); ExceptionCode ec = 0; { float result = imp->getSimpleDuration(ec); if (UNLIKELY(ec)) goto fail; return v8::Number::New(result); } fail: V8Proxy::setDOMException(ec); return v8::Handle<v8::Value>(); }
/*static*/ PLDHashOperator nsSMILAnimationController::SampleAnimation(AnimationElementPtrKey* aKey, void* aData) { NS_ENSURE_TRUE(aKey, PL_DHASH_NEXT); NS_ENSURE_TRUE(aKey->GetKey(), PL_DHASH_NEXT); NS_ENSURE_TRUE(aData, PL_DHASH_NEXT); SVGAnimationElement* animElem = aKey->GetKey(); if (animElem->PassesConditionalProcessingTests()) { SampleAnimationParams* params = static_cast<SampleAnimationParams*>(aData); SampleTimedElement(animElem, params->mActiveContainers); AddAnimationToCompositorTable(animElem, params->mCompositorTable); } return PL_DHASH_NEXT; }
// FIXME: This funtion will eventually become part of the AnimationCompositor void SVGTimer::applyAnimations(double elapsedSeconds, const SVGTimer::TargetAnimationMap& targetMap) { TargetAnimationMap::const_iterator targetIterator = targetMap.begin(); TargetAnimationMap::const_iterator tend = targetMap.end(); for (; targetIterator != tend; ++targetIterator) { // FIXME: This is still not 100% correct. Correct would be: // 1. Walk backwards through the priority list until a replace (!isAdditive()) is found // -- This optimization is not possible without careful consideration for dependent values (such as cx and fx in SVGRadialGradient) // 2. Set the initial value (or last replace) as the new animVal // 3. Call each enabled animation in turn, to have it apply its changes // 4. After building a new animVal, set it on the element. // Currenly we use the actual animVal on the element as "temporary storage" // and abstract the getting/setting of the attributes into the SVGAnimate* classes unsigned count = targetIterator->second.size(); for (unsigned i = 0; i < count; ++i) { SVGAnimationElement* animation = targetIterator->second[i]; if (!animation->isValidAnimation()) continue; if (!animation->updateAnimationBaseValueFromElement()) continue; if (!animation->updateAnimatedValueForElapsedSeconds(elapsedSeconds)) continue; animation->applyAnimatedValueToElement(); } } // Make a second pass through the map to avoid multiple setChanged calls on the same element. for (targetIterator = targetMap.begin(); targetIterator != tend; ++targetIterator) { SVGElement* key = targetIterator->first; if (key && key->isStyled()) static_cast<SVGStyledElement*>(key)->setChanged(true); } }
void nsSMILAnimationController::DoMilestoneSamples() { // We need to sample the timing model but because SMIL operates independently // of the frame-rate, we can get one sample at t=0s and the next at t=10min. // // In between those two sample times a whole string of significant events // might be expected to take place: events firing, new interdependencies // between animations resolved and dissolved, etc. // // Furthermore, at any given time, we want to sample all the intervals that // end at that time BEFORE any that begin. This behaviour is implied by SMIL's // endpoint-exclusive timing model. // // So we have the animations (specifically the timed elements) register the // next significant moment (called a milestone) in their lifetime and then we // step through the model at each of these moments and sample those animations // registered for those times. This way events can fire in the correct order, // dependencies can be resolved etc. nsSMILTime sampleTime = INT64_MIN; while (true) { // We want to find any milestones AT OR BEFORE the current sample time so we // initialise the next milestone to the moment after (1ms after, to be // precise) the current sample time and see if there are any milestones // before that. Any other milestones will be dealt with in a subsequent // sample. nsSMILMilestone nextMilestone(GetCurrentTime() + 1, true); mChildContainerTable.EnumerateEntries(GetNextMilestone, &nextMilestone); if (nextMilestone.mTime > GetCurrentTime()) { break; } GetMilestoneElementsParams params; params.mMilestone = nextMilestone; mChildContainerTable.EnumerateEntries(GetMilestoneElements, ¶ms); uint32_t length = params.mElements.Length(); // During the course of a sampling we don't want to actually go backwards. // Due to negative offsets, early ends and the like, a timed element might // register a milestone that is actually in the past. That's fine, but it's // still only going to get *sampled* with whatever time we're up to and no // earlier. // // Because we're only performing this clamping at the last moment, the // animations will still all get sampled in the correct order and // dependencies will be appropriately resolved. sampleTime = std::max(nextMilestone.mTime, sampleTime); for (uint32_t i = 0; i < length; ++i) { SVGAnimationElement* elem = params.mElements[i].get(); NS_ABORT_IF_FALSE(elem, "nullptr animation element in list"); nsSMILTimeContainer* container = elem->GetTimeContainer(); if (!container) // The container may be nullptr if the element has been detached from its // parent since registering a milestone. continue; nsSMILTimeValue containerTimeValue = container->ParentToContainerTime(sampleTime); if (!containerTimeValue.IsDefinite()) continue; // Clamp the converted container time to non-negative values. nsSMILTime containerTime = std::max<nsSMILTime>(0, containerTimeValue.GetMillis()); if (nextMilestone.mIsEnd) { elem->TimedElement().SampleEndAt(containerTime); } else { elem->TimedElement().SampleAt(containerTime); } } } }
static v8::Handle<v8::Value> getCurrentTimeCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGAnimationElement.getCurrentTime"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(args.Holder()); return v8::Number::New(imp->getCurrentTime()); }
static v8::Handle<v8::Value> targetElementAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { INC_STATS("DOM.SVGAnimationElement.targetElement._get"); SVGAnimationElement* imp = V8SVGAnimationElement::toNative(info.Holder()); return toV8(imp->targetElement()); }