DOMSVGLength.cpp (17131B)
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/. */ 6 7 #include "DOMSVGLength.h" 8 9 #include "DOMSVGAnimatedLengthList.h" 10 #include "DOMSVGLengthList.h" 11 #include "SVGAnimatedLength.h" 12 #include "SVGAnimatedLengthList.h" 13 #include "SVGAttrTearoffTable.h" 14 #include "SVGLength.h" 15 #include "mozilla/dom/SVGElement.h" 16 #include "mozilla/dom/SVGLengthBinding.h" 17 #include "nsError.h" 18 #include "nsMathUtils.h" 19 20 // See the architecture comment in DOMSVGAnimatedLengthList.h. 21 22 namespace mozilla::dom { 23 24 constinit static SVGAttrTearoffTable<SVGAnimatedLength, DOMSVGLength> 25 sBaseSVGLengthTearOffTable, sAnimSVGLengthTearOffTable; 26 27 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to 28 // clear our list's weak ref to us to be safe. (The other option would be to 29 // not unlink and rely on the breaking of the other edges in the cycle, as 30 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.) 31 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength) 32 33 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength) 34 tmp->CleanupWeakRefs(); 35 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) 36 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 37 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 38 39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength) 40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 42 43 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength) 44 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 45 NS_IMPL_CYCLE_COLLECTION_TRACE_END 46 47 DOMSVGLength::DOMSVGLength(DOMSVGLengthList* aList, uint8_t aAttrEnum, 48 uint32_t aListIndex, bool aIsAnimValItem) 49 : mOwner(aList), 50 mListIndex(aListIndex), 51 mAttrEnum(aAttrEnum), 52 mIsAnimValItem(aIsAnimValItem), 53 mIsInTearoffTable(false), 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!"); 59 } 60 61 DOMSVGLength::DOMSVGLength() 62 : mOwner(nullptr), 63 mListIndex(0), 64 mAttrEnum(0), 65 mIsAnimValItem(false), 66 mIsInTearoffTable(false), 67 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER) {} 68 69 DOMSVGLength::DOMSVGLength(SVGAnimatedLength* aVal, SVGElement* aSVGElement, 70 bool aAnimVal) 71 : mOwner(aSVGElement), 72 mListIndex(0), 73 mAttrEnum(aVal->mAttrEnum), 74 mIsAnimValItem(aAnimVal), 75 mIsInTearoffTable(false), 76 mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER) { 77 MOZ_ASSERT(aVal, "bad arg"); 78 MOZ_ASSERT(mAttrEnum == aVal->mAttrEnum, "bitfield too small"); 79 } 80 81 void DOMSVGLength::CleanupWeakRefs() { 82 // Our mList's weak ref to us must be nulled out when we die (or when we're 83 // cycle collected), so we that don't leave behind a pointer to 84 // free / soon-to-be-free memory. 85 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 86 MOZ_ASSERT(lengthList->mItems[mListIndex] == this, 87 "Clearing out the wrong list index...?"); 88 lengthList->mItems[mListIndex] = nullptr; 89 } 90 91 // Similarly, we must update the tearoff table to remove its (non-owning) 92 // pointer to mVal. 93 if (mIsInTearoffTable) { 94 nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner); 95 MOZ_ASSERT(svg, 96 "We need our svgElement reference in order to remove " 97 "ourselves from tearoff table..."); 98 if (MOZ_LIKELY(svg)) { 99 auto& table = mIsAnimValItem ? sAnimSVGLengthTearOffTable 100 : sBaseSVGLengthTearOffTable; 101 table.RemoveTearoff(svg->GetAnimatedLength(mAttrEnum)); 102 mIsInTearoffTable = false; 103 } 104 } 105 } 106 107 already_AddRefed<DOMSVGLength> DOMSVGLength::GetTearOff(SVGAnimatedLength* aVal, 108 SVGElement* aSVGElement, 109 bool aAnimVal) { 110 MOZ_ASSERT(aVal && aSVGElement, "Expecting non-null aVal and aSVGElement"); 111 MOZ_ASSERT(aVal == aSVGElement->GetAnimatedLength(aVal->mAttrEnum), 112 "Mismatched aVal/SVGElement?"); 113 auto& table = 114 aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable; 115 RefPtr<DOMSVGLength> domLength = table.GetTearoff(aVal); 116 if (!domLength) { 117 domLength = new DOMSVGLength(aVal, aSVGElement, aAnimVal); 118 table.AddTearoff(aVal, domLength); 119 domLength->mIsInTearoffTable = true; 120 } 121 122 return domLength.forget(); 123 } 124 125 DOMSVGLength* DOMSVGLength::Copy() { 126 NS_ASSERTION(HasOwner(), "unexpected caller"); 127 DOMSVGLength* copy = new DOMSVGLength(); 128 uint16_t unit; 129 float value; 130 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 131 SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); 132 if (mIsAnimValItem) { 133 unit = length->GetAnimUnitType(); 134 value = length->GetAnimValInSpecifiedUnits(); 135 } else { 136 unit = length->GetBaseUnitType(); 137 value = length->GetBaseValInSpecifiedUnits(); 138 } 139 } else { 140 const SVGLength& length = InternalItem(); 141 unit = length.GetUnit(); 142 value = length.GetValueInCurrentUnits(); 143 } 144 copy->NewValueSpecifiedUnits(unit, value, IgnoreErrors()); 145 return copy; 146 } 147 148 uint16_t DOMSVGLength::UnitType() { 149 if (mIsAnimValItem) { 150 Element()->FlushAnimations(); 151 } 152 uint16_t unitType; 153 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 154 unitType = mIsAnimValItem 155 ? svg->GetAnimatedLength(mAttrEnum)->GetAnimUnitType() 156 : svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType(); 157 } else { 158 unitType = HasOwner() ? InternalItem().GetUnit() : mUnit; 159 } 160 161 return SVGLength::IsValidUnitType(unitType) 162 ? unitType 163 : SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN; 164 } 165 166 float DOMSVGLength::GetValue(ErrorResult& aRv) { 167 if (mIsAnimValItem) { 168 Element()->FlushAnimations(); // May make HasOwner() == false 169 } 170 171 // If the unit depends on style or layout then we need to flush before 172 // converting to pixels. 173 FlushIfNeeded(); 174 175 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 176 SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); 177 return mIsAnimValItem ? length->GetAnimValue(svg) 178 : length->GetBaseValue(svg); 179 } 180 181 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 182 float value = InternalItem().GetValueInPixels(lengthList->Element(), 183 lengthList->Axis()); 184 if (!std::isfinite(value)) { 185 aRv.ThrowTypeError<MSG_NOT_FINITE>("value"); 186 return 0.0f; 187 } 188 return value; 189 } 190 191 if (SVGLength::IsAbsoluteUnit(mUnit)) { 192 return SVGLength(mValue, mUnit).GetValueInPixels(nullptr, 0); 193 } 194 195 // else [SVGWG issue] Can't convert this length's value to user units 196 // ReportToConsole 197 aRv.Throw(NS_ERROR_FAILURE); 198 return 0.0f; 199 } 200 201 void DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv) { 202 if (mIsAnimValItem) { 203 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 204 return; 205 } 206 207 // If the unit depends on style or layout then we need to flush before 208 // converting from pixels. 209 FlushIfNeeded(); 210 211 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 212 aRv = svg->GetAnimatedLength(mAttrEnum)->SetBaseValue(aUserUnitValue, svg, 213 true); 214 return; 215 } 216 217 // Although the value passed in is in user units, this method does not turn 218 // this length into a user unit length. Instead it converts the user unit 219 // value to this length's current unit and sets that, leaving this length's 220 // unit as it is. 221 222 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 223 SVGLength& internalItem = InternalItem(); 224 if (internalItem.GetValueInPixels(lengthList->Element(), 225 lengthList->Axis()) == aUserUnitValue) { 226 return; 227 } 228 float uuPerUnit = internalItem.GetPixelsPerUnit( 229 SVGElementMetrics(lengthList->Element()), lengthList->Axis()); 230 if (uuPerUnit > 0) { 231 float newValue = aUserUnitValue / uuPerUnit; 232 if (!std::isfinite(newValue)) { 233 aRv.ThrowTypeError<MSG_NOT_FINITE>("value"); 234 return; 235 } 236 AutoChangeLengthListNotifier notifier(this); 237 internalItem.SetValueAndUnit(newValue, internalItem.GetUnit()); 238 return; 239 } 240 } else if (SVGLength::IsAbsoluteUnit(mUnit)) { 241 mValue = aUserUnitValue * SVGLength::GetAbsUnitsPerAbsUnit( 242 mUnit, SVGLength_Binding::SVG_LENGTHTYPE_PX); 243 return; 244 } 245 // else [SVGWG issue] Can't convert user unit value to this length's unit 246 // ReportToConsole 247 aRv.Throw(NS_ERROR_FAILURE); 248 } 249 250 float DOMSVGLength::ValueInSpecifiedUnits() { 251 if (mIsAnimValItem) { 252 Element()->FlushAnimations(); // May make HasOwner() == false 253 } 254 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 255 SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); 256 return mIsAnimValItem ? length->GetAnimValInSpecifiedUnits() 257 : length->GetBaseValInSpecifiedUnits(); 258 } 259 260 return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue; 261 } 262 263 void DOMSVGLength::SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv) { 264 if (mIsAnimValItem) { 265 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 266 return; 267 } 268 269 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 270 svg->GetAnimatedLength(mAttrEnum)->SetBaseValueInSpecifiedUnits(aValue, svg, 271 true); 272 return; 273 } 274 275 if (HasOwner()) { 276 SVGLength& internalItem = InternalItem(); 277 if (internalItem.GetValueInCurrentUnits() == aValue) { 278 return; 279 } 280 AutoChangeLengthListNotifier notifier(this); 281 internalItem.SetValueInCurrentUnits(aValue); 282 return; 283 } 284 mValue = aValue; 285 } 286 287 void DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv) { 288 if (mIsAnimValItem) { 289 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 290 return; 291 } 292 293 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 294 aRv = svg->GetAnimatedLength(mAttrEnum)->SetBaseValueString(aValue, svg, 295 true); 296 return; 297 } 298 299 SVGLength value; 300 if (!value.SetValueFromString(aValue)) { 301 NS_ConvertUTF16toUTF8 value(aValue); 302 aRv.ThrowSyntaxError("Cannot parse "_ns + value); 303 return; 304 } 305 if (HasOwner()) { 306 SVGLength& internalItem = InternalItem(); 307 if (internalItem == value) { 308 return; 309 } 310 AutoChangeLengthListNotifier notifier(this); 311 internalItem = value; 312 return; 313 } 314 mValue = value.GetValueInCurrentUnits(); 315 mUnit = value.GetUnit(); 316 } 317 318 void DOMSVGLength::GetValueAsString(nsAString& aValue) { 319 if (mIsAnimValItem) { 320 Element()->FlushAnimations(); // May make HasOwner() == false 321 } 322 323 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 324 SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); 325 if (mIsAnimValItem) { 326 length->GetAnimValueString(aValue); 327 } else { 328 length->GetBaseValueString(aValue); 329 } 330 return; 331 } 332 if (HasOwner()) { 333 InternalItem().GetValueAsString(aValue); 334 return; 335 } 336 SVGLength(mValue, mUnit).GetValueAsString(aValue); 337 } 338 339 void DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue, 340 ErrorResult& aRv) { 341 if (mIsAnimValItem) { 342 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 343 return; 344 } 345 346 if (!SVGLength::IsValidUnitType(aUnit)) { 347 aRv.ThrowNotSupportedError("Unknown unit type"); 348 return; 349 } 350 351 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 352 svg->GetAnimatedLength(mAttrEnum)->NewValueSpecifiedUnits(aUnit, aValue, 353 svg); 354 return; 355 } 356 357 if (HasOwner()) { 358 SVGLength& internalItem = InternalItem(); 359 if (internalItem == SVGLength(aValue, aUnit)) { 360 return; 361 } 362 AutoChangeLengthListNotifier notifier(this); 363 internalItem.SetValueAndUnit(aValue, uint8_t(aUnit)); 364 return; 365 } 366 mUnit = uint8_t(aUnit); 367 mValue = aValue; 368 } 369 370 void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv) { 371 if (mIsAnimValItem) { 372 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 373 return; 374 } 375 376 if (!SVGLength::IsValidUnitType(aUnit)) { 377 aRv.ThrowNotSupportedError("Unknown unit type"); 378 return; 379 } 380 381 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 382 svg->GetAnimatedLength(mAttrEnum)->ConvertToSpecifiedUnits(aUnit, svg, aRv); 383 return; 384 } 385 386 float val; 387 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 388 SVGLength& length = InternalItem(); 389 if (length.GetUnit() == aUnit) { 390 return; 391 } 392 val = length.GetValueInSpecifiedUnit(aUnit, lengthList->Element(), 393 lengthList->Axis()); 394 } else { 395 if (mUnit == aUnit) { 396 return; 397 } 398 val = SVGLength(mValue, mUnit).GetValueInSpecifiedUnit(aUnit, nullptr, 0); 399 } 400 if (!std::isfinite(val)) { 401 aRv.ThrowTypeError<MSG_NOT_FINITE>("value"); 402 return; 403 } 404 if (HasOwner()) { 405 AutoChangeLengthListNotifier notifier(this); 406 InternalItem().SetValueAndUnit(val, aUnit); 407 } else { 408 mValue = val; 409 mUnit = aUnit; 410 } 411 } 412 413 JSObject* DOMSVGLength::WrapObject(JSContext* aCx, 414 JS::Handle<JSObject*> aGivenProto) { 415 return SVGLength_Binding::Wrap(aCx, this, aGivenProto); 416 } 417 418 void DOMSVGLength::InsertingIntoList(DOMSVGLengthList* aList, uint8_t aAttrEnum, 419 uint32_t aListIndex, bool aIsAnimValItem) { 420 NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list"); 421 422 mOwner = aList; 423 mAttrEnum = aAttrEnum; 424 mListIndex = aListIndex; 425 mIsAnimValItem = aIsAnimValItem; 426 427 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!"); 428 } 429 430 void DOMSVGLength::RemovingFromList() { 431 mValue = InternalItem().GetValueInCurrentUnits(); 432 mUnit = InternalItem().GetUnit(); 433 mOwner = nullptr; 434 mIsAnimValItem = false; 435 } 436 437 SVGLength DOMSVGLength::ToSVGLength() { 438 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 439 SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); 440 if (mIsAnimValItem) { 441 return SVGLength(length->GetAnimValInSpecifiedUnits(), 442 length->GetAnimUnitType()); 443 } 444 return SVGLength(length->GetBaseValInSpecifiedUnits(), 445 length->GetBaseUnitType()); 446 } 447 return HasOwner() ? InternalItem() : SVGLength(mValue, mUnit); 448 } 449 450 bool DOMSVGLength::IsAnimating() const { 451 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 452 return lengthList->IsAnimating(); 453 } 454 nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner); 455 return svg && svg->GetAnimatedLength(mAttrEnum)->IsAnimated(); 456 } 457 458 SVGElement* DOMSVGLength::Element() { 459 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 460 return lengthList->Element(); 461 } 462 nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner); 463 return svg; 464 } 465 466 SVGLength& DOMSVGLength::InternalItem() { 467 nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner); 468 SVGAnimatedLengthList* alist = 469 lengthList->Element()->GetAnimatedLengthList(mAttrEnum); 470 return mIsAnimValItem && alist->mAnimVal ? (*alist->mAnimVal)[mListIndex] 471 : alist->mBaseVal[mListIndex]; 472 } 473 474 void DOMSVGLength::FlushIfNeeded() { 475 auto MaybeFlush = [](uint16_t aUnitType, SVGElement* aSVGElement) { 476 FlushType flushType; 477 if (SVGLength::IsPercentageUnit(aUnitType)) { 478 flushType = FlushType::Layout; 479 } else if (SVGLength::IsFontRelativeUnit(aUnitType)) { 480 flushType = FlushType::Style; 481 } else { 482 return; 483 } 484 if (auto* currentDoc = aSVGElement->GetComposedDoc()) { 485 currentDoc->FlushPendingNotifications(flushType); 486 } 487 }; 488 489 if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) { 490 if (mIsAnimValItem) { 491 MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetAnimUnitType(), svg); 492 } else { 493 MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType(), svg); 494 } 495 } 496 if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) { 497 MaybeFlush(InternalItem().GetUnit(), lengthList->Element()); 498 } 499 } 500 501 #ifdef DEBUG 502 bool DOMSVGLength::IndexIsValid() { 503 nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner); 504 SVGAnimatedLengthList* alist = 505 lengthList->Element()->GetAnimatedLengthList(mAttrEnum); 506 return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) || 507 (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length()); 508 } 509 #endif 510 511 } // namespace mozilla::dom