1/*
2 * Copyright (C) 2011 Adobe Systems Incorporated. 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 *
8 * 1. Redistributions of source code must retain the above
9 *    copyright notice, this list of conditions and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 *    copyright notice, this list of conditions and the following
13 *    disclaimer in the documentation and/or other materials
14 *    provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "core/css/CSSBasicShapes.h"
32
33#include "wtf/text/StringBuilder.h"
34
35using namespace WTF;
36
37namespace WebCore {
38
39static String buildRectangleString(const String& x, const String& y, const String& width, const String& height, const String& radiusX, const String& radiusY)
40{
41    char opening[] = "rectangle(";
42    char separator[] = ", ";
43    StringBuilder result;
44    // Compute the required capacity in advance to reduce allocations.
45    result.reserveCapacity((sizeof(opening) - 1) + (5 * (sizeof(separator) - 1)) + 1 + x.length() + y.length() + width.length() + height.length() + radiusX.length() + radiusY.length());
46    result.appendLiteral(opening);
47    result.append(x);
48    result.appendLiteral(separator);
49    result.append(y);
50    result.appendLiteral(separator);
51    result.append(width);
52    result.appendLiteral(separator);
53    result.append(height);
54    if (!radiusX.isNull()) {
55        result.appendLiteral(separator);
56        result.append(radiusX);
57        if (!radiusY.isNull()) {
58            result.appendLiteral(separator);
59            result.append(radiusY);
60        }
61    }
62    result.append(')');
63    return result.toString();
64}
65
66String CSSBasicShapeRectangle::cssText() const
67{
68    return buildRectangleString(m_x->cssText(),
69        m_y->cssText(),
70        m_width->cssText(),
71        m_height->cssText(),
72        m_radiusX.get() ? m_radiusX->cssText() : String(),
73        m_radiusY.get() ? m_radiusY->cssText() : String());
74}
75
76bool CSSBasicShapeRectangle::equals(const CSSBasicShape& shape) const
77{
78    if (shape.type() != CSSBasicShapeRectangleType)
79        return false;
80
81    const CSSBasicShapeRectangle& other = static_cast<const CSSBasicShapeRectangle&>(shape);
82    return compareCSSValuePtr(m_x, other.m_x)
83        && compareCSSValuePtr(m_y, other.m_y)
84        && compareCSSValuePtr(m_width, other.m_width)
85        && compareCSSValuePtr(m_height, other.m_height)
86        && compareCSSValuePtr(m_radiusX, other.m_radiusX)
87        && compareCSSValuePtr(m_radiusY, other.m_radiusY);
88}
89
90String CSSBasicShapeRectangle::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
91{
92    return buildRectangleString(m_x->serializeResolvingVariables(variables),
93        m_y->serializeResolvingVariables(variables),
94        m_width->serializeResolvingVariables(variables),
95        m_height->serializeResolvingVariables(variables),
96        m_radiusX.get() ? m_radiusX->serializeResolvingVariables(variables) : String(),
97        m_radiusY.get() ? m_radiusY->serializeResolvingVariables(variables) : String());
98}
99
100bool CSSBasicShapeRectangle::hasVariableReference() const
101{
102    return m_x->hasVariableReference()
103        || m_y->hasVariableReference()
104        || m_width->hasVariableReference()
105        || m_height->hasVariableReference()
106        || (m_radiusX.get() && m_radiusX->hasVariableReference())
107        || (m_radiusY.get() && m_radiusY->hasVariableReference());
108}
109
110static String buildCircleString(const String& x, const String& y, const String& radius)
111{
112    return "circle(" + x + ", " + y + ", " + radius + ')';
113}
114
115String CSSBasicShapeCircle::cssText() const
116{
117    return buildCircleString(m_centerX->cssText(), m_centerY->cssText(), m_radius->cssText());
118}
119
120bool CSSBasicShapeCircle::equals(const CSSBasicShape& shape) const
121{
122    if (shape.type() != CSSBasicShapeCircleType)
123        return false;
124
125    const CSSBasicShapeCircle& other = static_cast<const CSSBasicShapeCircle&>(shape);
126    return compareCSSValuePtr(m_centerX, other.m_centerX)
127        && compareCSSValuePtr(m_centerY, other.m_centerY)
128        && compareCSSValuePtr(m_radius, other.m_radius);
129}
130
131String CSSBasicShapeCircle::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
132{
133    return buildCircleString(m_centerX->serializeResolvingVariables(variables),
134        m_centerY->serializeResolvingVariables(variables),
135        m_radius->serializeResolvingVariables(variables));
136}
137
138bool CSSBasicShapeCircle::hasVariableReference() const
139{
140    return m_centerX->hasVariableReference()
141        || m_centerY->hasVariableReference()
142        || m_radius->hasVariableReference();
143}
144
145static String buildEllipseString(const String& x, const String& y, const String& radiusX, const String& radiusY)
146{
147    return "ellipse(" + x + ", " + y + ", " + radiusX + ", " + radiusY + ')';
148}
149
150String CSSBasicShapeEllipse::cssText() const
151{
152    return buildEllipseString(m_centerX->cssText(), m_centerY->cssText(), m_radiusX->cssText(), m_radiusY->cssText());
153}
154
155bool CSSBasicShapeEllipse::equals(const CSSBasicShape& shape) const
156{
157    if (shape.type() != CSSBasicShapeEllipseType)
158        return false;
159
160    const CSSBasicShapeEllipse& other = static_cast<const CSSBasicShapeEllipse&>(shape);
161    return compareCSSValuePtr(m_centerX, other.m_centerX)
162        && compareCSSValuePtr(m_centerY, other.m_centerY)
163        && compareCSSValuePtr(m_radiusX, other.m_radiusX)
164        && compareCSSValuePtr(m_radiusY, other.m_radiusY);
165}
166
167String CSSBasicShapeEllipse::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
168{
169    return buildEllipseString(m_centerX->serializeResolvingVariables(variables),
170        m_centerY->serializeResolvingVariables(variables),
171        m_radiusX->serializeResolvingVariables(variables),
172        m_radiusY->serializeResolvingVariables(variables));
173}
174
175bool CSSBasicShapeEllipse::hasVariableReference() const
176{
177    return m_centerX->hasVariableReference()
178        || m_centerY->hasVariableReference()
179        || m_radiusX->hasVariableReference()
180        || m_radiusY->hasVariableReference();
181}
182
183static String buildPolygonString(const WindRule& windRule, const Vector<String>& points)
184{
185    ASSERT(!(points.size() % 2));
186
187    StringBuilder result;
188    char evenOddOpening[] = "polygon(evenodd, ";
189    char nonZeroOpening[] = "polygon(nonzero, ";
190    char commaSeparator[] = ", ";
191    COMPILE_ASSERT(sizeof(evenOddOpening) == sizeof(nonZeroOpening), polygon_string_openings_have_same_length);
192
193    // Compute the required capacity in advance to reduce allocations.
194    size_t length = sizeof(evenOddOpening) - 1;
195    for (size_t i = 0; i < points.size(); i += 2) {
196        if (i)
197            length += (sizeof(commaSeparator) - 1);
198        // add length of two strings, plus one for the space separator.
199        length += points[i].length() + 1 + points[i + 1].length();
200    }
201    result.reserveCapacity(length);
202
203    if (windRule == RULE_EVENODD)
204        result.appendLiteral(evenOddOpening);
205    else
206        result.appendLiteral(nonZeroOpening);
207
208    for (size_t i = 0; i < points.size(); i += 2) {
209        if (i)
210            result.appendLiteral(commaSeparator);
211        result.append(points[i]);
212        result.append(' ');
213        result.append(points[i + 1]);
214    }
215
216    result.append(')');
217
218    return result.toString();
219}
220
221String CSSBasicShapePolygon::cssText() const
222{
223    Vector<String> points;
224    points.reserveInitialCapacity(m_values.size());
225
226    for (size_t i = 0; i < m_values.size(); ++i)
227        points.append(m_values.at(i)->cssText());
228
229    return buildPolygonString(m_windRule, points);
230}
231
232bool CSSBasicShapePolygon::equals(const CSSBasicShape& shape) const
233{
234    if (shape.type() != CSSBasicShapePolygonType)
235        return false;
236
237    const CSSBasicShapePolygon& rhs = static_cast<const CSSBasicShapePolygon&>(shape);
238    return compareCSSValueVector<CSSPrimitiveValue>(m_values, rhs.m_values);
239}
240
241String CSSBasicShapePolygon::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
242{
243    Vector<String> points;
244    points.reserveInitialCapacity(m_values.size());
245
246    for (size_t i = 0; i < m_values.size(); ++i)
247        points.append(m_values.at(i)->serializeResolvingVariables(variables));
248
249    return buildPolygonString(m_windRule, points);
250}
251
252bool CSSBasicShapePolygon::hasVariableReference() const
253{
254    for (size_t i = 0; i < m_values.size(); ++i) {
255        if (m_values.at(i)->hasVariableReference())
256            return true;
257    }
258    return false;
259}
260
261static String buildInsetRectangleString(const String& top, const String& right, const String& bottom, const String& left, const String& radiusX, const String& radiusY)
262{
263    char opening[] = "inset-rectangle(";
264    char separator[] = ", ";
265    StringBuilder result;
266    // Compute the required capacity in advance to reduce allocations.
267    result.reserveCapacity((sizeof(opening) - 1) + (5 * (sizeof(separator) - 1)) + 1 + top.length() + right.length() + bottom.length() + left.length() + radiusX.length() + radiusY.length());
268    result.appendLiteral(opening);
269    result.append(top);
270    result.appendLiteral(separator);
271    result.append(right);
272    result.appendLiteral(separator);
273    result.append(bottom);
274    result.appendLiteral(separator);
275    result.append(left);
276    if (!radiusX.isNull()) {
277        result.appendLiteral(separator);
278        result.append(radiusX);
279        if (!radiusY.isNull()) {
280            result.appendLiteral(separator);
281            result.append(radiusY);
282        }
283    }
284    result.append(')');
285    return result.toString();
286}
287
288String CSSBasicShapeInsetRectangle::cssText() const
289{
290    return buildInsetRectangleString(m_top->cssText(),
291        m_right->cssText(),
292        m_bottom->cssText(),
293        m_left->cssText(),
294        m_radiusX.get() ? m_radiusX->cssText() : String(),
295        m_radiusY.get() ? m_radiusY->cssText() : String());
296}
297
298bool CSSBasicShapeInsetRectangle::equals(const CSSBasicShape& shape) const
299{
300    if (shape.type() != CSSBasicShapeInsetRectangleType)
301        return false;
302
303    const CSSBasicShapeInsetRectangle& other = static_cast<const CSSBasicShapeInsetRectangle&>(shape);
304    return compareCSSValuePtr(m_top, other.m_top)
305        && compareCSSValuePtr(m_right, other.m_right)
306        && compareCSSValuePtr(m_bottom, other.m_bottom)
307        && compareCSSValuePtr(m_left, other.m_left)
308        && compareCSSValuePtr(m_radiusX, other.m_radiusX)
309        && compareCSSValuePtr(m_radiusY, other.m_radiusY);
310}
311
312String CSSBasicShapeInsetRectangle::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
313{
314    return buildInsetRectangleString(m_top->serializeResolvingVariables(variables),
315        m_right->serializeResolvingVariables(variables),
316        m_bottom->serializeResolvingVariables(variables),
317        m_left->serializeResolvingVariables(variables),
318        m_radiusX.get() ? m_radiusX->serializeResolvingVariables(variables) : String(),
319        m_radiusY.get() ? m_radiusY->serializeResolvingVariables(variables) : String());
320}
321
322bool CSSBasicShapeInsetRectangle::hasVariableReference() const
323{
324    return m_top->hasVariableReference()
325        || m_right->hasVariableReference()
326        || m_bottom->hasVariableReference()
327        || m_left->hasVariableReference()
328        || (m_radiusX.get() && m_radiusX->hasVariableReference())
329        || (m_radiusY.get() && m_radiusY->hasVariableReference());
330}
331
332} // namespace WebCore
333
334