summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/core/svg/properties/SVGListPropertyHelper.h
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/svg/properties/SVGListPropertyHelper.h')
-rw-r--r--chromium/third_party/WebKit/Source/core/svg/properties/SVGListPropertyHelper.h371
1 files changed, 371 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/core/svg/properties/SVGListPropertyHelper.h b/chromium/third_party/WebKit/Source/core/svg/properties/SVGListPropertyHelper.h
new file mode 100644
index 00000000000..b8ffb22fc1a
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/core/svg/properties/SVGListPropertyHelper.h
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SVGListPropertyHelper_h
+#define SVGListPropertyHelper_h
+
+#include "bindings/v8/ExceptionMessages.h"
+#include "bindings/v8/ExceptionStatePlaceholder.h"
+#include "core/dom/ExceptionCode.h"
+#include "core/svg/properties/SVGProperty.h"
+#include "wtf/PassRefPtr.h"
+#include "wtf/Vector.h"
+
+namespace WebCore {
+
+// This is an implementation of the SVG*List property spec:
+// http://www.w3.org/TR/SVG/single-page.html#types-InterfaceSVGLengthList
+template<typename Derived, typename ItemProperty>
+class SVGListPropertyHelper : public SVGPropertyBase {
+public:
+ typedef ItemProperty ItemPropertyType;
+
+ SVGListPropertyHelper()
+ : SVGPropertyBase(Derived::classType())
+ {
+ }
+
+ ~SVGListPropertyHelper()
+ {
+ clear();
+ }
+
+ // used from Blink C++ code:
+
+ ItemPropertyType* at(size_t index)
+ {
+ ASSERT(index < m_values.size());
+ ASSERT(m_values.at(index)->ownerList() == this);
+ return m_values.at(index).get();
+ }
+
+ const ItemPropertyType* at(size_t index) const
+ {
+ return const_cast<SVGListPropertyHelper<Derived, ItemProperty>*>(this)->at(index);
+ }
+
+ class ConstIterator {
+ private:
+ typedef typename Vector<RefPtr<ItemPropertyType> >::const_iterator WrappedType;
+
+ public:
+ ConstIterator(WrappedType it)
+ : m_it(it)
+ {
+ }
+
+ ConstIterator& operator++() { ++m_it; return *this; }
+
+ bool operator==(const ConstIterator& o) const { return m_it == o.m_it; }
+ bool operator!=(const ConstIterator& o) const { return m_it != o.m_it; }
+
+ PassRefPtr<ItemPropertyType> operator*() { return *m_it; }
+ PassRefPtr<ItemPropertyType> operator->() { return *m_it; }
+
+ private:
+ WrappedType m_it;
+ };
+
+ ConstIterator begin() const
+ {
+ return ConstIterator(m_values.begin());
+ }
+
+ ConstIterator lastAppended() const
+ {
+ return ConstIterator(m_values.begin() + m_values.size() - 1);
+ }
+
+ ConstIterator end() const
+ {
+ return ConstIterator(m_values.end());
+ }
+
+ void append(PassRefPtr<ItemPropertyType> passNewItem)
+ {
+ RefPtr<ItemPropertyType> newItem = passNewItem;
+
+ ASSERT(newItem);
+ m_values.append(newItem);
+ newItem->setOwnerList(this);
+ }
+
+ bool operator==(const Derived&) const;
+ bool operator!=(const Derived& other) const
+ {
+ return !(*this == other);
+ }
+
+ bool isEmpty() const
+ {
+ return !length();
+ }
+
+ // SVGList*Property DOM spec:
+
+ size_t length() const
+ {
+ return m_values.size();
+ }
+
+ void clear();
+
+ PassRefPtr<ItemPropertyType> initialize(PassRefPtr<ItemPropertyType>);
+ PassRefPtr<ItemPropertyType> getItem(size_t, ExceptionState&);
+ PassRefPtr<ItemPropertyType> insertItemBefore(PassRefPtr<ItemPropertyType>, size_t);
+ PassRefPtr<ItemPropertyType> removeItem(size_t, ExceptionState&);
+ PassRefPtr<ItemPropertyType> appendItem(PassRefPtr<ItemPropertyType>);
+ PassRefPtr<ItemPropertyType> replaceItem(PassRefPtr<ItemPropertyType>, size_t, ExceptionState&);
+
+protected:
+ void deepCopy(PassRefPtr<Derived>);
+
+private:
+ inline bool checkIndexBound(size_t, ExceptionState&);
+ bool removeFromOldOwnerListAndAdjustIndex(PassRefPtr<ItemPropertyType>, size_t* indexToModify);
+ size_t findItem(PassRefPtr<ItemPropertyType>);
+
+ Vector<RefPtr<ItemPropertyType> > m_values;
+
+ static PassRefPtr<Derived> toDerived(PassRefPtr<SVGPropertyBase> passBase)
+ {
+ if (!passBase)
+ return nullptr;
+
+ RefPtr<SVGPropertyBase> base = passBase;
+ ASSERT(base->type() == Derived::classType());
+ return static_pointer_cast<Derived>(base);
+ }
+};
+
+template<typename Derived, typename ItemProperty>
+bool SVGListPropertyHelper<Derived, ItemProperty>::operator==(const Derived& other) const
+{
+ if (length() != other.length())
+ return false;
+
+ size_t size = length();
+ for (size_t i = 0; i < size; ++i) {
+ if (*at(i) != *other.at(i))
+ return false;
+ }
+
+ return true;
+}
+
+template<typename Derived, typename ItemProperty>
+void SVGListPropertyHelper<Derived, ItemProperty>::clear()
+{
+ // detach all list items as they are no longer part of this list
+ typename Vector<RefPtr<ItemPropertyType> >::const_iterator it = m_values.begin();
+ typename Vector<RefPtr<ItemPropertyType> >::const_iterator itEnd = m_values.end();
+ for (; it != itEnd; ++it) {
+ ASSERT((*it)->ownerList() == this);
+ (*it)->setOwnerList(0);
+ }
+
+ m_values.clear();
+}
+
+template<typename Derived, typename ItemProperty>
+PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::initialize(PassRefPtr<ItemProperty> passNewItem)
+{
+ RefPtr<ItemPropertyType> newItem = passNewItem;
+
+ // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list.
+ removeFromOldOwnerListAndAdjustIndex(newItem, 0);
+
+ // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter.
+ clear();
+ append(newItem);
+ return newItem.release();
+}
+
+template<typename Derived, typename ItemProperty>
+PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::getItem(size_t index, ExceptionState& exceptionState)
+{
+ if (!checkIndexBound(index, exceptionState))
+ return nullptr;
+
+ ASSERT(index < m_values.size());
+ ASSERT(m_values.at(index)->ownerList() == this);
+ return m_values.at(index);
+}
+
+template<typename Derived, typename ItemProperty>
+PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::insertItemBefore(PassRefPtr<ItemProperty> passNewItem, size_t index)
+{
+ // Spec: If the index is greater than or equal to length, then the new item is appended to the end of the list.
+ if (index > m_values.size())
+ index = m_values.size();
+
+ RefPtr<ItemPropertyType> newItem = passNewItem;
+
+ // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
+ if (!removeFromOldOwnerListAndAdjustIndex(newItem, &index)) {
+ // Inserting the item before itself is a no-op.
+ return newItem.release();
+ }
+
+ // 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
+ // 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.
+ m_values.insert(index, newItem);
+ newItem->setOwnerList(this);
+
+ return newItem.release();
+}
+
+template<typename Derived, typename ItemProperty>
+PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::removeItem(size_t index, ExceptionState& exceptionState)
+{
+ if (index >= m_values.size()) {
+ exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("index", index, m_values.size()));
+ return nullptr;
+ }
+ ASSERT(m_values.at(index)->ownerList() == this);
+ RefPtr<ItemPropertyType> oldItem = m_values.at(index);
+ m_values.remove(index);
+ oldItem->setOwnerList(0);
+ return oldItem.release();
+}
+
+template<typename Derived, typename ItemProperty>
+PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::appendItem(PassRefPtr<ItemProperty> passNewItem)
+{
+ RefPtr<ItemPropertyType> newItem = passNewItem;
+
+ // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
+ removeFromOldOwnerListAndAdjustIndex(newItem, 0);
+
+ // Append the value and wrapper at the end of the list.
+ append(newItem);
+
+ return newItem.release();
+}
+
+template<typename Derived, typename ItemProperty>
+PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::replaceItem(PassRefPtr<ItemProperty> passNewItem, size_t index, ExceptionState& exceptionState)
+{
+ if (!checkIndexBound(index, exceptionState))
+ return nullptr;
+
+ RefPtr<ItemPropertyType> newItem = passNewItem;
+
+ // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
+ // 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.
+ if (!removeFromOldOwnerListAndAdjustIndex(newItem, &index)) {
+ // Replacing the item with itself is a no-op.
+ return newItem.release();
+ }
+
+ if (m_values.isEmpty()) {
+ // 'newItem' already lived in our list, we removed it, and now we're empty, which means there's nothing to replace.
+ exceptionState.throwDOMException(IndexSizeError, String::format("Failed to replace the provided item at index %zu.", index));
+ return nullptr;
+ }
+
+ // Update the value at the desired position 'index'.
+ RefPtr<ItemPropertyType>& position = m_values[index];
+ ASSERT(position->ownerList() == this);
+ position->setOwnerList(0);
+ position = newItem;
+ newItem->setOwnerList(this);
+
+ return newItem.release();
+}
+
+template<typename Derived, typename ItemProperty>
+bool SVGListPropertyHelper<Derived, ItemProperty>::checkIndexBound(size_t index, ExceptionState& exceptionState)
+{
+ if (index >= m_values.size()) {
+ exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("index", index, m_values.size()));
+ return false;
+ }
+
+ return true;
+}
+
+template<typename Derived, typename ItemProperty>
+bool SVGListPropertyHelper<Derived, ItemProperty>::removeFromOldOwnerListAndAdjustIndex(PassRefPtr<ItemPropertyType> passItem, size_t* indexToModify)
+{
+ RefPtr<ItemPropertyType> item = passItem;
+ ASSERT(item);
+ RefPtr<Derived> ownerList = toDerived(item->ownerList());
+ if (!ownerList)
+ return true;
+
+ // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
+ // 'newItem' is already living in another list. If it's not our list, synchronize the other lists wrappers after the removal.
+ bool livesInOtherList = ownerList.get() != this;
+ size_t indexToRemove = ownerList->findItem(item);
+ ASSERT(indexToRemove != WTF::kNotFound);
+
+ // Do not remove newItem if already in this list at the target index.
+ if (!livesInOtherList && indexToModify && indexToRemove == *indexToModify)
+ return false;
+
+ ownerList->removeItem(indexToRemove, ASSERT_NO_EXCEPTION);
+
+ if (!indexToModify)
+ return true;
+
+ // If the item lived in our list, adjust the insertion index.
+ if (!livesInOtherList) {
+ size_t& index = *indexToModify;
+ // 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.
+ if (static_cast<size_t>(indexToRemove) < index)
+ --index;
+ }
+
+ return true;
+}
+
+template<typename Derived, typename ItemProperty>
+size_t SVGListPropertyHelper<Derived, ItemProperty>::findItem(PassRefPtr<ItemPropertyType> item)
+{
+ return m_values.find(item);
+}
+
+template<typename Derived, typename ItemProperty>
+void SVGListPropertyHelper<Derived, ItemProperty>::deepCopy(PassRefPtr<Derived> passFrom)
+{
+ RefPtr<Derived> from = passFrom;
+
+ clear();
+ typename Vector<RefPtr<ItemPropertyType> >::const_iterator it = from->m_values.begin();
+ typename Vector<RefPtr<ItemPropertyType> >::const_iterator itEnd = from->m_values.end();
+ for (; it != itEnd; ++it) {
+ append((*it)->clone());
+ }
+}
+
+}
+
+#endif // SVGListPropertyHelper_h