tor-browser

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

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