tor-browser

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

SVGAnimatedPathSegList.cpp (11717B)


      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 "SVGAnimatedPathSegList.h"
      8 
      9 #include <utility>
     10 
     11 #include "SVGPathSegListSMILType.h"
     12 #include "mozilla/SMILValue.h"
     13 #include "mozilla/StaticPrefs_dom.h"
     14 #include "mozilla/dom/SVGElement.h"
     15 #include "mozilla/dom/SVGPathElementBinding.h"
     16 #include "mozilla/dom/SVGPathSegment.h"
     17 
     18 using namespace mozilla::dom;
     19 
     20 // See the comments in this file's header!
     21 
     22 namespace mozilla {
     23 
     24 nsresult SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue) {
     25  // We don't need to call DidChange* here - we're only called by
     26  // SVGElement::ParseAttribute under Element::SetAttr,
     27  // which takes care of notifying.
     28  return mBaseVal.SetValueFromString(NS_ConvertUTF16toUTF8(aValue));
     29 }
     30 
     31 enum class PositionType { Absolute, Relative };
     32 
     33 static StyleEndPoint<float> MakeEndPoint(PositionType type, float x, float y) {
     34  if (type == PositionType::Absolute) {
     35    return StyleEndPoint<float>::ToPosition({x, y});
     36  } else {
     37    return StyleEndPoint<float>::ByCoordinate({x, y});
     38  }
     39 }
     40 
     41 static StyleCurveControlPoint<float> MakeControlPoint(PositionType type,
     42                                                      float x, float y) {
     43  if (type == PositionType::Absolute) {
     44    return StyleCurveControlPoint<float>::Absolute({x, y});
     45  } else {
     46    const auto rcp =
     47        StyleRelativeControlPoint<float>{{x, y}, StyleControlReference::Start};
     48    return StyleCurveControlPoint<float>::Relative(rcp);
     49  }
     50 }
     51 
     52 static StyleAxisEndPoint<float> MakeAxisEndPoint(PositionType type,
     53                                                 float end_point) {
     54  if (type == PositionType::Absolute) {
     55    const auto pos = StyleAxisPosition<float>::LengthPercent(end_point);
     56    return StyleAxisEndPoint<float>::ToPosition(pos);
     57  } else {
     58    return StyleAxisEndPoint<float>::ByCoordinate(end_point);
     59  }
     60 }
     61 
     62 class MOZ_STACK_CLASS SVGPathSegmentInitWrapper final {
     63 public:
     64  explicit SVGPathSegmentInitWrapper(const SVGPathSegmentInit& aSVGPathSegment)
     65      : mInit(aSVGPathSegment) {}
     66 
     67  bool IsMove() const {
     68    return mInit.mType.EqualsLiteral("M") || mInit.mType.EqualsLiteral("m");
     69  }
     70 
     71  bool IsArc() const {
     72    return mInit.mType.EqualsLiteral("A") || mInit.mType.EqualsLiteral("a");
     73  }
     74 
     75  bool IsValid() const {
     76    if (mInit.mType.Length() != 1) {
     77      return false;
     78    }
     79    auto expectedArgCount = ArgCountForType(mInit.mType.First());
     80    if (expectedArgCount < 0 ||
     81        mInit.mValues.Length() != uint32_t(expectedArgCount)) {
     82      return false;
     83    }
     84    if (IsArc() &&
     85        !(IsValidFlag(mInit.mValues[3]) && IsValidFlag(mInit.mValues[4]))) {
     86      return false;
     87    }
     88    return true;
     89  }
     90 
     91  StylePathCommand ToStylePathCommand() const {
     92    MOZ_ASSERT(IsValid(), "Trying to convert invalid SVGPathSegment");
     93    switch (mInit.mType.First()) {
     94      case 'M':
     95        return StylePathCommand::Move(MakeEndPoint(
     96            PositionType::Absolute, mInit.mValues[0], mInit.mValues[1]));
     97      case 'm':
     98        return StylePathCommand::Move(MakeEndPoint(
     99            PositionType::Relative, mInit.mValues[0], mInit.mValues[1]));
    100      case 'L':
    101        return StylePathCommand::Line(MakeEndPoint(
    102            PositionType::Absolute, mInit.mValues[0], mInit.mValues[1]));
    103      case 'l':
    104        return StylePathCommand::Line(MakeEndPoint(
    105            PositionType::Relative, mInit.mValues[0], mInit.mValues[1]));
    106      case 'C':
    107        return StylePathCommand::CubicCurve(
    108            MakeEndPoint(PositionType::Absolute, mInit.mValues[4],
    109                         mInit.mValues[5]),
    110            MakeControlPoint(PositionType::Absolute, mInit.mValues[0],
    111                             mInit.mValues[1]),
    112            MakeControlPoint(PositionType::Absolute, mInit.mValues[2],
    113                             mInit.mValues[3]));
    114      case 'c':
    115        return StylePathCommand::CubicCurve(
    116            MakeEndPoint(PositionType::Relative, mInit.mValues[4],
    117                         mInit.mValues[5]),
    118            MakeControlPoint(PositionType::Relative, mInit.mValues[0],
    119                             mInit.mValues[1]),
    120            MakeControlPoint(PositionType::Relative, mInit.mValues[2],
    121                             mInit.mValues[3]));
    122      case 'Q':
    123        return StylePathCommand::QuadCurve(
    124            MakeEndPoint(PositionType::Absolute, mInit.mValues[2],
    125                         mInit.mValues[3]),
    126            MakeControlPoint(PositionType::Absolute, mInit.mValues[0],
    127                             mInit.mValues[1]));
    128      case 'q':
    129        return StylePathCommand::QuadCurve(
    130            MakeEndPoint(PositionType::Relative, mInit.mValues[2],
    131                         mInit.mValues[3]),
    132            MakeControlPoint(PositionType::Relative, mInit.mValues[0],
    133                             mInit.mValues[1]));
    134      case 'A':
    135        return StylePathCommand::Arc(
    136            MakeEndPoint(PositionType::Absolute, mInit.mValues[5],
    137                         mInit.mValues[6]),
    138            StyleArcRadii<float>(mInit.mValues[0],
    139                                 StyleOptional<float>::Some(mInit.mValues[1])),
    140            mInit.mValues[4] ? StyleArcSweep::Cw : StyleArcSweep::Ccw,
    141            mInit.mValues[3] ? StyleArcSize::Large : StyleArcSize::Small,
    142            mInit.mValues[2]);
    143      case 'a':
    144        return StylePathCommand::Arc(
    145            MakeEndPoint(PositionType::Relative, mInit.mValues[5],
    146                         mInit.mValues[6]),
    147            StyleArcRadii<float>(mInit.mValues[0],
    148                                 StyleOptional<float>::Some(mInit.mValues[1])),
    149            mInit.mValues[4] ? StyleArcSweep::Cw : StyleArcSweep::Ccw,
    150            mInit.mValues[3] ? StyleArcSize::Large : StyleArcSize::Small,
    151            mInit.mValues[2]);
    152      case 'H':
    153        return StylePathCommand::HLine(
    154            MakeAxisEndPoint(PositionType::Absolute, mInit.mValues[0]));
    155      case 'h':
    156        return StylePathCommand::HLine(
    157            MakeAxisEndPoint(PositionType::Relative, mInit.mValues[0]));
    158      case 'V':
    159        return StylePathCommand::VLine(
    160            MakeAxisEndPoint(PositionType::Absolute, mInit.mValues[0]));
    161      case 'v':
    162        return StylePathCommand::VLine(
    163            MakeAxisEndPoint(PositionType::Relative, mInit.mValues[0]));
    164      case 'S':
    165        return StylePathCommand::SmoothCubic(
    166            MakeEndPoint(PositionType::Absolute, mInit.mValues[2],
    167                         mInit.mValues[3]),
    168            MakeControlPoint(PositionType::Absolute, mInit.mValues[0],
    169                             mInit.mValues[1]));
    170      case 's':
    171        return StylePathCommand::SmoothCubic(
    172            MakeEndPoint(PositionType::Relative, mInit.mValues[2],
    173                         mInit.mValues[3]),
    174            MakeControlPoint(PositionType::Relative, mInit.mValues[0],
    175                             mInit.mValues[1]));
    176      case 'T':
    177        return StylePathCommand::SmoothQuad(MakeEndPoint(
    178            PositionType::Absolute, mInit.mValues[0], mInit.mValues[1]));
    179      case 't':
    180        return StylePathCommand::SmoothQuad(MakeEndPoint(
    181            PositionType::Relative, mInit.mValues[0], mInit.mValues[1]));
    182    }
    183    return StylePathCommand::Close();
    184  }
    185 
    186 private:
    187  static bool IsValidFlag(float aFlag) {
    188    return aFlag == 0.0f || aFlag == 1.0f;
    189  }
    190 
    191  static int32_t ArgCountForType(char aType) {
    192    switch (ToLowerCase(aType)) {
    193      case 'z':
    194        return 0;
    195      case 'm':
    196      case 'l':
    197        return 2;
    198      case 'c':
    199        return 6;
    200      case 'q':
    201        return 4;
    202      case 'a':
    203        return 7;
    204      case 'h':
    205      case 'v':
    206        return 1;
    207      case 's':
    208        return 4;
    209      case 't':
    210        return 2;
    211    }
    212    return -1;
    213  }
    214 
    215  const SVGPathSegmentInit& mInit;
    216 };
    217 
    218 void SVGAnimatedPathSegList::SetBaseValueFromPathSegments(
    219    const Sequence<SVGPathSegmentInit>& aValues) {
    220  AutoTArray<StylePathCommand, 10> pathData;
    221  if (!aValues.IsEmpty() && SVGPathSegmentInitWrapper(aValues[0]).IsMove()) {
    222    for (const auto& value : aValues) {
    223      SVGPathSegmentInitWrapper seg(value);
    224      if (!seg.IsValid()) {
    225        break;
    226      }
    227      pathData.AppendElement(seg.ToStylePathCommand());
    228    }
    229  }
    230  if (pathData.IsEmpty()) {
    231    mBaseVal.Clear();
    232    return;
    233  }
    234  Servo_CreatePathDataFromCommands(&pathData, &mBaseVal.RawData());
    235 }
    236 
    237 void SVGAnimatedPathSegList::ClearBaseValue() {
    238  mBaseVal.Clear();
    239  // Caller notifies
    240 }
    241 
    242 nsresult SVGAnimatedPathSegList::SetAnimValue(const SVGPathData& aNewAnimValue,
    243                                              SVGElement* aElement) {
    244  // Note that a new animation may totally change the number of items in the
    245  // animVal list, either replacing what was essentially a mirror of the
    246  // baseVal list, or else replacing and overriding an existing animation.
    247  // Unfortunately it is not possible for us to reliably distinguish between
    248  // calls to this method that are setting a new sample for an existing
    249  // animation, and calls that are setting the first sample of an animation
    250  // that will override an existing animation.
    251 
    252  if (!mAnimVal) {
    253    mAnimVal = MakeUnique<SVGPathData>();
    254  }
    255  *mAnimVal = aNewAnimValue;
    256  aElement->DidAnimatePathSegList();
    257  return NS_OK;
    258 }
    259 
    260 void SVGAnimatedPathSegList::ClearAnimValue(SVGElement* aElement) {
    261  mAnimVal = nullptr;
    262  aElement->DidAnimatePathSegList();
    263 }
    264 
    265 bool SVGAnimatedPathSegList::IsRendered() const {
    266  return mAnimVal ? !mAnimVal->IsEmpty() : !mBaseVal.IsEmpty();
    267 }
    268 
    269 UniquePtr<SMILAttr> SVGAnimatedPathSegList::ToSMILAttr(SVGElement* aElement) {
    270  return MakeUnique<SMILAnimatedPathSegList>(this, aElement);
    271 }
    272 
    273 nsresult SVGAnimatedPathSegList::SMILAnimatedPathSegList::ValueFromString(
    274    const nsAString& aStr, const dom::SVGAnimationElement* /*aSrcElement*/,
    275    SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
    276  SMILValue val(SVGPathSegListSMILType::Singleton());
    277  SVGPathDataAndInfo* list = static_cast<SVGPathDataAndInfo*>(val.mU.mPtr);
    278  nsresult rv = list->SetValueFromString(NS_ConvertUTF16toUTF8(aStr));
    279  if (NS_SUCCEEDED(rv)) {
    280    list->SetElement(mElement);
    281    aValue = std::move(val);
    282  }
    283  return rv;
    284 }
    285 
    286 SMILValue SVGAnimatedPathSegList::SMILAnimatedPathSegList::GetBaseValue()
    287    const {
    288  // To benefit from Return Value Optimization and avoid copy constructor calls
    289  // due to our use of return-by-value, we must return the exact same object
    290  // from ALL return points. This function must only return THIS variable:
    291  SMILValue tmp(SVGPathSegListSMILType::Singleton());
    292  auto* list = static_cast<SVGPathDataAndInfo*>(tmp.mU.mPtr);
    293  list->CopyFrom(mVal->mBaseVal);
    294  list->SetElement(mElement);
    295  return tmp;
    296 }
    297 
    298 nsresult SVGAnimatedPathSegList::SMILAnimatedPathSegList::SetAnimValue(
    299    const SMILValue& aValue) {
    300  NS_ASSERTION(aValue.mType == SVGPathSegListSMILType::Singleton(),
    301               "Unexpected type to assign animated value");
    302  if (aValue.mType == SVGPathSegListSMILType::Singleton()) {
    303    mVal->SetAnimValue(*static_cast<SVGPathDataAndInfo*>(aValue.mU.mPtr),
    304                       mElement);
    305  }
    306  return NS_OK;
    307 }
    308 
    309 void SVGAnimatedPathSegList::SMILAnimatedPathSegList::ClearAnimValue() {
    310  if (mVal->mAnimVal) {
    311    mVal->ClearAnimValue(mElement);
    312  }
    313 }
    314 
    315 size_t SVGAnimatedPathSegList::SizeOfExcludingThis(
    316    MallocSizeOf aMallocSizeOf) const {
    317  size_t total = mBaseVal.SizeOfExcludingThis(aMallocSizeOf);
    318  if (mAnimVal) {
    319    mAnimVal->SizeOfIncludingThis(aMallocSizeOf);
    320  }
    321  return total;
    322 }
    323 
    324 }  // namespace mozilla