Home | History | Annotate | Download | only in properties
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #ifndef SVGListPropertyHelper_h
     32 #define SVGListPropertyHelper_h
     33 
     34 #include "bindings/core/v8/ExceptionMessages.h"
     35 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
     36 #include "core/dom/ExceptionCode.h"
     37 #include "core/svg/SVGAnimationElement.h"
     38 #include "core/svg/properties/SVGPropertyHelper.h"
     39 #include "wtf/PassRefPtr.h"
     40 #include "wtf/Vector.h"
     41 
     42 namespace blink {
     43 
     44 // This is an implementation of the SVG*List property spec:
     45 // http://www.w3.org/TR/SVG/single-page.html#types-InterfaceSVGLengthList
     46 template<typename Derived, typename ItemProperty>
     47 class SVGListPropertyHelper : public SVGPropertyHelper<Derived> {
     48 public:
     49     typedef ItemProperty ItemPropertyType;
     50 
     51     SVGListPropertyHelper()
     52     {
     53     }
     54 
     55     ~SVGListPropertyHelper()
     56     {
     57         clear();
     58     }
     59 
     60     // used from Blink C++ code:
     61 
     62     ItemPropertyType* at(size_t index)
     63     {
     64         ASSERT(index < m_values.size());
     65         ASSERT(m_values.at(index)->ownerList() == this);
     66         return m_values.at(index).get();
     67     }
     68 
     69     const ItemPropertyType* at(size_t index) const
     70     {
     71         return const_cast<SVGListPropertyHelper<Derived, ItemProperty>*>(this)->at(index);
     72     }
     73 
     74     class ConstIterator {
     75     private:
     76         typedef typename Vector<RefPtr<ItemPropertyType> >::const_iterator WrappedType;
     77 
     78     public:
     79         ConstIterator(WrappedType it)
     80             : m_it(it)
     81         {
     82         }
     83 
     84         ConstIterator& operator++() { ++m_it; return *this; }
     85 
     86         bool operator==(const ConstIterator& o) const { return m_it == o.m_it; }
     87         bool operator!=(const ConstIterator& o) const { return m_it != o.m_it; }
     88 
     89         PassRefPtr<ItemPropertyType> operator*() { return *m_it; }
     90         PassRefPtr<ItemPropertyType> operator->() { return *m_it; }
     91 
     92     private:
     93         WrappedType m_it;
     94     };
     95 
     96     ConstIterator begin() const
     97     {
     98         return ConstIterator(m_values.begin());
     99     }
    100 
    101     ConstIterator lastAppended() const
    102     {
    103         return ConstIterator(m_values.begin() + m_values.size() - 1);
    104     }
    105 
    106     ConstIterator end() const
    107     {
    108         return ConstIterator(m_values.end());
    109     }
    110 
    111     void append(PassRefPtr<ItemPropertyType> passNewItem)
    112     {
    113         RefPtr<ItemPropertyType> newItem = passNewItem;
    114 
    115         ASSERT(newItem);
    116         m_values.append(newItem);
    117         newItem->setOwnerList(this);
    118     }
    119 
    120     bool operator==(const Derived&) const;
    121     bool operator!=(const Derived& other) const
    122     {
    123         return !(*this == other);
    124     }
    125 
    126     bool isEmpty() const
    127     {
    128         return !length();
    129     }
    130 
    131     virtual PassRefPtr<Derived> clone()
    132     {
    133         RefPtr<Derived> svgList = Derived::create();
    134         svgList->deepCopy(static_cast<Derived*>(this));
    135         return svgList.release();
    136     }
    137 
    138     // SVGList*Property DOM spec:
    139 
    140     size_t length() const
    141     {
    142         return m_values.size();
    143     }
    144 
    145     void clear();
    146 
    147     PassRefPtr<ItemPropertyType> initialize(PassRefPtr<ItemPropertyType>);
    148     PassRefPtr<ItemPropertyType> getItem(size_t, ExceptionState&);
    149     PassRefPtr<ItemPropertyType> insertItemBefore(PassRefPtr<ItemPropertyType>, size_t);
    150     PassRefPtr<ItemPropertyType> removeItem(size_t, ExceptionState&);
    151     PassRefPtr<ItemPropertyType> appendItem(PassRefPtr<ItemPropertyType>);
    152     PassRefPtr<ItemPropertyType> replaceItem(PassRefPtr<ItemPropertyType>, size_t, ExceptionState&);
    153 
    154 protected:
    155     void deepCopy(PassRefPtr<Derived>);
    156 
    157     bool adjustFromToListValues(PassRefPtr<Derived> fromList, PassRefPtr<Derived> toList, float percentage, AnimationMode);
    158 
    159     virtual PassRefPtr<ItemPropertyType> createPaddingItem() const
    160     {
    161         return ItemPropertyType::create();
    162     }
    163 
    164 private:
    165     inline bool checkIndexBound(size_t, ExceptionState&);
    166     bool removeFromOldOwnerListAndAdjustIndex(PassRefPtr<ItemPropertyType>, size_t* indexToModify);
    167     size_t findItem(PassRefPtr<ItemPropertyType>);
    168 
    169     Vector<RefPtr<ItemPropertyType> > m_values;
    170 
    171     static PassRefPtr<Derived> toDerived(PassRefPtr<SVGPropertyBase> passBase)
    172     {
    173         if (!passBase)
    174             return nullptr;
    175 
    176         RefPtr<SVGPropertyBase> base = passBase;
    177         ASSERT(base->type() == Derived::classType());
    178         return static_pointer_cast<Derived>(base);
    179     }
    180 };
    181 
    182 template<typename Derived, typename ItemProperty>
    183 bool SVGListPropertyHelper<Derived, ItemProperty>::operator==(const Derived& other) const
    184 {
    185     if (length() != other.length())
    186         return false;
    187 
    188     size_t size = length();
    189     for (size_t i = 0; i < size; ++i) {
    190         if (*at(i) != *other.at(i))
    191             return false;
    192     }
    193 
    194     return true;
    195 }
    196 
    197 template<typename Derived, typename ItemProperty>
    198 void SVGListPropertyHelper<Derived, ItemProperty>::clear()
    199 {
    200     // detach all list items as they are no longer part of this list
    201     typename Vector<RefPtr<ItemPropertyType> >::const_iterator it = m_values.begin();
    202     typename Vector<RefPtr<ItemPropertyType> >::const_iterator itEnd = m_values.end();
    203     for (; it != itEnd; ++it) {
    204         ASSERT((*it)->ownerList() == this);
    205         (*it)->setOwnerList(0);
    206     }
    207 
    208     m_values.clear();
    209 }
    210 
    211 template<typename Derived, typename ItemProperty>
    212 PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::initialize(PassRefPtr<ItemProperty> passNewItem)
    213 {
    214     RefPtr<ItemPropertyType> newItem = passNewItem;
    215 
    216     // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list.
    217     removeFromOldOwnerListAndAdjustIndex(newItem, 0);
    218 
    219     // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter.
    220     clear();
    221     append(newItem);
    222     return newItem.release();
    223 }
    224 
    225 template<typename Derived, typename ItemProperty>
    226 PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::getItem(size_t index, ExceptionState& exceptionState)
    227 {
    228     if (!checkIndexBound(index, exceptionState))
    229         return nullptr;
    230 
    231     ASSERT(index < m_values.size());
    232     ASSERT(m_values.at(index)->ownerList() == this);
    233     return m_values.at(index);
    234 }
    235 
    236 template<typename Derived, typename ItemProperty>
    237 PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::insertItemBefore(PassRefPtr<ItemProperty> passNewItem, size_t index)
    238 {
    239     // Spec: If the index is greater than or equal to length, then the new item is appended to the end of the list.
    240     if (index > m_values.size())
    241         index = m_values.size();
    242 
    243     RefPtr<ItemPropertyType> newItem = passNewItem;
    244 
    245     // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
    246     if (!removeFromOldOwnerListAndAdjustIndex(newItem, &index)) {
    247         // Inserting the item before itself is a no-op.
    248         return newItem.release();
    249     }
    250 
    251     // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be
    252     // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list.
    253     m_values.insert(index, newItem);
    254     newItem->setOwnerList(this);
    255 
    256     return newItem.release();
    257 }
    258 
    259 template<typename Derived, typename ItemProperty>
    260 PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::removeItem(size_t index, ExceptionState& exceptionState)
    261 {
    262     if (index >= m_values.size()) {
    263         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("index", index, m_values.size()));
    264         return nullptr;
    265     }
    266     ASSERT(m_values.at(index)->ownerList() == this);
    267     RefPtr<ItemPropertyType> oldItem = m_values.at(index);
    268     m_values.remove(index);
    269     oldItem->setOwnerList(0);
    270     return oldItem.release();
    271 }
    272 
    273 template<typename Derived, typename ItemProperty>
    274 PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::appendItem(PassRefPtr<ItemProperty> passNewItem)
    275 {
    276     RefPtr<ItemPropertyType> newItem = passNewItem;
    277 
    278     // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
    279     removeFromOldOwnerListAndAdjustIndex(newItem, 0);
    280 
    281     // Append the value and wrapper at the end of the list.
    282     append(newItem);
    283 
    284     return newItem.release();
    285 }
    286 
    287 template<typename Derived, typename ItemProperty>
    288 PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::replaceItem(PassRefPtr<ItemProperty> passNewItem, size_t index, ExceptionState& exceptionState)
    289 {
    290     if (!checkIndexBound(index, exceptionState))
    291         return nullptr;
    292 
    293     RefPtr<ItemPropertyType> newItem = passNewItem;
    294 
    295     // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
    296     // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item.
    297     if (!removeFromOldOwnerListAndAdjustIndex(newItem, &index)) {
    298         // Replacing the item with itself is a no-op.
    299         return newItem.release();
    300     }
    301 
    302     if (m_values.isEmpty()) {
    303         // 'newItem' already lived in our list, we removed it, and now we're empty, which means there's nothing to replace.
    304         exceptionState.throwDOMException(IndexSizeError, String::format("Failed to replace the provided item at index %zu.", index));
    305         return nullptr;
    306     }
    307 
    308     // Update the value at the desired position 'index'.
    309     RefPtr<ItemPropertyType>& position = m_values[index];
    310     ASSERT(position->ownerList() == this);
    311     position->setOwnerList(0);
    312     position = newItem;
    313     newItem->setOwnerList(this);
    314 
    315     return newItem.release();
    316 }
    317 
    318 template<typename Derived, typename ItemProperty>
    319 bool SVGListPropertyHelper<Derived, ItemProperty>::checkIndexBound(size_t index, ExceptionState& exceptionState)
    320 {
    321     if (index >= m_values.size()) {
    322         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("index", index, m_values.size()));
    323         return false;
    324     }
    325 
    326     return true;
    327 }
    328 
    329 template<typename Derived, typename ItemProperty>
    330 bool SVGListPropertyHelper<Derived, ItemProperty>::removeFromOldOwnerListAndAdjustIndex(PassRefPtr<ItemPropertyType> passItem, size_t* indexToModify)
    331 {
    332     RefPtr<ItemPropertyType> item = passItem;
    333     ASSERT(item);
    334     RefPtr<Derived> ownerList = toDerived(item->ownerList());
    335     if (!ownerList)
    336         return true;
    337 
    338     // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
    339     // 'newItem' is already living in another list. If it's not our list, synchronize the other lists wrappers after the removal.
    340     bool livesInOtherList = ownerList.get() != this;
    341     size_t indexToRemove = ownerList->findItem(item);
    342     ASSERT(indexToRemove != WTF::kNotFound);
    343 
    344     // Do not remove newItem if already in this list at the target index.
    345     if (!livesInOtherList && indexToModify && indexToRemove == *indexToModify)
    346         return false;
    347 
    348     ownerList->removeItem(indexToRemove, ASSERT_NO_EXCEPTION);
    349 
    350     if (!indexToModify)
    351         return true;
    352 
    353     // If the item lived in our list, adjust the insertion index.
    354     if (!livesInOtherList) {
    355         size_t& index = *indexToModify;
    356         // Spec: If the item is already in this list, note that the index of the item to (replace|insert before) is before the removal of the item.
    357         if (static_cast<size_t>(indexToRemove) < index)
    358             --index;
    359     }
    360 
    361     return true;
    362 }
    363 
    364 template<typename Derived, typename ItemProperty>
    365 size_t SVGListPropertyHelper<Derived, ItemProperty>::findItem(PassRefPtr<ItemPropertyType> item)
    366 {
    367     return m_values.find(item);
    368 }
    369 
    370 template<typename Derived, typename ItemProperty>
    371 void SVGListPropertyHelper<Derived, ItemProperty>::deepCopy(PassRefPtr<Derived> passFrom)
    372 {
    373     RefPtr<Derived> from = passFrom;
    374 
    375     clear();
    376     typename Vector<RefPtr<ItemPropertyType> >::const_iterator it = from->m_values.begin();
    377     typename Vector<RefPtr<ItemPropertyType> >::const_iterator itEnd = from->m_values.end();
    378     for (; it != itEnd; ++it) {
    379         append((*it)->clone());
    380     }
    381 }
    382 
    383 template<typename Derived, typename ItemProperty>
    384 bool SVGListPropertyHelper<Derived, ItemProperty>::adjustFromToListValues(PassRefPtr<Derived> passFromList, PassRefPtr<Derived> passToList, float percentage, AnimationMode mode)
    385 {
    386     RefPtr<Derived> fromList = passFromList;
    387     RefPtr<Derived> toList = passToList;
    388 
    389     // If no 'to' value is given, nothing to animate.
    390     size_t toListSize = toList->length();
    391     if (!toListSize)
    392         return false;
    393 
    394     // If the 'from' value is given and it's length doesn't match the 'to' value list length, fallback to a discrete animation.
    395     size_t fromListSize = fromList->length();
    396     if (fromListSize != toListSize && fromListSize) {
    397         if (percentage < 0.5) {
    398             if (mode != ToAnimation)
    399                 deepCopy(fromList);
    400         } else {
    401             deepCopy(toList);
    402         }
    403 
    404         return false;
    405     }
    406 
    407     ASSERT(!fromListSize || fromListSize == toListSize);
    408     if (length() < toListSize) {
    409         size_t paddingCount = toListSize - length();
    410         for (size_t i = 0; i < paddingCount; ++i)
    411             append(createPaddingItem());
    412     }
    413 
    414     return true;
    415 }
    416 
    417 }
    418 
    419 #endif // SVGListPropertyHelper_h
    420