tor-browser

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

SVGAnimatedViewBox.cpp (9117B)


      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 "SVGAnimatedViewBox.h"
      8 
      9 #include <utility>
     10 
     11 #include "SVGViewBoxSMILType.h"
     12 #include "mozAutoDocUpdate.h"
     13 #include "mozilla/Maybe.h"
     14 #include "mozilla/SMILValue.h"
     15 #include "mozilla/SVGContentUtils.h"
     16 #include "mozilla/dom/SVGRect.h"
     17 #include "nsCharSeparatedTokenizer.h"
     18 #include "nsTextFormatter.h"
     19 
     20 using namespace mozilla::dom;
     21 
     22 namespace mozilla {
     23 
     24 #define NUM_VIEWBOX_COMPONENTS 4
     25 
     26 /* Implementation of SVGViewBox methods */
     27 
     28 bool SVGViewBox::operator==(const SVGViewBox& aOther) const {
     29  if (&aOther == this) return true;
     30 
     31  return (none && aOther.none) ||
     32         (!none && !aOther.none && x == aOther.x && y == aOther.y &&
     33          width == aOther.width && height == aOther.height);
     34 }
     35 
     36 /* static */
     37 nsresult SVGViewBox::FromString(const nsAString& aStr, SVGViewBox* aViewBox) {
     38  if (aStr.EqualsLiteral("none")) {
     39    aViewBox->none = true;
     40    return NS_OK;
     41  }
     42 
     43  nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace,
     44                                   nsTokenizerFlags::SeparatorOptional>
     45      tokenizer(aStr, ',');
     46  float vals[NUM_VIEWBOX_COMPONENTS];
     47  uint32_t i;
     48  for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) {
     49    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) {
     50      return NS_ERROR_DOM_SYNTAX_ERR;
     51    }
     52  }
     53 
     54  if (i != NUM_VIEWBOX_COMPONENTS ||             // Too few values.
     55      tokenizer.hasMoreTokens() ||               // Too many values.
     56      tokenizer.separatorAfterCurrentToken()) {  // Trailing comma.
     57    return NS_ERROR_DOM_SYNTAX_ERR;
     58  }
     59 
     60  aViewBox->x = vals[0];
     61  aViewBox->y = vals[1];
     62  aViewBox->width = vals[2];
     63  aViewBox->height = vals[3];
     64  aViewBox->none = false;
     65 
     66  return NS_OK;
     67 }
     68 
     69 constinit static SVGAttrTearoffTable<SVGAnimatedViewBox, SVGRect>
     70    sBaseSVGViewBoxTearoffTable;
     71 constinit static SVGAttrTearoffTable<SVGAnimatedViewBox, SVGRect>
     72    sAnimSVGViewBoxTearoffTable;
     73 constinit SVGAttrTearoffTable<SVGAnimatedViewBox, SVGAnimatedRect>
     74    SVGAnimatedViewBox::sSVGAnimatedRectTearoffTable;
     75 
     76 //----------------------------------------------------------------------
     77 // Helper class: AutoChangeViewBoxNotifier
     78 // Stack-based helper class to pair calls to WillChangeViewBox and
     79 // DidChangeViewBox.
     80 class MOZ_RAII AutoChangeViewBoxNotifier {
     81 public:
     82  AutoChangeViewBoxNotifier(SVGAnimatedViewBox* aViewBox,
     83                            SVGElement* aSVGElement, bool aDoSetAttr = true)
     84      : mViewBox(aViewBox), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
     85    MOZ_ASSERT(mViewBox, "Expecting non-null viewBox");
     86    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
     87 
     88    if (mDoSetAttr) {
     89      mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
     90      mSVGElement->WillChangeViewBox(mUpdateBatch.ref());
     91    }
     92  }
     93 
     94  ~AutoChangeViewBoxNotifier() {
     95    if (mDoSetAttr) {
     96      mSVGElement->DidChangeViewBox(mUpdateBatch.ref());
     97    }
     98    if (mViewBox->mAnimVal) {
     99      mSVGElement->AnimationNeedsResample();
    100    }
    101  }
    102 
    103 private:
    104  SVGAnimatedViewBox* const mViewBox;
    105  SVGElement* const mSVGElement;
    106  Maybe<mozAutoDocUpdate> mUpdateBatch;
    107  bool mDoSetAttr;
    108 };
    109 
    110 /* Implementation of SVGAnimatedViewBox methods */
    111 
    112 void SVGAnimatedViewBox::Init() {
    113  mHasBaseVal = false;
    114  // We shouldn't use mBaseVal for rendering (its usages should be guarded with
    115  // "mHasBaseVal" checks), but just in case we do by accident, this will
    116  // ensure that we treat it as "none" and ignore its numeric values:
    117  mBaseVal.none = true;
    118 
    119  mAnimVal = nullptr;
    120 }
    121 
    122 bool SVGAnimatedViewBox::HasRect() const {
    123  // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one;
    124  // otherwise, just return false (we clearly do not have a rect).
    125  const SVGViewBox* rect = mAnimVal.get();
    126  if (!rect) {
    127    if (!mHasBaseVal) {
    128      // no anim val, no base val --> no viewbox rect
    129      return false;
    130    }
    131    rect = &mBaseVal;
    132  }
    133 
    134  return !rect->none && rect->width >= 0 && rect->height >= 0;
    135 }
    136 
    137 void SVGAnimatedViewBox::SetAnimValue(const SVGViewBox& aRect,
    138                                      SVGElement* aSVGElement) {
    139  if (!mAnimVal) {
    140    // it's okay if allocation fails - and no point in reporting that
    141    mAnimVal = MakeUnique<SVGViewBox>(aRect);
    142  } else {
    143    if (aRect == *mAnimVal) {
    144      return;
    145    }
    146    *mAnimVal = aRect;
    147  }
    148  aSVGElement->DidAnimateViewBox();
    149 }
    150 
    151 void SVGAnimatedViewBox::SetBaseField(float aValue, SVGElement* aSVGElement,
    152                                      float& aField) {
    153  if (!mHasBaseVal) {
    154    aField = aValue;
    155    return;
    156  }
    157  if (aField == aValue) {
    158    return;
    159  }
    160  AutoChangeViewBoxNotifier notifier(this, aSVGElement);
    161  aField = aValue;
    162 }
    163 
    164 void SVGAnimatedViewBox::SetBaseValue(const SVGViewBox& aRect,
    165                                      SVGElement* aSVGElement,
    166                                      bool aDoSetAttr) {
    167  // Comparison against mBaseVal is only valid if we currently have a base val.
    168  if (mHasBaseVal && mBaseVal == aRect) {
    169    return;
    170  }
    171 
    172  AutoChangeViewBoxNotifier notifier(this, aSVGElement, aDoSetAttr);
    173 
    174  mBaseVal = aRect;
    175  mHasBaseVal = true;
    176 }
    177 
    178 nsresult SVGAnimatedViewBox::SetBaseValueString(const nsAString& aValue,
    179                                                SVGElement* aSVGElement,
    180                                                bool aDoSetAttr) {
    181  SVGViewBox viewBox;
    182 
    183  nsresult rv = SVGViewBox::FromString(aValue, &viewBox);
    184  if (NS_FAILED(rv)) {
    185    return rv;
    186  }
    187  SetBaseValue(viewBox, aSVGElement, aDoSetAttr);
    188  return NS_OK;
    189 }
    190 
    191 void SVGAnimatedViewBox::GetBaseValueString(nsAString& aValue) const {
    192  if (mBaseVal.none) {
    193    aValue.AssignLiteral("none");
    194    return;
    195  }
    196  nsTextFormatter::ssprintf(aValue, u"%g %g %g %g", (double)mBaseVal.x,
    197                            (double)mBaseVal.y, (double)mBaseVal.width,
    198                            (double)mBaseVal.height);
    199 }
    200 
    201 already_AddRefed<SVGAnimatedRect> SVGAnimatedViewBox::ToSVGAnimatedRect(
    202    SVGElement* aSVGElement) {
    203  RefPtr<SVGAnimatedRect> domAnimatedRect =
    204      sSVGAnimatedRectTearoffTable.GetTearoff(this);
    205  if (!domAnimatedRect) {
    206    domAnimatedRect = new SVGAnimatedRect(this, aSVGElement);
    207    sSVGAnimatedRectTearoffTable.AddTearoff(this, domAnimatedRect);
    208  }
    209 
    210  return domAnimatedRect.forget();
    211 }
    212 
    213 already_AddRefed<SVGRect> SVGAnimatedViewBox::ToDOMBaseVal(
    214    SVGElement* aSVGElement) {
    215  if (!mHasBaseVal || mBaseVal.none) {
    216    return nullptr;
    217  }
    218 
    219  RefPtr<SVGRect> domBaseVal = sBaseSVGViewBoxTearoffTable.GetTearoff(this);
    220  if (!domBaseVal) {
    221    domBaseVal = new SVGRect(this, aSVGElement, SVGRect::RectType::BaseValue);
    222    sBaseSVGViewBoxTearoffTable.AddTearoff(this, domBaseVal);
    223  }
    224 
    225  return domBaseVal.forget();
    226 }
    227 
    228 SVGRect::~SVGRect() {
    229  switch (mType) {
    230    case RectType::BaseValue:
    231      sBaseSVGViewBoxTearoffTable.RemoveTearoff(mVal);
    232      break;
    233    case RectType::AnimValue:
    234      sAnimSVGViewBoxTearoffTable.RemoveTearoff(mVal);
    235      break;
    236    default:
    237      break;
    238  }
    239 }
    240 
    241 already_AddRefed<SVGRect> SVGAnimatedViewBox::ToDOMAnimVal(
    242    SVGElement* aSVGElement) {
    243  if ((mAnimVal && mAnimVal->none) ||
    244      (!mAnimVal && (!mHasBaseVal || mBaseVal.none))) {
    245    return nullptr;
    246  }
    247 
    248  RefPtr<SVGRect> domAnimVal = sAnimSVGViewBoxTearoffTable.GetTearoff(this);
    249  if (!domAnimVal) {
    250    domAnimVal = new SVGRect(this, aSVGElement, SVGRect::RectType::AnimValue);
    251    sAnimSVGViewBoxTearoffTable.AddTearoff(this, domAnimVal);
    252  }
    253 
    254  return domAnimVal.forget();
    255 }
    256 
    257 UniquePtr<SMILAttr> SVGAnimatedViewBox::ToSMILAttr(SVGElement* aSVGElement) {
    258  return MakeUnique<SMILViewBox>(this, aSVGElement);
    259 }
    260 
    261 nsresult SVGAnimatedViewBox::SMILViewBox ::ValueFromString(
    262    const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
    263    SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
    264  SVGViewBox viewBox;
    265  nsresult res = SVGViewBox::FromString(aStr, &viewBox);
    266  if (NS_FAILED(res)) {
    267    return res;
    268  }
    269  SMILValue val(&SVGViewBoxSMILType::sSingleton);
    270  *static_cast<SVGViewBox*>(val.mU.mPtr) = viewBox;
    271  aValue = std::move(val);
    272 
    273  return NS_OK;
    274 }
    275 
    276 SMILValue SVGAnimatedViewBox::SMILViewBox::GetBaseValue() const {
    277  SMILValue val(&SVGViewBoxSMILType::sSingleton);
    278  *static_cast<SVGViewBox*>(val.mU.mPtr) = mVal->mBaseVal;
    279  return val;
    280 }
    281 
    282 void SVGAnimatedViewBox::SMILViewBox::ClearAnimValue() {
    283  if (mVal->mAnimVal) {
    284    mVal->mAnimVal = nullptr;
    285    mSVGElement->DidAnimateViewBox();
    286  }
    287 }
    288 
    289 nsresult SVGAnimatedViewBox::SMILViewBox::SetAnimValue(
    290    const SMILValue& aValue) {
    291  NS_ASSERTION(aValue.mType == &SVGViewBoxSMILType::sSingleton,
    292               "Unexpected type to assign animated value");
    293  if (aValue.mType == &SVGViewBoxSMILType::sSingleton) {
    294    SVGViewBox& vb = *static_cast<SVGViewBox*>(aValue.mU.mPtr);
    295    mVal->SetAnimValue(vb, mSVGElement);
    296  }
    297  return NS_OK;
    298 }
    299 
    300 }  // namespace mozilla