Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / DOMSVGLength.cpp
blobe9081b638eac738104b56dbb191729d2dbafb5aa
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"
11 #include "nsError.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)
50 : mOwner(aList),
51 mListIndex(aListIndex),
52 mAttrEnum(aAttrEnum),
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()
62 : mOwner(nullptr),
63 mListIndex(0),
64 mAttrEnum(0),
65 mIsAnimValItem(false),
66 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER) {}
68 DOMSVGLength::DOMSVGLength(SVGAnimatedLength* aVal, SVGElement* aSVGElement,
69 bool aAnimVal)
70 : mOwner(aSVGElement),
71 mListIndex(0),
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)
90 // pointer to mVal.
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,
100 bool aAnimVal) {
101 auto& table =
102 aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable;
103 RefPtr<DOMSVGLength> domLength = table.GetTearoff(aVal);
104 if (!domLength) {
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();
115 uint16_t unit;
116 float value;
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();
122 } else {
123 unit = length->GetBaseUnitType();
124 value = length->GetBaseValInSpecifiedUnits();
126 } else {
127 const SVGLength& length = InternalItem();
128 unit = length.GetUnit();
129 value = length.GetValueInCurrentUnits();
131 copy->NewValueSpecifiedUnits(unit, value, IgnoreErrors());
132 return copy;
135 uint16_t DOMSVGLength::UnitType() {
136 if (mIsAnimValItem) {
137 Element()->FlushAnimations();
139 uint16_t unitType;
140 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
141 unitType = mIsAnimValItem
142 ? svg->GetAnimatedLength(mAttrEnum)->GetAnimUnitType()
143 : svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType();
144 } else {
145 unitType = HasOwner() ? InternalItem().GetUnit() : mUnit;
148 return SVGLength::IsValidUnitType(unitType)
149 ? 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.
160 FlushIfNeeded();
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(),
170 lengthList->Axis());
171 if (!std::isfinite(value)) {
172 aRv.Throw(NS_ERROR_FAILURE);
174 return value;
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
182 // ReportToConsole
183 aRv.Throw(NS_ERROR_FAILURE);
184 return 0.0f;
187 void DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv) {
188 if (mIsAnimValItem) {
189 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
190 return;
193 // If the unit depends on style or layout then we need to flush before
194 // converting from pixels.
195 FlushIfNeeded();
197 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
198 aRv = svg->GetAnimatedLength(mAttrEnum)->SetBaseValue(aUserUnitValue, svg,
199 true);
200 return;
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
206 // unit as it is.
208 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
209 SVGLength& internalItem = InternalItem();
210 if (internalItem.GetValueInPixels(lengthList->Element(),
211 lengthList->Axis()) == aUserUnitValue) {
212 return;
214 float uuPerUnit = internalItem.GetPixelsPerUnit(
215 SVGElementMetrics(lengthList->Element()), lengthList->Axis());
216 if (uuPerUnit > 0) {
217 float newValue = aUserUnitValue / uuPerUnit;
218 if (std::isfinite(newValue)) {
219 AutoChangeLengthListNotifier notifier(this);
220 internalItem.SetValueAndUnit(newValue, internalItem.GetUnit());
221 return;
224 } else if (SVGLength::IsAbsoluteUnit(mUnit)) {
225 mValue = aUserUnitValue * SVGLength::GetAbsUnitsPerAbsUnit(
226 mUnit, SVGLength_Binding::SVG_LENGTHTYPE_PX);
227 return;
229 // else [SVGWG issue] Can't convert user unit value to this length's unit
230 // ReportToConsole
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);
250 return;
253 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
254 svg->GetAnimatedLength(mAttrEnum)->SetBaseValueInSpecifiedUnits(aValue, svg,
255 true);
256 return;
259 if (HasOwner()) {
260 SVGLength& internalItem = InternalItem();
261 if (internalItem.GetValueInCurrentUnits() == aValue) {
262 return;
264 AutoChangeLengthListNotifier notifier(this);
265 internalItem.SetValueInCurrentUnits(aValue);
266 return;
268 mValue = aValue;
271 void DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv) {
272 if (mIsAnimValItem) {
273 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
274 return;
277 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
278 aRv = svg->GetAnimatedLength(mAttrEnum)->SetBaseValueString(aValue, svg,
279 true);
280 return;
283 SVGLength value;
284 if (!value.SetValueFromString(aValue)) {
285 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
286 return;
288 if (HasOwner()) {
289 SVGLength& internalItem = InternalItem();
290 if (internalItem == value) {
291 return;
293 AutoChangeLengthListNotifier notifier(this);
294 internalItem = value;
295 return;
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);
310 } else {
311 length->GetBaseValueString(aValue);
313 return;
315 if (HasOwner()) {
316 InternalItem().GetValueAsString(aValue);
317 return;
319 SVGLength(mValue, mUnit).GetValueAsString(aValue);
322 void DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue,
323 ErrorResult& aRv) {
324 if (mIsAnimValItem) {
325 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
326 return;
329 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
330 svg->GetAnimatedLength(mAttrEnum)->NewValueSpecifiedUnits(aUnit, aValue,
331 svg);
332 return;
335 if (!SVGLength::IsValidUnitType(aUnit)) {
336 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
337 return;
339 if (HasOwner()) {
340 SVGLength& internalItem = InternalItem();
341 if (internalItem == SVGLength(aValue, aUnit)) {
342 return;
344 AutoChangeLengthListNotifier notifier(this);
345 internalItem.SetValueAndUnit(aValue, uint8_t(aUnit));
346 return;
348 mUnit = uint8_t(aUnit);
349 mValue = aValue;
352 void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv) {
353 if (mIsAnimValItem) {
354 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
355 return;
358 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
359 svg->GetAnimatedLength(mAttrEnum)->ConvertToSpecifiedUnits(aUnit, svg);
360 return;
363 if (!SVGLength::IsValidUnitType(aUnit)) {
364 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
365 return;
368 float val;
369 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
370 SVGLength& length = InternalItem();
371 if (length.GetUnit() == aUnit) {
372 return;
374 val = length.GetValueInSpecifiedUnit(aUnit, lengthList->Element(),
375 lengthList->Axis());
376 } else {
377 if (mUnit == aUnit) {
378 return;
380 val = SVGLength(mValue, mUnit).GetValueInSpecifiedUnit(aUnit, nullptr, 0);
382 if (std::isfinite(val)) {
383 if (HasOwner()) {
384 AutoChangeLengthListNotifier notifier(this);
385 InternalItem().SetValueAndUnit(val, aUnit);
386 } else {
387 mValue = val;
388 mUnit = aUnit;
390 return;
392 // else [SVGWG issue] Can't convert unit
393 // ReportToConsole
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");
406 mOwner = aList;
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();
417 mOwner = nullptr;
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);
447 return svg;
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) {
460 FlushType flushType;
461 if (SVGLength::IsPercentageUnit(aUnitType)) {
462 flushType = FlushType::Layout;
463 } else if (SVGLength::IsFontRelativeUnit(aUnitType)) {
464 flushType = FlushType::Style;
465 } else {
466 return;
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);
476 } else {
477 MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType(), svg);
480 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
481 MaybeFlush(InternalItem().GetUnit(), lengthList->Element());
485 #ifdef DEBUG
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());
493 #endif
495 } // namespace mozilla::dom