tor-browser

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

SVGAnimatedOrient.cpp (15410B)


      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 "SVGAnimatedOrient.h"
      8 
      9 #include <utility>
     10 
     11 #include "DOMSVGAngle.h"
     12 #include "DOMSVGAnimatedAngle.h"
     13 #include "SVGAttrTearoffTable.h"
     14 #include "SVGOrientSMILType.h"
     15 #include "mozAutoDocUpdate.h"
     16 #include "mozilla/Maybe.h"
     17 #include "mozilla/SMILValue.h"
     18 #include "mozilla/dom/SVGMarkerElement.h"
     19 #include "nsContentUtils.h"
     20 #include "nsPrintfCString.h"
     21 #include "nsTextFormatter.h"
     22 
     23 using namespace mozilla::dom;
     24 using namespace mozilla::dom::SVGAngle_Binding;
     25 using namespace mozilla::dom::SVGMarkerElement_Binding;
     26 
     27 namespace mozilla {
     28 
     29 constinit static SVGAttrTearoffTable<SVGAnimatedOrient,
     30                                     DOMSVGAnimatedEnumeration>
     31    sSVGAnimatedEnumTearoffTable;
     32 constinit static SVGAttrTearoffTable<SVGAnimatedOrient, DOMSVGAnimatedAngle>
     33    sSVGAnimatedAngleTearoffTable;
     34 constinit static SVGAttrTearoffTable<SVGAnimatedOrient, DOMSVGAngle>
     35    sBaseSVGAngleTearoffTable;
     36 constinit static SVGAttrTearoffTable<SVGAnimatedOrient, DOMSVGAngle>
     37    sAnimSVGAngleTearoffTable;
     38 
     39 /* Helper functions */
     40 
     41 //----------------------------------------------------------------------
     42 // Helper class: AutoChangeOrientNotifier
     43 // Stack-based helper class to pair calls to WillChangeOrient and
     44 // DidChangeOrient with mozAutoDocUpdate.
     45 class MOZ_RAII AutoChangeOrientNotifier {
     46 public:
     47  AutoChangeOrientNotifier(SVGAnimatedOrient* aOrient, SVGElement* aSVGElement,
     48                           bool aDoSetAttr = true)
     49      : mOrient(aOrient), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
     50    MOZ_ASSERT(mOrient, "Expecting non-null orient");
     51    if (mSVGElement && mDoSetAttr) {
     52      mUpdateBatch.emplace(mSVGElement->GetComposedDoc(), true);
     53      mSVGElement->WillChangeOrient(mUpdateBatch.ref());
     54    }
     55  }
     56 
     57  ~AutoChangeOrientNotifier() {
     58    if (mSVGElement) {
     59      if (mDoSetAttr) {
     60        mSVGElement->DidChangeOrient(mUpdateBatch.ref());
     61      }
     62      if (mOrient->mIsAnimated) {
     63        mSVGElement->AnimationNeedsResample();
     64      }
     65    }
     66  }
     67 
     68 private:
     69  Maybe<mozAutoDocUpdate> mUpdateBatch;
     70  SVGAnimatedOrient* const mOrient;
     71  SVGElement* const mSVGElement;
     72  bool mDoSetAttr;
     73 };
     74 
     75 const unsigned short SVG_ANGLETYPE_TURN = 5;
     76 
     77 static void GetAngleUnitString(nsAString& aUnit, uint16_t aUnitType) {
     78  switch (aUnitType) {
     79    case SVG_ANGLETYPE_UNSPECIFIED:
     80      aUnit.Truncate();
     81      return;
     82    case SVG_ANGLETYPE_DEG:
     83      aUnit.AssignLiteral("deg");
     84      return;
     85    case SVG_ANGLETYPE_RAD:
     86      aUnit.AssignLiteral("rad");
     87      return;
     88    case SVG_ANGLETYPE_GRAD:
     89      aUnit.AssignLiteral("grad");
     90      return;
     91    case SVG_ANGLETYPE_TURN:
     92      aUnit.AssignLiteral("turn");
     93      return;
     94  }
     95 
     96  MOZ_ASSERT_UNREACHABLE("Unknown unit type");
     97 }
     98 
     99 static uint16_t GetAngleUnitTypeForString(const nsAString& aUnit) {
    100  if (aUnit.IsEmpty()) {
    101    return SVG_ANGLETYPE_UNSPECIFIED;
    102  }
    103  if (aUnit.LowerCaseEqualsLiteral("deg")) {
    104    return SVG_ANGLETYPE_DEG;
    105  }
    106  if (aUnit.LowerCaseEqualsLiteral("rad")) {
    107    return SVG_ANGLETYPE_RAD;
    108  }
    109  if (aUnit.LowerCaseEqualsLiteral("grad")) {
    110    return SVG_ANGLETYPE_GRAD;
    111  }
    112  if (aUnit.LowerCaseEqualsLiteral("turn")) {
    113    return SVG_ANGLETYPE_TURN;
    114  }
    115  return SVG_ANGLETYPE_UNKNOWN;
    116 }
    117 
    118 static void GetAngleValueString(nsAString& aValueAsString, float aValue,
    119                                uint16_t aUnitType) {
    120  nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue);
    121 
    122  nsAutoString unitString;
    123  GetAngleUnitString(unitString, aUnitType);
    124  aValueAsString.Append(unitString);
    125 }
    126 
    127 /*static*/
    128 bool SVGAnimatedOrient::IsValidUnitType(uint16_t aUnitType) {
    129  return aUnitType > SVG_ANGLETYPE_UNKNOWN && aUnitType <= SVG_ANGLETYPE_GRAD;
    130 }
    131 
    132 /* static */
    133 bool SVGAnimatedOrient::GetValueFromString(const nsAString& aString,
    134                                           float& aValue, uint16_t* aUnitType) {
    135  bool success;
    136  auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
    137 
    138  if (!success) {
    139    return false;
    140  }
    141 
    142  nsAString::const_iterator iter, end;
    143  token.BeginReading(iter);
    144  token.EndReading(end);
    145 
    146  if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
    147    return false;
    148  }
    149 
    150  const nsAString& units = Substring(iter, end);
    151  *aUnitType = GetAngleUnitTypeForString(units);
    152  return *aUnitType != SVG_ANGLETYPE_UNKNOWN;
    153 }
    154 
    155 /* static */
    156 float SVGAnimatedOrient::GetDegreesPerUnit(uint8_t aUnit) {
    157  switch (aUnit) {
    158    case SVG_ANGLETYPE_UNSPECIFIED:
    159    case SVG_ANGLETYPE_DEG:
    160      return 1;
    161    case SVG_ANGLETYPE_RAD:
    162      return static_cast<float>(180.0 / M_PI);
    163    case SVG_ANGLETYPE_GRAD:
    164      return 90.0f / 100.0f;
    165    case SVG_ANGLETYPE_TURN:
    166      return 360.0f;
    167    default:
    168      MOZ_ASSERT_UNREACHABLE("Unknown unit type");
    169      return 0;
    170  }
    171 }
    172 
    173 void SVGAnimatedOrient::SetBaseValueInSpecifiedUnits(float aValue,
    174                                                     SVGElement* aSVGElement) {
    175  if (mBaseVal == aValue && mBaseType == SVG_MARKER_ORIENT_ANGLE) {
    176    return;
    177  }
    178 
    179  AutoChangeOrientNotifier notifier(this, aSVGElement);
    180 
    181  mBaseVal = aValue;
    182  mBaseType = SVG_MARKER_ORIENT_ANGLE;
    183  if (!mIsAnimated) {
    184    mAnimVal = mBaseVal;
    185    mAnimType = mBaseType;
    186  }
    187 }
    188 
    189 void SVGAnimatedOrient::ConvertToSpecifiedUnits(uint16_t unitType,
    190                                                SVGElement* aSVGElement) {
    191  if (mBaseValUnit == uint8_t(unitType) &&
    192      mBaseType == SVG_MARKER_ORIENT_ANGLE) {
    193    return;
    194  }
    195 
    196  float valueInUserUnits = mBaseVal * GetDegreesPerUnit(mBaseValUnit);
    197  SetBaseValue(valueInUserUnits, unitType, aSVGElement, true);
    198 }
    199 
    200 void SVGAnimatedOrient::NewValueSpecifiedUnits(uint16_t aUnitType,
    201                                               float aValueInSpecifiedUnits,
    202                                               SVGElement* aSVGElement) {
    203  MOZ_ASSERT(IsValidUnitType(aUnitType), "Unexpected unit type");
    204 
    205  if (mBaseVal == aValueInSpecifiedUnits &&
    206      mBaseValUnit == uint8_t(aUnitType) &&
    207      mBaseType == SVG_MARKER_ORIENT_ANGLE) {
    208    return;
    209  }
    210 
    211  AutoChangeOrientNotifier notifier(this, aSVGElement);
    212 
    213  mBaseVal = aValueInSpecifiedUnits;
    214  mBaseValUnit = uint8_t(aUnitType);
    215  mBaseType = SVG_MARKER_ORIENT_ANGLE;
    216  if (!mIsAnimated) {
    217    mAnimVal = mBaseVal;
    218    mAnimValUnit = mBaseValUnit;
    219    mAnimType = mBaseType;
    220  }
    221 }
    222 
    223 already_AddRefed<DOMSVGAngle> SVGAnimatedOrient::ToDOMBaseVal(
    224    SVGElement* aSVGElement) {
    225  RefPtr<DOMSVGAngle> domBaseVal = sBaseSVGAngleTearoffTable.GetTearoff(this);
    226  if (!domBaseVal) {
    227    domBaseVal =
    228        new DOMSVGAngle(this, aSVGElement, DOMSVGAngle::AngleType::BaseValue);
    229    sBaseSVGAngleTearoffTable.AddTearoff(this, domBaseVal);
    230  }
    231 
    232  return domBaseVal.forget();
    233 }
    234 
    235 already_AddRefed<DOMSVGAngle> SVGAnimatedOrient::ToDOMAnimVal(
    236    SVGElement* aSVGElement) {
    237  RefPtr<DOMSVGAngle> domAnimVal = sAnimSVGAngleTearoffTable.GetTearoff(this);
    238  if (!domAnimVal) {
    239    domAnimVal =
    240        new DOMSVGAngle(this, aSVGElement, DOMSVGAngle::AngleType::AnimValue);
    241    sAnimSVGAngleTearoffTable.AddTearoff(this, domAnimVal);
    242  }
    243 
    244  return domAnimVal.forget();
    245 }
    246 
    247 DOMSVGAngle::~DOMSVGAngle() {
    248  switch (mType) {
    249    case AngleType::BaseValue:
    250      sBaseSVGAngleTearoffTable.RemoveTearoff(mVal);
    251      break;
    252    case AngleType::AnimValue:
    253      sAnimSVGAngleTearoffTable.RemoveTearoff(mVal);
    254      break;
    255    default:
    256      delete mVal;
    257  }
    258 }
    259 
    260 /* Implementation */
    261 
    262 nsresult SVGAnimatedOrient::SetBaseValueString(const nsAString& aValueAsString,
    263                                               SVGElement* aSVGElement,
    264                                               bool aDoSetAttr) {
    265  uint8_t type;
    266  float value;
    267  uint16_t unitType;
    268  bool valueChanged = false;
    269 
    270  if (aValueAsString.EqualsLiteral("auto")) {
    271    type = SVG_MARKER_ORIENT_AUTO;
    272    if (type == mBaseType) {
    273      return NS_OK;
    274    }
    275  } else if (aValueAsString.EqualsLiteral("auto-start-reverse")) {
    276    type = SVG_MARKER_ORIENT_AUTO_START_REVERSE;
    277    if (type == mBaseType) {
    278      return NS_OK;
    279    }
    280  } else {
    281    if (!GetValueFromString(aValueAsString, value, &unitType)) {
    282      return NS_ERROR_DOM_SYNTAX_ERR;
    283    }
    284    if (mBaseVal == value && mBaseValUnit == uint8_t(unitType) &&
    285        mBaseType == SVG_MARKER_ORIENT_ANGLE) {
    286      return NS_OK;
    287    }
    288    valueChanged = true;
    289  }
    290 
    291  AutoChangeOrientNotifier notifier(this, aSVGElement, aDoSetAttr);
    292 
    293  if (valueChanged) {
    294    mBaseVal = value;
    295    mBaseValUnit = uint8_t(unitType);
    296    mBaseType = SVG_MARKER_ORIENT_ANGLE;
    297  } else {
    298    mBaseVal = .0f;
    299    mBaseValUnit = SVG_ANGLETYPE_UNSPECIFIED;
    300    mBaseType = type;
    301  }
    302 
    303  if (!mIsAnimated) {
    304    mAnimVal = mBaseVal;
    305    mAnimValUnit = mBaseValUnit;
    306    mAnimType = mBaseType;
    307  }
    308 
    309  return NS_OK;
    310 }
    311 
    312 void SVGAnimatedOrient::GetBaseValueString(nsAString& aValueAsString) const {
    313  switch (mBaseType) {
    314    case SVG_MARKER_ORIENT_AUTO:
    315      aValueAsString.AssignLiteral("auto");
    316      return;
    317    case SVG_MARKER_ORIENT_AUTO_START_REVERSE:
    318      aValueAsString.AssignLiteral("auto-start-reverse");
    319      return;
    320  }
    321  GetAngleValueString(aValueAsString, mBaseVal, mBaseValUnit);
    322 }
    323 
    324 void SVGAnimatedOrient::GetBaseAngleValueString(
    325    nsAString& aValueAsString) const {
    326  GetAngleValueString(aValueAsString, mBaseVal, mBaseValUnit);
    327 }
    328 
    329 void SVGAnimatedOrient::GetAnimAngleValueString(
    330    nsAString& aValueAsString) const {
    331  GetAngleValueString(aValueAsString, mAnimVal, mAnimValUnit);
    332 }
    333 
    334 void SVGAnimatedOrient::SetBaseValue(float aValue, uint8_t aUnit,
    335                                     SVGElement* aSVGElement, bool aDoSetAttr) {
    336  float valueInSpecifiedUnits = aValue / GetDegreesPerUnit(aUnit);
    337  if (aUnit == mBaseValUnit && mBaseVal == valueInSpecifiedUnits &&
    338      mBaseType == SVG_MARKER_ORIENT_ANGLE) {
    339    return;
    340  }
    341 
    342  AutoChangeOrientNotifier notifier(this, aSVGElement, aDoSetAttr);
    343 
    344  mBaseValUnit = aUnit;
    345  mBaseVal = valueInSpecifiedUnits;
    346  mBaseType = SVG_MARKER_ORIENT_ANGLE;
    347  if (!mIsAnimated) {
    348    mAnimValUnit = mBaseValUnit;
    349    mAnimVal = mBaseVal;
    350    mAnimType = mBaseType;
    351  }
    352 }
    353 
    354 void SVGAnimatedOrient::SetBaseType(SVGEnumValue aValue,
    355                                    SVGElement* aSVGElement, ErrorResult& aRv) {
    356  if (mBaseType == aValue) {
    357    return;
    358  }
    359  if (aValue >= SVG_MARKER_ORIENT_AUTO &&
    360      aValue <= SVG_MARKER_ORIENT_AUTO_START_REVERSE) {
    361    AutoChangeOrientNotifier notifier(this, aSVGElement);
    362 
    363    mBaseVal = .0f;
    364    mBaseValUnit = SVG_ANGLETYPE_UNSPECIFIED;
    365    mBaseType = aValue;
    366    if (!mIsAnimated) {
    367      mAnimVal = mBaseVal;
    368      mAnimValUnit = mBaseValUnit;
    369      mAnimType = mBaseType;
    370    }
    371    return;
    372  }
    373  nsPrintfCString err("Invalid base value %u for marker orient", aValue);
    374  aRv.ThrowTypeError(err);
    375 }
    376 
    377 void SVGAnimatedOrient::SetAnimValue(float aValue, uint8_t aUnit,
    378                                     SVGElement* aSVGElement) {
    379  if (mIsAnimated && mAnimVal == aValue && mAnimValUnit == aUnit &&
    380      mAnimType == SVG_MARKER_ORIENT_ANGLE) {
    381    return;
    382  }
    383  mAnimVal = aValue;
    384  mAnimValUnit = aUnit;
    385  mAnimType = SVG_MARKER_ORIENT_ANGLE;
    386  mIsAnimated = true;
    387  aSVGElement->DidAnimateOrient();
    388 }
    389 
    390 void SVGAnimatedOrient::SetAnimType(SVGEnumValue aValue,
    391                                    SVGElement* aSVGElement) {
    392  if (mIsAnimated && mAnimType == aValue) {
    393    return;
    394  }
    395  mAnimVal = .0f;
    396  mAnimValUnit = SVG_ANGLETYPE_UNSPECIFIED;
    397  mAnimType = aValue;
    398  mIsAnimated = true;
    399  aSVGElement->DidAnimateOrient();
    400 }
    401 
    402 already_AddRefed<DOMSVGAnimatedAngle> SVGAnimatedOrient::ToDOMAnimatedAngle(
    403    SVGElement* aSVGElement) {
    404  RefPtr<DOMSVGAnimatedAngle> domAnimatedAngle =
    405      sSVGAnimatedAngleTearoffTable.GetTearoff(this);
    406  if (!domAnimatedAngle) {
    407    domAnimatedAngle = new DOMSVGAnimatedAngle(this, aSVGElement);
    408    sSVGAnimatedAngleTearoffTable.AddTearoff(this, domAnimatedAngle);
    409  }
    410 
    411  return domAnimatedAngle.forget();
    412 }
    413 
    414 already_AddRefed<DOMSVGAnimatedEnumeration>
    415 SVGAnimatedOrient::ToDOMAnimatedEnum(SVGElement* aSVGElement) {
    416  RefPtr<DOMSVGAnimatedEnumeration> domAnimatedEnum =
    417      sSVGAnimatedEnumTearoffTable.GetTearoff(this);
    418  if (!domAnimatedEnum) {
    419    domAnimatedEnum = new DOMAnimatedEnum(this, aSVGElement);
    420    sSVGAnimatedEnumTearoffTable.AddTearoff(this, domAnimatedEnum);
    421  }
    422 
    423  return domAnimatedEnum.forget();
    424 }
    425 
    426 DOMSVGAnimatedAngle::~DOMSVGAnimatedAngle() {
    427  sSVGAnimatedAngleTearoffTable.RemoveTearoff(mVal);
    428 }
    429 
    430 SVGAnimatedOrient::DOMAnimatedEnum::~DOMAnimatedEnum() {
    431  sSVGAnimatedEnumTearoffTable.RemoveTearoff(mVal);
    432 }
    433 
    434 UniquePtr<SMILAttr> SVGAnimatedOrient::ToSMILAttr(SVGElement* aSVGElement) {
    435  if (aSVGElement->IsSVGElement(nsGkAtoms::marker)) {
    436    return MakeUnique<SMILOrient>(this, aSVGElement);
    437  }
    438  // SMILOrient would not be useful for general angle attributes (also,
    439  // "orient" is the only animatable <angle>-valued attribute in SVG 1.1).
    440  MOZ_ASSERT_UNREACHABLE("Trying to animate unknown angle attribute.");
    441  return nullptr;
    442 }
    443 
    444 nsresult SVGAnimatedOrient::SMILOrient::ValueFromString(
    445    const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
    446    SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
    447  SMILValue val(&SVGOrientSMILType::sSingleton);
    448  if (aStr.EqualsLiteral("auto")) {
    449    val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO;
    450    val.mU.mOrient.mAngle = .0f;
    451    val.mU.mOrient.mUnit = SVG_ANGLETYPE_UNSPECIFIED;
    452  } else if (aStr.EqualsLiteral("auto-start-reverse")) {
    453    val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO_START_REVERSE;
    454    val.mU.mOrient.mAngle = .0f;
    455    val.mU.mOrient.mUnit = SVG_ANGLETYPE_UNSPECIFIED;
    456  } else {
    457    float value;
    458    uint16_t unitType;
    459    if (!GetValueFromString(aStr, value, &unitType)) {
    460      return NS_ERROR_DOM_SYNTAX_ERR;
    461    }
    462    val.mU.mOrient.mAngle = value;
    463    val.mU.mOrient.mUnit = unitType;
    464    val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_ANGLE;
    465  }
    466  aValue = std::move(val);
    467 
    468  return NS_OK;
    469 }
    470 
    471 SMILValue SVGAnimatedOrient::SMILOrient::GetBaseValue() const {
    472  SMILValue val(&SVGOrientSMILType::sSingleton);
    473  val.mU.mOrient.mAngle = mOrient->GetBaseValInSpecifiedUnits();
    474  val.mU.mOrient.mUnit = mOrient->GetBaseValueUnit();
    475  val.mU.mOrient.mOrientType = mOrient->mBaseType;
    476  return val;
    477 }
    478 
    479 void SVGAnimatedOrient::SMILOrient::ClearAnimValue() {
    480  if (mOrient->mIsAnimated) {
    481    mOrient->mIsAnimated = false;
    482    mOrient->mAnimVal = mOrient->mBaseVal;
    483    mOrient->mAnimValUnit = mOrient->mBaseValUnit;
    484    mOrient->mAnimType = mOrient->mBaseType;
    485    mSVGElement->DidAnimateOrient();
    486  }
    487 }
    488 
    489 nsresult SVGAnimatedOrient::SMILOrient::SetAnimValue(const SMILValue& aValue) {
    490  NS_ASSERTION(aValue.mType == &SVGOrientSMILType::sSingleton,
    491               "Unexpected type to assign animated value");
    492 
    493  if (aValue.mType == &SVGOrientSMILType::sSingleton) {
    494    if (aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_AUTO ||
    495        aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_AUTO_START_REVERSE) {
    496      mOrient->SetAnimType(aValue.mU.mOrient.mOrientType, mSVGElement);
    497    } else {
    498      mOrient->SetAnimValue(aValue.mU.mOrient.mAngle, aValue.mU.mOrient.mUnit,
    499                            mSVGElement);
    500    }
    501  }
    502  return NS_OK;
    503 }
    504 
    505 }  // namespace mozilla