1/*
2 * Copyright (C) 2007, 2010 Rob Buis <buis@kde.org>
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#include "config.h"
22#include "SVGViewSpec.h"
23
24#include "Document.h"
25#include "SVGElement.h"
26#include "SVGFitToViewBox.h"
27#include "SVGNames.h"
28#include "SVGParserUtilities.h"
29#include "SVGTransformList.h"
30#include "SVGTransformable.h"
31
32namespace WebCore {
33
34SVGViewSpec::SVGViewSpec(SVGElement& contextElement)
35 : SVGFitToViewBox(&contextElement, SVGPropertyAccess::ReadOnly)
36 , m_contextElement(makeWeakPtr(contextElement))
37 , m_transform(SVGTransformList::create(&contextElement, SVGPropertyAccess::ReadOnly))
38{
39 static std::once_flag onceFlag;
40 std::call_once(onceFlag, [] {
41 PropertyRegistry::registerProperty<SVGNames::transformAttr, &SVGViewSpec::m_transform>();
42 });
43}
44
45SVGElement* SVGViewSpec::viewTarget() const
46{
47 if (!m_contextElement)
48 return nullptr;
49 auto* element = m_contextElement->treeScope().getElementById(m_viewTargetString);
50 if (!is<SVGElement>(element))
51 return nullptr;
52 return downcast<SVGElement>(element);
53}
54
55void SVGViewSpec::reset()
56{
57 m_viewTargetString = emptyString();
58 m_transform->clearItems();
59 SVGFitToViewBox::reset();
60 SVGZoomAndPan::reset();
61}
62
63static const UChar svgViewSpec[] = {'s', 'v', 'g', 'V', 'i', 'e', 'w'};
64static const UChar viewBoxSpec[] = {'v', 'i', 'e', 'w', 'B', 'o', 'x'};
65static const UChar preserveAspectRatioSpec[] = {'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'A', 's', 'p', 'e', 'c', 't', 'R', 'a', 't', 'i', 'o'};
66static const UChar transformSpec[] = {'t', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm'};
67static const UChar zoomAndPanSpec[] = {'z', 'o', 'o', 'm', 'A', 'n', 'd', 'P', 'a', 'n'};
68static const UChar viewTargetSpec[] = {'v', 'i', 'e', 'w', 'T', 'a', 'r', 'g', 'e', 't'};
69
70bool SVGViewSpec::parseViewSpec(const String& viewSpec)
71{
72 auto upconvertedCharacters = StringView(viewSpec).upconvertedCharacters();
73 const UChar* currViewSpec = upconvertedCharacters;
74 const UChar* end = currViewSpec + viewSpec.length();
75
76 if (currViewSpec >= end || !m_contextElement)
77 return false;
78
79 if (!skipString(currViewSpec, end, svgViewSpec, WTF_ARRAY_LENGTH(svgViewSpec)))
80 return false;
81
82 if (currViewSpec >= end || *currViewSpec != '(')
83 return false;
84 currViewSpec++;
85
86 while (currViewSpec < end && *currViewSpec != ')') {
87 if (*currViewSpec == 'v') {
88 if (skipString(currViewSpec, end, viewBoxSpec, WTF_ARRAY_LENGTH(viewBoxSpec))) {
89 if (currViewSpec >= end || *currViewSpec != '(')
90 return false;
91 currViewSpec++;
92 FloatRect viewBox;
93 if (!SVGFitToViewBox::parseViewBox(currViewSpec, end, viewBox, false))
94 return false;
95 setViewBox(viewBox);
96 if (currViewSpec >= end || *currViewSpec != ')')
97 return false;
98 currViewSpec++;
99 } else if (skipString(currViewSpec, end, viewTargetSpec, WTF_ARRAY_LENGTH(viewTargetSpec))) {
100 if (currViewSpec >= end || *currViewSpec != '(')
101 return false;
102 const UChar* viewTargetStart = ++currViewSpec;
103 while (currViewSpec < end && *currViewSpec != ')')
104 currViewSpec++;
105 if (currViewSpec >= end)
106 return false;
107 m_viewTargetString = String(viewTargetStart, currViewSpec - viewTargetStart);
108 currViewSpec++;
109 } else
110 return false;
111 } else if (*currViewSpec == 'z') {
112 if (!skipString(currViewSpec, end, zoomAndPanSpec, WTF_ARRAY_LENGTH(zoomAndPanSpec)))
113 return false;
114 if (currViewSpec >= end || *currViewSpec != '(')
115 return false;
116 currViewSpec++;
117 if (!SVGZoomAndPan::parseZoomAndPan(currViewSpec, end))
118 return false;
119 if (currViewSpec >= end || *currViewSpec != ')')
120 return false;
121 currViewSpec++;
122 } else if (*currViewSpec == 'p') {
123 if (!skipString(currViewSpec, end, preserveAspectRatioSpec, WTF_ARRAY_LENGTH(preserveAspectRatioSpec)))
124 return false;
125 if (currViewSpec >= end || *currViewSpec != '(')
126 return false;
127 currViewSpec++;
128 SVGPreserveAspectRatioValue preserveAspectRatio;
129 if (!preserveAspectRatio.parse(currViewSpec, end, false))
130 return false;
131 setPreserveAspectRatio(preserveAspectRatio);
132 if (currViewSpec >= end || *currViewSpec != ')')
133 return false;
134 currViewSpec++;
135 } else if (*currViewSpec == 't') {
136 if (!skipString(currViewSpec, end, transformSpec, WTF_ARRAY_LENGTH(transformSpec)))
137 return false;
138 if (currViewSpec >= end || *currViewSpec != '(')
139 return false;
140 currViewSpec++;
141 m_transform->parse(currViewSpec, end);
142 if (currViewSpec >= end || *currViewSpec != ')')
143 return false;
144 currViewSpec++;
145 } else
146 return false;
147
148 if (currViewSpec < end && *currViewSpec == ';')
149 currViewSpec++;
150 }
151
152 if (currViewSpec >= end || *currViewSpec != ')')
153 return false;
154
155 return true;
156}
157
158}
159