1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/SVGAnimationElement.h"
7 #include "mozilla/dom/SVGSVGElement.h"
8 #include "nsSMILTimeContainer.h"
9 #include "nsSMILAnimationController.h"
10 #include "nsSMILAnimationFunction.h"
11 #include "nsContentUtils.h"
18 //----------------------------------------------------------------------
19 // nsISupports methods
21 NS_IMPL_ADDREF_INHERITED(SVGAnimationElement
, SVGAnimationElementBase
)
22 NS_IMPL_RELEASE_INHERITED(SVGAnimationElement
, SVGAnimationElementBase
)
24 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimationElement
)
25 NS_INTERFACE_MAP_ENTRY(mozilla::dom::SVGTests
)
26 NS_INTERFACE_MAP_END_INHERITING(SVGAnimationElementBase
)
28 NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAnimationElement
,
29 SVGAnimationElementBase
,
30 mHrefTarget
, mTimedElement
)
32 //----------------------------------------------------------------------
35 SVGAnimationElement::SVGAnimationElement(already_AddRefed
<mozilla::dom::NodeInfo
>& aNodeInfo
)
36 : SVGAnimationElementBase(aNodeInfo
),
41 SVGAnimationElement::~SVGAnimationElement()
46 SVGAnimationElement::Init()
48 nsresult rv
= SVGAnimationElementBase::Init();
49 NS_ENSURE_SUCCESS(rv
, rv
);
51 mTimedElement
.SetAnimationElement(this);
52 AnimationFunction().SetAnimationElement(this);
53 mTimedElement
.SetTimeClient(&AnimationFunction());
58 //----------------------------------------------------------------------
61 SVGAnimationElement::GetAnimAttr(nsIAtom
* aName
) const
63 return mAttrsAndChildren
.GetAttr(aName
, kNameSpaceID_None
);
67 SVGAnimationElement::GetAnimAttr(nsIAtom
* aAttName
,
68 nsAString
& aResult
) const
70 return GetAttr(kNameSpaceID_None
, aAttName
, aResult
);
74 SVGAnimationElement::HasAnimAttr(nsIAtom
* aAttName
) const
76 return HasAttr(kNameSpaceID_None
, aAttName
);
80 SVGAnimationElement::GetTargetElementContent()
82 if (HasAttr(kNameSpaceID_XLink
, nsGkAtoms::href
)) {
83 return mHrefTarget
.get();
85 NS_ABORT_IF_FALSE(!mHrefTarget
.get(),
86 "We shouldn't have an xlink:href target "
87 "if we don't have an xlink:href attribute");
89 // No "xlink:href" attribute --> I should target my parent.
90 nsIContent
* parent
= GetFlattenedTreeParent();
91 return parent
&& parent
->IsElement() ? parent
->AsElement() : nullptr;
95 SVGAnimationElement::GetTargetAttributeName(int32_t *aNamespaceID
,
96 nsIAtom
**aLocalName
) const
98 const nsAttrValue
* nameAttr
99 = mAttrsAndChildren
.GetAttr(nsGkAtoms::attributeName
);
104 NS_ASSERTION(nameAttr
->Type() == nsAttrValue::eAtom
,
105 "attributeName should have been parsed as an atom");
107 return NS_SUCCEEDED(nsContentUtils::SplitQName(
108 this, nsDependentAtomString(nameAttr
->GetAtomValue()),
109 aNamespaceID
, aLocalName
));
113 SVGAnimationElement::GetTargetAttributeType() const
115 nsIContent::AttrValuesArray typeValues
[] = { &nsGkAtoms::css
,
118 nsSMILTargetAttrType smilTypes
[] = { eSMILTargetAttrType_CSS
,
119 eSMILTargetAttrType_XML
};
120 int32_t index
= FindAttrValueIn(kNameSpaceID_None
,
121 nsGkAtoms::attributeType
,
124 return (index
>= 0) ? smilTypes
[index
] : eSMILTargetAttrType_auto
;
128 SVGAnimationElement::TimedElement()
130 return mTimedElement
;
134 SVGAnimationElement::GetTargetElement()
138 // We'll just call the other GetTargetElement method, and QI to the right type
139 nsIContent
* target
= GetTargetElementContent();
141 return (target
&& target
->IsSVG()) ? static_cast<nsSVGElement
*>(target
) : nullptr;
145 SVGAnimationElement::GetStartTime(ErrorResult
& rv
)
149 nsSMILTimeValue startTime
= mTimedElement
.GetStartTime();
150 if (!startTime
.IsDefinite()) {
151 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
155 return float(double(startTime
.GetMillis()) / PR_MSEC_PER_SEC
);
159 SVGAnimationElement::GetCurrentTime()
161 // Not necessary to call FlushAnimations() for this
163 nsSMILTimeContainer
* root
= GetTimeContainer();
165 return float(double(root
->GetCurrentTime()) / PR_MSEC_PER_SEC
);
172 SVGAnimationElement::GetSimpleDuration(ErrorResult
& rv
)
174 // Not necessary to call FlushAnimations() for this
176 nsSMILTimeValue simpleDur
= mTimedElement
.GetSimpleDuration();
177 if (!simpleDur
.IsDefinite()) {
178 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
182 return float(double(simpleDur
.GetMillis()) / PR_MSEC_PER_SEC
);
185 //----------------------------------------------------------------------
186 // nsIContent methods
189 SVGAnimationElement::BindToTree(nsIDocument
* aDocument
,
191 nsIContent
* aBindingParent
,
192 bool aCompileEventHandlers
)
194 NS_ABORT_IF_FALSE(!mHrefTarget
.get(),
195 "Shouldn't have href-target yet "
196 "(or it should've been cleared)");
197 nsresult rv
= SVGAnimationElementBase::BindToTree(aDocument
, aParent
,
199 aCompileEventHandlers
);
200 NS_ENSURE_SUCCESS(rv
,rv
);
202 // XXXdholbert is GetCtx (as a check for SVG parent) still needed here?
204 // No use proceeding. We don't have an SVG parent (yet) so we won't be able
205 // to register ourselves etc. Maybe next time we'll have more luck.
206 // (This sort of situation will arise a lot when trees are being constructed
207 // piece by piece via script)
211 // Add myself to the animation controller's master set of animation elements.
213 nsSMILAnimationController
*controller
= aDocument
->GetAnimationController();
215 controller
->RegisterAnimationElement(this);
217 const nsAttrValue
* href
= mAttrsAndChildren
.GetAttr(nsGkAtoms::href
,
220 nsAutoString hrefStr
;
221 href
->ToString(hrefStr
);
223 // Pass in |aParent| instead of |this| -- first argument is only used
224 // for a call to GetComposedDoc(), and |this| might not have a current
226 UpdateHrefTarget(aParent
, hrefStr
);
229 mTimedElement
.BindToTree(aParent
);
232 AnimationNeedsResample();
238 SVGAnimationElement::UnbindFromTree(bool aDeep
, bool aNullParent
)
240 nsSMILAnimationController
*controller
= OwnerDoc()->GetAnimationController();
242 controller
->UnregisterAnimationElement(this);
245 mHrefTarget
.Unlink();
246 mTimedElement
.DissolveReferences();
248 AnimationNeedsResample();
250 SVGAnimationElementBase::UnbindFromTree(aDeep
, aNullParent
);
254 SVGAnimationElement::ParseAttribute(int32_t aNamespaceID
,
256 const nsAString
& aValue
,
257 nsAttrValue
& aResult
)
259 if (aNamespaceID
== kNameSpaceID_None
) {
260 // Deal with target-related attributes here
261 if (aAttribute
== nsGkAtoms::attributeName
||
262 aAttribute
== nsGkAtoms::attributeType
) {
263 aResult
.ParseAtom(aValue
);
264 AnimationNeedsResample();
268 nsresult rv
= NS_ERROR_FAILURE
;
270 // First let the animation function try to parse it...
272 AnimationFunction().SetAttr(aAttribute
, aValue
, aResult
, &rv
);
274 // ... and if that didn't recognize the attribute, let the timed element
278 mTimedElement
.SetAttr(aAttribute
, aValue
, aResult
, this, &rv
);
282 AnimationNeedsResample();
284 ReportAttributeParseFailure(OwnerDoc(), aAttribute
, aValue
);
291 return SVGAnimationElementBase::ParseAttribute(aNamespaceID
, aAttribute
,
296 SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID
, nsIAtom
* aName
,
297 const nsAttrValue
* aValue
, bool aNotify
)
300 SVGAnimationElementBase::AfterSetAttr(aNamespaceID
, aName
, aValue
,
303 if (SVGTests::IsConditionalProcessingAttribute(aName
)) {
304 bool isDisabled
= !SVGTests::PassesConditionalProcessingTests();
305 if (mTimedElement
.SetIsDisabled(isDisabled
)) {
306 AnimationNeedsResample();
310 if (aNamespaceID
!= kNameSpaceID_XLink
|| aName
!= nsGkAtoms::href
)
314 mHrefTarget
.Unlink();
315 AnimationTargetChanged();
316 } else if (IsInDoc()) {
317 NS_ABORT_IF_FALSE(aValue
->Type() == nsAttrValue::eString
,
318 "Expected href attribute to be string type");
319 UpdateHrefTarget(this, aValue
->GetStringValue());
320 } // else: we're not yet in a document -- we'll update the target on
321 // next BindToTree call.
327 SVGAnimationElement::UnsetAttr(int32_t aNamespaceID
,
328 nsIAtom
* aAttribute
, bool aNotify
)
330 nsresult rv
= SVGAnimationElementBase::UnsetAttr(aNamespaceID
, aAttribute
,
332 NS_ENSURE_SUCCESS(rv
,rv
);
334 if (aNamespaceID
== kNameSpaceID_None
) {
335 if (AnimationFunction().UnsetAttr(aAttribute
) ||
336 mTimedElement
.UnsetAttr(aAttribute
)) {
337 AnimationNeedsResample();
345 SVGAnimationElement::IsNodeOfType(uint32_t aFlags
) const
347 return !(aFlags
& ~(eCONTENT
| eANIMATION
));
350 //----------------------------------------------------------------------
351 // SVG utility methods
354 SVGAnimationElement::ActivateByHyperlink()
358 // The behavior for when the target is an animation element is defined in
360 // http://www.w3.org/TR/smil-animation/#HyperlinkSemantics
361 nsSMILTimeValue seekTime
= mTimedElement
.GetHyperlinkTime();
362 if (seekTime
.IsDefinite()) {
363 nsSMILTimeContainer
* timeContainer
= GetTimeContainer();
365 timeContainer
->SetCurrentTime(seekTime
.GetMillis());
366 AnimationNeedsResample();
367 // As with SVGSVGElement::SetCurrentTime, we need to trigger
368 // a synchronous sample now.
371 // else, silently fail. We mustn't be part of an SVG document fragment that
372 // is attached to the document tree so there's nothing we can do here
379 //----------------------------------------------------------------------
380 // Implementation helpers
383 SVGAnimationElement::GetTimeContainer()
385 SVGSVGElement
*element
= SVGContentUtils::GetOuterSVGElement(this);
388 return element
->GetTimedDocumentRoot();
395 SVGAnimationElement::BeginElementAt(float offset
, ErrorResult
& rv
)
397 // Make sure the timegraph is up-to-date
400 // This will fail if we're not attached to a time container (SVG document
402 rv
= mTimedElement
.BeginElementAt(offset
);
406 AnimationNeedsResample();
407 // Force synchronous sample so that events resulting from this call arrive in
408 // the expected order and we get an up-to-date paint.
413 SVGAnimationElement::EndElementAt(float offset
, ErrorResult
& rv
)
415 // Make sure the timegraph is up-to-date
418 rv
= mTimedElement
.EndElementAt(offset
);
422 AnimationNeedsResample();
423 // Force synchronous sample
428 SVGAnimationElement::IsEventAttributeName(nsIAtom
* aName
)
430 return nsContentUtils::IsEventAttributeName(aName
, EventNameType_SMIL
);
434 SVGAnimationElement::UpdateHrefTarget(nsIContent
* aNodeForContext
,
435 const nsAString
& aHrefStr
)
437 nsCOMPtr
<nsIURI
> targetURI
;
438 nsCOMPtr
<nsIURI
> baseURI
= GetBaseURI();
439 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI
),
440 aHrefStr
, OwnerDoc(), baseURI
);
441 mHrefTarget
.Reset(aNodeForContext
, targetURI
);
442 AnimationTargetChanged();
446 SVGAnimationElement::AnimationTargetChanged()
448 mTimedElement
.HandleTargetElementChange(GetTargetElementContent());
449 AnimationNeedsResample();
453 } // namespace mozilla