SVG:Notification Mechanisms

From MozillaWiki
Jump to: navigation, search


This page describes possible refactorings to support animated values. In addition to this work we have Brian's description of his attempt to implement a SMIL Model which includes roc's description of the Animation Controller.

There is also a fairly detailed report of some efforts to date to implement SMIL in Mozilla at:

Changes to the content model

The current SVG content model directly maps to the SVG IDL, with heavyweight objects backing the attributes, meaning for a single attribute we currently have two objects (the animated type and its baseval) and possibly a third (for its animVal).

Given our current observer hookup mechanism, we would always need to allocate this animVal (since the frames hook to the result of GetAnimVal) unless we introduce some proxy that would switch between sending events from the baseVal and a newly allocated animVal resulting from animation of an attribute.

Robert O'Callahan has suggested that we might want to simplify our storage of attributes to save in code size, time, and space. Since direct use of the SVGAnimated* types in the IDL is rare, these could be tear-offs created on demand. Code like this:

 // DOM property: x ,  #IMPLIED attrib: x
   nsCOMPtr<nsISVGLength> length;
   rv = NS_NewSVGLength(getter_AddRefs(length),
   rv = NS_NewSVGAnimatedLength(getter_AddRefs(mX), length);
   rv = AddMappedSVGValue(nsSVGAtoms::x, mX);

Would change to:

 // DOM property: x ,  #IMPLIED attrib: x
 mX.Init(this, nsSVGAtoms::x);

Notification of events to frames will need adjustment for this.

roc: I would change the SVG observer system to observe content objects instead of individual properties; those objects are already XPCOM objects and adding a new interface and making them weakly referenceable is not such a big deal. Then Will/DidModifySVGObservable would pass an extra parameter indicating what property was modified. Furthermore, every SVG frame (or at least the primary frame for each SVG element) should automatically be treated as an observer of its content element without needing to say so explicitly. In that case most SVG elements won't need any explicit observers (right?), and we could perhaps move the observer lists to an external hashtable.

tor: we abuse the SVG observer system to handle cases where elements are dependent on other elements (ex: changing attributes of a gradient should force everything using that for a fill to redraw). Maybe those should be switched to using nsIDocumentObserver?

bz: Note that the setup roc suggests is very similar to the existing AttributeChanged notifications, which are unconditionally delivered to frames.

tor: what causes AttributeChanged to be called? We also need this to happen when the mapped attributed exposed through the DOM is modified.

bz: AttributeChanged is called any time the value returned by getAttribute() changes; generally this is done from the SetAttr() implementation. I'm not sure what you mean by "mapped attribute" here; I assume you mean something SVG-specific, not the generic Gecko "mapped into style" meaning? In any case, if things are changing but not affecting the state of getAttribute(), then we need a separate notification. Otherwise, AttributeChanged sort of does more or less what we want (though we may still want a separate notification for some reason, of course).

tor: I'm talking about how the attributes are mapped through the DOM as baseVal. Per our conversation I asked for clarification on when the return of getAttribute() should change. The response was that it should change for baseVal modification, but not during animations (where you either need to look at animVal in SVG 1.1 or the trait system in 1.2).

It probably makes sense for nsSVGLength etc to support animation values implicitly. Maybe we could get to something like this. Or am I dreaming? :-)

class nsSVGLength
     void SetContext(context);

     nsresult SetBaseValueString(const nsAString& aValue);
     void GetBaseValueString(nsAString& aValue);
     float GetBaseValue(nsSVGCoordCtx* aCtx);
     float GetBaseValue(nsSVGElement* aContent)
     { return GetBaseValue(aContent->GetCtxByType(mCtxType)); }

     float GetAnimValue(nsSVGCoordCtx* aCtx);
     float GetAnimValue(nsSVGElement* aContent)
     { return GetAnimValue(aContent->GetCtxByType(mCtxType)); }

     nsIDOMSVGLength* ToDOMBaseVal(nsSVGElement* aContent)
     { return new DOMBaseVal(this, aContent); }
     nsIDOMSVGLength* ToDOMAnimatedVal(nsSVGElement* aContent)
     { return new DOMAnimatedVal(this, aContent); }
     nsIDOMSVGAnimatedLength* ToDOMAnimatedLength(nsSVGElement* aContent)
     { return new DOMAnimatedLength(this, aContent); }

     struct {
         float mAnimatedValue;
         float mBaseValue;
         PRUint16 mAnimatedValueSpecifiedUnitType;
     } AnimatedValues;
     union {
         AnimatedValues* mAnimatedValues;
         float mBaseValueInSpecifiedUnits;
     } data;
     PRUint16 mBaseValue;
     PRUint8 mCtxType; // X, Y or Unspecified
     PRPackedBool mIsAnimated:1;

     float BaseValInSpecifiedUnits()
     { return mIsAnimated ? data.mAnimatedValues->mBaseValue : data.mBaseValue; }
     float AnimatedValInSpecifiedUnits()
     { return mIsAnimated ? data.mAnimatedValues->mAnimatedValue : data.mBaseValue; }

     // TODO various stuff

     struct DOMBaseVal : public nsIDOMSVGLength
         DOMLength(nsSVGValue* aVal, nsSVGElement* aContent)
           : val(aVal), content(aContent) {}
         nsSVGValue* val; // kept alive because it belongs to content
         nsCOMPtr<nsSVGElement> content;

         nsresult GetUnitType(PRUint16* aResult)
         { *aResult = val->mBaseValueSpecifiedUnitType; return NS_OK; }
         nsresult GetValue(float* aResult)
         { *aResult = val->GetBaseValue(content); return NS_OK; }

         // TODO etc

     // TODO DOMAnimateVal, DOMAnimatedLength

I also want to have a close look at the attribute mapping system. It seems to me that we don't need mapping information on every object, it should just be per-object-type.

Do we want to do this step now or continue with the existing scheme?

roc: I think this should be done before implementing animation.

New notes from jab:

On looking into the AttributeChanged issues that need to be solved, I've decided that my "have elements handle everything" is not necessarily the best approach. Something similar to tor's code above would be best. AttrChanged will still need some object implementing nsSVGValue or something similar.

Making use of nsIContent and nsIContentObserver is simple. However, a few questions remain: 1. How to differentiate between DOM/Base and Presentation/Anim values or changes in those values? 2. Quick access from the frame code to the layout value occurs how?

Historical discussion

Some historical discussion regarding svg animation:

roc: I like those ideas. But I think we need a global animation controller that is not SVG specific. I'll put something about that here: SMIL:Animation_Controller.

brian: I've put together a small prototype of some of these ideas.