LCOV - code coverage report
Current view: top level - dom/svg - DOMSVGTransformList.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 190 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "DOMSVGTransformList.h"
       8             : #include "mozilla/dom/SVGTransform.h"
       9             : #include "mozilla/dom/SVGMatrix.h"
      10             : #include "nsSVGAnimatedTransformList.h"
      11             : #include "nsSVGElement.h"
      12             : #include "mozilla/dom/SVGTransformListBinding.h"
      13             : #include "nsError.h"
      14             : #include <algorithm>
      15             : 
      16             : // local helper functions
      17             : namespace {
      18             : 
      19           0 : void UpdateListIndicesFromIndex(
      20             :   FallibleTArray<mozilla::dom::SVGTransform*>& aItemsArray,
      21             :   uint32_t aStartingIndex)
      22             : {
      23           0 :   uint32_t length = aItemsArray.Length();
      24             : 
      25           0 :   for (uint32_t i = aStartingIndex; i < length; ++i) {
      26           0 :     if (aItemsArray[i]) {
      27           0 :       aItemsArray[i]->UpdateListIndex(i);
      28             :     }
      29             :   }
      30           0 : }
      31             : 
      32             : } // namespace
      33             : 
      34             : namespace mozilla {
      35             : 
      36             : using namespace dom;
      37             : 
      38             : // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
      39             : // clear our SVGAnimatedTransformList's weak ref to us to be safe. (The other
      40             : // option would be to not unlink and rely on the breaking of the other edges in
      41             : // the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
      42             : NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGTransformList)
      43             : 
      44           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransformList)
      45           0 :   if (tmp->mAList) {
      46           0 :     if (tmp->IsAnimValList()) {
      47           0 :       tmp->mAList->mAnimVal = nullptr;
      48             :     } else {
      49           0 :       tmp->mAList->mBaseVal = nullptr;
      50             :     }
      51           0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK(mAList)
      52             :   }
      53           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
      54           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      55           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransformList)
      56           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAList)
      57           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      58           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGTransformList)
      59           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
      60           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
      61             : 
      62           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGTransformList)
      63           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGTransformList)
      64             : 
      65           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTransformList)
      66           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
      67           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
      68           0 : NS_INTERFACE_MAP_END
      69             : 
      70             : //----------------------------------------------------------------------
      71             : // DOMSVGTransformList methods:
      72             : 
      73             : JSObject*
      74           0 : DOMSVGTransformList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
      75             : {
      76           0 :   return mozilla::dom::SVGTransformListBinding::Wrap(cx, this, aGivenProto);
      77             : }
      78             : 
      79             : //----------------------------------------------------------------------
      80             : // Helper class: AutoChangeTransformListNotifier
      81             : // Stack-based helper class to pair calls to WillChangeTransformList and
      82             : // DidChangeTransformList.
      83             : class MOZ_RAII AutoChangeTransformListNotifier
      84             : {
      85             : public:
      86           0 :   explicit AutoChangeTransformListNotifier(DOMSVGTransformList* aTransformList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
      87           0 :     : mTransformList(aTransformList)
      88             :   {
      89           0 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
      90           0 :     MOZ_ASSERT(mTransformList, "Expecting non-null transformList");
      91             :     mEmptyOrOldValue =
      92           0 :       mTransformList->Element()->WillChangeTransformList();
      93           0 :   }
      94             : 
      95           0 :   ~AutoChangeTransformListNotifier()
      96           0 :   {
      97           0 :     mTransformList->Element()->DidChangeTransformList(mEmptyOrOldValue);
      98           0 :     if (mTransformList->IsAnimating()) {
      99           0 :       mTransformList->Element()->AnimationNeedsResample();
     100             :     }
     101           0 :   }
     102             : 
     103             : private:
     104             :   DOMSVGTransformList* const mTransformList;
     105             :   nsAttrValue          mEmptyOrOldValue;
     106             :   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     107             : };
     108             : 
     109             : void
     110           0 : DOMSVGTransformList::InternalListLengthWillChange(uint32_t aNewLength)
     111             : {
     112           0 :   uint32_t oldLength = mItems.Length();
     113             : 
     114           0 :   if (aNewLength > SVGTransform::MaxListIndex()) {
     115             :     // It's safe to get out of sync with our internal list as long as we have
     116             :     // FEWER items than it does.
     117           0 :     aNewLength = SVGTransform::MaxListIndex();
     118             :   }
     119             : 
     120           0 :   RefPtr<DOMSVGTransformList> kungFuDeathGrip;
     121           0 :   if (aNewLength < oldLength) {
     122             :     // RemovingFromList() might clear last reference to |this|.
     123             :     // Retain a temporary reference to keep from dying before returning.
     124           0 :     kungFuDeathGrip = this;
     125             :   }
     126             : 
     127             :   // If our length will decrease, notify the items that will be removed:
     128           0 :   for (uint32_t i = aNewLength; i < oldLength; ++i) {
     129           0 :     if (mItems[i]) {
     130           0 :       mItems[i]->RemovingFromList();
     131             :     }
     132             :   }
     133             : 
     134           0 :   if (!mItems.SetLength(aNewLength, fallible)) {
     135             :     // We silently ignore SetLength OOM failure since being out of sync is safe
     136             :     // so long as we have *fewer* items than our internal list.
     137           0 :     mItems.Clear();
     138           0 :     return;
     139             :   }
     140             : 
     141             :   // If our length has increased, null out the new pointers:
     142           0 :   for (uint32_t i = oldLength; i < aNewLength; ++i) {
     143           0 :     mItems[i] = nullptr;
     144             :   }
     145             : }
     146             : 
     147             : SVGTransformList&
     148           0 : DOMSVGTransformList::InternalList() const
     149             : {
     150           0 :   nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
     151           0 :   return IsAnimValList() && alist->mAnimVal ?
     152           0 :     *alist->mAnimVal :
     153           0 :     alist->mBaseVal;
     154             : }
     155             : 
     156             : //----------------------------------------------------------------------
     157             : void
     158           0 : DOMSVGTransformList::Clear(ErrorResult& error)
     159             : {
     160           0 :   if (IsAnimValList()) {
     161           0 :     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     162           0 :     return;
     163             :   }
     164             : 
     165           0 :   if (LengthNoFlush() > 0) {
     166           0 :     AutoChangeTransformListNotifier notifier(this);
     167             :     // Notify any existing DOM items of removal *before* truncating the lists
     168             :     // so that they can find their SVGTransform internal counterparts and copy
     169             :     // their values. This also notifies the animVal list:
     170           0 :     mAList->InternalBaseValListWillChangeLengthTo(0);
     171             : 
     172           0 :     mItems.Clear();
     173           0 :     InternalList().Clear();
     174             :   }
     175             : }
     176             : 
     177             : already_AddRefed<SVGTransform>
     178           0 : DOMSVGTransformList::Initialize(SVGTransform& newItem, ErrorResult& error)
     179             : {
     180           0 :   if (IsAnimValList()) {
     181           0 :     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     182           0 :     return nullptr;
     183             :   }
     184             : 
     185             :   // If newItem is already in a list we should insert a clone of newItem, and
     186             :   // for consistency, this should happen even if *this* is the list that
     187             :   // newItem is currently in. Note that in the case of newItem being in this
     188             :   // list, the Clear() call before the InsertItemBefore() call would remove it
     189             :   // from this list, and so the InsertItemBefore() call would not insert a
     190             :   // clone of newItem, it would actually insert newItem. To prevent that from
     191             :   // happening we have to do the clone here, if necessary.
     192             : 
     193           0 :   RefPtr<SVGTransform> domItem = &newItem;
     194           0 :   if (domItem->HasOwner()) {
     195           0 :     domItem = newItem.Clone();
     196             :   }
     197             : 
     198           0 :   Clear(error);
     199           0 :   MOZ_ASSERT(!error.Failed(), "How could this fail?");
     200           0 :   return InsertItemBefore(*domItem, 0, error);
     201             : }
     202             : 
     203             : already_AddRefed<SVGTransform>
     204           0 : DOMSVGTransformList::GetItem(uint32_t index, ErrorResult& error)
     205             : {
     206             :   bool found;
     207           0 :   RefPtr<SVGTransform> item = IndexedGetter(index, found, error);
     208           0 :   if (!found) {
     209           0 :     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     210             :   }
     211           0 :   return item.forget();
     212             : }
     213             : 
     214             : already_AddRefed<SVGTransform>
     215           0 : DOMSVGTransformList::IndexedGetter(uint32_t index, bool& found,
     216             :                                    ErrorResult& error)
     217             : {
     218           0 :   if (IsAnimValList()) {
     219           0 :     Element()->FlushAnimations();
     220             :   }
     221           0 :   found = index < LengthNoFlush();
     222           0 :   if (found) {
     223           0 :     return GetItemAt(index);
     224             :   }
     225           0 :   return nullptr;
     226             : }
     227             : 
     228             : already_AddRefed<SVGTransform>
     229           0 : DOMSVGTransformList::InsertItemBefore(SVGTransform& newItem,
     230             :                                       uint32_t index, ErrorResult& error)
     231             : {
     232           0 :   if (IsAnimValList()) {
     233           0 :     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     234           0 :     return nullptr;
     235             :   }
     236             : 
     237           0 :   index = std::min(index, LengthNoFlush());
     238           0 :   if (index >= SVGTransform::MaxListIndex()) {
     239           0 :     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     240           0 :     return nullptr;
     241             :   }
     242             : 
     243           0 :   RefPtr<SVGTransform> domItem = &newItem;
     244           0 :   if (newItem.HasOwner()) {
     245           0 :     domItem = newItem.Clone(); // must do this before changing anything!
     246             :   }
     247             : 
     248             :   // Ensure we have enough memory so we can avoid complex error handling below:
     249           0 :   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
     250           0 :       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     251           0 :     error.Throw(NS_ERROR_OUT_OF_MEMORY);
     252           0 :     return nullptr;
     253             :   }
     254           0 :   if (AnimListMirrorsBaseList()) {
     255           0 :     if (!mAList->mAnimVal->mItems.SetCapacity(
     256           0 :           mAList->mAnimVal->mItems.Length() + 1, fallible)) {
     257           0 :       error.Throw(NS_ERROR_OUT_OF_MEMORY);
     258           0 :       return nullptr;
     259             :     }
     260             :   }
     261             : 
     262           0 :   AutoChangeTransformListNotifier notifier(this);
     263             :   // Now that we know we're inserting, keep animVal list in sync as necessary.
     264           0 :   MaybeInsertNullInAnimValListAt(index);
     265             : 
     266           0 :   InternalList().InsertItem(index, domItem->ToSVGTransform());
     267           0 :   MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem.get(), fallible));
     268             : 
     269             :   // This MUST come after the insertion into InternalList(), or else under the
     270             :   // insertion into InternalList() the values read from domItem would be bad
     271             :   // data from InternalList() itself!:
     272           0 :   domItem->InsertingIntoList(this, index, IsAnimValList());
     273             : 
     274           0 :   UpdateListIndicesFromIndex(mItems, index + 1);
     275             : 
     276           0 :   return domItem.forget();
     277             : }
     278             : 
     279             : already_AddRefed<SVGTransform>
     280           0 : DOMSVGTransformList::ReplaceItem(SVGTransform& newItem,
     281             :                                  uint32_t index, ErrorResult& error)
     282             : {
     283           0 :   if (IsAnimValList()) {
     284           0 :     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     285           0 :     return nullptr;
     286             :   }
     287             : 
     288           0 :   if (index >= LengthNoFlush()) {
     289           0 :     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     290           0 :     return nullptr;
     291             :   }
     292             : 
     293           0 :   RefPtr<SVGTransform> domItem = &newItem;
     294           0 :   if (newItem.HasOwner()) {
     295           0 :     domItem = newItem.Clone(); // must do this before changing anything!
     296             :   }
     297             : 
     298           0 :   AutoChangeTransformListNotifier notifier(this);
     299           0 :   if (mItems[index]) {
     300             :     // Notify any existing DOM item of removal *before* modifying the lists so
     301             :     // that the DOM item can copy the *old* value at its index:
     302           0 :     mItems[index]->RemovingFromList();
     303             :   }
     304             : 
     305           0 :   InternalList()[index] = domItem->ToSVGTransform();
     306           0 :   mItems[index] = domItem;
     307             : 
     308             :   // This MUST come after the ToSVGPoint() call, otherwise that call
     309             :   // would end up reading bad data from InternalList()!
     310           0 :   domItem->InsertingIntoList(this, index, IsAnimValList());
     311             : 
     312           0 :   return domItem.forget();
     313             : }
     314             : 
     315             : already_AddRefed<SVGTransform>
     316           0 : DOMSVGTransformList::RemoveItem(uint32_t index, ErrorResult& error)
     317             : {
     318           0 :   if (IsAnimValList()) {
     319           0 :     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     320           0 :     return nullptr;
     321             :   }
     322             : 
     323           0 :   if (index >= LengthNoFlush()) {
     324           0 :     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     325           0 :     return nullptr;
     326             :   }
     327             : 
     328           0 :   AutoChangeTransformListNotifier notifier(this);
     329             :   // Now that we know we're removing, keep animVal list in sync as necessary.
     330             :   // Do this *before* touching InternalList() so the removed item can get its
     331             :   // internal value.
     332           0 :   MaybeRemoveItemFromAnimValListAt(index);
     333             : 
     334             :   // We have to return the removed item, so get it, creating it if necessary:
     335           0 :   RefPtr<SVGTransform> result = GetItemAt(index);
     336             : 
     337             :   // Notify the DOM item of removal *before* modifying the lists so that the
     338             :   // DOM item can copy its *old* value:
     339           0 :   result->RemovingFromList();
     340             : 
     341           0 :   InternalList().RemoveItem(index);
     342           0 :   mItems.RemoveElementAt(index);
     343             : 
     344           0 :   UpdateListIndicesFromIndex(mItems, index);
     345             : 
     346           0 :   return result.forget();
     347             : }
     348             : 
     349             : already_AddRefed<SVGTransform>
     350           0 : DOMSVGTransformList::CreateSVGTransformFromMatrix(dom::SVGMatrix& matrix)
     351             : {
     352           0 :   RefPtr<SVGTransform> result = new SVGTransform(matrix.GetMatrix());
     353           0 :   return result.forget();
     354             : }
     355             : 
     356             : already_AddRefed<SVGTransform>
     357           0 : DOMSVGTransformList::Consolidate(ErrorResult& error)
     358             : {
     359           0 :   if (IsAnimValList()) {
     360           0 :     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     361           0 :     return nullptr;
     362             :   }
     363             : 
     364           0 :   if (LengthNoFlush() == 0) {
     365           0 :     return nullptr;
     366             :   }
     367             : 
     368             :   // Note that SVG 1.1 says, "The consolidation operation creates new
     369             :   // SVGTransform object as the first and only item in the list" hence, even if
     370             :   // LengthNoFlush() == 1 we can't return that one item (after making it a
     371             :   // matrix type). We must orphan the existing item and then make a new one.
     372             : 
     373             :   // First calculate our matrix
     374           0 :   gfxMatrix mx = InternalList().GetConsolidationMatrix();
     375             : 
     376             :   // Then orphan the existing items
     377           0 :   Clear(error);
     378           0 :   MOZ_ASSERT(!error.Failed(), "How could this fail?");
     379             : 
     380             :   // And append the new transform
     381           0 :   RefPtr<SVGTransform> transform = new SVGTransform(mx);
     382           0 :   return InsertItemBefore(*transform, LengthNoFlush(), error);
     383             : }
     384             : 
     385             : //----------------------------------------------------------------------
     386             : // Implementation helpers:
     387             : 
     388             : already_AddRefed<SVGTransform>
     389           0 : DOMSVGTransformList::GetItemAt(uint32_t aIndex)
     390             : {
     391           0 :   MOZ_ASSERT(aIndex < mItems.Length());
     392             : 
     393           0 :   if (!mItems[aIndex]) {
     394           0 :     mItems[aIndex] = new SVGTransform(this, aIndex, IsAnimValList());
     395             :   }
     396           0 :   RefPtr<SVGTransform> result = mItems[aIndex];
     397           0 :   return result.forget();
     398             : }
     399             : 
     400             : void
     401           0 : DOMSVGTransformList::MaybeInsertNullInAnimValListAt(uint32_t aIndex)
     402             : {
     403           0 :   MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
     404             : 
     405           0 :   if (!AnimListMirrorsBaseList()) {
     406           0 :     return;
     407             :   }
     408             : 
     409           0 :   DOMSVGTransformList* animVal = mAList->mAnimVal;
     410             : 
     411           0 :   MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
     412           0 :   MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
     413             :              "animVal list not in sync!");
     414           0 :   MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible));
     415             : 
     416           0 :   UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1);
     417             : }
     418             : 
     419             : void
     420           0 : DOMSVGTransformList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex)
     421             : {
     422           0 :   MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
     423             : 
     424           0 :   if (!AnimListMirrorsBaseList()) {
     425           0 :     return;
     426             :   }
     427             : 
     428             :   // This needs to be a strong reference; otherwise, the RemovingFromList call
     429             :   // below might drop the last reference to animVal before we're done with it.
     430           0 :   RefPtr<DOMSVGTransformList> animVal = mAList->mAnimVal;
     431             : 
     432           0 :   MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
     433           0 :   MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
     434             :              "animVal list not in sync!");
     435             : 
     436           0 :   if (animVal->mItems[aIndex]) {
     437           0 :     animVal->mItems[aIndex]->RemovingFromList();
     438             :   }
     439           0 :   animVal->mItems.RemoveElementAt(aIndex);
     440             : 
     441           0 :   UpdateListIndicesFromIndex(animVal->mItems, aIndex);
     442             : }
     443             : 
     444             : } // namespace mozilla

Generated by: LCOV version 1.13