SVGAnimatedLength.cpp (22716B)
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 "SVGAnimatedLength.h" 8 9 #include "DOMSVGAnimatedLength.h" 10 #include "DOMSVGLength.h" 11 #include "LayoutLogging.h" 12 #include "SVGAttrTearoffTable.h" 13 #include "SVGGeometryProperty.h" 14 #include "SVGLengthSMILType.h" 15 #include "mozAutoDocUpdate.h" 16 #include "mozilla/GeckoBindings.h" 17 #include "mozilla/Maybe.h" 18 #include "mozilla/PresShell.h" 19 #include "mozilla/SMILValue.h" 20 #include "mozilla/SVGIntegrationUtils.h" 21 #include "mozilla/StaticPresData.h" 22 #include "mozilla/dom/SVGViewportElement.h" 23 #include "nsContentUtils.h" 24 #include "nsIFrame.h" 25 #include "nsLayoutUtils.h" 26 #include "nsPresContextInlines.h" 27 #include "nsTextFormatter.h" 28 29 using namespace mozilla::dom; 30 31 namespace mozilla { 32 33 //---------------------------------------------------------------------- 34 // Helper class: AutoChangeLengthNotifier 35 // Stack-based helper class to pair calls to WillChangeLength and 36 // DidChangeLength. 37 class MOZ_RAII AutoChangeLengthNotifier { 38 public: 39 AutoChangeLengthNotifier(SVGAnimatedLength* aLength, SVGElement* aSVGElement, 40 bool aDoSetAttr = true) 41 : mLength(aLength), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) { 42 MOZ_ASSERT(mLength, "Expecting non-null length"); 43 MOZ_ASSERT(mSVGElement, "Expecting non-null element"); 44 45 if (mDoSetAttr) { 46 mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true); 47 mSVGElement->WillChangeLength(mLength->mAttrEnum, mUpdateBatch.ref()); 48 } 49 } 50 51 ~AutoChangeLengthNotifier() { 52 if (mDoSetAttr) { 53 mSVGElement->DidChangeLength(mLength->mAttrEnum, mUpdateBatch.ref()); 54 } 55 if (mLength->mIsAnimated) { 56 mSVGElement->AnimationNeedsResample(); 57 } 58 } 59 60 private: 61 SVGAnimatedLength* const mLength; 62 SVGElement* const mSVGElement; 63 Maybe<mozAutoDocUpdate> mUpdateBatch; 64 bool mDoSetAttr; 65 }; 66 67 constinit static SVGAttrTearoffTable<SVGAnimatedLength, DOMSVGAnimatedLength> 68 sSVGAnimatedLengthTearoffTable; 69 70 /* Helper functions */ 71 72 static void GetValueString(nsAString& aValueAsString, float aValue, 73 uint16_t aUnitType) { 74 nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue); 75 76 nsAutoString unitString; 77 SVGLength::GetUnitString(unitString, aUnitType); 78 aValueAsString.Append(unitString); 79 } 80 81 static bool GetValueFromString(const nsAString& aString, float& aValue, 82 uint16_t* aUnitType) { 83 bool success; 84 auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success); 85 86 if (!success) { 87 return false; 88 } 89 90 nsAString::const_iterator iter, end; 91 token.BeginReading(iter); 92 token.EndReading(end); 93 94 if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { 95 return false; 96 } 97 const nsAString& units = Substring(iter, end); 98 *aUnitType = SVGLength::GetUnitTypeForString(units); 99 return *aUnitType != SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN; 100 } 101 102 static float FixAxisLength(float aLength) { 103 if (aLength == 0.0f) { 104 LAYOUT_WARNING("zero axis length"); 105 return 1e-20f; 106 } 107 return aLength; 108 } 109 110 GeckoFontMetrics UserSpaceMetrics::DefaultFontMetrics() { 111 return {StyleLength::FromPixels(16.0f), 112 StyleLength::FromPixels(-1), 113 StyleLength::FromPixels(-1), 114 StyleLength::FromPixels(-1), 115 StyleLength::FromPixels(-1), 116 StyleLength::FromPixels(16.0f), 117 0.0f, 118 0.0f}; 119 } 120 121 GeckoFontMetrics UserSpaceMetrics::GetFontMetrics(const Element* aElement) { 122 GeckoFontMetrics metrics = DefaultFontMetrics(); 123 auto* presContext = 124 aElement ? nsContentUtils::GetContextForContent(aElement) : nullptr; 125 if (presContext) { 126 SVGGeometryProperty::DoForComputedStyle( 127 aElement, [&](const ComputedStyle* style) { 128 metrics = Gecko_GetFontMetrics( 129 presContext, WritingMode(style).IsVertical(), style->StyleFont(), 130 style->StyleFont()->mFont.size, 131 StyleQueryFontMetricsFlags::USE_USER_FONT_SET | 132 StyleQueryFontMetricsFlags::NEEDS_CH | 133 StyleQueryFontMetricsFlags::NEEDS_IC); 134 }); 135 } 136 return metrics; 137 } 138 139 WritingMode UserSpaceMetrics::GetWritingMode(const Element* aElement) { 140 WritingMode writingMode; 141 SVGGeometryProperty::DoForComputedStyle( 142 aElement, 143 [&](const ComputedStyle* style) { writingMode = WritingMode(style); }); 144 return writingMode; 145 } 146 147 float UserSpaceMetrics::GetZoom(const Element* aElement) { 148 float zoom = 1.0f; 149 SVGGeometryProperty::DoForComputedStyle( 150 aElement, [&](const ComputedStyle* style) { 151 zoom = style->EffectiveZoom().ToFloat(); 152 }); 153 return zoom; 154 } 155 156 float UserSpaceMetrics::GetExLength(Type aType) const { 157 return GetFontMetricsForType(aType).mXSize.ToCSSPixels(); 158 } 159 160 float UserSpaceMetrics::GetChSize(Type aType) const { 161 auto metrics = GetFontMetricsForType(aType); 162 if (metrics.mChSize.ToCSSPixels() > 0.0) { 163 return metrics.mChSize.ToCSSPixels(); 164 } 165 auto emLength = GetEmLength(aType); 166 WritingMode writingMode = GetWritingModeForType(aType); 167 return writingMode.IsVertical() && !writingMode.IsSideways() 168 ? emLength 169 : emLength * 0.5f; 170 } 171 172 float UserSpaceMetrics::GetIcWidth(Type aType) const { 173 auto metrics = GetFontMetricsForType(aType); 174 if (metrics.mIcWidth.ToCSSPixels() > 0.0) { 175 return metrics.mIcWidth.ToCSSPixels(); 176 } 177 return GetEmLength(aType); 178 } 179 180 float UserSpaceMetrics::GetCapHeight(Type aType) const { 181 auto metrics = GetFontMetricsForType(aType); 182 if (metrics.mCapHeight.ToCSSPixels() > 0.0) { 183 return metrics.mCapHeight.ToCSSPixels(); 184 } 185 return GetEmLength(aType); 186 } 187 188 CSSSize UserSpaceMetrics::GetCSSViewportSizeFromContext( 189 const nsPresContext* aContext) { 190 return CSSPixel::FromAppUnits(aContext->GetSizeForViewportUnits()); 191 } 192 193 SVGElementMetrics::SVGElementMetrics(const SVGElement* aSVGElement, 194 const SVGViewportElement* aCtx) 195 : mSVGElement(aSVGElement), mCtx(aCtx) {} 196 197 const Element* SVGElementMetrics::GetElementForType(Type aType) const { 198 switch (aType) { 199 case Type::This: 200 return mSVGElement; 201 case Type::Root: 202 return mSVGElement ? mSVGElement->OwnerDoc()->GetRootElement() : nullptr; 203 default: 204 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); 205 return nullptr; 206 } 207 } 208 209 GeckoFontMetrics SVGElementMetrics::GetFontMetricsForType(Type aType) const { 210 return GetFontMetrics(GetElementForType(aType)); 211 } 212 213 WritingMode SVGElementMetrics::GetWritingModeForType(Type aType) const { 214 return GetWritingMode(GetElementForType(aType)); 215 } 216 217 float SVGElementMetrics::GetZoom() const { 218 return UserSpaceMetrics::GetZoom(mSVGElement); 219 } 220 221 float SVGElementMetrics::GetRootZoom() const { 222 return UserSpaceMetrics::GetZoom( 223 mSVGElement ? mSVGElement->OwnerDoc()->GetRootElement() : nullptr); 224 } 225 226 float SVGElementMetrics::GetAxisLength(uint8_t aCtxType) const { 227 if (!EnsureCtx()) { 228 return 1.0f; 229 } 230 231 return FixAxisLength(mCtx->GetLength(aCtxType)); 232 } 233 234 CSSSize SVGElementMetrics::GetCSSViewportSize() const { 235 if (!mSVGElement) { 236 return {0.0f, 0.0f}; 237 } 238 nsPresContext* context = nsContentUtils::GetContextForContent(mSVGElement); 239 if (!context) { 240 return {0.0f, 0.0f}; 241 } 242 return GetCSSViewportSizeFromContext(context); 243 } 244 245 float SVGElementMetrics::GetLineHeight(Type aType) const { 246 return SVGContentUtils::GetLineHeight(GetElementForType(aType)); 247 } 248 249 bool SVGElementMetrics::EnsureCtx() const { 250 if (!mCtx && mSVGElement) { 251 mCtx = mSVGElement->GetCtx(); 252 if (!mCtx && mSVGElement->IsSVGElement(nsGkAtoms::svg)) { 253 const auto* e = static_cast<const SVGViewportElement*>(mSVGElement); 254 255 if (!e->IsInner()) { 256 // mSVGElement must be the outer svg element 257 mCtx = e; 258 } 259 } 260 } 261 return mCtx != nullptr; 262 } 263 264 NonSVGFrameUserSpaceMetrics::NonSVGFrameUserSpaceMetrics(nsIFrame* aFrame) 265 : mFrame(aFrame) { 266 MOZ_ASSERT(mFrame, "Need a frame"); 267 } 268 269 float NonSVGFrameUserSpaceMetrics::GetEmLength(Type aType) const { 270 switch (aType) { 271 case Type::This: 272 return SVGContentUtils::GetFontSize(mFrame); 273 case Type::Root: 274 return SVGContentUtils::GetFontSize( 275 mFrame->PresContext()->Document()->GetRootElement()); 276 default: 277 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); 278 return 1.0f; 279 } 280 } 281 282 GeckoFontMetrics NonSVGFrameUserSpaceMetrics::GetFontMetricsForType( 283 Type aType) const { 284 switch (aType) { 285 case Type::This: 286 return Gecko_GetFontMetrics( 287 mFrame->PresContext(), mFrame->GetWritingMode().IsVertical(), 288 mFrame->StyleFont(), mFrame->StyleFont()->mFont.size, 289 StyleQueryFontMetricsFlags::USE_USER_FONT_SET | 290 StyleQueryFontMetricsFlags::NEEDS_CH | 291 StyleQueryFontMetricsFlags::NEEDS_IC); 292 case Type::Root: 293 return GetFontMetrics( 294 mFrame->PresContext()->Document()->GetRootElement()); 295 default: 296 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); 297 return DefaultFontMetrics(); 298 } 299 } 300 301 WritingMode NonSVGFrameUserSpaceMetrics::GetWritingModeForType( 302 Type aType) const { 303 switch (aType) { 304 case Type::This: 305 return mFrame->GetWritingMode(); 306 case Type::Root: 307 return GetWritingMode( 308 mFrame->PresContext()->Document()->GetRootElement()); 309 default: 310 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); 311 return WritingMode(); 312 } 313 } 314 315 float NonSVGFrameUserSpaceMetrics::GetZoom() const { 316 return mFrame->Style()->EffectiveZoom().ToFloat(); 317 } 318 319 float NonSVGFrameUserSpaceMetrics::GetRootZoom() const { 320 return mFrame->PresContext() 321 ->FrameConstructor() 322 ->GetRootElementStyleFrame() 323 ->Style() 324 ->EffectiveZoom() 325 .ToFloat(); 326 } 327 328 gfx::Size NonSVGFrameUserSpaceMetrics::GetSize() const { 329 return SVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(mFrame); 330 } 331 332 CSSSize NonSVGFrameUserSpaceMetrics::GetCSSViewportSize() const { 333 return GetCSSViewportSizeFromContext(mFrame->PresContext()); 334 } 335 336 float NonSVGFrameUserSpaceMetrics::GetLineHeight(Type aType) const { 337 auto* context = mFrame->PresContext(); 338 switch (aType) { 339 case Type::This: { 340 const auto lineHeightAu = ReflowInput::CalcLineHeight( 341 *mFrame->Style(), context, mFrame->GetContent(), NS_UNCONSTRAINEDSIZE, 342 1.0f); 343 return CSSPixel::FromAppUnits(lineHeightAu); 344 } 345 case Type::Root: 346 return SVGContentUtils::GetLineHeight( 347 context->Document()->GetRootElement()); 348 } 349 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); 350 return 1.0f; 351 } 352 353 float UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const { 354 gfx::Size size = GetSize(); 355 float length; 356 switch (aCtxType) { 357 case SVGContentUtils::X: 358 length = size.width; 359 break; 360 case SVGContentUtils::Y: 361 length = size.height; 362 break; 363 case SVGContentUtils::XY: 364 length = 365 SVGContentUtils::ComputeNormalizedHypotenuse(size.width, size.height); 366 break; 367 default: 368 MOZ_ASSERT_UNREACHABLE("Unknown axis type"); 369 length = 1; 370 break; 371 } 372 return FixAxisLength(length); 373 } 374 375 float SVGAnimatedLength::GetPixelsPerUnit(const SVGElement* aSVGElement, 376 uint8_t aUnitType) const { 377 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType, 378 mCtxType, false); 379 } 380 381 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(const SVGElement* aSVGElement, 382 uint8_t aUnitType) const { 383 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType, 384 mCtxType, true); 385 } 386 387 float SVGAnimatedLength::GetPixelsPerUnitWithZoom( 388 const SVGViewportElement* aCtx, uint8_t aUnitType) const { 389 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aCtx, aCtx), aUnitType, 390 mCtxType, true); 391 } 392 393 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(nsIFrame* aFrame, 394 uint8_t aUnitType) const { 395 const nsIContent* content = aFrame->GetContent(); 396 MOZ_ASSERT(!content->IsText(), "Not expecting text content"); 397 if (content->IsSVGElement()) { 398 return SVGLength::GetPixelsPerUnit( 399 SVGElementMetrics(static_cast<const SVGElement*>(content)), aUnitType, 400 mCtxType, true); 401 } 402 return SVGLength::GetPixelsPerUnit(NonSVGFrameUserSpaceMetrics(aFrame), 403 aUnitType, mCtxType, true); 404 } 405 406 float SVGAnimatedLength::GetPixelsPerUnitWithZoom( 407 const UserSpaceMetrics& aMetrics, uint8_t aUnitType) const { 408 return SVGLength::GetPixelsPerUnit(aMetrics, aUnitType, mCtxType, true); 409 } 410 411 void SVGAnimatedLength::SetBaseValueInSpecifiedUnits(float aValue, 412 SVGElement* aSVGElement, 413 bool aDoSetAttr) { 414 if (mIsBaseSet && mBaseVal == aValue) { 415 return; 416 } 417 418 AutoChangeLengthNotifier notifier(this, aSVGElement, aDoSetAttr); 419 420 mBaseVal = aValue; 421 mIsBaseSet = true; 422 if (!mIsAnimated) { 423 mAnimVal = mBaseVal; 424 } 425 } 426 427 void SVGAnimatedLength::ConvertToSpecifiedUnits(uint16_t aUnitType, 428 SVGElement* aSVGElement, 429 ErrorResult& aRv) { 430 MOZ_ASSERT(SVGLength::IsValidUnitType(aUnitType), "Unexpected unit type"); 431 432 if (mIsBaseSet && mBaseUnitType == uint8_t(aUnitType)) return; 433 434 float valueInSpecifiedUnits; 435 436 if (SVGLength::IsAbsoluteUnit(aUnitType) && 437 SVGLength::IsAbsoluteUnit(mBaseUnitType)) { 438 valueInSpecifiedUnits = 439 mBaseVal * SVGLength::GetAbsUnitsPerAbsUnit(aUnitType, mBaseUnitType); 440 } else { 441 float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, aUnitType); 442 float valueInPixels = 443 mBaseVal * GetPixelsPerUnit(aSVGElement, mBaseUnitType); 444 valueInSpecifiedUnits = valueInPixels / pixelsPerUnit; 445 } 446 447 if (!std::isfinite(valueInSpecifiedUnits)) { 448 aRv.ThrowTypeError<MSG_NOT_FINITE>("value"); 449 return; 450 } 451 452 // Even though we're not changing the visual effect this length will have 453 // on the document, we still need to send out notifications in case we have 454 // mutation listeners, since the actual string value of the attribute will 455 // change. 456 AutoChangeLengthNotifier notifier(this, aSVGElement); 457 458 mBaseUnitType = uint8_t(aUnitType); 459 if (!mIsAnimated) { 460 mAnimUnitType = mBaseUnitType; 461 } 462 // Setting aDoSetAttr to false here will ensure we don't call 463 // Will/DidChangeAngle a second time (and dispatch duplicate notifications). 464 SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, false); 465 } 466 467 void SVGAnimatedLength::NewValueSpecifiedUnits(uint16_t aUnitType, 468 float aValueInSpecifiedUnits, 469 SVGElement* aSVGElement) { 470 MOZ_ASSERT(SVGLength::IsValidUnitType(aUnitType), "Unexpected unit type"); 471 472 if (mIsBaseSet && mBaseVal == aValueInSpecifiedUnits && 473 mBaseUnitType == uint8_t(aUnitType)) { 474 return; 475 } 476 477 AutoChangeLengthNotifier notifier(this, aSVGElement); 478 479 mBaseVal = aValueInSpecifiedUnits; 480 mIsBaseSet = true; 481 mBaseUnitType = uint8_t(aUnitType); 482 if (!mIsAnimated) { 483 mAnimVal = mBaseVal; 484 mAnimUnitType = mBaseUnitType; 485 } 486 } 487 488 already_AddRefed<DOMSVGLength> SVGAnimatedLength::ToDOMBaseVal( 489 SVGElement* aSVGElement) { 490 return DOMSVGLength::GetTearOff(this, aSVGElement, false); 491 } 492 493 already_AddRefed<DOMSVGLength> SVGAnimatedLength::ToDOMAnimVal( 494 SVGElement* aSVGElement) { 495 return DOMSVGLength::GetTearOff(this, aSVGElement, true); 496 } 497 498 /* Implementation */ 499 500 nsresult SVGAnimatedLength::SetBaseValueString(const nsAString& aValueAsString, 501 SVGElement* aSVGElement, 502 bool aDoSetAttr) { 503 float value; 504 uint16_t unitType; 505 506 if (!GetValueFromString(aValueAsString, value, &unitType)) { 507 return NS_ERROR_DOM_SYNTAX_ERR; 508 } 509 if (aSVGElement->LengthAttrIsNonNegative(mAttrEnum) && value < 0.0f) { 510 return NS_ERROR_DOM_SYNTAX_ERR; 511 } 512 513 if (mIsBaseSet && mBaseVal == float(value) && 514 mBaseUnitType == uint8_t(unitType)) { 515 return NS_OK; 516 } 517 518 AutoChangeLengthNotifier notifier(this, aSVGElement, aDoSetAttr); 519 520 mBaseVal = value; 521 mIsBaseSet = true; 522 mBaseUnitType = uint8_t(unitType); 523 if (!mIsAnimated) { 524 mAnimVal = mBaseVal; 525 mAnimUnitType = mBaseUnitType; 526 } 527 528 return NS_OK; 529 } 530 531 void SVGAnimatedLength::GetBaseValueString(nsAString& aValueAsString) const { 532 GetValueString(aValueAsString, mBaseVal, mBaseUnitType); 533 } 534 535 void SVGAnimatedLength::GetAnimValueString(nsAString& aValueAsString) const { 536 GetValueString(aValueAsString, mAnimVal, mAnimUnitType); 537 } 538 539 nsresult SVGAnimatedLength::SetBaseValue(float aValue, SVGElement* aSVGElement, 540 bool aDoSetAttr) { 541 float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, mBaseUnitType); 542 if (pixelsPerUnit == 0.0f) { 543 return NS_ERROR_ILLEGAL_VALUE; 544 } 545 546 float valueInSpecifiedUnits = aValue / pixelsPerUnit; 547 if (!std::isfinite(valueInSpecifiedUnits)) { 548 return NS_ERROR_ILLEGAL_VALUE; 549 } 550 551 SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, aDoSetAttr); 552 return NS_OK; 553 } 554 555 void SVGAnimatedLength::SetAnimValueInSpecifiedUnits(float aValue, 556 SVGElement* aSVGElement) { 557 if (mAnimVal == aValue && mIsAnimated) { 558 return; 559 } 560 mAnimVal = aValue; 561 mIsAnimated = true; 562 aSVGElement->DidAnimateLength(mAttrEnum); 563 } 564 565 void SVGAnimatedLength::SetAnimValue(float aValue, uint16_t aUnitType, 566 SVGElement* aSVGElement) { 567 if (mIsAnimated && mAnimVal == aValue && mAnimUnitType == aUnitType) { 568 return; 569 } 570 mAnimVal = aValue; 571 mAnimUnitType = aUnitType; 572 mIsAnimated = true; 573 aSVGElement->DidAnimateLength(mAttrEnum); 574 } 575 576 already_AddRefed<DOMSVGAnimatedLength> SVGAnimatedLength::ToDOMAnimatedLength( 577 SVGElement* aSVGElement) { 578 RefPtr<DOMSVGAnimatedLength> svgAnimatedLength = 579 sSVGAnimatedLengthTearoffTable.GetTearoff(this); 580 if (!svgAnimatedLength) { 581 svgAnimatedLength = new DOMSVGAnimatedLength(this, aSVGElement); 582 sSVGAnimatedLengthTearoffTable.AddTearoff(this, svgAnimatedLength); 583 } 584 585 return svgAnimatedLength.forget(); 586 } 587 588 DOMSVGAnimatedLength::~DOMSVGAnimatedLength() { 589 sSVGAnimatedLengthTearoffTable.RemoveTearoff(mVal); 590 } 591 592 UniquePtr<SMILAttr> SVGAnimatedLength::ToSMILAttr(SVGElement* aSVGElement) { 593 return MakeUnique<SMILLength>(this, aSVGElement); 594 } 595 596 nsresult SVGAnimatedLength::SMILLength::ValueFromString( 597 const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/, 598 SMILValue& aValue, bool& aPreventCachingOfSandwich) const { 599 float value; 600 uint16_t unitType; 601 602 if (!GetValueFromString(aStr, value, &unitType)) { 603 return NS_ERROR_DOM_SYNTAX_ERR; 604 } 605 606 SMILValue val(SVGLengthSMILType::Singleton()); 607 SVGLengthAndInfo* lai = static_cast<SVGLengthAndInfo*>(val.mU.mPtr); 608 lai->Set(value, unitType, mVal->GetCtxType()); 609 lai->SetInfo(mSVGElement); 610 aValue = std::move(val); 611 aPreventCachingOfSandwich = !SVGLength::IsAbsoluteUnit(unitType); 612 613 return NS_OK; 614 } 615 616 SMILValue SVGAnimatedLength::SMILLength::GetBaseValue() const { 617 SMILValue val(SVGLengthSMILType::Singleton()); 618 auto* lai = static_cast<SVGLengthAndInfo*>(val.mU.mPtr); 619 lai->CopyBaseFrom(*mVal); 620 lai->SetInfo(mSVGElement); 621 return val; 622 } 623 624 void SVGAnimatedLength::SMILLength::ClearAnimValue() { 625 if (mVal->mIsAnimated) { 626 mVal->mIsAnimated = false; 627 mVal->mAnimVal = mVal->mBaseVal; 628 mVal->mAnimUnitType = mVal->mBaseUnitType; 629 mSVGElement->DidAnimateLength(mVal->mAttrEnum); 630 } 631 } 632 633 nsresult SVGAnimatedLength::SMILLength::SetAnimValue(const SMILValue& aValue) { 634 NS_ASSERTION(aValue.mType == SVGLengthSMILType::Singleton(), 635 "Unexpected type to assign animated value"); 636 if (aValue.mType == SVGLengthSMILType::Singleton()) { 637 SVGLengthAndInfo* lai = static_cast<SVGLengthAndInfo*>(aValue.mU.mPtr); 638 mVal->SetAnimValue(lai->Value(), lai->UnitType(), mSVGElement); 639 } 640 return NS_OK; 641 } 642 643 float SVGLengthAndInfo::ConvertUnits(const SVGLengthAndInfo& aTo) const { 644 if (aTo.mUnitType == mUnitType) { 645 return mValue; 646 } 647 return SVGLength(mValue, mUnitType) 648 .GetValueInSpecifiedUnit(aTo.mUnitType, aTo.Element(), aTo.mCtxType); 649 } 650 651 float SVGLengthAndInfo::ValueInPixels(const UserSpaceMetrics& aMetrics) const { 652 return mValue == 0.0f ? 0.0f 653 : mValue * SVGLength::GetPixelsPerUnit( 654 aMetrics, mUnitType, mCtxType, false); 655 } 656 657 void SVGLengthAndInfo::Add(const SVGLengthAndInfo& aValueToAdd, 658 uint32_t aCount) { 659 mElement = aValueToAdd.mElement; 660 661 SVGElementMetrics metrics(Element()); 662 663 // We may be dealing with two different length units, so we normalize to 664 // pixels for the add: 665 666 float currentLength = ValueInPixels(metrics); 667 float lengthToAdd = aValueToAdd.ValueInPixels(metrics) * aCount; 668 669 // And then we give the resulting value the same units as the value 670 // that we're animating to/by (i.e. the same as aValueToAdd): 671 mUnitType = aValueToAdd.mUnitType; 672 mCtxType = aValueToAdd.mCtxType; 673 mValue = (currentLength + lengthToAdd) / 674 SVGLength::GetPixelsPerUnit(metrics, mUnitType, mCtxType, false); 675 } 676 677 void SVGLengthAndInfo::Interpolate(const SVGLengthAndInfo& aStart, 678 const SVGLengthAndInfo& aEnd, 679 double aUnitDistance, 680 SVGLengthAndInfo& aResult) { 681 MOZ_ASSERT(!aStart.mElement || aStart.mElement == aEnd.mElement, 682 "Should not be interpolating between different elements"); 683 684 float startValue, endValue; 685 if (!aStart.mElement || aUnitDistance > 0.5) { 686 aResult.mUnitType = aEnd.mUnitType; 687 aResult.mCtxType = aEnd.mCtxType; 688 startValue = aStart.ConvertUnits(aEnd); 689 endValue = aEnd.mValue; 690 } else { 691 aResult.mUnitType = aStart.mUnitType; 692 aResult.mCtxType = aStart.mCtxType; 693 startValue = aStart.mValue; 694 endValue = aEnd.ConvertUnits(aStart); 695 } 696 aResult.mElement = aEnd.mElement; 697 aResult.mValue = startValue + (endValue - startValue) * aUnitDistance; 698 } 699 700 } // namespace mozilla