tor-browser

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

DOMSVGTransform.cpp (9634B)


      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 "DOMSVGTransform.h"
      8 
      9 #include "SVGAnimatedTransformList.h"
     10 #include "SVGAttrTearoffTable.h"
     11 #include "mozAutoDocUpdate.h"
     12 #include "mozilla/DebugOnly.h"
     13 #include "mozilla/dom/DOMMatrix.h"
     14 #include "mozilla/dom/DOMMatrixBinding.h"
     15 #include "mozilla/dom/SVGMatrix.h"
     16 #include "mozilla/dom/SVGTransformBinding.h"
     17 #include "nsError.h"
     18 
     19 namespace {
     20 const double kRadPerDegree = 2.0 * M_PI / 360.0;
     21 }  // namespace
     22 
     23 namespace mozilla::dom {
     24 
     25 using namespace SVGTransform_Binding;
     26 
     27 static SVGAttrTearoffTable<DOMSVGTransform, SVGMatrix>&
     28 SVGMatrixTearoffTable() {
     29  static SVGAttrTearoffTable<DOMSVGTransform, SVGMatrix> sSVGMatrixTearoffTable;
     30  return sSVGMatrixTearoffTable;
     31 }
     32 
     33 //----------------------------------------------------------------------
     34 
     35 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
     36 // clear our list's weak ref to us to be safe. (The other option would be to
     37 // not unlink and rely on the breaking of the other edges in the cycle, as
     38 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
     39 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGTransform)
     40 
     41 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransform)
     42  // We may not belong to a list, so we must null check tmp->mList.
     43  if (tmp->mList) {
     44    tmp->mList->mItems[tmp->mListIndex] = nullptr;
     45  }
     46  NS_IMPL_CYCLE_COLLECTION_UNLINK(mList)
     47  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     48 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     49 
     50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransform)
     51  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList)
     52  SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(tmp);
     53  CycleCollectionNoteChild(cb, matrix, "matrix");
     54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     55 
     56 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGTransform)
     57  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     58 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     59 
     60 JSObject* DOMSVGTransform::WrapObject(JSContext* aCx,
     61                                      JS::Handle<JSObject*> aGivenProto) {
     62  return SVGTransform_Binding::Wrap(aCx, this, aGivenProto);
     63 }
     64 
     65 //----------------------------------------------------------------------
     66 // Ctors:
     67 
     68 DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList* aList,
     69                                 uint32_t aListIndex, bool aIsAnimValItem)
     70    : mList(aList),
     71      mListIndex(aListIndex),
     72      mIsAnimValItem(aIsAnimValItem),
     73      mTransform(nullptr) {
     74  // These shifts are in sync with the members in the header.
     75  MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg");
     76 
     77  MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
     78 }
     79 
     80 DOMSVGTransform::DOMSVGTransform()
     81    : mList(nullptr),
     82      mListIndex(0),
     83      mIsAnimValItem(false),
     84      mTransform(new SVGTransform())  // Default ctor for objects not in a
     85                                      // list initialises to matrix type with
     86                                      // identity matrix
     87 {}
     88 
     89 DOMSVGTransform::DOMSVGTransform(const gfxMatrix& aMatrix)
     90    : mList(nullptr),
     91      mListIndex(0),
     92      mIsAnimValItem(false),
     93      mTransform(new SVGTransform(aMatrix)) {}
     94 
     95 DOMSVGTransform::DOMSVGTransform(const DOMMatrix2DInit& aMatrix,
     96                                 ErrorResult& aRv)
     97    : mList(nullptr),
     98      mListIndex(0),
     99      mIsAnimValItem(false),
    100      mTransform(new SVGTransform()) {
    101  SetMatrix(aMatrix, aRv);
    102 }
    103 
    104 DOMSVGTransform::DOMSVGTransform(const SVGTransform& aTransform)
    105    : mList(nullptr),
    106      mListIndex(0),
    107      mIsAnimValItem(false),
    108      mTransform(new SVGTransform(aTransform)) {}
    109 
    110 DOMSVGTransform::~DOMSVGTransform() {
    111  SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(this);
    112  if (matrix) {
    113    SVGMatrixTearoffTable().RemoveTearoff(this);
    114    NS_RELEASE(matrix);
    115  }
    116  // Our mList's weak ref to us must be nulled out when we die. If GC has
    117  // unlinked us using the cycle collector code, then that has already
    118  // happened, and mList is null.
    119  if (mList) {
    120    mList->mItems[mListIndex] = nullptr;
    121  }
    122 }
    123 
    124 uint16_t DOMSVGTransform::Type() const { return Transform().Type(); }
    125 
    126 SVGMatrix* DOMSVGTransform::GetMatrix() {
    127  SVGMatrix* wrapper = SVGMatrixTearoffTable().GetTearoff(this);
    128  if (!wrapper) {
    129    NS_ADDREF(wrapper = new SVGMatrix(*this));
    130    SVGMatrixTearoffTable().AddTearoff(this, wrapper);
    131  }
    132  return wrapper;
    133 }
    134 
    135 float DOMSVGTransform::Angle() const { return Transform().Angle(); }
    136 
    137 void DOMSVGTransform::SetMatrix(const DOMMatrix2DInit& aMatrix,
    138                                ErrorResult& aRv) {
    139  if (mIsAnimValItem) {
    140    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    141    return;
    142  }
    143  auto matrix2D = DOMMatrixReadOnly::ToValidatedMatrixDouble(aMatrix, aRv);
    144  if (aRv.Failed()) {
    145    return;
    146  }
    147  if (!matrix2D.IsFinite()) {
    148    aRv.ThrowTypeError<MSG_NOT_FINITE>("Matrix setter");
    149    return;
    150  }
    151  SetMatrix(matrix2D);
    152 }
    153 
    154 void DOMSVGTransform::SetTranslate(float tx, float ty, ErrorResult& aRv) {
    155  if (mIsAnimValItem) {
    156    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    157    return;
    158  }
    159 
    160  if (Transform().Type() == SVG_TRANSFORM_TRANSLATE && Matrixgfx()._31 == tx &&
    161      Matrixgfx()._32 == ty) {
    162    return;
    163  }
    164 
    165  AutoChangeTransformListNotifier notifier(this);
    166  Transform().SetTranslate(tx, ty);
    167 }
    168 
    169 void DOMSVGTransform::SetScale(float sx, float sy, ErrorResult& aRv) {
    170  if (mIsAnimValItem) {
    171    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    172    return;
    173  }
    174 
    175  if (Transform().Type() == SVG_TRANSFORM_SCALE && Matrixgfx()._11 == sx &&
    176      Matrixgfx()._22 == sy) {
    177    return;
    178  }
    179  AutoChangeTransformListNotifier notifier(this);
    180  Transform().SetScale(sx, sy);
    181 }
    182 
    183 void DOMSVGTransform::SetRotate(float angle, float cx, float cy,
    184                                ErrorResult& aRv) {
    185  if (mIsAnimValItem) {
    186    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    187    return;
    188  }
    189 
    190  if (Transform().Type() == SVG_TRANSFORM_ROTATE) {
    191    float currentCx, currentCy;
    192    Transform().GetRotationOrigin(currentCx, currentCy);
    193    if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
    194      return;
    195    }
    196  }
    197 
    198  AutoChangeTransformListNotifier notifier(this);
    199  Transform().SetRotate(angle, cx, cy);
    200 }
    201 
    202 void DOMSVGTransform::SetSkewX(float angle, ErrorResult& aRv) {
    203  if (mIsAnimValItem) {
    204    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    205    return;
    206  }
    207 
    208  if (Transform().Type() == SVG_TRANSFORM_SKEWX &&
    209      Transform().Angle() == angle) {
    210    return;
    211  }
    212 
    213  if (!std::isfinite(tan(angle * kRadPerDegree))) {
    214    aRv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
    215    return;
    216  }
    217 
    218  AutoChangeTransformListNotifier notifier(this);
    219  DebugOnly<nsresult> result = Transform().SetSkewX(angle);
    220  MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewX unexpectedly failed");
    221 }
    222 
    223 void DOMSVGTransform::SetSkewY(float angle, ErrorResult& aRv) {
    224  if (mIsAnimValItem) {
    225    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    226    return;
    227  }
    228 
    229  if (Transform().Type() == SVG_TRANSFORM_SKEWY &&
    230      Transform().Angle() == angle) {
    231    return;
    232  }
    233 
    234  if (!std::isfinite(tan(angle * kRadPerDegree))) {
    235    aRv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
    236    return;
    237  }
    238 
    239  AutoChangeTransformListNotifier notifier(this);
    240  DebugOnly<nsresult> result = Transform().SetSkewY(angle);
    241  MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewY unexpectedly failed");
    242 }
    243 
    244 //----------------------------------------------------------------------
    245 // List management methods:
    246 
    247 void DOMSVGTransform::InsertingIntoList(DOMSVGTransformList* aList,
    248                                        uint32_t aListIndex,
    249                                        bool aIsAnimValItem) {
    250  MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list");
    251 
    252  mList = aList;
    253  mListIndex = aListIndex;
    254  mIsAnimValItem = aIsAnimValItem;
    255  mTransform = nullptr;
    256 
    257  MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
    258 }
    259 
    260 void DOMSVGTransform::RemovingFromList() {
    261  MOZ_ASSERT(!mTransform,
    262             "Item in list also has another non-list value associated with it");
    263 
    264  mTransform = MakeUnique<SVGTransform>(InternalItem());
    265  mList = nullptr;
    266  mIsAnimValItem = false;
    267 }
    268 
    269 SVGTransform& DOMSVGTransform::InternalItem() {
    270  SVGAnimatedTransformList* alist = Element()->GetAnimatedTransformList();
    271  return mIsAnimValItem && alist->mAnimVal ? (*alist->mAnimVal)[mListIndex]
    272                                           : alist->mBaseVal[mListIndex];
    273 }
    274 
    275 const SVGTransform& DOMSVGTransform::InternalItem() const {
    276  return const_cast<DOMSVGTransform*>(this)->InternalItem();
    277 }
    278 
    279 #ifdef DEBUG
    280 bool DOMSVGTransform::IndexIsValid() {
    281  SVGAnimatedTransformList* alist = Element()->GetAnimatedTransformList();
    282  return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) ||
    283         (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length());
    284 }
    285 #endif  // DEBUG
    286 
    287 //----------------------------------------------------------------------
    288 // Interface for SVGMatrix's use
    289 
    290 void DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix) {
    291  MOZ_ASSERT(!mIsAnimValItem, "Attempting to modify read-only transform");
    292 
    293  if (Transform().Type() == SVG_TRANSFORM_MATRIX &&
    294      SVGTransform::MatricesEqual(Matrixgfx(), aMatrix)) {
    295    return;
    296  }
    297 
    298  AutoChangeTransformListNotifier notifier(this);
    299  Transform().SetMatrix(aMatrix);
    300 }
    301 
    302 }  // namespace mozilla::dom