1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DOMSVGLength.h"
9 #include "DOMSVGLengthList.h"
10 #include "DOMSVGAnimatedLengthList.h"
12 #include "nsMathUtils.h"
13 #include "SVGAnimatedLength.h"
14 #include "SVGAnimatedLengthList.h"
15 #include "SVGAttrTearoffTable.h"
16 #include "SVGLength.h"
17 #include "mozilla/dom/SVGElement.h"
18 #include "mozilla/dom/SVGLengthBinding.h"
19 #include "mozilla/FloatingPoint.h"
21 // See the architecture comment in DOMSVGAnimatedLengthList.h.
27 static SVGAttrTearoffTable
<SVGAnimatedLength
, DOMSVGLength
>
28 sBaseSVGLengthTearOffTable
, sAnimSVGLengthTearOffTable
;
30 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
31 // clear our list's weak ref to us to be safe. (The other option would be to
32 // not unlink and rely on the breaking of the other edges in the cycle, as
33 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
34 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength
)
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength
)
37 tmp
->CleanupWeakRefs();
38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
)
39 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
40 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength
)
43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
)
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
46 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength
)
47 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
48 NS_IMPL_CYCLE_COLLECTION_TRACE_END
50 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGLength
, AddRef
)
51 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGLength
, Release
)
53 DOMSVGLength::DOMSVGLength(DOMSVGLengthList
* aList
, uint8_t aAttrEnum
,
54 uint32_t aListIndex
, bool aIsAnimValItem
)
56 mListIndex(aListIndex
),
58 mIsAnimValItem(aIsAnimValItem
),
59 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
) {
60 MOZ_ASSERT(aList
, "bad arg");
61 MOZ_ASSERT(mAttrEnum
== aAttrEnum
, "bitfield too small");
62 MOZ_ASSERT(aListIndex
<= MaxListIndex(), "list index too large");
63 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
66 DOMSVGLength::DOMSVGLength()
70 mIsAnimValItem(false),
71 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
) {}
73 DOMSVGLength::DOMSVGLength(SVGAnimatedLength
* aVal
, SVGElement
* aSVGElement
,
75 : mOwner(aSVGElement
),
77 mAttrEnum(aVal
->mAttrEnum
),
78 mIsAnimValItem(aAnimVal
),
79 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
) {
80 MOZ_ASSERT(aVal
, "bad arg");
81 MOZ_ASSERT(mAttrEnum
== aVal
->mAttrEnum
, "bitfield too small");
84 void DOMSVGLength::CleanupWeakRefs() {
85 // Our mList's weak ref to us must be nulled out when we die (or when we're
86 // cycle collected), so we that don't leave behind a pointer to
87 // free / soon-to-be-free memory.
88 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
89 MOZ_ASSERT(lengthList
->mItems
[mListIndex
] == this,
90 "Clearing out the wrong list index...?");
91 lengthList
->mItems
[mListIndex
] = nullptr;
94 // Similarly, we must update the tearoff table to remove its (non-owning)
96 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
97 auto& table
= mIsAnimValItem
? sAnimSVGLengthTearOffTable
98 : sBaseSVGLengthTearOffTable
;
99 table
.RemoveTearoff(svg
->GetAnimatedLength(mAttrEnum
));
103 already_AddRefed
<DOMSVGLength
> DOMSVGLength::GetTearOff(SVGAnimatedLength
* aVal
,
104 SVGElement
* aSVGElement
,
107 aAnimVal
? sAnimSVGLengthTearOffTable
: sBaseSVGLengthTearOffTable
;
108 RefPtr
<DOMSVGLength
> domLength
= table
.GetTearoff(aVal
);
110 domLength
= new DOMSVGLength(aVal
, aSVGElement
, aAnimVal
);
111 table
.AddTearoff(aVal
, domLength
);
114 return domLength
.forget();
117 DOMSVGLength
* DOMSVGLength::Copy() {
118 NS_ASSERTION(HasOwner(), "unexpected caller");
119 DOMSVGLength
* copy
= new DOMSVGLength();
122 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
123 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
124 unit
= length
->GetSpecifiedUnitType();
125 value
= mIsAnimValItem
? length
->GetAnimValInSpecifiedUnits()
126 : length
->GetBaseValInSpecifiedUnits();
128 const SVGLength
& length
= InternalItem();
129 unit
= length
.GetUnit();
130 value
= length
.GetValueInCurrentUnits();
132 copy
->NewValueSpecifiedUnits(unit
, value
, IgnoreErrors());
136 uint16_t DOMSVGLength::UnitType() {
137 if (mIsAnimValItem
) {
138 Element()->FlushAnimations();
140 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
141 return svg
->GetAnimatedLength(mAttrEnum
)->GetSpecifiedUnitType();
143 return HasOwner() ? InternalItem().GetUnit() : mUnit
;
146 float DOMSVGLength::GetValue(ErrorResult
& aRv
) {
147 if (mIsAnimValItem
) {
148 Element()->FlushAnimations(); // May make HasOwner() == false
151 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
152 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
153 return mIsAnimValItem
? length
->GetAnimValue(svg
)
154 : length
->GetBaseValue(svg
);
157 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
158 float value
= InternalItem().GetValueInUserUnits(lengthList
->Element(),
160 if (!IsFinite(value
)) {
161 aRv
.Throw(NS_ERROR_FAILURE
);
167 if (UserSpaceMetrics::ResolveAbsoluteUnit(mUnit
, unitToPx
)) {
168 return mValue
* unitToPx
;
171 // else [SVGWG issue] Can't convert this length's value to user units
173 aRv
.Throw(NS_ERROR_FAILURE
);
177 void DOMSVGLength::SetValue(float aUserUnitValue
, ErrorResult
& aRv
) {
178 if (mIsAnimValItem
) {
179 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
183 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
184 aRv
= svg
->GetAnimatedLength(mAttrEnum
)->SetBaseValue(aUserUnitValue
, svg
,
189 // Although the value passed in is in user units, this method does not turn
190 // this length into a user unit length. Instead it converts the user unit
191 // value to this length's current unit and sets that, leaving this length's
194 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
195 SVGLength
& internalItem
= InternalItem();
196 if (internalItem
.GetValueInUserUnits(
197 lengthList
->Element(), lengthList
->Axis()) == aUserUnitValue
) {
200 float uuPerUnit
= internalItem
.GetUserUnitsPerUnit(lengthList
->Element(),
203 float newValue
= aUserUnitValue
/ uuPerUnit
;
204 if (IsFinite(newValue
)) {
205 AutoChangeLengthListNotifier
notifier(this);
206 internalItem
.SetValueAndUnit(newValue
, internalItem
.GetUnit());
210 } else if (mUnit
== SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
||
211 mUnit
== SVGLength_Binding::SVG_LENGTHTYPE_PX
) {
212 mValue
= aUserUnitValue
;
215 // else [SVGWG issue] Can't convert user unit value to this length's unit
217 aRv
.Throw(NS_ERROR_FAILURE
);
220 float DOMSVGLength::ValueInSpecifiedUnits() {
221 if (mIsAnimValItem
) {
222 Element()->FlushAnimations(); // May make HasOwner() == false
224 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
225 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
226 return mIsAnimValItem
? length
->GetAnimValInSpecifiedUnits()
227 : length
->GetBaseValInSpecifiedUnits();
230 return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue
;
233 void DOMSVGLength::SetValueInSpecifiedUnits(float aValue
, ErrorResult
& aRv
) {
234 if (mIsAnimValItem
) {
235 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
239 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
240 svg
->GetAnimatedLength(mAttrEnum
)->SetBaseValueInSpecifiedUnits(aValue
, svg
,
246 SVGLength
& internalItem
= InternalItem();
247 if (internalItem
.GetValueInCurrentUnits() == aValue
) {
250 AutoChangeLengthListNotifier
notifier(this);
251 internalItem
.SetValueInCurrentUnits(aValue
);
257 void DOMSVGLength::SetValueAsString(const nsAString
& aValue
, ErrorResult
& aRv
) {
258 if (mIsAnimValItem
) {
259 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
263 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
264 aRv
= svg
->GetAnimatedLength(mAttrEnum
)->SetBaseValueString(aValue
, svg
,
270 if (!value
.SetValueFromString(aValue
)) {
271 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
275 SVGLength
& internalItem
= InternalItem();
276 if (internalItem
== value
) {
279 AutoChangeLengthListNotifier
notifier(this);
280 internalItem
= value
;
283 mValue
= value
.GetValueInCurrentUnits();
284 mUnit
= value
.GetUnit();
287 void DOMSVGLength::GetValueAsString(nsAString
& aValue
) {
288 if (mIsAnimValItem
) {
289 Element()->FlushAnimations(); // May make HasOwner() == false
292 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
293 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
294 if (mIsAnimValItem
) {
295 length
->GetAnimValueString(aValue
);
297 length
->GetBaseValueString(aValue
);
302 InternalItem().GetValueAsString(aValue
);
305 SVGLength(mValue
, mUnit
).GetValueAsString(aValue
);
308 void DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit
, float aValue
,
310 if (mIsAnimValItem
) {
311 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
315 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
316 svg
->GetAnimatedLength(mAttrEnum
)->NewValueSpecifiedUnits(aUnit
, aValue
,
321 if (!SVGLength::IsValidUnitType(aUnit
)) {
322 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
326 SVGLength
& internalItem
= InternalItem();
327 if (internalItem
== SVGLength(aValue
, aUnit
)) {
330 AutoChangeLengthListNotifier
notifier(this);
331 internalItem
.SetValueAndUnit(aValue
, uint8_t(aUnit
));
334 mUnit
= uint8_t(aUnit
);
338 void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit
, ErrorResult
& aRv
) {
339 if (mIsAnimValItem
) {
340 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
344 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
345 svg
->GetAnimatedLength(mAttrEnum
)->ConvertToSpecifiedUnits(aUnit
, svg
);
349 if (!SVGLength::IsValidUnitType(aUnit
)) {
350 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
355 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
356 SVGLength
& length
= InternalItem();
357 if (length
.GetUnit() == aUnit
) {
360 val
= length
.GetValueInSpecifiedUnit(aUnit
, lengthList
->Element(),
363 val
= SVGLength(mValue
, mUnit
).GetValueInSpecifiedUnit(aUnit
, nullptr, 0);
367 AutoChangeLengthListNotifier
notifier(this);
368 InternalItem().SetValueAndUnit(val
, aUnit
);
375 // else [SVGWG issue] Can't convert unit
377 aRv
.Throw(NS_ERROR_FAILURE
);
380 JSObject
* DOMSVGLength::WrapObject(JSContext
* aCx
,
381 JS::Handle
<JSObject
*> aGivenProto
) {
382 return SVGLength_Binding::Wrap(aCx
, this, aGivenProto
);
385 void DOMSVGLength::InsertingIntoList(DOMSVGLengthList
* aList
, uint8_t aAttrEnum
,
386 uint32_t aListIndex
, bool aIsAnimValItem
) {
387 NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list");
390 mAttrEnum
= aAttrEnum
;
391 mListIndex
= aListIndex
;
392 mIsAnimValItem
= aIsAnimValItem
;
394 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
397 void DOMSVGLength::RemovingFromList() {
398 mValue
= InternalItem().GetValueInCurrentUnits();
399 mUnit
= InternalItem().GetUnit();
401 mIsAnimValItem
= false;
404 SVGLength
DOMSVGLength::ToSVGLength() {
405 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
406 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
407 return SVGLength(mIsAnimValItem
? length
->GetAnimValInSpecifiedUnits()
408 : length
->GetBaseValInSpecifiedUnits(),
409 length
->GetSpecifiedUnitType());
411 return HasOwner() ? InternalItem() : SVGLength(mValue
, mUnit
);
414 bool DOMSVGLength::IsAnimating() const {
415 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
416 return lengthList
->IsAnimating();
418 nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
);
419 return svg
&& svg
->GetAnimatedLength(mAttrEnum
)->IsAnimated();
422 SVGElement
* DOMSVGLength::Element() {
423 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
424 return lengthList
->Element();
426 nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
);
430 SVGLength
& DOMSVGLength::InternalItem() {
431 nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
);
432 SVGAnimatedLengthList
* alist
=
433 lengthList
->Element()->GetAnimatedLengthList(mAttrEnum
);
434 return mIsAnimValItem
&& alist
->mAnimVal
? (*alist
->mAnimVal
)[mListIndex
]
435 : alist
->mBaseVal
[mListIndex
];
439 bool DOMSVGLength::IndexIsValid() {
440 nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
);
441 SVGAnimatedLengthList
* alist
=
442 lengthList
->Element()->GetAnimatedLengthList(mAttrEnum
);
443 return (mIsAnimValItem
&& mListIndex
< alist
->GetAnimValue().Length()) ||
444 (!mIsAnimValItem
&& mListIndex
< alist
->GetBaseValue().Length());
449 } // namespace mozilla