SVGAnimatedLengthList.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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 "SVGAnimatedLengthList.h"
  6. #include "DOMSVGAnimatedLengthList.h"
  7. #include "mozilla/Move.h"
  8. #include "nsSVGElement.h"
  9. #include "nsSVGAttrTearoffTable.h"
  10. #include "nsSMILValue.h"
  11. #include "SVGLengthListSMILType.h"
  12. namespace mozilla {
  13. nsresult
  14. SVGAnimatedLengthList::SetBaseValueString(const nsAString& aValue)
  15. {
  16. SVGLengthList newBaseValue;
  17. nsresult rv = newBaseValue.SetValueFromString(aValue);
  18. if (NS_FAILED(rv)) {
  19. return rv;
  20. }
  21. DOMSVGAnimatedLengthList *domWrapper =
  22. DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
  23. if (domWrapper) {
  24. // We must send this notification *before* changing mBaseVal! If the length
  25. // of our baseVal is being reduced, our baseVal's DOM wrapper list may have
  26. // to remove DOM items from itself, and any removed DOM items need to copy
  27. // their internal counterpart values *before* we change them.
  28. //
  29. domWrapper->InternalBaseValListWillChangeTo(newBaseValue);
  30. }
  31. // We don't need to call DidChange* here - we're only called by
  32. // nsSVGElement::ParseAttribute under Element::SetAttr,
  33. // which takes care of notifying.
  34. rv = mBaseVal.CopyFrom(newBaseValue);
  35. if (NS_FAILED(rv) && domWrapper) {
  36. // Attempting to increase mBaseVal's length failed - reduce domWrapper
  37. // back to the same length:
  38. domWrapper->InternalBaseValListWillChangeTo(mBaseVal);
  39. }
  40. return rv;
  41. }
  42. void
  43. SVGAnimatedLengthList::ClearBaseValue(uint32_t aAttrEnum)
  44. {
  45. DOMSVGAnimatedLengthList *domWrapper =
  46. DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
  47. if (domWrapper) {
  48. // We must send this notification *before* changing mBaseVal! (See above.)
  49. domWrapper->InternalBaseValListWillChangeTo(SVGLengthList());
  50. }
  51. mBaseVal.Clear();
  52. // Caller notifies
  53. }
  54. nsresult
  55. SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue,
  56. nsSVGElement *aElement,
  57. uint32_t aAttrEnum)
  58. {
  59. DOMSVGAnimatedLengthList *domWrapper =
  60. DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
  61. if (domWrapper) {
  62. // A new animation may totally change the number of items in the animVal
  63. // list, replacing what was essentially a mirror of the baseVal list, or
  64. // else replacing and overriding an existing animation. When this happens
  65. // we must try and keep our animVal's DOM wrapper in sync (see the comment
  66. // in DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo).
  67. //
  68. // It's not possible for us to reliably distinguish between calls to this
  69. // method that are setting a new sample for an existing animation, and
  70. // calls that are setting the first sample of an animation that will
  71. // override an existing animation. Happily it's cheap to just blindly
  72. // notify our animVal's DOM wrapper of its internal counterpart's new value
  73. // each time this method is called, so that's what we do.
  74. //
  75. // Note that we must send this notification *before* setting or changing
  76. // mAnimVal! (See the comment in SetBaseValueString above.)
  77. //
  78. domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue);
  79. }
  80. if (!mAnimVal) {
  81. mAnimVal = new SVGLengthList();
  82. }
  83. nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
  84. if (NS_FAILED(rv)) {
  85. // OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
  86. // that mAnimVal and its DOM wrapper (if any) will have the same length!
  87. ClearAnimValue(aElement, aAttrEnum);
  88. return rv;
  89. }
  90. aElement->DidAnimateLengthList(aAttrEnum);
  91. return NS_OK;
  92. }
  93. void
  94. SVGAnimatedLengthList::ClearAnimValue(nsSVGElement *aElement,
  95. uint32_t aAttrEnum)
  96. {
  97. DOMSVGAnimatedLengthList *domWrapper =
  98. DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
  99. if (domWrapper) {
  100. // When all animation ends, animVal simply mirrors baseVal, which may have
  101. // a different number of items to the last active animated value. We must
  102. // keep the length of our animVal's DOM wrapper list in sync, and again we
  103. // must do that before touching mAnimVal. See comments above.
  104. //
  105. domWrapper->InternalAnimValListWillChangeTo(mBaseVal);
  106. }
  107. mAnimVal = nullptr;
  108. aElement->DidAnimateLengthList(aAttrEnum);
  109. }
  110. nsISMILAttr*
  111. SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement,
  112. uint8_t aAttrEnum,
  113. uint8_t aAxis,
  114. bool aCanZeroPadList)
  115. {
  116. return new SMILAnimatedLengthList(this, aSVGElement, aAttrEnum, aAxis, aCanZeroPadList);
  117. }
  118. nsresult
  119. SVGAnimatedLengthList::
  120. SMILAnimatedLengthList::ValueFromString(const nsAString& aStr,
  121. const dom::SVGAnimationElement* /*aSrcElement*/,
  122. nsSMILValue& aValue,
  123. bool& aPreventCachingOfSandwich) const
  124. {
  125. nsSMILValue val(&SVGLengthListSMILType::sSingleton);
  126. SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(val.mU.mPtr);
  127. nsresult rv = llai->SetValueFromString(aStr);
  128. if (NS_SUCCEEDED(rv)) {
  129. llai->SetInfo(mElement, mAxis, mCanZeroPadList);
  130. aValue = Move(val);
  131. // If any of the lengths in the list depend on their context, then we must
  132. // prevent caching of the entire animation sandwich. This is because the
  133. // units of a length at a given index can change from sandwich layer to
  134. // layer, and indeed even be different within a single sandwich layer. If
  135. // any length in the result of an animation sandwich is the result of the
  136. // addition of lengths where one or more of those lengths is context
  137. // dependent, then naturally the resultant length is also context
  138. // dependent, regardless of whether its actual unit is context dependent or
  139. // not. Unfortunately normal invalidation mechanisms won't cause us to
  140. // recalculate the result of the sandwich if the context changes, so we
  141. // take the (substantial) performance hit of preventing caching of the
  142. // sandwich layer, causing the animation sandwich to be recalculated every
  143. // single sample.
  144. aPreventCachingOfSandwich = false;
  145. for (uint32_t i = 0; i < llai->Length(); ++i) {
  146. uint8_t unit = (*llai)[i].GetUnit();
  147. if (unit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE ||
  148. unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS ||
  149. unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) {
  150. aPreventCachingOfSandwich = true;
  151. break;
  152. }
  153. }
  154. }
  155. return rv;
  156. }
  157. nsSMILValue
  158. SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const
  159. {
  160. // To benefit from Return Value Optimization and avoid copy constructor calls
  161. // due to our use of return-by-value, we must return the exact same object
  162. // from ALL return points. This function must only return THIS variable:
  163. nsSMILValue val;
  164. nsSMILValue tmp(&SVGLengthListSMILType::sSingleton);
  165. SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(tmp.mU.mPtr);
  166. nsresult rv = llai->CopyFrom(mVal->mBaseVal);
  167. if (NS_SUCCEEDED(rv)) {
  168. llai->SetInfo(mElement, mAxis, mCanZeroPadList);
  169. val = Move(tmp);
  170. }
  171. return val;
  172. }
  173. nsresult
  174. SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(const nsSMILValue& aValue)
  175. {
  176. NS_ASSERTION(aValue.mType == &SVGLengthListSMILType::sSingleton,
  177. "Unexpected type to assign animated value");
  178. if (aValue.mType == &SVGLengthListSMILType::sSingleton) {
  179. mVal->SetAnimValue(*static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr),
  180. mElement,
  181. mAttrEnum);
  182. }
  183. return NS_OK;
  184. }
  185. void
  186. SVGAnimatedLengthList::SMILAnimatedLengthList::ClearAnimValue()
  187. {
  188. if (mVal->mAnimVal) {
  189. mVal->ClearAnimValue(mElement, mAttrEnum);
  190. }
  191. }
  192. } // namespace mozilla