1/*
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3 * Copyright (C) 2018-2019 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#pragma once
22
23#include "Path.h"
24#include "SVGPathByteStream.h"
25#include "SVGPathSeg.h"
26#include "SVGPropertyList.h"
27
28namespace WebCore {
29
30class SVGPathSegList final : public SVGPropertyList<SVGPathSeg> {
31 friend class SVGAnimatedPathSegListAnimator;
32 friend class SVGPathSegListBuilder;
33 friend class SVGPathSegListSource;
34
35 using Base = SVGPropertyList<SVGPathSeg>;
36 using Base::Base;
37
38public:
39 static Ref<SVGPathSegList> create(SVGPropertyOwner* owner, SVGPropertyAccess access)
40 {
41 return adoptRef(*new SVGPathSegList(owner, access));
42 }
43
44 static Ref<SVGPathSegList> create(const SVGPathSegList& other, SVGPropertyAccess access)
45 {
46 return adoptRef(*new SVGPathSegList(other, access));
47 }
48
49 static Ref<SVGPathSegList> create(Ref<SVGPathSeg>&& newItem)
50 {
51 return adoptRef(*new SVGPathSegList(WTFMove(newItem)));
52 }
53
54 SVGPathSegList& operator=(const SVGPathSegList& other)
55 {
56 pathByteStreamWillChange();
57 m_pathByteStream = other.pathByteStream();
58 return *this;
59 }
60
61 unsigned numberOfItems() const
62 {
63 const_cast<SVGPathSegList*>(this)->ensureItems();
64 return Base::numberOfItems();
65 }
66
67 ExceptionOr<void> clear()
68 {
69 itemsWillChange();
70 return Base::clear();
71 }
72
73 ExceptionOr<Ref<SVGPathSeg>> getItem(unsigned index)
74 {
75 ensureItems();
76 return Base::getItem(index);
77 }
78
79 ExceptionOr<Ref<SVGPathSeg>> initialize(Ref<SVGPathSeg>&& newItem)
80 {
81 itemsWillChange();
82 return Base::initialize(WTFMove(newItem));
83 }
84
85 ExceptionOr<Ref<SVGPathSeg>> insertItemBefore(Ref<SVGPathSeg>&& newItem, unsigned index)
86 {
87 ensureItems();
88 itemsWillChange();
89 return Base::insertItemBefore(WTFMove(newItem), index);
90 }
91
92 ExceptionOr<Ref<SVGPathSeg>> replaceItem(Ref<SVGPathSeg>&& newItem, unsigned index)
93 {
94 ensureItems();
95 itemsWillChange();
96 return Base::replaceItem(WTFMove(newItem), index);
97 }
98
99 ExceptionOr<Ref<SVGPathSeg>> removeItem(unsigned index)
100 {
101 ensureItems();
102 itemsWillChange();
103 return Base::removeItem(index);
104 }
105
106 ExceptionOr<Ref<SVGPathSeg>> appendItem(Ref<SVGPathSeg>&& newItem)
107 {
108 ensureItems();
109 appendPathSegToPathByteStream(newItem);
110 clearPath();
111 return Base::appendItem(WTFMove(newItem));
112 }
113
114 const SVGPathByteStream& pathByteStream() const { return const_cast<SVGPathSegList*>(this)->pathByteStream(); }
115 SVGPathByteStream& pathByteStream()
116 {
117 ensurePathByteStream();
118 return m_pathByteStream;
119 }
120
121 bool parse(const String& value)
122 {
123 pathByteStreamWillChange();
124 return buildSVGPathByteStreamFromString(value, m_pathByteStream, UnalteredParsing);
125 }
126
127 Path path() const
128 {
129 if (!m_path)
130 m_path = buildPathFromByteStream(pathByteStream());
131 return *m_path;
132 }
133
134 size_t approximateMemoryCost() const
135 {
136 // This is an approximation for path memory cost since the path is parsed on demand.
137 size_t pathMemoryCost = (m_pathByteStream.size() / 10) * sizeof(FloatPoint);
138 // We need to account for the memory which is allocated by the m_path.
139 return m_path ? pathMemoryCost + sizeof(*m_path) : pathMemoryCost;
140 }
141
142 String valueAsString() const override
143 {
144 String value;
145 buildStringFromByteStream(pathByteStream(), value, UnalteredParsing);
146 return value;
147 }
148
149private:
150 SVGPathSegList(const SVGPathSegList& other, SVGPropertyAccess access)
151 : Base(other.owner(), access)
152 , m_pathByteStream(other.pathByteStream())
153 {
154 }
155
156 // Used by appendPathSegToPathByteStream() to create a temporary SVGPathSegList with one item.
157 SVGPathSegList(Ref<SVGPathSeg>&& newItem)
158 {
159 append(WTFMove(newItem));
160 }
161
162 // Called when changing an item in the list.
163 void commitPropertyChange(SVGProperty* property) override
164 {
165 itemsWillChange();
166 Base::commitPropertyChange(property);
167 }
168
169 void ensureItems()
170 {
171 if (!m_items.isEmpty() || m_pathByteStream.isEmpty())
172 return;
173 buildSVGPathSegListFromByteStream(m_pathByteStream, *this, UnalteredParsing);
174 }
175
176 void ensurePathByteStream()
177 {
178 if (!m_pathByteStream.isEmpty() || m_items.isEmpty())
179 return;
180 buildSVGPathByteStreamFromSVGPathSegList(*this, m_pathByteStream, UnalteredParsing);
181 }
182
183 // Optimize appending an SVGPathSeg to the list. Instead of creating the whole
184 // byte stream, a temporary byte stream will be creating just for the new item
185 // and this temporary byte stream will be appended to m_pathByteStream.
186 void appendPathSegToPathByteStream(const Ref<SVGPathSeg>& item)
187 {
188 if (m_pathByteStream.isEmpty())
189 return;
190
191 Ref<SVGPathSegList> pathSegList = SVGPathSegList::create(item.copyRef());
192 SVGPathByteStream pathSegStream;
193
194 if (!buildSVGPathByteStreamFromSVGPathSegList(pathSegList, pathSegStream, UnalteredParsing, false))
195 return;
196
197 m_pathByteStream.append(pathSegStream);
198 }
199
200 void clearPathByteStream() { m_pathByteStream.clear(); }
201 void clearPath() { m_path = WTF::nullopt; }
202
203 void pathByteStreamWillChange()
204 {
205 clearItems();
206 clearPath();
207 }
208
209 void itemsWillChange()
210 {
211 clearPathByteStream();
212 clearPath();
213 }
214
215 SVGPathByteStream m_pathByteStream;
216 mutable Optional<Path> m_path;
217};
218
219}
220