Upstream version 9.38.198.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 blink {
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(50, CSSPrimitiveValue::CSS_PERCENTAGE);
97     } else if ((side == CSSValueRight || side == CSSValueBottom)
98         && amount->isPercentage()) {
99         side = defaultSide;
100         amount = cssValuePool().createValue(100 - amount->getFloatValue(), CSSPrimitiveValue::CSS_PERCENTAGE);
101     } else if (amount->isLength() && !amount->getFloatValue()) {
102         if (side == CSSValueRight || side == CSSValueBottom)
103             amount = cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCENTAGE);
104         else
105             amount = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE);
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     String radius;
118     if (m_radius && m_radius->getValueID() != CSSValueClosestSide)
119         radius = m_radius->cssText();
120
121     return buildCircleString(radius,
122         serializePositionOffset(*normalizedCX->getPairValue(), *normalizedCY->getPairValue()),
123         serializePositionOffset(*normalizedCY->getPairValue(), *normalizedCX->getPairValue()),
124         m_referenceBox ? m_referenceBox->cssText() : String());
125 }
126
127 bool CSSBasicShapeCircle::equals(const CSSBasicShape& shape) const
128 {
129     if (shape.type() != CSSBasicShapeCircleType)
130         return false;
131
132     const CSSBasicShapeCircle& other = static_cast<const CSSBasicShapeCircle&>(shape);
133     return compareCSSValuePtr(m_centerX, other.m_centerX)
134         && compareCSSValuePtr(m_centerY, other.m_centerY)
135         && compareCSSValuePtr(m_radius, other.m_radius)
136         && compareCSSValuePtr(m_referenceBox, other.m_referenceBox);
137 }
138
139 void CSSBasicShapeCircle::trace(Visitor* visitor)
140 {
141     visitor->trace(m_centerX);
142     visitor->trace(m_centerY);
143     visitor->trace(m_radius);
144     CSSBasicShape::trace(visitor);
145 }
146
147 static String buildEllipseString(const String& radiusX, const String& radiusY, const String& centerX, const String& centerY, const String& box)
148 {
149     char at[] = "at";
150     char separator[] = " ";
151     StringBuilder result;
152     result.appendLiteral("ellipse(");
153     bool needsSeparator = false;
154     if (!radiusX.isNull()) {
155         result.append(radiusX);
156         needsSeparator = true;
157     }
158     if (!radiusY.isNull()) {
159         if (needsSeparator)
160             result.appendLiteral(separator);
161         result.append(radiusY);
162         needsSeparator = true;
163     }
164
165     if (!centerX.isNull() || !centerY.isNull()) {
166         if (needsSeparator)
167             result.appendLiteral(separator);
168         result.appendLiteral(at);
169         result.appendLiteral(separator);
170         result.append(centerX);
171         result.appendLiteral(separator);
172         result.append(centerY);
173     }
174     result.append(")");
175     if (box.length()) {
176         result.appendLiteral(separator);
177         result.append(box);
178     }
179     return result.toString();
180 }
181
182 String CSSBasicShapeEllipse::cssText() const
183 {
184     RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
185     RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
186
187     String radiusX;
188     String radiusY;
189     if (m_radiusX) {
190         bool shouldSerializeRadiusXValue = m_radiusX->getValueID() != CSSValueClosestSide;
191         bool shouldSerializeRadiusYValue = false;
192
193         if (m_radiusY) {
194             shouldSerializeRadiusYValue = m_radiusY->getValueID() != CSSValueClosestSide;
195             if (shouldSerializeRadiusYValue)
196                 radiusY = m_radiusY->cssText();
197         }
198         if (shouldSerializeRadiusXValue || (!shouldSerializeRadiusXValue && shouldSerializeRadiusYValue))
199             radiusX = m_radiusX->cssText();
200     }
201
202     return buildEllipseString(radiusX, radiusY,
203         serializePositionOffset(*normalizedCX->getPairValue(), *normalizedCY->getPairValue()),
204         serializePositionOffset(*normalizedCY->getPairValue(), *normalizedCX->getPairValue()),
205         m_referenceBox ? m_referenceBox->cssText() : String());
206 }
207
208 bool CSSBasicShapeEllipse::equals(const CSSBasicShape& shape) const
209 {
210     if (shape.type() != CSSBasicShapeEllipseType)
211         return false;
212
213     const CSSBasicShapeEllipse& other = static_cast<const CSSBasicShapeEllipse&>(shape);
214     return compareCSSValuePtr(m_centerX, other.m_centerX)
215         && compareCSSValuePtr(m_centerY, other.m_centerY)
216         && compareCSSValuePtr(m_radiusX, other.m_radiusX)
217         && compareCSSValuePtr(m_radiusY, other.m_radiusY)
218         && compareCSSValuePtr(m_referenceBox, other.m_referenceBox);
219 }
220
221 void CSSBasicShapeEllipse::trace(Visitor* visitor)
222 {
223     visitor->trace(m_centerX);
224     visitor->trace(m_centerY);
225     visitor->trace(m_radiusX);
226     visitor->trace(m_radiusY);
227     CSSBasicShape::trace(visitor);
228 }
229
230 static String buildPolygonString(const WindRule& windRule, const Vector<String>& points, const String& box)
231 {
232     ASSERT(!(points.size() % 2));
233
234     StringBuilder result;
235     const char evenOddOpening[] = "polygon(evenodd, ";
236     const char nonZeroOpening[] = "polygon(";
237     const char commaSeparator[] = ", ";
238     COMPILE_ASSERT(sizeof(evenOddOpening) > sizeof(nonZeroOpening), polygon_string_openings_have_same_length);
239
240     // Compute the required capacity in advance to reduce allocations.
241     size_t length = sizeof(evenOddOpening) - 1;
242     for (size_t i = 0; i < points.size(); i += 2) {
243         if (i)
244             length += (sizeof(commaSeparator) - 1);
245         // add length of two strings, plus one for the space separator.
246         length += points[i].length() + 1 + points[i + 1].length();
247     }
248     if (!box.isEmpty())
249         length += box.length() + 1;
250     result.reserveCapacity(length);
251
252     if (windRule == RULE_EVENODD)
253         result.appendLiteral(evenOddOpening);
254     else
255         result.appendLiteral(nonZeroOpening);
256
257     for (size_t i = 0; i < points.size(); i += 2) {
258         if (i)
259             result.appendLiteral(commaSeparator);
260         result.append(points[i]);
261         result.append(' ');
262         result.append(points[i + 1]);
263     }
264
265     result.append(')');
266
267     if (!box.isEmpty()) {
268         result.append(' ');
269         result.append(box);
270     }
271
272     return result.toString();
273 }
274
275 String CSSBasicShapePolygon::cssText() const
276 {
277     Vector<String> points;
278     points.reserveInitialCapacity(m_values.size());
279
280     for (size_t i = 0; i < m_values.size(); ++i)
281         points.append(m_values.at(i)->cssText());
282
283     return buildPolygonString(m_windRule, points, m_referenceBox ? m_referenceBox->cssText() : String());
284 }
285
286 bool CSSBasicShapePolygon::equals(const CSSBasicShape& shape) const
287 {
288     if (shape.type() != CSSBasicShapePolygonType)
289         return false;
290
291     const CSSBasicShapePolygon& rhs = static_cast<const CSSBasicShapePolygon&>(shape);
292
293     if (!compareCSSValuePtr(m_referenceBox, rhs.m_referenceBox))
294         return false;
295
296     return compareCSSValueVector(m_values, rhs.m_values);
297 }
298
299 void CSSBasicShapePolygon::trace(Visitor* visitor)
300 {
301     visitor->trace(m_values);
302     CSSBasicShape::trace(visitor);
303 }
304
305 static bool buildInsetRadii(Vector<String> &radii, const String& topLeftRadius, const String& topRightRadius, const String& bottomRightRadius, const String& bottomLeftRadius)
306 {
307     bool showBottomLeft = topRightRadius != bottomLeftRadius;
308     bool showBottomRight = showBottomLeft || (bottomRightRadius != topLeftRadius);
309     bool showTopRight = showBottomRight || (topRightRadius != topLeftRadius);
310
311     radii.append(topLeftRadius);
312     if (showTopRight)
313         radii.append(topRightRadius);
314     if (showBottomRight)
315         radii.append(bottomRightRadius);
316     if (showBottomLeft)
317         radii.append(bottomLeftRadius);
318
319     return radii.size() == 1 && radii[0] == "0px";
320 }
321
322 static String buildInsetString(const String& top, const String& right, const String& bottom, const String& left,
323     const String& topLeftRadiusWidth, const String& topLeftRadiusHeight,
324     const String& topRightRadiusWidth, const String& topRightRadiusHeight,
325     const String& bottomRightRadiusWidth, const String& bottomRightRadiusHeight,
326     const String& bottomLeftRadiusWidth, const String& bottomLeftRadiusHeight)
327 {
328     char opening[] = "inset(";
329     char separator[] = " ";
330     char cornersSeparator[] = "round";
331     StringBuilder result;
332     result.appendLiteral(opening);
333     result.append(top);
334     bool showLeftArg = !left.isNull() && left != right;
335     bool showBottomArg = !bottom.isNull() && (bottom != top || showLeftArg);
336     bool showRightArg = !right.isNull() && (right != top || showBottomArg);
337     if (showRightArg) {
338         result.appendLiteral(separator);
339         result.append(right);
340     }
341     if (showBottomArg) {
342         result.appendLiteral(separator);
343         result.append(bottom);
344     }
345     if (showLeftArg) {
346         result.appendLiteral(separator);
347         result.append(left);
348     }
349
350     if (!topLeftRadiusWidth.isNull() && !topLeftRadiusHeight.isNull()) {
351         Vector<String> horizontalRadii;
352         bool areDefaultCornerRadii = buildInsetRadii(horizontalRadii, topLeftRadiusWidth, topRightRadiusWidth, bottomRightRadiusWidth, bottomLeftRadiusWidth);
353
354         Vector<String> verticalRadii;
355         areDefaultCornerRadii &= buildInsetRadii(verticalRadii, topLeftRadiusHeight, topRightRadiusHeight, bottomRightRadiusHeight, bottomLeftRadiusHeight);
356
357         if (!areDefaultCornerRadii) {
358             result.appendLiteral(separator);
359             result.appendLiteral(cornersSeparator);
360
361             for (size_t i = 0; i < horizontalRadii.size(); ++i) {
362                 result.appendLiteral(separator);
363                 result.append(horizontalRadii[i]);
364             }
365             if (horizontalRadii != verticalRadii) {
366                 result.appendLiteral(separator);
367                 result.appendLiteral("/");
368
369                 for (size_t i = 0; i < verticalRadii.size(); ++i) {
370                     result.appendLiteral(separator);
371                     result.append(verticalRadii[i]);
372                 }
373             }
374         }
375     }
376     result.append(')');
377
378     return result.toString();
379 }
380
381 static inline void updateCornerRadiusWidthAndHeight(CSSPrimitiveValue* corner, String& width, String& height)
382 {
383     if (!corner)
384         return;
385
386     Pair* radius = corner->getPairValue();
387     width = radius->first() ? radius->first()->cssText() : String("0");
388     if (radius->second())
389         height = radius->second()->cssText();
390 }
391
392 String CSSBasicShapeInset::cssText() const
393 {
394     String topLeftRadiusWidth;
395     String topLeftRadiusHeight;
396     String topRightRadiusWidth;
397     String topRightRadiusHeight;
398     String bottomRightRadiusWidth;
399     String bottomRightRadiusHeight;
400     String bottomLeftRadiusWidth;
401     String bottomLeftRadiusHeight;
402
403     updateCornerRadiusWidthAndHeight(topLeftRadius(), topLeftRadiusWidth, topLeftRadiusHeight);
404     updateCornerRadiusWidthAndHeight(topRightRadius(), topRightRadiusWidth, topRightRadiusHeight);
405     updateCornerRadiusWidthAndHeight(bottomRightRadius(), bottomRightRadiusWidth, bottomRightRadiusHeight);
406     updateCornerRadiusWidthAndHeight(bottomLeftRadius(), bottomLeftRadiusWidth, bottomLeftRadiusHeight);
407
408     return buildInsetString(m_top ? m_top->cssText() : String(),
409         m_right ? m_right->cssText() : String(),
410         m_bottom ? m_bottom->cssText() : String(),
411         m_left ? m_left->cssText() : String(),
412         topLeftRadiusWidth,
413         topLeftRadiusHeight,
414         topRightRadiusWidth,
415         topRightRadiusHeight,
416         bottomRightRadiusWidth,
417         bottomRightRadiusHeight,
418         bottomLeftRadiusWidth,
419         bottomLeftRadiusHeight);
420 }
421
422 bool CSSBasicShapeInset::equals(const CSSBasicShape& shape) const
423 {
424     if (shape.type() != CSSBasicShapeInsetType)
425         return false;
426
427     const CSSBasicShapeInset& other = static_cast<const CSSBasicShapeInset&>(shape);
428     return compareCSSValuePtr(m_top, other.m_top)
429         && compareCSSValuePtr(m_right, other.m_right)
430         && compareCSSValuePtr(m_bottom, other.m_bottom)
431         && compareCSSValuePtr(m_left, other.m_left)
432         && compareCSSValuePtr(m_topLeftRadius, other.m_topLeftRadius)
433         && compareCSSValuePtr(m_topRightRadius, other.m_topRightRadius)
434         && compareCSSValuePtr(m_bottomRightRadius, other.m_bottomRightRadius)
435         && compareCSSValuePtr(m_bottomLeftRadius, other.m_bottomLeftRadius);
436 }
437
438 void CSSBasicShapeInset::trace(Visitor* visitor)
439 {
440     visitor->trace(m_top);
441     visitor->trace(m_right);
442     visitor->trace(m_bottom);
443     visitor->trace(m_left);
444     visitor->trace(m_topLeftRadius);
445     visitor->trace(m_topRightRadius);
446     visitor->trace(m_bottomRightRadius);
447     visitor->trace(m_bottomLeftRadius);
448     CSSBasicShape::trace(visitor);
449 }
450
451 } // namespace blink
452