1/*
2 * Copyright (C) 2007, 2010 Rob Buis <buis@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "SVGViewSpec.h"
22
23#include "Document.h"
24#include "SVGAnimatedTransformList.h"
25#include "SVGFitToViewBox.h"
26#include "SVGNames.h"
27#include "SVGParserUtilities.h"
28#include "SVGSVGElement.h"
29#include "SVGTransformable.h"
30
31namespace WebCore {
32
33// Define custom animated property 'viewBox'.
34const SVGPropertyInfo* SVGViewSpec::viewBoxPropertyInfo()
35{
36    static const SVGPropertyInfo* s_propertyInfo = 0;
37    if (!s_propertyInfo) {
38        s_propertyInfo = new SVGPropertyInfo(AnimatedRect,
39                                             PropertyIsReadOnly,
40                                             SVGNames::viewBoxAttr,
41                                             viewBoxIdentifier(),
42                                             0,
43                                             0);
44    }
45    return s_propertyInfo;
46}
47
48// Define custom animated property 'preserveAspectRatio'.
49const SVGPropertyInfo* SVGViewSpec::preserveAspectRatioPropertyInfo()
50{
51    static const SVGPropertyInfo* s_propertyInfo = 0;
52    if (!s_propertyInfo) {
53        s_propertyInfo = new SVGPropertyInfo(AnimatedPreserveAspectRatio,
54                                             PropertyIsReadOnly,
55                                             SVGNames::preserveAspectRatioAttr,
56                                             preserveAspectRatioIdentifier(),
57                                             0,
58                                             0);
59    }
60    return s_propertyInfo;
61}
62
63
64// Define custom non-animated property 'transform'.
65const SVGPropertyInfo* SVGViewSpec::transformPropertyInfo()
66{
67    static const SVGPropertyInfo* s_propertyInfo = 0;
68    if (!s_propertyInfo) {
69        s_propertyInfo = new SVGPropertyInfo(AnimatedTransformList,
70                                             PropertyIsReadOnly,
71                                             SVGNames::transformAttr,
72                                             transformIdentifier(),
73                                             0,
74                                             0);
75    }
76    return s_propertyInfo;
77}
78
79SVGViewSpec::SVGViewSpec(SVGElement* contextElement)
80    : m_contextElement(contextElement)
81    , m_zoomAndPan(SVGZoomAndPanMagnify)
82{
83    ASSERT(m_contextElement);
84}
85
86const AtomicString& SVGViewSpec::viewBoxIdentifier()
87{
88    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGViewSpecViewBoxAttribute", AtomicString::ConstructFromLiteral));
89    return s_identifier;
90}
91
92const AtomicString& SVGViewSpec::preserveAspectRatioIdentifier()
93{
94    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGViewSpecPreserveAspectRatioAttribute", AtomicString::ConstructFromLiteral));
95    return s_identifier;
96}
97
98const AtomicString& SVGViewSpec::transformIdentifier()
99{
100    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGViewSpecTransformAttribute", AtomicString::ConstructFromLiteral));
101    return s_identifier;
102}
103
104void SVGViewSpec::setZoomAndPan(unsigned short, ExceptionCode& ec)
105{
106    // SVGViewSpec and all of its content is read-only.
107    ec = NO_MODIFICATION_ALLOWED_ERR;
108}
109
110void SVGViewSpec::setTransformString(const String& transform)
111{
112    if (!m_contextElement)
113        return;
114
115    SVGTransformList newList;
116    newList.parse(transform);
117
118    if (SVGAnimatedProperty* wrapper = SVGAnimatedProperty::lookupWrapper<SVGElement, SVGAnimatedTransformList>(m_contextElement, transformPropertyInfo()))
119        static_cast<SVGAnimatedTransformList*>(wrapper)->detachListWrappers(newList.size());
120
121    m_transform = newList;
122}
123
124String SVGViewSpec::transformString() const
125{
126    return SVGPropertyTraits<SVGTransformList>::toString(m_transform);
127}
128
129String SVGViewSpec::viewBoxString() const
130{
131    return SVGPropertyTraits<FloatRect>::toString(viewBoxBaseValue());
132}
133
134String SVGViewSpec::preserveAspectRatioString() const
135{
136    return SVGPropertyTraits<SVGPreserveAspectRatio>::toString(preserveAspectRatioBaseValue());
137}
138
139SVGElement* SVGViewSpec::viewTarget() const
140{
141    if (!m_contextElement)
142        return 0;
143    Element* element = m_contextElement->treeScope().getElementById(m_viewTargetString);
144    if (!element || !element->isSVGElement())
145        return 0;
146    return toSVGElement(element);
147}
148
149SVGTransformListPropertyTearOff* SVGViewSpec::transform()
150{
151    if (!m_contextElement)
152        return 0;
153    // Return the animVal here, as its readonly by default - which is exactly what we want here.
154    return static_cast<SVGTransformListPropertyTearOff*>(static_pointer_cast<SVGAnimatedTransformList>(lookupOrCreateTransformWrapper(this))->animVal());
155}
156
157PassRefPtr<SVGAnimatedRect> SVGViewSpec::viewBoxAnimated()
158{
159    if (!m_contextElement)
160        return 0;
161    return static_pointer_cast<SVGAnimatedRect>(lookupOrCreateViewBoxWrapper(this));
162}
163
164PassRefPtr<SVGAnimatedPreserveAspectRatio> SVGViewSpec::preserveAspectRatioAnimated()
165{
166    if (!m_contextElement)
167        return 0;
168    return static_pointer_cast<SVGAnimatedPreserveAspectRatio>(lookupOrCreatePreserveAspectRatioWrapper(this));
169}
170
171PassRefPtr<SVGAnimatedProperty> SVGViewSpec::lookupOrCreateViewBoxWrapper(SVGViewSpec* ownerType)
172{
173    ASSERT(ownerType);
174    ASSERT(ownerType->contextElement());
175    return SVGAnimatedProperty::lookupOrCreateWrapper<SVGElement, SVGAnimatedRect, FloatRect>(ownerType->contextElement(), viewBoxPropertyInfo(), ownerType->m_viewBox);
176}
177
178PassRefPtr<SVGAnimatedProperty> SVGViewSpec::lookupOrCreatePreserveAspectRatioWrapper(SVGViewSpec* ownerType)
179{
180    ASSERT(ownerType);
181    ASSERT(ownerType->contextElement());
182    return SVGAnimatedProperty::lookupOrCreateWrapper<SVGElement, SVGAnimatedPreserveAspectRatio, SVGPreserveAspectRatio>(ownerType->contextElement(), preserveAspectRatioPropertyInfo(), ownerType->m_preserveAspectRatio);
183}
184
185PassRefPtr<SVGAnimatedProperty> SVGViewSpec::lookupOrCreateTransformWrapper(SVGViewSpec* ownerType)
186{
187    ASSERT(ownerType);
188    ASSERT(ownerType->contextElement());
189    return SVGAnimatedProperty::lookupOrCreateWrapper<SVGElement, SVGAnimatedTransformList, SVGTransformList>(ownerType->contextElement(), transformPropertyInfo(), ownerType->m_transform);
190}
191
192void SVGViewSpec::reset()
193{
194    m_zoomAndPan = SVGZoomAndPanMagnify;
195    m_transform.clear();
196    m_viewBox = FloatRect();
197    m_preserveAspectRatio = SVGPreserveAspectRatio();
198    m_viewTargetString = emptyString();
199}
200
201static const UChar svgViewSpec[] = {'s', 'v', 'g', 'V', 'i', 'e', 'w'};
202static const UChar viewBoxSpec[] = {'v', 'i', 'e', 'w', 'B', 'o', 'x'};
203static const UChar preserveAspectRatioSpec[] = {'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'A', 's', 'p', 'e', 'c', 't', 'R', 'a', 't', 'i', 'o'};
204static const UChar transformSpec[] = {'t', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm'};
205static const UChar zoomAndPanSpec[] = {'z', 'o', 'o', 'm', 'A', 'n', 'd', 'P', 'a', 'n'};
206static const UChar viewTargetSpec[] =  {'v', 'i', 'e', 'w', 'T', 'a', 'r', 'g', 'e', 't'};
207
208bool SVGViewSpec::parseViewSpec(const String& viewSpec)
209{
210    auto upconvertedCharacters = StringView(viewSpec).upconvertedCharacters();
211    const UChar* currViewSpec = upconvertedCharacters;
212    const UChar* end = currViewSpec + viewSpec.length();
213
214    if (currViewSpec >= end || !m_contextElement)
215        return false;
216
217    if (!skipString(currViewSpec, end, svgViewSpec, WTF_ARRAY_LENGTH(svgViewSpec)))
218        return false;
219
220    if (currViewSpec >= end || *currViewSpec != '(')
221        return false;
222    currViewSpec++;
223
224    while (currViewSpec < end && *currViewSpec != ')') {
225        if (*currViewSpec == 'v') {
226            if (skipString(currViewSpec, end, viewBoxSpec, WTF_ARRAY_LENGTH(viewBoxSpec))) {
227                if (currViewSpec >= end || *currViewSpec != '(')
228                    return false;
229                currViewSpec++;
230                FloatRect viewBox;
231                if (!SVGFitToViewBox::parseViewBox(&m_contextElement->document(), currViewSpec, end, viewBox, false))
232                    return false;
233                setViewBoxBaseValue(viewBox);
234                if (currViewSpec >= end || *currViewSpec != ')')
235                    return false;
236                currViewSpec++;
237            } else if (skipString(currViewSpec, end, viewTargetSpec, WTF_ARRAY_LENGTH(viewTargetSpec))) {
238                if (currViewSpec >= end || *currViewSpec != '(')
239                    return false;
240                const UChar* viewTargetStart = ++currViewSpec;
241                while (currViewSpec < end && *currViewSpec != ')')
242                    currViewSpec++;
243                if (currViewSpec >= end)
244                    return false;
245                setViewTargetString(String(viewTargetStart, currViewSpec - viewTargetStart));
246                currViewSpec++;
247            } else
248                return false;
249        } else if (*currViewSpec == 'z') {
250            if (!skipString(currViewSpec, end, zoomAndPanSpec, WTF_ARRAY_LENGTH(zoomAndPanSpec)))
251                return false;
252            if (currViewSpec >= end || *currViewSpec != '(')
253                return false;
254            currViewSpec++;
255            if (!parseZoomAndPan(currViewSpec, end, m_zoomAndPan))
256                return false;
257            if (currViewSpec >= end || *currViewSpec != ')')
258                return false;
259            currViewSpec++;
260        } else if (*currViewSpec == 'p') {
261            if (!skipString(currViewSpec, end, preserveAspectRatioSpec, WTF_ARRAY_LENGTH(preserveAspectRatioSpec)))
262                return false;
263            if (currViewSpec >= end || *currViewSpec != '(')
264                return false;
265            currViewSpec++;
266            SVGPreserveAspectRatio preserveAspectRatio;
267            if (!preserveAspectRatio.parse(currViewSpec, end, false))
268                return false;
269            setPreserveAspectRatioBaseValue(preserveAspectRatio);
270            if (currViewSpec >= end || *currViewSpec != ')')
271                return false;
272            currViewSpec++;
273        } else if (*currViewSpec == 't') {
274            if (!skipString(currViewSpec, end, transformSpec, WTF_ARRAY_LENGTH(transformSpec)))
275                return false;
276            if (currViewSpec >= end || *currViewSpec != '(')
277                return false;
278            currViewSpec++;
279            SVGTransformable::parseTransformAttribute(m_transform, currViewSpec, end, SVGTransformable::DoNotClearList);
280            if (currViewSpec >= end || *currViewSpec != ')')
281                return false;
282            currViewSpec++;
283        } else
284            return false;
285
286        if (currViewSpec < end && *currViewSpec == ';')
287            currViewSpec++;
288    }
289
290    if (currViewSpec >= end || *currViewSpec != ')')
291        return false;
292
293    return true;
294}
295
296}
297