Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / CSSBasicShapes.cpp
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 "core/css/CSSValuePool.h"
34 #include "core/css/Pair.h"
35 #include "platform/Length.h"
36 #include "wtf/text/StringBuilder.h"
37
38 using namespace WTF;
39
40 namespace WebCore {
41
42 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CSSBasicShape)
43
44 static String buildCircleString(const String& radius, const String& centerX, const String& centerY, const String& box)
45 {
46     char at[] = "at";
47     char separator[] = " ";
48     StringBuilder result;
49     result.appendLiteral("circle(");
50     if (!radius.isNull())
51         result.append(radius);
52
53     if (!centerX.isNull() || !centerY.isNull()) {
54         if (!radius.isNull())
55             result.appendLiteral(separator);
56         result.append(at);
57         result.appendLiteral(separator);
58         result.append(centerX);
59         result.appendLiteral(separator);
60         result.append(centerY);
61     }
62     result.append(")");
63     if (box.length()) {
64         result.appendLiteral(separator);
65         result.append(box);
66     }
67     return result.toString();
68 }
69
70 static String serializePositionOffset(const Pair& offset, const Pair& other)
71 {
72     if ((offset.first()->getValueID() == CSSValueLeft && other.first()->getValueID() == CSSValueTop)
73         || (offset.first()->getValueID() == CSSValueTop && other.first()->getValueID() == CSSValueLeft))
74         return offset.second()->cssText();
75     return offset.cssText();
76 }
77
78 static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> buildSerializablePositionOffset(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> offset, CSSValueID defaultSide)
79 {
80     CSSValueID side = defaultSide;
81     RefPtrWillBeRawPtr<CSSPrimitiveValue> amount = nullptr;
82
83     if (!offset) {
84         side = CSSValueCenter;
85     } else if (offset->isValueID()) {
86         side = offset->getValueID();
87     } else if (Pair* pair = offset->getPairValue()) {
88         side = pair->first()->getValueID();
89         amount = pair->second();
90     } else {
91         amount = offset;
92     }
93
94     if (side == CSSValueCenter) {
95         side = defaultSide;
96         amount = cssValuePool().createValue(Length(50, Percent));
97     } else if ((side == CSSValueRight || side == CSSValueBottom)
98         && amount->isPercentage()) {
99         side = defaultSide;
100         amount = cssValuePool().createValue(Length(100 - amount->getFloatValue(), Percent));
101     } else if (amount->isLength() && !amount->getFloatValue()) {
102         if (side == CSSValueRight || side == CSSValueBottom)
103             amount = cssValuePool().createValue(Length(100, Percent));
104         else
105             amount = cssValuePool().createValue(Length(0, Percent));
106         side = defaultSide;
107     }
108
109     return cssValuePool().createValue(Pair::create(cssValuePool().createValue(side), amount.release(), Pair::KeepIdenticalValues));
110 }
111
112 String CSSBasicShapeCircle::cssText() const
113 {
114     RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
115     RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
116
117     return buildCircleString(m_radius ? m_radius->cssText() : String(),
118         serializePositionOffset(*normalizedCX->getPairValue(), *normalizedCY->getPairValue()),
119         serializePositionOffset(*normalizedCY->getPairValue(), *normalizedCX->getPairValue()),
120         m_referenceBox ? m_referenceBox->cssText() : String());
121 }
122
123 bool CSSBasicShapeCircle::equals(const CSSBasicShape& shape) const
124 {
125     if (shape.type() != CSSBasicShapeCircleType)
126         return false;
127
128     const CSSBasicShapeCircle& other = static_cast<const CSSBasicShapeCircle&>(shape);
129     return compareCSSValuePtr(m_centerX, other.m_centerX)
130         && compareCSSValuePtr(m_centerY, other.m_centerY)
131         && compareCSSValuePtr(m_radius, other.m_radius)
132         && compareCSSValuePtr(m_referenceBox, other.m_referenceBox);
133 }
134
135 void CSSBasicShapeCircle::trace(Visitor* visitor)
136 {
137     visitor->trace(m_centerX);
138     visitor->trace(m_centerY);
139     visitor->trace(m_radius);
140     CSSBasicShape::trace(visitor);
141 }
142
143 static String buildEllipseString(const String& radiusX, const String& radiusY, const String& centerX, const String& centerY, const String& box)
144 {
145     char at[] = "at";
146     char separator[] = " ";
147     StringBuilder result;
148     result.appendLiteral("ellipse(");
149     bool needsSeparator = false;
150     if (!radiusX.isNull()) {
151         result.append(radiusX);
152         needsSeparator = true;
153     }
154     if (!radiusY.isNull()) {
155         if (needsSeparator)
156             result.appendLiteral(separator);
157         result.append(radiusY);
158         needsSeparator = true;
159     }
160
161     if (!centerX.isNull() || !centerY.isNull()) {
162         if (needsSeparator)
163             result.appendLiteral(separator);
164         result.appendLiteral(at);
165         result.appendLiteral(separator);
166         result.append(centerX);
167         result.appendLiteral(separator);
168         result.append(centerY);
169     }
170     result.append(")");
171     if (box.length()) {
172         result.appendLiteral(separator);
173         result.append(box);
174     }
175     return result.toString();
176 }
177
178 String CSSBasicShapeEllipse::cssText() const
179 {
180     RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
181     RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
182
183     return buildEllipseString(m_radiusX ? m_radiusX->cssText() : String(),
184         m_radiusY ? m_radiusY->cssText() : String(),
185         serializePositionOffset(*normalizedCX->getPairValue(), *normalizedCY->getPairValue()),
186         serializePositionOffset(*normalizedCY->getPairValue(), *normalizedCX->getPairValue()),
187         m_referenceBox ? m_referenceBox->cssText() : String());
188 }
189
190 bool CSSBasicShapeEllipse::equals(const CSSBasicShape& shape) const
191 {
192     if (shape.type() != CSSBasicShapeEllipseType)
193         return false;
194
195     const CSSBasicShapeEllipse& other = static_cast<const CSSBasicShapeEllipse&>(shape);
196     return compareCSSValuePtr(m_centerX, other.m_centerX)
197         && compareCSSValuePtr(m_centerY, other.m_centerY)
198         && compareCSSValuePtr(m_radiusX, other.m_radiusX)
199         && compareCSSValuePtr(m_radiusY, other.m_radiusY)
200         && compareCSSValuePtr(m_referenceBox, other.m_referenceBox);
201 }
202
203 void CSSBasicShapeEllipse::trace(Visitor* visitor)
204 {
205     visitor->trace(m_centerX);
206     visitor->trace(m_centerY);
207     visitor->trace(m_radiusX);
208     visitor->trace(m_radiusY);
209     CSSBasicShape::trace(visitor);
210 }
211
212 static String buildPolygonString(const WindRule& windRule, const Vector<String>& points, const String& box)
213 {
214     ASSERT(!(points.size() % 2));
215
216     StringBuilder result;
217     const char evenOddOpening[] = "polygon(evenodd, ";
218     const char nonZeroOpening[] = "polygon(";
219     const char commaSeparator[] = ", ";
220     COMPILE_ASSERT(sizeof(evenOddOpening) > sizeof(nonZeroOpening), polygon_string_openings_have_same_length);
221
222     // Compute the required capacity in advance to reduce allocations.
223     size_t length = sizeof(evenOddOpening) - 1;
224     for (size_t i = 0; i < points.size(); i += 2) {
225         if (i)
226             length += (sizeof(commaSeparator) - 1);
227         // add length of two strings, plus one for the space separator.
228         length += points[i].length() + 1 + points[i + 1].length();
229     }
230     if (!box.isEmpty())
231         length += box.length() + 1;
232     result.reserveCapacity(length);
233
234     if (windRule == RULE_EVENODD)
235         result.appendLiteral(evenOddOpening);
236     else
237         result.appendLiteral(nonZeroOpening);
238
239     for (size_t i = 0; i < points.size(); i += 2) {
240         if (i)
241             result.appendLiteral(commaSeparator);
242         result.append(points[i]);
243         result.append(' ');
244         result.append(points[i + 1]);
245     }
246
247     result.append(')');
248
249     if (!box.isEmpty()) {
250         result.append(' ');
251         result.append(box);
252     }
253
254     return result.toString();
255 }
256
257 String CSSBasicShapePolygon::cssText() const
258 {
259     Vector<String> points;
260     points.reserveInitialCapacity(m_values.size());
261
262     for (size_t i = 0; i < m_values.size(); ++i)
263         points.append(m_values.at(i)->cssText());
264
265     return buildPolygonString(m_windRule, points, m_referenceBox ? m_referenceBox->cssText() : String());
266 }
267
268 bool CSSBasicShapePolygon::equals(const CSSBasicShape& shape) const
269 {
270     if (shape.type() != CSSBasicShapePolygonType)
271         return false;
272
273     const CSSBasicShapePolygon& rhs = static_cast<const CSSBasicShapePolygon&>(shape);
274
275     if (!compareCSSValuePtr(m_referenceBox, rhs.m_referenceBox))
276         return false;
277
278     return compareCSSValueVector(m_values, rhs.m_values);
279 }
280
281 void CSSBasicShapePolygon::trace(Visitor* visitor)
282 {
283     visitor->trace(m_values);
284     CSSBasicShape::trace(visitor);
285 }
286
287 static bool buildInsetRadii(Vector<String> &radii, const String& topLeftRadius, const String& topRightRadius, const String& bottomRightRadius, const String& bottomLeftRadius)
288 {
289     bool showBottomLeft = topRightRadius != bottomLeftRadius;
290     bool showBottomRight = showBottomLeft || (bottomRightRadius != topLeftRadius);
291     bool showTopRight = showBottomRight || (topRightRadius != topLeftRadius);
292
293     radii.append(topLeftRadius);
294     if (showTopRight)
295         radii.append(topRightRadius);
296     if (showBottomRight)
297         radii.append(bottomRightRadius);
298     if (showBottomLeft)
299         radii.append(bottomLeftRadius);
300
301     return radii.size() == 1 && radii[0] == "0px";
302 }
303
304 static String buildInsetString(const String& top, const String& right, const String& bottom, const String& left,
305     const String& topLeftRadiusWidth, const String& topLeftRadiusHeight,
306     const String& topRightRadiusWidth, const String& topRightRadiusHeight,
307     const String& bottomRightRadiusWidth, const String& bottomRightRadiusHeight,
308     const String& bottomLeftRadiusWidth, const String& bottomLeftRadiusHeight)
309 {
310     char opening[] = "inset(";
311     char separator[] = " ";
312     char cornersSeparator[] = "round";
313     StringBuilder result;
314     result.appendLiteral(opening);
315     result.append(top);
316     bool showLeftArg = !left.isNull() && left != right;
317     bool showBottomArg = !bottom.isNull() && (bottom != top || showLeftArg);
318     bool showRightArg = !right.isNull() && (right != top || showBottomArg);
319     if (showRightArg) {
320         result.appendLiteral(separator);
321         result.append(right);
322     }
323     if (showBottomArg) {
324         result.appendLiteral(separator);
325         result.append(bottom);
326     }
327     if (showLeftArg) {
328         result.appendLiteral(separator);
329         result.append(left);
330     }
331
332     if (!topLeftRadiusWidth.isNull() && !topLeftRadiusHeight.isNull()) {
333         Vector<String> horizontalRadii;
334         bool areDefaultCornerRadii = buildInsetRadii(horizontalRadii, topLeftRadiusWidth, topRightRadiusWidth, bottomRightRadiusWidth, bottomLeftRadiusWidth);
335
336         Vector<String> verticalRadii;
337         areDefaultCornerRadii &= buildInsetRadii(verticalRadii, topLeftRadiusHeight, topRightRadiusHeight, bottomRightRadiusHeight, bottomLeftRadiusHeight);
338
339         if (!areDefaultCornerRadii) {
340             result.appendLiteral(separator);
341             result.appendLiteral(cornersSeparator);
342
343             for (size_t i = 0; i < horizontalRadii.size(); ++i) {
344                 result.appendLiteral(separator);
345                 result.append(horizontalRadii[i]);
346             }
347             if (horizontalRadii != verticalRadii) {
348                 result.appendLiteral(separator);
349                 result.appendLiteral("/");
350
351                 for (size_t i = 0; i < verticalRadii.size(); ++i) {
352                     result.appendLiteral(separator);
353                     result.append(verticalRadii[i]);
354                 }
355             }
356         }
357     }
358     result.append(')');
359
360     return result.toString();
361 }
362
363 static inline void updateCornerRadiusWidthAndHeight(CSSPrimitiveValue* corner, String& width, String& height)
364 {
365     if (!corner)
366         return;
367
368     Pair* radius = corner->getPairValue();
369     width = radius->first() ? radius->first()->cssText() : String("0");
370     if (radius->second())
371         height = radius->second()->cssText();
372 }
373
374 String CSSBasicShapeInset::cssText() const
375 {
376     String topLeftRadiusWidth;
377     String topLeftRadiusHeight;
378     String topRightRadiusWidth;
379     String topRightRadiusHeight;
380     String bottomRightRadiusWidth;
381     String bottomRightRadiusHeight;
382     String bottomLeftRadiusWidth;
383     String bottomLeftRadiusHeight;
384
385     updateCornerRadiusWidthAndHeight(topLeftRadius(), topLeftRadiusWidth, topLeftRadiusHeight);
386     updateCornerRadiusWidthAndHeight(topRightRadius(), topRightRadiusWidth, topRightRadiusHeight);
387     updateCornerRadiusWidthAndHeight(bottomRightRadius(), bottomRightRadiusWidth, bottomRightRadiusHeight);
388     updateCornerRadiusWidthAndHeight(bottomLeftRadius(), bottomLeftRadiusWidth, bottomLeftRadiusHeight);
389
390     return buildInsetString(m_top ? m_top->cssText() : String(),
391         m_right ? m_right->cssText() : String(),
392         m_bottom ? m_bottom->cssText() : String(),
393         m_left ? m_left->cssText() : String(),
394         topLeftRadiusWidth,
395         topLeftRadiusHeight,
396         topRightRadiusWidth,
397         topRightRadiusHeight,
398         bottomRightRadiusWidth,
399         bottomRightRadiusHeight,
400         bottomLeftRadiusWidth,
401         bottomLeftRadiusHeight);
402 }
403
404 bool CSSBasicShapeInset::equals(const CSSBasicShape& shape) const
405 {
406     if (shape.type() != CSSBasicShapeInsetType)
407         return false;
408
409     const CSSBasicShapeInset& other = static_cast<const CSSBasicShapeInset&>(shape);
410     return compareCSSValuePtr(m_top, other.m_top)
411         && compareCSSValuePtr(m_right, other.m_right)
412         && compareCSSValuePtr(m_bottom, other.m_bottom)
413         && compareCSSValuePtr(m_left, other.m_left)
414         && compareCSSValuePtr(m_topLeftRadius, other.m_topLeftRadius)
415         && compareCSSValuePtr(m_topRightRadius, other.m_topRightRadius)
416         && compareCSSValuePtr(m_bottomRightRadius, other.m_bottomRightRadius)
417         && compareCSSValuePtr(m_bottomLeftRadius, other.m_bottomLeftRadius);
418 }
419
420 void CSSBasicShapeInset::trace(Visitor* visitor)
421 {
422     visitor->trace(m_top);
423     visitor->trace(m_right);
424     visitor->trace(m_bottom);
425     visitor->trace(m_left);
426     visitor->trace(m_topLeftRadius);
427     visitor->trace(m_topRightRadius);
428     visitor->trace(m_bottomRightRadius);
429     visitor->trace(m_bottomLeftRadius);
430     CSSBasicShape::trace(visitor);
431 }
432
433 } // namespace WebCore
434