SVGAnimationElement.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* -*- Mode: C++; tab-width: 8; 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/. */
  5. #include "mozilla/dom/SVGAnimationElement.h"
  6. #include "mozilla/dom/SVGSVGElement.h"
  7. #include "nsSMILTimeContainer.h"
  8. #include "nsSMILAnimationController.h"
  9. #include "nsSMILAnimationFunction.h"
  10. #include "nsContentUtils.h"
  11. #include "nsIContentInlines.h"
  12. #include "nsIURI.h"
  13. #include "prtime.h"
  14. namespace mozilla {
  15. namespace dom {
  16. //----------------------------------------------------------------------
  17. // nsISupports methods
  18. NS_IMPL_ADDREF_INHERITED(SVGAnimationElement, SVGAnimationElementBase)
  19. NS_IMPL_RELEASE_INHERITED(SVGAnimationElement, SVGAnimationElementBase)
  20. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimationElement)
  21. NS_INTERFACE_MAP_ENTRY(mozilla::dom::SVGTests)
  22. NS_INTERFACE_MAP_END_INHERITING(SVGAnimationElementBase)
  23. NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAnimationElement,
  24. SVGAnimationElementBase,
  25. mHrefTarget, mTimedElement)
  26. //----------------------------------------------------------------------
  27. // Implementation
  28. SVGAnimationElement::SVGAnimationElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
  29. : SVGAnimationElementBase(aNodeInfo),
  30. mHrefTarget(this)
  31. {
  32. }
  33. SVGAnimationElement::~SVGAnimationElement()
  34. {
  35. }
  36. nsresult
  37. SVGAnimationElement::Init()
  38. {
  39. nsresult rv = SVGAnimationElementBase::Init();
  40. NS_ENSURE_SUCCESS(rv, rv);
  41. mTimedElement.SetAnimationElement(this);
  42. AnimationFunction().SetAnimationElement(this);
  43. mTimedElement.SetTimeClient(&AnimationFunction());
  44. return NS_OK;
  45. }
  46. //----------------------------------------------------------------------
  47. const nsAttrValue*
  48. SVGAnimationElement::GetAnimAttr(nsIAtom* aName) const
  49. {
  50. return mAttrsAndChildren.GetAttr(aName, kNameSpaceID_None);
  51. }
  52. bool
  53. SVGAnimationElement::GetAnimAttr(nsIAtom* aAttName,
  54. nsAString& aResult) const
  55. {
  56. return GetAttr(kNameSpaceID_None, aAttName, aResult);
  57. }
  58. bool
  59. SVGAnimationElement::HasAnimAttr(nsIAtom* aAttName) const
  60. {
  61. return HasAttr(kNameSpaceID_None, aAttName);
  62. }
  63. Element*
  64. SVGAnimationElement::GetTargetElementContent()
  65. {
  66. if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) ||
  67. HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
  68. return mHrefTarget.get();
  69. }
  70. MOZ_ASSERT(!mHrefTarget.get(),
  71. "We shouldn't have a href target "
  72. "if we don't have an xlink:href or href attribute");
  73. // No "href" or "xlink:href" attribute --> I should target my parent.
  74. nsIContent* parent = GetFlattenedTreeParent();
  75. return parent && parent->IsElement() ? parent->AsElement() : nullptr;
  76. }
  77. bool
  78. SVGAnimationElement::GetTargetAttributeName(int32_t *aNamespaceID,
  79. nsIAtom **aLocalName) const
  80. {
  81. const nsAttrValue* nameAttr
  82. = mAttrsAndChildren.GetAttr(nsGkAtoms::attributeName);
  83. if (!nameAttr)
  84. return false;
  85. NS_ASSERTION(nameAttr->Type() == nsAttrValue::eAtom,
  86. "attributeName should have been parsed as an atom");
  87. return NS_SUCCEEDED(nsContentUtils::SplitQName(
  88. this, nsDependentAtomString(nameAttr->GetAtomValue()),
  89. aNamespaceID, aLocalName));
  90. }
  91. nsSMILTargetAttrType
  92. SVGAnimationElement::GetTargetAttributeType() const
  93. {
  94. nsIContent::AttrValuesArray typeValues[] = { &nsGkAtoms::css,
  95. &nsGkAtoms::XML,
  96. nullptr};
  97. nsSMILTargetAttrType smilTypes[] = { eSMILTargetAttrType_CSS,
  98. eSMILTargetAttrType_XML };
  99. int32_t index = FindAttrValueIn(kNameSpaceID_None,
  100. nsGkAtoms::attributeType,
  101. typeValues,
  102. eCaseMatters);
  103. return (index >= 0) ? smilTypes[index] : eSMILTargetAttrType_auto;
  104. }
  105. nsSMILTimedElement&
  106. SVGAnimationElement::TimedElement()
  107. {
  108. return mTimedElement;
  109. }
  110. nsSVGElement*
  111. SVGAnimationElement::GetTargetElement()
  112. {
  113. FlushAnimations();
  114. // We'll just call the other GetTargetElement method, and QI to the right type
  115. nsIContent* target = GetTargetElementContent();
  116. return (target && target->IsSVGElement())
  117. ? static_cast<nsSVGElement*>(target) : nullptr;
  118. }
  119. float
  120. SVGAnimationElement::GetStartTime(ErrorResult& rv)
  121. {
  122. FlushAnimations();
  123. nsSMILTimeValue startTime = mTimedElement.GetStartTime();
  124. if (!startTime.IsDefinite()) {
  125. rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  126. return 0.f;
  127. }
  128. return float(double(startTime.GetMillis()) / PR_MSEC_PER_SEC);
  129. }
  130. float
  131. SVGAnimationElement::GetCurrentTime()
  132. {
  133. // Not necessary to call FlushAnimations() for this
  134. nsSMILTimeContainer* root = GetTimeContainer();
  135. if (root) {
  136. return float(double(root->GetCurrentTime()) / PR_MSEC_PER_SEC);
  137. }
  138. return 0.0f;
  139. }
  140. float
  141. SVGAnimationElement::GetSimpleDuration(ErrorResult& rv)
  142. {
  143. // Not necessary to call FlushAnimations() for this
  144. nsSMILTimeValue simpleDur = mTimedElement.GetSimpleDuration();
  145. if (!simpleDur.IsDefinite()) {
  146. rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  147. return 0.f;
  148. }
  149. return float(double(simpleDur.GetMillis()) / PR_MSEC_PER_SEC);
  150. }
  151. //----------------------------------------------------------------------
  152. // nsIContent methods
  153. nsresult
  154. SVGAnimationElement::BindToTree(nsIDocument* aDocument,
  155. nsIContent* aParent,
  156. nsIContent* aBindingParent,
  157. bool aCompileEventHandlers)
  158. {
  159. MOZ_ASSERT(!mHrefTarget.get(),
  160. "Shouldn't have href-target yet (or it should've been cleared)");
  161. nsresult rv = SVGAnimationElementBase::BindToTree(aDocument, aParent,
  162. aBindingParent,
  163. aCompileEventHandlers);
  164. NS_ENSURE_SUCCESS(rv,rv);
  165. // XXXdholbert is GetCtx (as a check for SVG parent) still needed here?
  166. if (!GetCtx()) {
  167. // No use proceeding. We don't have an SVG parent (yet) so we won't be able
  168. // to register ourselves etc. Maybe next time we'll have more luck.
  169. // (This sort of situation will arise a lot when trees are being constructed
  170. // piece by piece via script)
  171. return NS_OK;
  172. }
  173. // Add myself to the animation controller's master set of animation elements.
  174. if (aDocument) {
  175. nsSMILAnimationController *controller = aDocument->GetAnimationController();
  176. if (controller) {
  177. controller->RegisterAnimationElement(this);
  178. }
  179. const nsAttrValue* href =
  180. HasAttr(kNameSpaceID_None, nsGkAtoms::href)
  181. ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None)
  182. : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink);
  183. if (href) {
  184. nsAutoString hrefStr;
  185. href->ToString(hrefStr);
  186. // Pass in |aParent| instead of |this| -- first argument is only used
  187. // for a call to GetComposedDoc(), and |this| might not have a current
  188. // document yet.
  189. UpdateHrefTarget(aParent, hrefStr);
  190. }
  191. mTimedElement.BindToTree(aParent);
  192. }
  193. AnimationNeedsResample();
  194. return NS_OK;
  195. }
  196. void
  197. SVGAnimationElement::UnbindFromTree(bool aDeep, bool aNullParent)
  198. {
  199. nsSMILAnimationController *controller = OwnerDoc()->GetAnimationController();
  200. if (controller) {
  201. controller->UnregisterAnimationElement(this);
  202. }
  203. mHrefTarget.Unlink();
  204. mTimedElement.DissolveReferences();
  205. AnimationNeedsResample();
  206. SVGAnimationElementBase::UnbindFromTree(aDeep, aNullParent);
  207. }
  208. bool
  209. SVGAnimationElement::ParseAttribute(int32_t aNamespaceID,
  210. nsIAtom* aAttribute,
  211. const nsAString& aValue,
  212. nsAttrValue& aResult)
  213. {
  214. if (aNamespaceID == kNameSpaceID_None) {
  215. // Deal with target-related attributes here
  216. if (aAttribute == nsGkAtoms::attributeName ||
  217. aAttribute == nsGkAtoms::attributeType) {
  218. aResult.ParseAtom(aValue);
  219. AnimationNeedsResample();
  220. return true;
  221. }
  222. nsresult rv = NS_ERROR_FAILURE;
  223. // First let the animation function try to parse it...
  224. bool foundMatch =
  225. AnimationFunction().SetAttr(aAttribute, aValue, aResult, &rv);
  226. // ... and if that didn't recognize the attribute, let the timed element
  227. // try to parse it.
  228. if (!foundMatch) {
  229. foundMatch =
  230. mTimedElement.SetAttr(aAttribute, aValue, aResult, this, &rv);
  231. }
  232. if (foundMatch) {
  233. AnimationNeedsResample();
  234. if (NS_FAILED(rv)) {
  235. ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
  236. return false;
  237. }
  238. return true;
  239. }
  240. }
  241. return SVGAnimationElementBase::ParseAttribute(aNamespaceID, aAttribute,
  242. aValue, aResult);
  243. }
  244. nsresult
  245. SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
  246. const nsAttrValue* aValue,
  247. const nsAttrValue* aOldValue, bool aNotify)
  248. {
  249. nsresult rv =
  250. SVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue,
  251. aOldValue, aNotify);
  252. if (SVGTests::IsConditionalProcessingAttribute(aName)) {
  253. bool isDisabled = !SVGTests::PassesConditionalProcessingTests();
  254. if (mTimedElement.SetIsDisabled(isDisabled)) {
  255. AnimationNeedsResample();
  256. }
  257. }
  258. if (!((aNamespaceID == kNameSpaceID_None ||
  259. aNamespaceID == kNameSpaceID_XLink) &&
  260. aName == nsGkAtoms::href)) {
  261. return rv;
  262. }
  263. if (!aValue) {
  264. if (aNamespaceID == kNameSpaceID_None) {
  265. mHrefTarget.Unlink();
  266. AnimationTargetChanged();
  267. // After unsetting href, we may still have xlink:href, so we
  268. // should try to add it back.
  269. const nsAttrValue* xlinkHref =
  270. mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink);
  271. if (xlinkHref) {
  272. UpdateHrefTarget(this, xlinkHref->GetStringValue());
  273. }
  274. } else if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
  275. mHrefTarget.Unlink();
  276. AnimationTargetChanged();
  277. } // else: we unset xlink:href, but we still have href attribute, so keep
  278. // mHrefTarget linking to href.
  279. } else if (IsInUncomposedDoc() &&
  280. !(aNamespaceID == kNameSpaceID_XLink &&
  281. HasAttr(kNameSpaceID_None, nsGkAtoms::href))) {
  282. // Note: "href" takes priority over xlink:href. So if "xlink:href" is being
  283. // set here, we only let that update our target if "href" is *unset*.
  284. MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
  285. "Expected href attribute to be string type");
  286. UpdateHrefTarget(this, aValue->GetStringValue());
  287. } // else: we're not yet in a document -- we'll update the target on
  288. // next BindToTree call.
  289. return rv;
  290. }
  291. nsresult
  292. SVGAnimationElement::UnsetAttr(int32_t aNamespaceID,
  293. nsIAtom* aAttribute, bool aNotify)
  294. {
  295. nsresult rv = SVGAnimationElementBase::UnsetAttr(aNamespaceID, aAttribute,
  296. aNotify);
  297. NS_ENSURE_SUCCESS(rv,rv);
  298. if (aNamespaceID == kNameSpaceID_None) {
  299. if (AnimationFunction().UnsetAttr(aAttribute) ||
  300. mTimedElement.UnsetAttr(aAttribute)) {
  301. AnimationNeedsResample();
  302. }
  303. }
  304. return NS_OK;
  305. }
  306. bool
  307. SVGAnimationElement::IsNodeOfType(uint32_t aFlags) const
  308. {
  309. return !(aFlags & ~(eCONTENT | eANIMATION));
  310. }
  311. //----------------------------------------------------------------------
  312. // SVGTests methods
  313. bool
  314. SVGAnimationElement::IsInChromeDoc() const
  315. {
  316. return nsContentUtils::IsChromeDoc(OwnerDoc());
  317. }
  318. //----------------------------------------------------------------------
  319. // SVG utility methods
  320. void
  321. SVGAnimationElement::ActivateByHyperlink()
  322. {
  323. FlushAnimations();
  324. // The behavior for when the target is an animation element is defined in
  325. // SMIL Animation:
  326. // http://www.w3.org/TR/smil-animation/#HyperlinkSemantics
  327. nsSMILTimeValue seekTime = mTimedElement.GetHyperlinkTime();
  328. if (seekTime.IsDefinite()) {
  329. nsSMILTimeContainer* timeContainer = GetTimeContainer();
  330. if (timeContainer) {
  331. timeContainer->SetCurrentTime(seekTime.GetMillis());
  332. AnimationNeedsResample();
  333. // As with SVGSVGElement::SetCurrentTime, we need to trigger
  334. // a synchronous sample now.
  335. FlushAnimations();
  336. }
  337. // else, silently fail. We mustn't be part of an SVG document fragment that
  338. // is attached to the document tree so there's nothing we can do here
  339. } else {
  340. IgnoredErrorResult rv;
  341. BeginElement(rv);
  342. }
  343. }
  344. //----------------------------------------------------------------------
  345. // Implementation helpers
  346. nsSMILTimeContainer*
  347. SVGAnimationElement::GetTimeContainer()
  348. {
  349. SVGSVGElement *element = SVGContentUtils::GetOuterSVGElement(this);
  350. if (element) {
  351. return element->GetTimedDocumentRoot();
  352. }
  353. return nullptr;
  354. }
  355. void
  356. SVGAnimationElement::BeginElementAt(float offset, ErrorResult& rv)
  357. {
  358. // Make sure the timegraph is up-to-date
  359. FlushAnimations();
  360. // This will fail if we're not attached to a time container (SVG document
  361. // fragment).
  362. rv = mTimedElement.BeginElementAt(offset);
  363. if (rv.Failed())
  364. return;
  365. AnimationNeedsResample();
  366. // Force synchronous sample so that events resulting from this call arrive in
  367. // the expected order and we get an up-to-date paint.
  368. FlushAnimations();
  369. }
  370. void
  371. SVGAnimationElement::EndElementAt(float offset, ErrorResult& rv)
  372. {
  373. // Make sure the timegraph is up-to-date
  374. FlushAnimations();
  375. rv = mTimedElement.EndElementAt(offset);
  376. if (rv.Failed())
  377. return;
  378. AnimationNeedsResample();
  379. // Force synchronous sample
  380. FlushAnimations();
  381. }
  382. bool
  383. SVGAnimationElement::IsEventAttributeName(nsIAtom* aName)
  384. {
  385. return nsContentUtils::IsEventAttributeName(aName, EventNameType_SMIL);
  386. }
  387. void
  388. SVGAnimationElement::UpdateHrefTarget(nsIContent* aNodeForContext,
  389. const nsAString& aHrefStr)
  390. {
  391. nsCOMPtr<nsIURI> targetURI;
  392. nsCOMPtr<nsIURI> baseURI = GetBaseURI();
  393. nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),
  394. aHrefStr, OwnerDoc(), baseURI);
  395. mHrefTarget.Reset(aNodeForContext, targetURI);
  396. AnimationTargetChanged();
  397. }
  398. void
  399. SVGAnimationElement::AnimationTargetChanged()
  400. {
  401. mTimedElement.HandleTargetElementChange(GetTargetElementContent());
  402. AnimationNeedsResample();
  403. }
  404. } // namespace dom
  405. } // namespace mozilla