Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGAnimatedLength.cpp
blobf36e7b0b877893ecaa229ec8bcfaa51ddf7addc3
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 "SVGAnimatedLength.h"
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/GeckoBindings.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/SMILValue.h"
15 #include "mozilla/StaticPresData.h"
16 #include "nsPresContextInlines.h"
17 #include "mozilla/SVGIntegrationUtils.h"
18 #include "mozilla/dom/SVGViewportElement.h"
19 #include "DOMSVGAnimatedLength.h"
20 #include "DOMSVGLength.h"
21 #include "LayoutLogging.h"
22 #include "nsContentUtils.h"
23 #include "nsIFrame.h"
24 #include "nsLayoutUtils.h"
25 #include "nsTextFormatter.h"
26 #include "SVGLengthSMILType.h"
27 #include "SVGAttrTearoffTable.h"
28 #include "SVGGeometryProperty.h"
30 using namespace mozilla::dom;
32 namespace mozilla {
34 //----------------------------------------------------------------------
35 // Helper class: AutoChangeLengthNotifier
36 // Stack-based helper class to pair calls to WillChangeLength and
37 // DidChangeLength.
38 class MOZ_RAII AutoChangeLengthNotifier {
39 public:
40 AutoChangeLengthNotifier(SVGAnimatedLength* aLength, SVGElement* aSVGElement,
41 bool aDoSetAttr = true)
42 : mLength(aLength), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
43 MOZ_ASSERT(mLength, "Expecting non-null length");
44 MOZ_ASSERT(mSVGElement, "Expecting non-null element");
46 if (mDoSetAttr) {
47 mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
48 mEmptyOrOldValue =
49 mSVGElement->WillChangeLength(mLength->mAttrEnum, mUpdateBatch.ref());
53 ~AutoChangeLengthNotifier() {
54 if (mDoSetAttr) {
55 mSVGElement->DidChangeLength(mLength->mAttrEnum, mEmptyOrOldValue,
56 mUpdateBatch.ref());
58 if (mLength->mIsAnimated) {
59 mSVGElement->AnimationNeedsResample();
63 private:
64 SVGAnimatedLength* const mLength;
65 SVGElement* const mSVGElement;
66 Maybe<mozAutoDocUpdate> mUpdateBatch;
67 nsAttrValue mEmptyOrOldValue;
68 bool mDoSetAttr;
71 MOZ_CONSTINIT static SVGAttrTearoffTable<SVGAnimatedLength,
72 DOMSVGAnimatedLength>
73 sSVGAnimatedLengthTearoffTable;
75 /* Helper functions */
77 static void GetValueString(nsAString& aValueAsString, float aValue,
78 uint16_t aUnitType) {
79 nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue);
81 nsAutoString unitString;
82 SVGLength::GetUnitString(unitString, aUnitType);
83 aValueAsString.Append(unitString);
86 static bool GetValueFromString(const nsAString& aString, float& aValue,
87 uint16_t* aUnitType) {
88 bool success;
89 auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
91 if (!success) {
92 return false;
95 nsAString::const_iterator iter, end;
96 token.BeginReading(iter);
97 token.EndReading(end);
99 if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
100 return false;
102 const nsAString& units = Substring(iter, end);
103 *aUnitType = SVGLength::GetUnitTypeForString(units);
104 return *aUnitType != SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN;
107 static float FixAxisLength(float aLength) {
108 if (aLength == 0.0f) {
109 LAYOUT_WARNING("zero axis length");
110 return 1e-20f;
112 return aLength;
115 GeckoFontMetrics UserSpaceMetrics::DefaultFontMetrics() {
116 return {StyleLength::FromPixels(16.0f),
117 StyleLength::FromPixels(-1),
118 StyleLength::FromPixels(-1),
119 StyleLength::FromPixels(-1),
120 StyleLength::FromPixels(-1),
121 StyleLength::FromPixels(16.0f),
122 0.0f,
123 0.0f};
126 GeckoFontMetrics UserSpaceMetrics::GetFontMetrics(const Element* aElement) {
127 GeckoFontMetrics metrics = DefaultFontMetrics();
128 auto* presContext =
129 aElement ? nsContentUtils::GetContextForContent(aElement) : nullptr;
130 if (presContext) {
131 SVGGeometryProperty::DoForComputedStyle(
132 aElement, [&](const ComputedStyle* style) {
133 metrics = Gecko_GetFontMetrics(
134 presContext, WritingMode(style).IsVertical(), style->StyleFont(),
135 style->StyleFont()->mFont.size,
136 /* aUseUserFontSet = */ true,
137 /* aRetrieveMathScales */ false);
140 return metrics;
143 WritingMode UserSpaceMetrics::GetWritingMode(const Element* aElement) {
144 WritingMode writingMode;
145 SVGGeometryProperty::DoForComputedStyle(
146 aElement,
147 [&](const ComputedStyle* style) { writingMode = WritingMode(style); });
148 return writingMode;
151 float UserSpaceMetrics::GetZoom(const Element* aElement) {
152 float zoom = 1.0f;
153 SVGGeometryProperty::DoForComputedStyle(
154 aElement, [&](const ComputedStyle* style) {
155 zoom = style->EffectiveZoom().ToFloat();
157 return zoom;
160 float UserSpaceMetrics::GetExLength(Type aType) const {
161 return GetFontMetricsForType(aType).mXSize.ToCSSPixels();
164 float UserSpaceMetrics::GetChSize(Type aType) const {
165 auto metrics = GetFontMetricsForType(aType);
166 if (metrics.mChSize.ToCSSPixels() > 0.0) {
167 return metrics.mChSize.ToCSSPixels();
169 auto emLength = GetEmLength(aType);
170 WritingMode writingMode = GetWritingModeForType(aType);
171 return writingMode.IsVertical() && !writingMode.IsSideways()
172 ? emLength
173 : emLength * 0.5f;
176 float UserSpaceMetrics::GetIcWidth(Type aType) const {
177 auto metrics = GetFontMetricsForType(aType);
178 if (metrics.mIcWidth.ToCSSPixels() > 0.0) {
179 return metrics.mIcWidth.ToCSSPixels();
181 return GetEmLength(aType);
184 float UserSpaceMetrics::GetCapHeight(Type aType) const {
185 auto metrics = GetFontMetricsForType(aType);
186 if (metrics.mCapHeight.ToCSSPixels() > 0.0) {
187 return metrics.mCapHeight.ToCSSPixels();
189 return GetEmLength(aType);
192 CSSSize UserSpaceMetrics::GetCSSViewportSizeFromContext(
193 const nsPresContext* aContext) {
194 return CSSPixel::FromAppUnits(aContext->GetSizeForViewportUnits());
197 SVGElementMetrics::SVGElementMetrics(const SVGElement* aSVGElement,
198 const SVGViewportElement* aCtx)
199 : mSVGElement(aSVGElement), mCtx(aCtx) {}
201 const Element* SVGElementMetrics::GetElementForType(Type aType) const {
202 switch (aType) {
203 case Type::This:
204 return mSVGElement;
205 case Type::Root:
206 return mSVGElement ? mSVGElement->OwnerDoc()->GetRootElement() : nullptr;
207 default:
208 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
209 return nullptr;
213 GeckoFontMetrics SVGElementMetrics::GetFontMetricsForType(Type aType) const {
214 return GetFontMetrics(GetElementForType(aType));
217 WritingMode SVGElementMetrics::GetWritingModeForType(Type aType) const {
218 return GetWritingMode(GetElementForType(aType));
221 float SVGElementMetrics::GetZoom() const {
222 return UserSpaceMetrics::GetZoom(mSVGElement);
225 float SVGElementMetrics::GetRootZoom() const {
226 return UserSpaceMetrics::GetZoom(
227 mSVGElement ? mSVGElement->OwnerDoc()->GetRootElement() : nullptr);
230 float SVGElementMetrics::GetAxisLength(uint8_t aCtxType) const {
231 if (!EnsureCtx()) {
232 return 1.0f;
235 return FixAxisLength(mCtx->GetLength(aCtxType));
238 CSSSize SVGElementMetrics::GetCSSViewportSize() const {
239 if (!mSVGElement) {
240 return {0.0f, 0.0f};
242 nsPresContext* context = nsContentUtils::GetContextForContent(mSVGElement);
243 if (!context) {
244 return {0.0f, 0.0f};
246 return GetCSSViewportSizeFromContext(context);
249 float SVGElementMetrics::GetLineHeight(Type aType) const {
250 return SVGContentUtils::GetLineHeight(GetElementForType(aType));
253 bool SVGElementMetrics::EnsureCtx() const {
254 if (!mCtx && mSVGElement) {
255 mCtx = mSVGElement->GetCtx();
256 if (!mCtx && mSVGElement->IsSVGElement(nsGkAtoms::svg)) {
257 const auto* e = static_cast<const SVGViewportElement*>(mSVGElement);
259 if (!e->IsInner()) {
260 // mSVGElement must be the outer svg element
261 mCtx = e;
265 return mCtx != nullptr;
268 NonSVGFrameUserSpaceMetrics::NonSVGFrameUserSpaceMetrics(nsIFrame* aFrame)
269 : mFrame(aFrame) {
270 MOZ_ASSERT(mFrame, "Need a frame");
273 float NonSVGFrameUserSpaceMetrics::GetEmLength(Type aType) const {
274 switch (aType) {
275 case Type::This:
276 return SVGContentUtils::GetFontSize(mFrame);
277 case Type::Root:
278 return SVGContentUtils::GetFontSize(
279 mFrame->PresContext()->Document()->GetRootElement());
280 default:
281 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
282 return 1.0f;
286 GeckoFontMetrics NonSVGFrameUserSpaceMetrics::GetFontMetricsForType(
287 Type aType) const {
288 switch (aType) {
289 case Type::This:
290 return Gecko_GetFontMetrics(
291 mFrame->PresContext(), mFrame->GetWritingMode().IsVertical(),
292 mFrame->StyleFont(), mFrame->StyleFont()->mFont.size,
293 /* aUseUserFontSet = */ true,
294 /* aRetrieveMathScales */ false);
295 case Type::Root:
296 return GetFontMetrics(
297 mFrame->PresContext()->Document()->GetRootElement());
298 default:
299 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
300 return DefaultFontMetrics();
304 WritingMode NonSVGFrameUserSpaceMetrics::GetWritingModeForType(
305 Type aType) const {
306 switch (aType) {
307 case Type::This:
308 return mFrame->GetWritingMode();
309 case Type::Root:
310 return GetWritingMode(
311 mFrame->PresContext()->Document()->GetRootElement());
312 default:
313 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
314 return WritingMode();
318 float NonSVGFrameUserSpaceMetrics::GetZoom() const {
319 return mFrame->Style()->EffectiveZoom().ToFloat();
322 float NonSVGFrameUserSpaceMetrics::GetRootZoom() const {
323 return mFrame->PresContext()
324 ->FrameConstructor()
325 ->GetRootElementStyleFrame()
326 ->Style()
327 ->EffectiveZoom()
328 .ToFloat();
331 gfx::Size NonSVGFrameUserSpaceMetrics::GetSize() const {
332 return SVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(mFrame);
335 CSSSize NonSVGFrameUserSpaceMetrics::GetCSSViewportSize() const {
336 return GetCSSViewportSizeFromContext(mFrame->PresContext());
339 float NonSVGFrameUserSpaceMetrics::GetLineHeight(Type aType) const {
340 auto* context = mFrame->PresContext();
341 switch (aType) {
342 case Type::This: {
343 const auto lineHeightAu = ReflowInput::CalcLineHeight(
344 *mFrame->Style(), context, mFrame->GetContent(), NS_UNCONSTRAINEDSIZE,
345 1.0f);
346 return CSSPixel::FromAppUnits(lineHeightAu);
348 case Type::Root:
349 return SVGContentUtils::GetLineHeight(
350 context->Document()->GetRootElement());
352 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
353 return 1.0f;
356 float UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const {
357 gfx::Size size = GetSize();
358 float length;
359 switch (aCtxType) {
360 case SVGContentUtils::X:
361 length = size.width;
362 break;
363 case SVGContentUtils::Y:
364 length = size.height;
365 break;
366 case SVGContentUtils::XY:
367 length =
368 SVGContentUtils::ComputeNormalizedHypotenuse(size.width, size.height);
369 break;
370 default:
371 MOZ_ASSERT_UNREACHABLE("Unknown axis type");
372 length = 1;
373 break;
375 return FixAxisLength(length);
378 float SVGAnimatedLength::GetPixelsPerUnit(const SVGElement* aSVGElement,
379 uint8_t aUnitType) const {
380 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType,
381 mCtxType, false);
384 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(const SVGElement* aSVGElement,
385 uint8_t aUnitType) const {
386 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType,
387 mCtxType, true);
390 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(
391 const SVGViewportElement* aCtx, uint8_t aUnitType) const {
392 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aCtx, aCtx), aUnitType,
393 mCtxType, true);
396 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(nsIFrame* aFrame,
397 uint8_t aUnitType) const {
398 const nsIContent* content = aFrame->GetContent();
399 MOZ_ASSERT(!content->IsText(), "Not expecting text content");
400 if (content->IsSVGElement()) {
401 return SVGLength::GetPixelsPerUnit(
402 SVGElementMetrics(static_cast<const SVGElement*>(content)), aUnitType,
403 mCtxType, true);
405 return SVGLength::GetPixelsPerUnit(NonSVGFrameUserSpaceMetrics(aFrame),
406 aUnitType, mCtxType, true);
409 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(
410 const UserSpaceMetrics& aMetrics, uint8_t aUnitType) const {
411 return SVGLength::GetPixelsPerUnit(aMetrics, aUnitType, mCtxType, true);
414 void SVGAnimatedLength::SetBaseValueInSpecifiedUnits(float aValue,
415 SVGElement* aSVGElement,
416 bool aDoSetAttr) {
417 if (mIsBaseSet && mBaseVal == aValue) {
418 return;
421 AutoChangeLengthNotifier notifier(this, aSVGElement, aDoSetAttr);
423 mBaseVal = aValue;
424 mIsBaseSet = true;
425 if (!mIsAnimated) {
426 mAnimVal = mBaseVal;
430 nsresult SVGAnimatedLength::ConvertToSpecifiedUnits(uint16_t unitType,
431 SVGElement* aSVGElement) {
432 if (!SVGLength::IsValidUnitType(unitType)) {
433 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
436 if (mIsBaseSet && mBaseUnitType == uint8_t(unitType)) return NS_OK;
438 float valueInSpecifiedUnits;
440 if (SVGLength::IsAbsoluteUnit(unitType) &&
441 SVGLength::IsAbsoluteUnit(mBaseUnitType)) {
442 valueInSpecifiedUnits =
443 mBaseVal * SVGLength::GetAbsUnitsPerAbsUnit(unitType, mBaseUnitType);
444 } else {
445 float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, unitType);
446 if (pixelsPerUnit == 0.0f) {
447 return NS_ERROR_ILLEGAL_VALUE;
449 float valueInPixels =
450 mBaseVal * GetPixelsPerUnit(aSVGElement, mBaseUnitType);
451 valueInSpecifiedUnits = valueInPixels / pixelsPerUnit;
454 if (!std::isfinite(valueInSpecifiedUnits)) {
455 return NS_ERROR_ILLEGAL_VALUE;
458 // Even though we're not changing the visual effect this length will have
459 // on the document, we still need to send out notifications in case we have
460 // mutation listeners, since the actual string value of the attribute will
461 // change.
462 AutoChangeLengthNotifier notifier(this, aSVGElement);
464 mBaseUnitType = uint8_t(unitType);
465 if (!mIsAnimated) {
466 mAnimUnitType = mBaseUnitType;
468 // Setting aDoSetAttr to false here will ensure we don't call
469 // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
470 SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, false);
472 return NS_OK;
475 nsresult SVGAnimatedLength::NewValueSpecifiedUnits(uint16_t aUnitType,
476 float aValueInSpecifiedUnits,
477 SVGElement* aSVGElement) {
478 NS_ENSURE_FINITE(aValueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
480 if (!SVGLength::IsValidUnitType(aUnitType)) {
481 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
484 if (mIsBaseSet && mBaseVal == aValueInSpecifiedUnits &&
485 mBaseUnitType == uint8_t(aUnitType)) {
486 return NS_OK;
489 AutoChangeLengthNotifier notifier(this, aSVGElement);
491 mBaseVal = aValueInSpecifiedUnits;
492 mIsBaseSet = true;
493 mBaseUnitType = uint8_t(aUnitType);
494 if (!mIsAnimated) {
495 mAnimVal = mBaseVal;
496 mAnimUnitType = mBaseUnitType;
498 return NS_OK;
501 already_AddRefed<DOMSVGLength> SVGAnimatedLength::ToDOMBaseVal(
502 SVGElement* aSVGElement) {
503 return DOMSVGLength::GetTearOff(this, aSVGElement, false);
506 already_AddRefed<DOMSVGLength> SVGAnimatedLength::ToDOMAnimVal(
507 SVGElement* aSVGElement) {
508 return DOMSVGLength::GetTearOff(this, aSVGElement, true);
511 /* Implementation */
513 nsresult SVGAnimatedLength::SetBaseValueString(const nsAString& aValueAsString,
514 SVGElement* aSVGElement,
515 bool aDoSetAttr) {
516 float value;
517 uint16_t unitType;
519 if (!GetValueFromString(aValueAsString, value, &unitType)) {
520 return NS_ERROR_DOM_SYNTAX_ERR;
523 if (mIsBaseSet && mBaseVal == float(value) &&
524 mBaseUnitType == uint8_t(unitType)) {
525 return NS_OK;
528 AutoChangeLengthNotifier notifier(this, aSVGElement, aDoSetAttr);
530 mBaseVal = value;
531 mIsBaseSet = true;
532 mBaseUnitType = uint8_t(unitType);
533 if (!mIsAnimated) {
534 mAnimVal = mBaseVal;
535 mAnimUnitType = mBaseUnitType;
538 return NS_OK;
541 void SVGAnimatedLength::GetBaseValueString(nsAString& aValueAsString) const {
542 GetValueString(aValueAsString, mBaseVal, mBaseUnitType);
545 void SVGAnimatedLength::GetAnimValueString(nsAString& aValueAsString) const {
546 GetValueString(aValueAsString, mAnimVal, mAnimUnitType);
549 nsresult SVGAnimatedLength::SetBaseValue(float aValue, SVGElement* aSVGElement,
550 bool aDoSetAttr) {
551 float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, mBaseUnitType);
552 if (pixelsPerUnit == 0.0f) {
553 return NS_ERROR_ILLEGAL_VALUE;
556 float valueInSpecifiedUnits = aValue / pixelsPerUnit;
557 if (!std::isfinite(valueInSpecifiedUnits)) {
558 return NS_ERROR_ILLEGAL_VALUE;
561 SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, aDoSetAttr);
562 return NS_OK;
565 void SVGAnimatedLength::SetAnimValueInSpecifiedUnits(float aValue,
566 SVGElement* aSVGElement) {
567 if (mAnimVal == aValue && mIsAnimated) {
568 return;
570 mAnimVal = aValue;
571 mIsAnimated = true;
572 aSVGElement->DidAnimateLength(mAttrEnum);
575 void SVGAnimatedLength::SetAnimValue(float aValue, uint16_t aUnitType,
576 SVGElement* aSVGElement) {
577 if (mIsAnimated && mAnimVal == aValue && mAnimUnitType == aUnitType) {
578 return;
580 mAnimVal = aValue;
581 mAnimUnitType = aUnitType;
582 mIsAnimated = true;
583 aSVGElement->DidAnimateLength(mAttrEnum);
586 already_AddRefed<DOMSVGAnimatedLength> SVGAnimatedLength::ToDOMAnimatedLength(
587 SVGElement* aSVGElement) {
588 RefPtr<DOMSVGAnimatedLength> svgAnimatedLength =
589 sSVGAnimatedLengthTearoffTable.GetTearoff(this);
590 if (!svgAnimatedLength) {
591 svgAnimatedLength = new DOMSVGAnimatedLength(this, aSVGElement);
592 sSVGAnimatedLengthTearoffTable.AddTearoff(this, svgAnimatedLength);
595 return svgAnimatedLength.forget();
598 DOMSVGAnimatedLength::~DOMSVGAnimatedLength() {
599 sSVGAnimatedLengthTearoffTable.RemoveTearoff(mVal);
602 UniquePtr<SMILAttr> SVGAnimatedLength::ToSMILAttr(SVGElement* aSVGElement) {
603 return MakeUnique<SMILLength>(this, aSVGElement);
606 nsresult SVGAnimatedLength::SMILLength::ValueFromString(
607 const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
608 SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
609 float value;
610 uint16_t unitType;
612 if (!GetValueFromString(aStr, value, &unitType)) {
613 return NS_ERROR_DOM_SYNTAX_ERR;
616 SMILValue val(SVGLengthSMILType::Singleton());
617 SVGLengthAndInfo* lai = static_cast<SVGLengthAndInfo*>(val.mU.mPtr);
618 lai->Set(value, unitType, mVal->GetCtxType());
619 lai->SetInfo(mSVGElement);
620 aValue = std::move(val);
621 aPreventCachingOfSandwich = !SVGLength::IsAbsoluteUnit(unitType);
623 return NS_OK;
626 SMILValue SVGAnimatedLength::SMILLength::GetBaseValue() const {
627 SMILValue val(SVGLengthSMILType::Singleton());
628 auto* lai = static_cast<SVGLengthAndInfo*>(val.mU.mPtr);
629 lai->CopyBaseFrom(*mVal);
630 lai->SetInfo(mSVGElement);
631 return val;
634 void SVGAnimatedLength::SMILLength::ClearAnimValue() {
635 if (mVal->mIsAnimated) {
636 mVal->mIsAnimated = false;
637 mVal->mAnimVal = mVal->mBaseVal;
638 mVal->mAnimUnitType = mVal->mBaseUnitType;
639 mSVGElement->DidAnimateLength(mVal->mAttrEnum);
643 nsresult SVGAnimatedLength::SMILLength::SetAnimValue(const SMILValue& aValue) {
644 NS_ASSERTION(aValue.mType == SVGLengthSMILType::Singleton(),
645 "Unexpected type to assign animated value");
646 if (aValue.mType == SVGLengthSMILType::Singleton()) {
647 SVGLengthAndInfo* lai = static_cast<SVGLengthAndInfo*>(aValue.mU.mPtr);
648 mVal->SetAnimValue(lai->Value(), lai->UnitType(), mSVGElement);
650 return NS_OK;
653 float SVGLengthAndInfo::ConvertUnits(const SVGLengthAndInfo& aTo) const {
654 if (aTo.mUnitType == mUnitType) {
655 return mValue;
657 return SVGLength(mValue, mUnitType)
658 .GetValueInSpecifiedUnit(aTo.mUnitType, aTo.Element(), aTo.mCtxType);
661 float SVGLengthAndInfo::ValueInPixels(const UserSpaceMetrics& aMetrics) const {
662 return mValue == 0.0f ? 0.0f
663 : mValue * SVGLength::GetPixelsPerUnit(
664 aMetrics, mUnitType, mCtxType, false);
667 void SVGLengthAndInfo::Add(const SVGLengthAndInfo& aValueToAdd,
668 uint32_t aCount) {
669 mElement = aValueToAdd.mElement;
671 SVGElementMetrics metrics(Element());
673 // We may be dealing with two different length units, so we normalize to
674 // pixels for the add:
676 float currentLength = ValueInPixels(metrics);
677 float lengthToAdd = aValueToAdd.ValueInPixels(metrics) * aCount;
679 // And then we give the resulting value the same units as the value
680 // that we're animating to/by (i.e. the same as aValueToAdd):
681 mUnitType = aValueToAdd.mUnitType;
682 mCtxType = aValueToAdd.mCtxType;
683 mValue = (currentLength + lengthToAdd) /
684 SVGLength::GetPixelsPerUnit(metrics, mUnitType, mCtxType, false);
687 void SVGLengthAndInfo::Interpolate(const SVGLengthAndInfo& aStart,
688 const SVGLengthAndInfo& aEnd,
689 double aUnitDistance,
690 SVGLengthAndInfo& aResult) {
691 MOZ_ASSERT(!aStart.mElement || aStart.mElement == aEnd.mElement,
692 "Should not be interpolating between different elements");
694 float startValue, endValue;
695 if (!aStart.mElement || aUnitDistance > 0.5) {
696 aResult.mUnitType = aEnd.mUnitType;
697 aResult.mCtxType = aEnd.mCtxType;
698 startValue = aStart.ConvertUnits(aEnd);
699 endValue = aEnd.mValue;
700 } else {
701 aResult.mUnitType = aStart.mUnitType;
702 aResult.mCtxType = aStart.mCtxType;
703 startValue = aStart.mValue;
704 endValue = aEnd.ConvertUnits(aStart);
706 aResult.mElement = aEnd.mElement;
707 aResult.mValue = startValue + (endValue - startValue) * aUnitDistance;
710 } // namespace mozilla