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
32#include "CSSBasicShapes.h"
33
34#include "CSSPrimitiveValueMappings.h"
35
36#include <wtf/text/StringBuilder.h>
37
38using namespace WTF;
39
40namespace WebCore {
41
42static String buildRectangleString(const String& x, const String& y, const String& width, const String& height, const String& radiusX, const String& radiusY)
43{
44    char opening[] = "rectangle(";
45    char separator[] = ", ";
46    StringBuilder result;
47    // Compute the required capacity in advance to reduce allocations.
48    result.reserveCapacity((sizeof(opening) - 1) + (5 * (sizeof(separator) - 1)) + 1 + x.length() + y.length() + width.length() + height.length() + radiusX.length() + radiusY.length());
49    result.appendLiteral(opening);
50    result.append(x);
51    result.appendLiteral(separator);
52    result.append(y);
53    result.appendLiteral(separator);
54    result.append(width);
55    result.appendLiteral(separator);
56    result.append(height);
57    if (!radiusX.isNull()) {
58        result.appendLiteral(separator);
59        result.append(radiusX);
60        if (!radiusY.isNull()) {
61            result.appendLiteral(separator);
62            result.append(radiusY);
63        }
64    }
65    result.append(')');
66    return result.toString();
67}
68
69String CSSBasicShapeRectangle::cssText() const
70{
71    return buildRectangleString(m_x->cssText(),
72        m_y->cssText(),
73        m_width->cssText(),
74        m_height->cssText(),
75        m_radiusX.get() ? m_radiusX->cssText() : String(),
76        m_radiusY.get() ? m_radiusY->cssText() : String());
77}
78
79bool CSSBasicShapeRectangle::equals(const CSSBasicShape& shape) const
80{
81    if (shape.type() != CSSBasicShapeRectangleType)
82        return false;
83
84    const CSSBasicShapeRectangle& other = static_cast<const CSSBasicShapeRectangle&>(shape);
85    return compareCSSValuePtr(m_x, other.m_x)
86        && compareCSSValuePtr(m_y, other.m_y)
87        && compareCSSValuePtr(m_width, other.m_width)
88        && compareCSSValuePtr(m_height, other.m_height)
89        && compareCSSValuePtr(m_radiusX, other.m_radiusX)
90        && compareCSSValuePtr(m_radiusY, other.m_radiusY);
91}
92
93#if ENABLE(CSS_VARIABLES)
94String CSSBasicShapeRectangle::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
95{
96    return buildRectangleString(m_x->serializeResolvingVariables(variables),
97        m_y->serializeResolvingVariables(variables),
98        m_width->serializeResolvingVariables(variables),
99        m_height->serializeResolvingVariables(variables),
100        m_radiusX.get() ? m_radiusX->serializeResolvingVariables(variables) : String(),
101        m_radiusY.get() ? m_radiusY->serializeResolvingVariables(variables) : String());
102}
103
104bool CSSBasicShapeRectangle::hasVariableReference() const
105{
106    return m_x->hasVariableReference()
107        || m_y->hasVariableReference()
108        || m_width->hasVariableReference()
109        || m_height->hasVariableReference()
110        || (m_radiusX.get() && m_radiusX->hasVariableReference())
111        || (m_radiusY.get() && m_radiusY->hasVariableReference());
112}
113#endif
114
115static String buildCircleString(const String& x, const String& y, const String& radius)
116{
117    return "circle(" + x + ", " + y + ", " + radius + ')';
118}
119
120String CSSBasicShapeCircle::cssText() const
121{
122    return buildCircleString(m_centerX->cssText(), m_centerY->cssText(), m_radius->cssText());
123}
124
125bool CSSBasicShapeCircle::equals(const CSSBasicShape& shape) const
126{
127    if (shape.type() != CSSBasicShapeCircleType)
128        return false;
129
130    const CSSBasicShapeCircle& other = static_cast<const CSSBasicShapeCircle&>(shape);
131    return compareCSSValuePtr(m_centerX, other.m_centerX)
132        && compareCSSValuePtr(m_centerY, other.m_centerY)
133        && compareCSSValuePtr(m_radius, other.m_radius);
134}
135
136#if ENABLE(CSS_VARIABLES)
137String CSSBasicShapeCircle::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
138{
139    return buildCircleString(m_centerX->serializeResolvingVariables(variables),
140        m_centerY->serializeResolvingVariables(variables),
141        m_radius->serializeResolvingVariables(variables));
142}
143
144bool CSSBasicShapeCircle::hasVariableReference() const
145{
146    return m_centerX->hasVariableReference()
147        || m_centerY->hasVariableReference()
148        || m_radius->hasVariableReference();
149}
150#endif
151
152static String buildEllipseString(const String& x, const String& y, const String& radiusX, const String& radiusY)
153{
154    return "ellipse(" + x + ", " + y + ", " + radiusX + ", " + radiusY + ')';
155}
156
157String CSSBasicShapeEllipse::cssText() const
158{
159    return buildEllipseString(m_centerX->cssText(), m_centerY->cssText(), m_radiusX->cssText(), m_radiusY->cssText());
160}
161
162bool CSSBasicShapeEllipse::equals(const CSSBasicShape& shape) const
163{
164    if (shape.type() != CSSBasicShapeEllipseType)
165        return false;
166
167    const CSSBasicShapeEllipse& other = static_cast<const CSSBasicShapeEllipse&>(shape);
168    return compareCSSValuePtr(m_centerX, other.m_centerX)
169        && compareCSSValuePtr(m_centerY, other.m_centerY)
170        && compareCSSValuePtr(m_radiusX, other.m_radiusX)
171        && compareCSSValuePtr(m_radiusY, other.m_radiusY);
172}
173
174#if ENABLE(CSS_VARIABLES)
175String CSSBasicShapeEllipse::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
176{
177    return buildEllipseString(m_centerX->serializeResolvingVariables(variables),
178        m_centerY->serializeResolvingVariables(variables),
179        m_radiusX->serializeResolvingVariables(variables),
180        m_radiusY->serializeResolvingVariables(variables));
181}
182
183bool CSSBasicShapeEllipse::hasVariableReference() const
184{
185    return m_centerX->hasVariableReference()
186        || m_centerY->hasVariableReference()
187        || m_radiusX->hasVariableReference()
188        || m_radiusY->hasVariableReference();
189}
190#endif
191
192static String buildPolygonString(const WindRule& windRule, const Vector<String>& points)
193{
194    ASSERT(!(points.size() % 2));
195
196    StringBuilder result;
197    char evenOddOpening[] = "polygon(evenodd, ";
198    char nonZeroOpening[] = "polygon(nonzero, ";
199    char commaSeparator[] = ", ";
200    COMPILE_ASSERT(sizeof(evenOddOpening) == sizeof(nonZeroOpening), polygon_string_openings_have_same_length);
201
202    // Compute the required capacity in advance to reduce allocations.
203    size_t length = sizeof(evenOddOpening) - 1;
204    for (size_t i = 0; i < points.size(); i += 2) {
205        if (i)
206            length += (sizeof(commaSeparator) - 1);
207        // add length of two strings, plus one for the space separator.
208        length += points[i].length() + 1 + points[i + 1].length();
209    }
210    result.reserveCapacity(length);
211
212    if (windRule == RULE_EVENODD)
213        result.appendLiteral(evenOddOpening);
214    else
215        result.appendLiteral(nonZeroOpening);
216
217    for (size_t i = 0; i < points.size(); i += 2) {
218        if (i)
219            result.appendLiteral(commaSeparator);
220        result.append(points[i]);
221        result.append(' ');
222        result.append(points[i + 1]);
223    }
224
225    result.append(')');
226
227    return result.toString();
228}
229
230String CSSBasicShapePolygon::cssText() const
231{
232    Vector<String> points;
233    points.reserveInitialCapacity(m_values.size());
234
235    for (size_t i = 0; i < m_values.size(); ++i)
236        points.append(m_values.at(i)->cssText());
237
238    return buildPolygonString(m_windRule, points);
239}
240
241bool CSSBasicShapePolygon::equals(const CSSBasicShape& shape) const
242{
243    if (shape.type() != CSSBasicShapePolygonType)
244        return false;
245
246    const CSSBasicShapePolygon& rhs = static_cast<const CSSBasicShapePolygon&>(shape);
247    return compareCSSValueVector<CSSPrimitiveValue>(m_values, rhs.m_values);
248}
249
250#if ENABLE(CSS_VARIABLES)
251String CSSBasicShapePolygon::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
252{
253    Vector<String> points;
254    points.reserveInitialCapacity(m_values.size());
255
256    for (size_t i = 0; i < m_values.size(); ++i)
257        points.append(m_values.at(i)->serializeResolvingVariables(variables));
258
259    return buildPolygonString(m_windRule, points);
260}
261
262bool CSSBasicShapePolygon::hasVariableReference() const
263{
264    for (size_t i = 0; i < m_values.size(); ++i) {
265        if (m_values.at(i)->hasVariableReference())
266            return true;
267    }
268    return false;
269}
270#endif
271
272static String buildInsetRectangleString(const String& top, const String& right, const String& bottom, const String& left, const String& radiusX, const String& radiusY)
273{
274    char opening[] = "inset-rectangle(";
275    char separator[] = ", ";
276    StringBuilder result;
277    // Compute the required capacity in advance to reduce allocations.
278    result.reserveCapacity((sizeof(opening) - 1) + (5 * (sizeof(separator) - 1)) + 1 + top.length() + right.length() + bottom.length() + left.length() + radiusX.length() + radiusY.length());
279    result.appendLiteral(opening);
280    result.append(top);
281    result.appendLiteral(separator);
282    result.append(right);
283    result.appendLiteral(separator);
284    result.append(bottom);
285    result.appendLiteral(separator);
286    result.append(left);
287    if (!radiusX.isNull()) {
288        result.appendLiteral(separator);
289        result.append(radiusX);
290        if (!radiusY.isNull()) {
291            result.appendLiteral(separator);
292            result.append(radiusY);
293        }
294    }
295    result.append(')');
296    return result.toString();
297}
298
299String CSSBasicShapeInsetRectangle::cssText() const
300{
301    return buildInsetRectangleString(m_top->cssText(),
302        m_right->cssText(),
303        m_bottom->cssText(),
304        m_left->cssText(),
305        m_radiusX.get() ? m_radiusX->cssText() : String(),
306        m_radiusY.get() ? m_radiusY->cssText() : String());
307}
308
309bool CSSBasicShapeInsetRectangle::equals(const CSSBasicShape& shape) const
310{
311    if (shape.type() != CSSBasicShapeInsetRectangleType)
312        return false;
313
314    const CSSBasicShapeInsetRectangle& other = static_cast<const CSSBasicShapeInsetRectangle&>(shape);
315    return compareCSSValuePtr(m_top, other.m_top)
316        && compareCSSValuePtr(m_right, other.m_right)
317        && compareCSSValuePtr(m_bottom, other.m_bottom)
318        && compareCSSValuePtr(m_left, other.m_left)
319        && compareCSSValuePtr(m_radiusX, other.m_radiusX)
320        && compareCSSValuePtr(m_radiusY, other.m_radiusY);
321}
322
323#if ENABLE(CSS_VARIABLES)
324String CSSBasicShapeInsetRectangle::serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
325{
326    return buildInsetRectangleString(m_top->serializeResolvingVariables(variables),
327        m_right->serializeResolvingVariables(variables),
328        m_bottom->serializeResolvingVariables(variables),
329        m_left->serializeResolvingVariables(variables),
330        m_radiusX.get() ? m_radiusX->serializeResolvingVariables(variables) : String(),
331        m_radiusY.get() ? m_radiusY->serializeResolvingVariables(variables) : String());
332}
333
334bool CSSBasicShapeInsetRectangle::hasVariableReference() const
335{
336    return m_top->hasVariableReference()
337        || m_right->hasVariableReference()
338        || m_bottom->hasVariableReference()
339        || m_left->hasVariableReference()
340        || (m_radiusX.get() && m_radiusX->hasVariableReference())
341        || (m_radiusY.get() && m_radiusY->hasVariableReference());
342}
343#endif
344
345} // namespace WebCore
346
347