nsSVGViewBox.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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 "nsSVGViewBox.h"
  6. #include "mozilla/Move.h"
  7. #include "nsCharSeparatedTokenizer.h"
  8. #include "nsSMILValue.h"
  9. #include "nsTextFormatter.h"
  10. #include "SVGContentUtils.h"
  11. #include "SVGViewBoxSMILType.h"
  12. #define NUM_VIEWBOX_COMPONENTS 4
  13. using namespace mozilla;
  14. /* Implementation of nsSVGViewBoxRect methods */
  15. bool
  16. nsSVGViewBoxRect::operator==(const nsSVGViewBoxRect& aOther) const
  17. {
  18. if (&aOther == this)
  19. return true;
  20. return (none && aOther.none) ||
  21. (!none && !aOther.none &&
  22. x == aOther.x &&
  23. y == aOther.y &&
  24. width == aOther.width &&
  25. height == aOther.height);
  26. }
  27. /* Cycle collection macros for nsSVGViewBox */
  28. NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMBaseVal, mSVGElement)
  29. NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMAnimVal, mSVGElement)
  30. NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMBaseVal)
  31. NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMBaseVal)
  32. NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMAnimVal)
  33. NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMAnimVal)
  34. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMBaseVal)
  35. NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  36. NS_INTERFACE_MAP_ENTRY(nsISupports)
  37. NS_INTERFACE_MAP_END
  38. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMAnimVal)
  39. NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  40. NS_INTERFACE_MAP_ENTRY(nsISupports)
  41. NS_INTERFACE_MAP_END
  42. static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMBaseVal>
  43. sBaseSVGViewBoxTearoffTable;
  44. static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMAnimVal>
  45. sAnimSVGViewBoxTearoffTable;
  46. nsSVGAttrTearoffTable<nsSVGViewBox, dom::SVGAnimatedRect>
  47. nsSVGViewBox::sSVGAnimatedRectTearoffTable;
  48. /* Implementation of nsSVGViewBox methods */
  49. void
  50. nsSVGViewBox::Init()
  51. {
  52. mHasBaseVal = false;
  53. // We shouldn't use mBaseVal for rendering (its usages should be guarded with
  54. // "mHasBaseVal" checks), but just in case we do by accident, this will
  55. // ensure that we treat it as "none" and ignore its numeric values:
  56. mBaseVal.none = true;
  57. mAnimVal = nullptr;
  58. }
  59. bool
  60. nsSVGViewBox::HasRect() const
  61. {
  62. // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one;
  63. // otherwise, just return false (we clearly do not have a rect).
  64. const nsSVGViewBoxRect* rect = mAnimVal;
  65. if (!rect) {
  66. if (!mHasBaseVal) {
  67. // no anim val, no base val --> no viewbox rect
  68. return false;
  69. }
  70. rect = &mBaseVal;
  71. }
  72. return !rect->none && rect->width >= 0 && rect->height >= 0;
  73. }
  74. void
  75. nsSVGViewBox::SetAnimValue(const nsSVGViewBoxRect& aRect,
  76. nsSVGElement *aSVGElement)
  77. {
  78. if (!mAnimVal) {
  79. // it's okay if allocation fails - and no point in reporting that
  80. mAnimVal = new nsSVGViewBoxRect(aRect);
  81. } else {
  82. if (aRect == *mAnimVal) {
  83. return;
  84. }
  85. *mAnimVal = aRect;
  86. }
  87. aSVGElement->DidAnimateViewBox();
  88. }
  89. void
  90. nsSVGViewBox::SetBaseValue(const nsSVGViewBoxRect& aRect,
  91. nsSVGElement *aSVGElement)
  92. {
  93. if (!mHasBaseVal || mBaseVal == aRect) {
  94. // This method is used to set a single x, y, width
  95. // or height value. It can't create a base value
  96. // as the other components may be undefined. We record
  97. // the new value though, so as not to lose data.
  98. mBaseVal = aRect;
  99. return;
  100. }
  101. nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox();
  102. mBaseVal = aRect;
  103. mHasBaseVal = true;
  104. aSVGElement->DidChangeViewBox(emptyOrOldValue);
  105. if (mAnimVal) {
  106. aSVGElement->AnimationNeedsResample();
  107. }
  108. }
  109. static nsresult
  110. ToSVGViewBoxRect(const nsAString& aStr, nsSVGViewBoxRect *aViewBox)
  111. {
  112. if (aStr.EqualsLiteral("none")) {
  113. aViewBox->none = true;
  114. return NS_OK;
  115. }
  116. nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
  117. tokenizer(aStr, ',',
  118. nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
  119. float vals[NUM_VIEWBOX_COMPONENTS];
  120. uint32_t i;
  121. for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) {
  122. if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) {
  123. return NS_ERROR_DOM_SYNTAX_ERR;
  124. }
  125. }
  126. if (i != NUM_VIEWBOX_COMPONENTS || // Too few values.
  127. tokenizer.hasMoreTokens() || // Too many values.
  128. tokenizer.separatorAfterCurrentToken()) { // Trailing comma.
  129. return NS_ERROR_DOM_SYNTAX_ERR;
  130. }
  131. aViewBox->x = vals[0];
  132. aViewBox->y = vals[1];
  133. aViewBox->width = vals[2];
  134. aViewBox->height = vals[3];
  135. aViewBox->none = false;
  136. return NS_OK;
  137. }
  138. nsresult
  139. nsSVGViewBox::SetBaseValueString(const nsAString& aValue,
  140. nsSVGElement *aSVGElement,
  141. bool aDoSetAttr)
  142. {
  143. nsSVGViewBoxRect viewBox;
  144. nsresult rv = ToSVGViewBoxRect(aValue, &viewBox);
  145. if (NS_FAILED(rv)) {
  146. return rv;
  147. }
  148. // Comparison against mBaseVal is only valid if we currently have a base val.
  149. if (mHasBaseVal && viewBox == mBaseVal) {
  150. return NS_OK;
  151. }
  152. nsAttrValue emptyOrOldValue;
  153. if (aDoSetAttr) {
  154. emptyOrOldValue = aSVGElement->WillChangeViewBox();
  155. }
  156. mHasBaseVal = true;
  157. mBaseVal = viewBox;
  158. if (aDoSetAttr) {
  159. aSVGElement->DidChangeViewBox(emptyOrOldValue);
  160. }
  161. if (mAnimVal) {
  162. aSVGElement->AnimationNeedsResample();
  163. }
  164. return NS_OK;
  165. }
  166. void
  167. nsSVGViewBox::GetBaseValueString(nsAString& aValue) const
  168. {
  169. if (mBaseVal.none) {
  170. aValue.AssignLiteral("none");
  171. return;
  172. }
  173. char16_t buf[200];
  174. nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
  175. u"%g %g %g %g",
  176. (double)mBaseVal.x, (double)mBaseVal.y,
  177. (double)mBaseVal.width, (double)mBaseVal.height);
  178. aValue.Assign(buf);
  179. }
  180. already_AddRefed<dom::SVGAnimatedRect>
  181. nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement)
  182. {
  183. RefPtr<dom::SVGAnimatedRect> domAnimatedRect =
  184. sSVGAnimatedRectTearoffTable.GetTearoff(this);
  185. if (!domAnimatedRect) {
  186. domAnimatedRect = new dom::SVGAnimatedRect(this, aSVGElement);
  187. sSVGAnimatedRectTearoffTable.AddTearoff(this, domAnimatedRect);
  188. }
  189. return domAnimatedRect.forget();
  190. }
  191. already_AddRefed<dom::SVGIRect>
  192. nsSVGViewBox::ToDOMBaseVal(nsSVGElement *aSVGElement)
  193. {
  194. if (!mHasBaseVal || mBaseVal.none) {
  195. return nullptr;
  196. }
  197. RefPtr<DOMBaseVal> domBaseVal =
  198. sBaseSVGViewBoxTearoffTable.GetTearoff(this);
  199. if (!domBaseVal) {
  200. domBaseVal = new DOMBaseVal(this, aSVGElement);
  201. sBaseSVGViewBoxTearoffTable.AddTearoff(this, domBaseVal);
  202. }
  203. return domBaseVal.forget();
  204. }
  205. nsSVGViewBox::DOMBaseVal::~DOMBaseVal()
  206. {
  207. sBaseSVGViewBoxTearoffTable.RemoveTearoff(mVal);
  208. }
  209. already_AddRefed<dom::SVGIRect>
  210. nsSVGViewBox::ToDOMAnimVal(nsSVGElement *aSVGElement)
  211. {
  212. if ((mAnimVal && mAnimVal->none) ||
  213. (!mAnimVal && (!mHasBaseVal || mBaseVal.none))) {
  214. return nullptr;
  215. }
  216. RefPtr<DOMAnimVal> domAnimVal =
  217. sAnimSVGViewBoxTearoffTable.GetTearoff(this);
  218. if (!domAnimVal) {
  219. domAnimVal = new DOMAnimVal(this, aSVGElement);
  220. sAnimSVGViewBoxTearoffTable.AddTearoff(this, domAnimVal);
  221. }
  222. return domAnimVal.forget();
  223. }
  224. nsSVGViewBox::DOMAnimVal::~DOMAnimVal()
  225. {
  226. sAnimSVGViewBoxTearoffTable.RemoveTearoff(mVal);
  227. }
  228. void
  229. nsSVGViewBox::DOMBaseVal::SetX(float aX, ErrorResult& aRv)
  230. {
  231. nsSVGViewBoxRect rect = mVal->GetBaseValue();
  232. rect.x = aX;
  233. mVal->SetBaseValue(rect, mSVGElement);
  234. }
  235. void
  236. nsSVGViewBox::DOMBaseVal::SetY(float aY, ErrorResult& aRv)
  237. {
  238. nsSVGViewBoxRect rect = mVal->GetBaseValue();
  239. rect.y = aY;
  240. mVal->SetBaseValue(rect, mSVGElement);
  241. }
  242. void
  243. nsSVGViewBox::DOMBaseVal::SetWidth(float aWidth, ErrorResult& aRv)
  244. {
  245. nsSVGViewBoxRect rect = mVal->GetBaseValue();
  246. rect.width = aWidth;
  247. mVal->SetBaseValue(rect, mSVGElement);
  248. }
  249. void
  250. nsSVGViewBox::DOMBaseVal::SetHeight(float aHeight, ErrorResult& aRv)
  251. {
  252. nsSVGViewBoxRect rect = mVal->GetBaseValue();
  253. rect.height = aHeight;
  254. mVal->SetBaseValue(rect, mSVGElement);
  255. }
  256. nsISMILAttr*
  257. nsSVGViewBox::ToSMILAttr(nsSVGElement *aSVGElement)
  258. {
  259. return new SMILViewBox(this, aSVGElement);
  260. }
  261. nsresult
  262. nsSVGViewBox::SMILViewBox
  263. ::ValueFromString(const nsAString& aStr,
  264. const dom::SVGAnimationElement* /*aSrcElement*/,
  265. nsSMILValue& aValue,
  266. bool& aPreventCachingOfSandwich) const
  267. {
  268. nsSVGViewBoxRect viewBox;
  269. nsresult res = ToSVGViewBoxRect(aStr, &viewBox);
  270. if (NS_FAILED(res)) {
  271. return res;
  272. }
  273. nsSMILValue val(&SVGViewBoxSMILType::sSingleton);
  274. *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = viewBox;
  275. aValue = Move(val);
  276. aPreventCachingOfSandwich = false;
  277. return NS_OK;
  278. }
  279. nsSMILValue
  280. nsSVGViewBox::SMILViewBox::GetBaseValue() const
  281. {
  282. nsSMILValue val(&SVGViewBoxSMILType::sSingleton);
  283. *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = mVal->mBaseVal;
  284. return val;
  285. }
  286. void
  287. nsSVGViewBox::SMILViewBox::ClearAnimValue()
  288. {
  289. if (mVal->mAnimVal) {
  290. mVal->mAnimVal = nullptr;
  291. mSVGElement->DidAnimateViewBox();
  292. }
  293. }
  294. nsresult
  295. nsSVGViewBox::SMILViewBox::SetAnimValue(const nsSMILValue& aValue)
  296. {
  297. NS_ASSERTION(aValue.mType == &SVGViewBoxSMILType::sSingleton,
  298. "Unexpected type to assign animated value");
  299. if (aValue.mType == &SVGViewBoxSMILType::sSingleton) {
  300. nsSVGViewBoxRect &vb = *static_cast<nsSVGViewBoxRect*>(aValue.mU.mPtr);
  301. mVal->SetAnimValue(vb, mSVGElement);
  302. }
  303. return NS_OK;
  304. }