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.
23 namespace mozilla::dom
{
25 MOZ_CONSTINIT
static SVGAttrTearoffTable
<SVGAnimatedLength
, DOMSVGLength
>
26 sBaseSVGLengthTearOffTable
, sAnimSVGLengthTearOffTable
;
28 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
29 // clear our list's weak ref to us to be safe. (The other option would be to
30 // not unlink and rely on the breaking of the other edges in the cycle, as
31 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
32 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength
)
34 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength
)
35 tmp
->CleanupWeakRefs();
36 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
38 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength
)
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
)
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
44 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength
)
45 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
46 NS_IMPL_CYCLE_COLLECTION_TRACE_END
48 DOMSVGLength::DOMSVGLength(DOMSVGLengthList
* aList
, uint8_t aAttrEnum
,
49 uint32_t aListIndex
, bool aIsAnimValItem
)
51 mListIndex(aListIndex
),
53 mIsAnimValItem(aIsAnimValItem
),
54 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
) {
55 MOZ_ASSERT(aList
, "bad arg");
56 MOZ_ASSERT(mAttrEnum
== aAttrEnum
, "bitfield too small");
57 MOZ_ASSERT(aListIndex
<= MaxListIndex(), "list index too large");
58 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
61 DOMSVGLength::DOMSVGLength()
65 mIsAnimValItem(false),
66 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
) {}
68 DOMSVGLength::DOMSVGLength(SVGAnimatedLength
* aVal
, SVGElement
* aSVGElement
,
70 : mOwner(aSVGElement
),
72 mAttrEnum(aVal
->mAttrEnum
),
73 mIsAnimValItem(aAnimVal
),
74 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
) {
75 MOZ_ASSERT(aVal
, "bad arg");
76 MOZ_ASSERT(mAttrEnum
== aVal
->mAttrEnum
, "bitfield too small");
79 void DOMSVGLength::CleanupWeakRefs() {
80 // Our mList's weak ref to us must be nulled out when we die (or when we're
81 // cycle collected), so we that don't leave behind a pointer to
82 // free / soon-to-be-free memory.
83 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
84 MOZ_ASSERT(lengthList
->mItems
[mListIndex
] == this,
85 "Clearing out the wrong list index...?");
86 lengthList
->mItems
[mListIndex
] = nullptr;
89 // Similarly, we must update the tearoff table to remove its (non-owning)
91 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
92 auto& table
= mIsAnimValItem
? sAnimSVGLengthTearOffTable
93 : sBaseSVGLengthTearOffTable
;
94 table
.RemoveTearoff(svg
->GetAnimatedLength(mAttrEnum
));
98 already_AddRefed
<DOMSVGLength
> DOMSVGLength::GetTearOff(SVGAnimatedLength
* aVal
,
99 SVGElement
* aSVGElement
,
102 aAnimVal
? sAnimSVGLengthTearOffTable
: sBaseSVGLengthTearOffTable
;
103 RefPtr
<DOMSVGLength
> domLength
= table
.GetTearoff(aVal
);
105 domLength
= new DOMSVGLength(aVal
, aSVGElement
, aAnimVal
);
106 table
.AddTearoff(aVal
, domLength
);
109 return domLength
.forget();
112 DOMSVGLength
* DOMSVGLength::Copy() {
113 NS_ASSERTION(HasOwner(), "unexpected caller");
114 DOMSVGLength
* copy
= new DOMSVGLength();
117 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
118 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
119 if (mIsAnimValItem
) {
120 unit
= length
->GetAnimUnitType();
121 value
= length
->GetAnimValInSpecifiedUnits();
123 unit
= length
->GetBaseUnitType();
124 value
= length
->GetBaseValInSpecifiedUnits();
127 const SVGLength
& length
= InternalItem();
128 unit
= length
.GetUnit();
129 value
= length
.GetValueInCurrentUnits();
131 copy
->NewValueSpecifiedUnits(unit
, value
, IgnoreErrors());
135 uint16_t DOMSVGLength::UnitType() {
136 if (mIsAnimValItem
) {
137 Element()->FlushAnimations();
140 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
141 unitType
= mIsAnimValItem
142 ? svg
->GetAnimatedLength(mAttrEnum
)->GetAnimUnitType()
143 : svg
->GetAnimatedLength(mAttrEnum
)->GetBaseUnitType();
145 unitType
= HasOwner() ? InternalItem().GetUnit() : mUnit
;
148 return SVGLength::IsValidUnitType(unitType
)
150 : SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN
;
153 float DOMSVGLength::GetValue(ErrorResult
& aRv
) {
154 if (mIsAnimValItem
) {
155 Element()->FlushAnimations(); // May make HasOwner() == false
158 // If the unit depends on style or layout then we need to flush before
159 // converting to pixels.
162 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
163 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
164 return mIsAnimValItem
? length
->GetAnimValue(svg
)
165 : length
->GetBaseValue(svg
);
168 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
169 float value
= InternalItem().GetValueInPixels(lengthList
->Element(),
171 if (!std::isfinite(value
)) {
172 aRv
.Throw(NS_ERROR_FAILURE
);
177 if (SVGLength::IsAbsoluteUnit(mUnit
)) {
178 return SVGLength(mValue
, mUnit
).GetValueInPixels(nullptr, 0);
181 // else [SVGWG issue] Can't convert this length's value to user units
183 aRv
.Throw(NS_ERROR_FAILURE
);
187 void DOMSVGLength::SetValue(float aUserUnitValue
, ErrorResult
& aRv
) {
188 if (mIsAnimValItem
) {
189 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
193 // If the unit depends on style or layout then we need to flush before
194 // converting from pixels.
197 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
198 aRv
= svg
->GetAnimatedLength(mAttrEnum
)->SetBaseValue(aUserUnitValue
, svg
,
203 // Although the value passed in is in user units, this method does not turn
204 // this length into a user unit length. Instead it converts the user unit
205 // value to this length's current unit and sets that, leaving this length's
208 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
209 SVGLength
& internalItem
= InternalItem();
210 if (internalItem
.GetValueInPixels(lengthList
->Element(),
211 lengthList
->Axis()) == aUserUnitValue
) {
214 float uuPerUnit
= internalItem
.GetPixelsPerUnit(
215 SVGElementMetrics(lengthList
->Element()), lengthList
->Axis());
217 float newValue
= aUserUnitValue
/ uuPerUnit
;
218 if (std::isfinite(newValue
)) {
219 AutoChangeLengthListNotifier
notifier(this);
220 internalItem
.SetValueAndUnit(newValue
, internalItem
.GetUnit());
224 } else if (SVGLength::IsAbsoluteUnit(mUnit
)) {
225 mValue
= aUserUnitValue
* SVGLength::GetAbsUnitsPerAbsUnit(
226 mUnit
, SVGLength_Binding::SVG_LENGTHTYPE_PX
);
229 // else [SVGWG issue] Can't convert user unit value to this length's unit
231 aRv
.Throw(NS_ERROR_FAILURE
);
234 float DOMSVGLength::ValueInSpecifiedUnits() {
235 if (mIsAnimValItem
) {
236 Element()->FlushAnimations(); // May make HasOwner() == false
238 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
239 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
240 return mIsAnimValItem
? length
->GetAnimValInSpecifiedUnits()
241 : length
->GetBaseValInSpecifiedUnits();
244 return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue
;
247 void DOMSVGLength::SetValueInSpecifiedUnits(float aValue
, ErrorResult
& aRv
) {
248 if (mIsAnimValItem
) {
249 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
253 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
254 svg
->GetAnimatedLength(mAttrEnum
)->SetBaseValueInSpecifiedUnits(aValue
, svg
,
260 SVGLength
& internalItem
= InternalItem();
261 if (internalItem
.GetValueInCurrentUnits() == aValue
) {
264 AutoChangeLengthListNotifier
notifier(this);
265 internalItem
.SetValueInCurrentUnits(aValue
);
271 void DOMSVGLength::SetValueAsString(const nsAString
& aValue
, ErrorResult
& aRv
) {
272 if (mIsAnimValItem
) {
273 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
277 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
278 aRv
= svg
->GetAnimatedLength(mAttrEnum
)->SetBaseValueString(aValue
, svg
,
284 if (!value
.SetValueFromString(aValue
)) {
285 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
289 SVGLength
& internalItem
= InternalItem();
290 if (internalItem
== value
) {
293 AutoChangeLengthListNotifier
notifier(this);
294 internalItem
= value
;
297 mValue
= value
.GetValueInCurrentUnits();
298 mUnit
= value
.GetUnit();
301 void DOMSVGLength::GetValueAsString(nsAString
& aValue
) {
302 if (mIsAnimValItem
) {
303 Element()->FlushAnimations(); // May make HasOwner() == false
306 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
307 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
308 if (mIsAnimValItem
) {
309 length
->GetAnimValueString(aValue
);
311 length
->GetBaseValueString(aValue
);
316 InternalItem().GetValueAsString(aValue
);
319 SVGLength(mValue
, mUnit
).GetValueAsString(aValue
);
322 void DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit
, float aValue
,
324 if (mIsAnimValItem
) {
325 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
329 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
330 svg
->GetAnimatedLength(mAttrEnum
)->NewValueSpecifiedUnits(aUnit
, aValue
,
335 if (!SVGLength::IsValidUnitType(aUnit
)) {
336 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
340 SVGLength
& internalItem
= InternalItem();
341 if (internalItem
== SVGLength(aValue
, aUnit
)) {
344 AutoChangeLengthListNotifier
notifier(this);
345 internalItem
.SetValueAndUnit(aValue
, uint8_t(aUnit
));
348 mUnit
= uint8_t(aUnit
);
352 void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit
, ErrorResult
& aRv
) {
353 if (mIsAnimValItem
) {
354 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
358 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
359 svg
->GetAnimatedLength(mAttrEnum
)->ConvertToSpecifiedUnits(aUnit
, svg
);
363 if (!SVGLength::IsValidUnitType(aUnit
)) {
364 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
369 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
370 SVGLength
& length
= InternalItem();
371 if (length
.GetUnit() == aUnit
) {
374 val
= length
.GetValueInSpecifiedUnit(aUnit
, lengthList
->Element(),
377 if (mUnit
== aUnit
) {
380 val
= SVGLength(mValue
, mUnit
).GetValueInSpecifiedUnit(aUnit
, nullptr, 0);
382 if (std::isfinite(val
)) {
384 AutoChangeLengthListNotifier
notifier(this);
385 InternalItem().SetValueAndUnit(val
, aUnit
);
392 // else [SVGWG issue] Can't convert unit
394 aRv
.Throw(NS_ERROR_FAILURE
);
397 JSObject
* DOMSVGLength::WrapObject(JSContext
* aCx
,
398 JS::Handle
<JSObject
*> aGivenProto
) {
399 return SVGLength_Binding::Wrap(aCx
, this, aGivenProto
);
402 void DOMSVGLength::InsertingIntoList(DOMSVGLengthList
* aList
, uint8_t aAttrEnum
,
403 uint32_t aListIndex
, bool aIsAnimValItem
) {
404 NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list");
407 mAttrEnum
= aAttrEnum
;
408 mListIndex
= aListIndex
;
409 mIsAnimValItem
= aIsAnimValItem
;
411 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
414 void DOMSVGLength::RemovingFromList() {
415 mValue
= InternalItem().GetValueInCurrentUnits();
416 mUnit
= InternalItem().GetUnit();
418 mIsAnimValItem
= false;
421 SVGLength
DOMSVGLength::ToSVGLength() {
422 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
423 SVGAnimatedLength
* length
= svg
->GetAnimatedLength(mAttrEnum
);
424 if (mIsAnimValItem
) {
425 return SVGLength(length
->GetAnimValInSpecifiedUnits(),
426 length
->GetAnimUnitType());
428 return SVGLength(length
->GetBaseValInSpecifiedUnits(),
429 length
->GetBaseUnitType());
431 return HasOwner() ? InternalItem() : SVGLength(mValue
, mUnit
);
434 bool DOMSVGLength::IsAnimating() const {
435 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
436 return lengthList
->IsAnimating();
438 nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
);
439 return svg
&& svg
->GetAnimatedLength(mAttrEnum
)->IsAnimated();
442 SVGElement
* DOMSVGLength::Element() {
443 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
444 return lengthList
->Element();
446 nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
);
450 SVGLength
& DOMSVGLength::InternalItem() {
451 nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
);
452 SVGAnimatedLengthList
* alist
=
453 lengthList
->Element()->GetAnimatedLengthList(mAttrEnum
);
454 return mIsAnimValItem
&& alist
->mAnimVal
? (*alist
->mAnimVal
)[mListIndex
]
455 : alist
->mBaseVal
[mListIndex
];
458 void DOMSVGLength::FlushIfNeeded() {
459 auto MaybeFlush
= [](uint16_t aUnitType
, SVGElement
* aSVGElement
) {
461 if (SVGLength::IsPercentageUnit(aUnitType
)) {
462 flushType
= FlushType::Layout
;
463 } else if (SVGLength::IsFontRelativeUnit(aUnitType
)) {
464 flushType
= FlushType::Style
;
468 if (auto* currentDoc
= aSVGElement
->GetComposedDoc()) {
469 currentDoc
->FlushPendingNotifications(flushType
);
473 if (nsCOMPtr
<SVGElement
> svg
= do_QueryInterface(mOwner
)) {
474 if (mIsAnimValItem
) {
475 MaybeFlush(svg
->GetAnimatedLength(mAttrEnum
)->GetAnimUnitType(), svg
);
477 MaybeFlush(svg
->GetAnimatedLength(mAttrEnum
)->GetBaseUnitType(), svg
);
480 if (nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
)) {
481 MaybeFlush(InternalItem().GetUnit(), lengthList
->Element());
486 bool DOMSVGLength::IndexIsValid() {
487 nsCOMPtr
<DOMSVGLengthList
> lengthList
= do_QueryInterface(mOwner
);
488 SVGAnimatedLengthList
* alist
=
489 lengthList
->Element()->GetAnimatedLengthList(mAttrEnum
);
490 return (mIsAnimValItem
&& mListIndex
< alist
->GetAnimValue().Length()) ||
491 (!mIsAnimValItem
&& mListIndex
< alist
->GetBaseValue().Length());
495 } // namespace mozilla::dom