tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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