1/*
2 * Copyright (C) 2019 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "ExceptionOr.h"
29#include "SVGProperty.h"
30
31namespace WebCore {
32
33template<typename ItemType>
34class SVGList : public SVGProperty {
35public:
36 unsigned numberOfItems() const
37 {
38 return m_items.size();
39 }
40
41 ExceptionOr<void> clear()
42 {
43 auto result = canAlterList();
44 if (result.hasException())
45 return result.releaseException();
46 ASSERT(result.releaseReturnValue());
47
48 clearItems();
49 commitChange();
50 return { };
51 }
52
53 ExceptionOr<ItemType> getItem(unsigned index)
54 {
55 auto result = canGetItem(index);
56 if (result.hasException())
57 return result.releaseException();
58 ASSERT(result.releaseReturnValue());
59
60 return at(index);
61 }
62
63 ExceptionOr<ItemType> initialize(ItemType&& newItem)
64 {
65 auto result = canAlterList();
66 if (result.hasException())
67 return result.releaseException();
68
69 // Spec: Clears all existing current items from the list.
70 clearItems();
71
72 auto item = append(WTFMove(newItem));
73 commitChange();
74 return item;
75 }
76
77 ExceptionOr<ItemType> insertItemBefore(ItemType&& newItem, unsigned index)
78 {
79 auto result = canAlterList();
80 if (result.hasException())
81 return result.releaseException();
82 ASSERT(result.releaseReturnValue());
83
84 // Spec: If the index is greater than or equal to numberOfItems,
85 // then the new item is appended to the end of the list.
86 if (index > numberOfItems())
87 index = numberOfItems();
88
89 auto item = insert(index, WTFMove(newItem));
90 commitChange();
91 return item;
92 }
93
94 ExceptionOr<ItemType> replaceItem(ItemType&& newItem, unsigned index)
95 {
96 auto result = canReplaceItem(index);
97 if (result.hasException())
98 return result.releaseException();
99 ASSERT(result.releaseReturnValue());
100
101 auto item = replace(index, WTFMove(newItem));
102 commitChange();
103 return item;
104 }
105
106 ExceptionOr<ItemType> removeItem(unsigned index)
107 {
108 auto result = canRemoveItem(index);
109 if (result.hasException())
110 return result.releaseException();
111 ASSERT(result.releaseReturnValue());
112
113 auto item = remove(index);
114 commitChange();
115 return item;
116 }
117
118 ExceptionOr<ItemType> appendItem(ItemType&& newItem)
119 {
120 auto result = canAlterList();
121 if (result.hasException())
122 return result.releaseException();
123 ASSERT(result.releaseReturnValue());
124
125 auto item = append(WTFMove(newItem));
126 commitChange();
127 return item;
128 }
129
130 // Parsers and animators need to have a direct access to the items.
131 Vector<ItemType>& items() { return m_items; }
132 const Vector<ItemType>& items() const { return m_items; }
133 size_t size() const { return m_items.size(); }
134 bool isEmpty() const { return m_items.isEmpty(); }
135
136 void clearItems()
137 {
138 detachItems();
139 m_items.clear();
140 }
141
142protected:
143 using SVGProperty::SVGProperty;
144
145 ExceptionOr<bool> canAlterList() const
146 {
147 if (isReadOnly())
148 return Exception { NoModificationAllowedError };
149 return true;
150 }
151
152 ExceptionOr<bool> canGetItem(unsigned index)
153 {
154 if (index >= m_items.size())
155 return Exception { IndexSizeError };
156 return true;
157 }
158
159 ExceptionOr<bool> canReplaceItem(unsigned index)
160 {
161 auto result = canAlterList();
162 if (result.hasException())
163 return result.releaseException();
164 ASSERT(result.releaseReturnValue());
165
166 if (index >= m_items.size())
167 return Exception { IndexSizeError };
168 return true;
169 }
170
171 ExceptionOr<bool> canRemoveItem(unsigned index)
172 {
173 auto result = canAlterList();
174 if (result.hasException())
175 return result.releaseException();
176 ASSERT(result.releaseReturnValue());
177
178 if (index >= m_items.size())
179 return Exception { IndexSizeError };
180 return true;
181 }
182
183 virtual void detachItems() { }
184 virtual ItemType at(unsigned index) const = 0;
185 virtual ItemType insert(unsigned index, ItemType&&) = 0;
186 virtual ItemType replace(unsigned index, ItemType&&) = 0;
187 virtual ItemType remove(unsigned index) = 0;
188 virtual ItemType append(ItemType&&) = 0;
189
190 Vector<ItemType> m_items;
191};
192
193}
194