123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "SVGTransform.h"
- #include "mozilla/dom/SVGTransform.h"
- #include "mozilla/dom/SVGMatrix.h"
- #include "mozilla/dom/SVGTransformBinding.h"
- #include "nsError.h"
- #include "nsSVGAnimatedTransformList.h"
- #include "nsSVGAttrTearoffTable.h"
- #include "mozilla/DebugOnly.h"
- #include "mozilla/FloatingPoint.h"
- namespace {
- const double kRadPerDegree = 2.0 * M_PI / 360.0;
- } // namespace
- namespace mozilla {
- namespace dom {
- static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix>&
- SVGMatrixTearoffTable()
- {
- static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix> sSVGMatrixTearoffTable;
- return sSVGMatrixTearoffTable;
- }
- //----------------------------------------------------------------------
- // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
- // clear our list's weak ref to us to be safe. (The other option would be to
- // not unlink and rely on the breaking of the other edges in the cycle, as
- // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
- NS_IMPL_CYCLE_COLLECTION_CLASS(SVGTransform)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGTransform)
- // We may not belong to a list, so we must null check tmp->mList.
- if (tmp->mList) {
- tmp->mList->mItems[tmp->mListIndex] = nullptr;
- }
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mList)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGTransform)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList)
- SVGMatrix* matrix =
- SVGMatrixTearoffTable().GetTearoff(tmp);
- CycleCollectionNoteChild(cb, matrix, "matrix");
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SVGTransform)
- NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
- NS_IMPL_CYCLE_COLLECTION_TRACE_END
- NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGTransform, AddRef)
- NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGTransform, Release)
- JSObject*
- SVGTransform::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return SVGTransformBinding::Wrap(aCx, this, aGivenProto);
- }
- //----------------------------------------------------------------------
- // Helper class: AutoChangeTransformNotifier
- // Stack-based helper class to pair calls to WillChangeTransformList
- // and DidChangeTransformList.
- class MOZ_RAII AutoChangeTransformNotifier
- {
- public:
- explicit AutoChangeTransformNotifier(SVGTransform* aTransform MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
- : mTransform(aTransform)
- {
- MOZ_GUARD_OBJECT_NOTIFIER_INIT;
- MOZ_ASSERT(mTransform, "Expecting non-null transform");
- if (mTransform->HasOwner()) {
- mEmptyOrOldValue =
- mTransform->Element()->WillChangeTransformList();
- }
- }
- ~AutoChangeTransformNotifier()
- {
- if (mTransform->HasOwner()) {
- mTransform->Element()->DidChangeTransformList(mEmptyOrOldValue);
- if (mTransform->mList->IsAnimating()) {
- mTransform->Element()->AnimationNeedsResample();
- }
- }
- }
- private:
- SVGTransform* const mTransform;
- nsAttrValue mEmptyOrOldValue;
- MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
- };
- //----------------------------------------------------------------------
- // Ctors:
- SVGTransform::SVGTransform(DOMSVGTransformList *aList,
- uint32_t aListIndex,
- bool aIsAnimValItem)
- : mList(aList)
- , mListIndex(aListIndex)
- , mIsAnimValItem(aIsAnimValItem)
- , mTransform(nullptr)
- {
- // These shifts are in sync with the members in the header.
- MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg");
- MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
- }
- SVGTransform::SVGTransform()
- : mList(nullptr)
- , mListIndex(0)
- , mIsAnimValItem(false)
- , mTransform(new nsSVGTransform()) // Default ctor for objects not in a list
- // initialises to matrix type with identity
- // matrix
- {
- }
- SVGTransform::SVGTransform(const gfxMatrix &aMatrix)
- : mList(nullptr)
- , mListIndex(0)
- , mIsAnimValItem(false)
- , mTransform(new nsSVGTransform(aMatrix))
- {
- }
- SVGTransform::SVGTransform(const nsSVGTransform &aTransform)
- : mList(nullptr)
- , mListIndex(0)
- , mIsAnimValItem(false)
- , mTransform(new nsSVGTransform(aTransform))
- {
- }
- SVGTransform::~SVGTransform()
- {
- SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(this);
- if (matrix) {
- SVGMatrixTearoffTable().RemoveTearoff(this);
- NS_RELEASE(matrix);
- }
- // Our mList's weak ref to us must be nulled out when we die. If GC has
- // unlinked us using the cycle collector code, then that has already
- // happened, and mList is null.
- if (mList) {
- mList->mItems[mListIndex] = nullptr;
- }
- }
- uint16_t
- SVGTransform::Type() const
- {
- return Transform().Type();
- }
- SVGMatrix*
- SVGTransform::GetMatrix()
- {
- SVGMatrix* wrapper =
- SVGMatrixTearoffTable().GetTearoff(this);
- if (!wrapper) {
- NS_ADDREF(wrapper = new SVGMatrix(*this));
- SVGMatrixTearoffTable().AddTearoff(this, wrapper);
- }
- return wrapper;
- }
- float
- SVGTransform::Angle() const
- {
- return Transform().Angle();
- }
- void
- SVGTransform::SetMatrix(SVGMatrix& aMatrix, ErrorResult& rv)
- {
- if (mIsAnimValItem) {
- rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
- return;
- }
- SetMatrix(aMatrix.GetMatrix());
- }
- void
- SVGTransform::SetTranslate(float tx, float ty, ErrorResult& rv)
- {
- if (mIsAnimValItem) {
- rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
- return;
- }
- if (Transform().Type() == SVG_TRANSFORM_TRANSLATE &&
- Matrixgfx()._31 == tx && Matrixgfx()._32 == ty) {
- return;
- }
- AutoChangeTransformNotifier notifier(this);
- Transform().SetTranslate(tx, ty);
- }
- void
- SVGTransform::SetScale(float sx, float sy, ErrorResult& rv)
- {
- if (mIsAnimValItem) {
- rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
- return;
- }
- if (Transform().Type() == SVG_TRANSFORM_SCALE &&
- Matrixgfx()._11 == sx && Matrixgfx()._22 == sy) {
- return;
- }
- AutoChangeTransformNotifier notifier(this);
- Transform().SetScale(sx, sy);
- }
- void
- SVGTransform::SetRotate(float angle, float cx, float cy, ErrorResult& rv)
- {
- if (mIsAnimValItem) {
- rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
- return;
- }
- if (Transform().Type() == SVG_TRANSFORM_ROTATE) {
- float currentCx, currentCy;
- Transform().GetRotationOrigin(currentCx, currentCy);
- if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
- return;
- }
- }
- AutoChangeTransformNotifier notifier(this);
- Transform().SetRotate(angle, cx, cy);
- }
- void
- SVGTransform::SetSkewX(float angle, ErrorResult& rv)
- {
- if (mIsAnimValItem) {
- rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
- return;
- }
- if (Transform().Type() == SVG_TRANSFORM_SKEWX &&
- Transform().Angle() == angle) {
- return;
- }
- if (!IsFinite(tan(angle * kRadPerDegree))) {
- rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
- return;
- }
- AutoChangeTransformNotifier notifier(this);
- DebugOnly<nsresult> result = Transform().SetSkewX(angle);
- MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewX unexpectedly failed");
- }
- void
- SVGTransform::SetSkewY(float angle, ErrorResult& rv)
- {
- if (mIsAnimValItem) {
- rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
- return;
- }
- if (Transform().Type() == SVG_TRANSFORM_SKEWY &&
- Transform().Angle() == angle) {
- return;
- }
- if (!IsFinite(tan(angle * kRadPerDegree))) {
- rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
- return;
- }
- AutoChangeTransformNotifier notifier(this);
- DebugOnly<nsresult> result = Transform().SetSkewY(angle);
- MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewY unexpectedly failed");
- }
- //----------------------------------------------------------------------
- // List management methods:
- void
- SVGTransform::InsertingIntoList(DOMSVGTransformList *aList,
- uint32_t aListIndex,
- bool aIsAnimValItem)
- {
- MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list");
- mList = aList;
- mListIndex = aListIndex;
- mIsAnimValItem = aIsAnimValItem;
- mTransform = nullptr;
- MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
- }
- void
- SVGTransform::RemovingFromList()
- {
- MOZ_ASSERT(!mTransform,
- "Item in list also has another non-list value associated with it");
- mTransform = new nsSVGTransform(InternalItem());
- mList = nullptr;
- mIsAnimValItem = false;
- }
- nsSVGTransform&
- SVGTransform::InternalItem()
- {
- nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
- return mIsAnimValItem && alist->mAnimVal ?
- (*alist->mAnimVal)[mListIndex] :
- alist->mBaseVal[mListIndex];
- }
- const nsSVGTransform&
- SVGTransform::InternalItem() const
- {
- return const_cast<SVGTransform*>(this)->InternalItem();
- }
- #ifdef DEBUG
- bool
- SVGTransform::IndexIsValid()
- {
- nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
- return (mIsAnimValItem &&
- mListIndex < alist->GetAnimValue().Length()) ||
- (!mIsAnimValItem &&
- mListIndex < alist->GetBaseValue().Length());
- }
- #endif // DEBUG
- //----------------------------------------------------------------------
- // Interface for SVGMatrix's use
- void
- SVGTransform::SetMatrix(const gfxMatrix& aMatrix)
- {
- MOZ_ASSERT(!mIsAnimValItem,
- "Attempting to modify read-only transform");
- if (Transform().Type() == SVG_TRANSFORM_MATRIX &&
- nsSVGTransform::MatricesEqual(Matrixgfx(), aMatrix)) {
- return;
- }
- AutoChangeTransformNotifier notifier(this);
- Transform().SetMatrix(aMatrix);
- }
- } // namespace dom
- } // namespace mozilla
|