2 * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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
31 #include "core/css/CSSBasicShapes.h"
33 #include "core/css/CSSValuePool.h"
34 #include "core/css/Pair.h"
35 #include "platform/Length.h"
36 #include "wtf/text/StringBuilder.h"
42 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CSSBasicShape)
44 static String buildCircleString(const String& radius, const String& centerX, const String& centerY, const String& box)
47 char separator[] = " ";
49 result.appendLiteral("circle(");
51 result.append(radius);
53 if (!centerX.isNull() || !centerY.isNull()) {
55 result.appendLiteral(separator);
57 result.appendLiteral(separator);
58 result.append(centerX);
59 result.appendLiteral(separator);
60 result.append(centerY);
64 result.appendLiteral(separator);
67 return result.toString();
70 static String serializePositionOffset(const Pair& offset, const Pair& other)
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();
78 static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> buildSerializablePositionOffset(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> offset, CSSValueID defaultSide)
80 CSSValueID side = defaultSide;
81 RefPtrWillBeRawPtr<CSSPrimitiveValue> amount = nullptr;
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();
94 if (side == CSSValueCenter) {
96 amount = cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE);
97 } else if ((side == CSSValueRight || side == CSSValueBottom)
98 && amount->isPercentage()) {
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);
105 amount = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE);
109 return cssValuePool().createValue(Pair::create(cssValuePool().createValue(side), amount.release(), Pair::KeepIdenticalValues));
112 String CSSBasicShapeCircle::cssText() const
114 RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
115 RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
118 if (m_radius && m_radius->getValueID() != CSSValueClosestSide)
119 radius = m_radius->cssText();
121 return buildCircleString(radius,
122 serializePositionOffset(*normalizedCX->getPairValue(), *normalizedCY->getPairValue()),
123 serializePositionOffset(*normalizedCY->getPairValue(), *normalizedCX->getPairValue()),
124 m_referenceBox ? m_referenceBox->cssText() : String());
127 bool CSSBasicShapeCircle::equals(const CSSBasicShape& shape) const
129 if (shape.type() != CSSBasicShapeCircleType)
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);
139 void CSSBasicShapeCircle::trace(Visitor* visitor)
141 visitor->trace(m_centerX);
142 visitor->trace(m_centerY);
143 visitor->trace(m_radius);
144 CSSBasicShape::trace(visitor);
147 static String buildEllipseString(const String& radiusX, const String& radiusY, const String& centerX, const String& centerY, const String& box)
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;
158 if (!radiusY.isNull()) {
160 result.appendLiteral(separator);
161 result.append(radiusY);
162 needsSeparator = true;
165 if (!centerX.isNull() || !centerY.isNull()) {
167 result.appendLiteral(separator);
168 result.appendLiteral(at);
169 result.appendLiteral(separator);
170 result.append(centerX);
171 result.appendLiteral(separator);
172 result.append(centerY);
176 result.appendLiteral(separator);
179 return result.toString();
182 String CSSBasicShapeEllipse::cssText() const
184 RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
185 RefPtrWillBeRawPtr<CSSPrimitiveValue> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
190 bool shouldSerializeRadiusXValue = m_radiusX->getValueID() != CSSValueClosestSide;
191 bool shouldSerializeRadiusYValue = false;
194 shouldSerializeRadiusYValue = m_radiusY->getValueID() != CSSValueClosestSide;
195 if (shouldSerializeRadiusYValue)
196 radiusY = m_radiusY->cssText();
198 if (shouldSerializeRadiusXValue || (!shouldSerializeRadiusXValue && shouldSerializeRadiusYValue))
199 radiusX = m_radiusX->cssText();
202 return buildEllipseString(radiusX, radiusY,
203 serializePositionOffset(*normalizedCX->getPairValue(), *normalizedCY->getPairValue()),
204 serializePositionOffset(*normalizedCY->getPairValue(), *normalizedCX->getPairValue()),
205 m_referenceBox ? m_referenceBox->cssText() : String());
208 bool CSSBasicShapeEllipse::equals(const CSSBasicShape& shape) const
210 if (shape.type() != CSSBasicShapeEllipseType)
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);
221 void CSSBasicShapeEllipse::trace(Visitor* visitor)
223 visitor->trace(m_centerX);
224 visitor->trace(m_centerY);
225 visitor->trace(m_radiusX);
226 visitor->trace(m_radiusY);
227 CSSBasicShape::trace(visitor);
230 static String buildPolygonString(const WindRule& windRule, const Vector<String>& points, const String& box)
232 ASSERT(!(points.size() % 2));
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);
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) {
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();
249 length += box.length() + 1;
250 result.reserveCapacity(length);
252 if (windRule == RULE_EVENODD)
253 result.appendLiteral(evenOddOpening);
255 result.appendLiteral(nonZeroOpening);
257 for (size_t i = 0; i < points.size(); i += 2) {
259 result.appendLiteral(commaSeparator);
260 result.append(points[i]);
262 result.append(points[i + 1]);
267 if (!box.isEmpty()) {
272 return result.toString();
275 String CSSBasicShapePolygon::cssText() const
277 Vector<String> points;
278 points.reserveInitialCapacity(m_values.size());
280 for (size_t i = 0; i < m_values.size(); ++i)
281 points.append(m_values.at(i)->cssText());
283 return buildPolygonString(m_windRule, points, m_referenceBox ? m_referenceBox->cssText() : String());
286 bool CSSBasicShapePolygon::equals(const CSSBasicShape& shape) const
288 if (shape.type() != CSSBasicShapePolygonType)
291 const CSSBasicShapePolygon& rhs = static_cast<const CSSBasicShapePolygon&>(shape);
293 if (!compareCSSValuePtr(m_referenceBox, rhs.m_referenceBox))
296 return compareCSSValueVector(m_values, rhs.m_values);
299 void CSSBasicShapePolygon::trace(Visitor* visitor)
301 visitor->trace(m_values);
302 CSSBasicShape::trace(visitor);
305 static bool buildInsetRadii(Vector<String> &radii, const String& topLeftRadius, const String& topRightRadius, const String& bottomRightRadius, const String& bottomLeftRadius)
307 bool showBottomLeft = topRightRadius != bottomLeftRadius;
308 bool showBottomRight = showBottomLeft || (bottomRightRadius != topLeftRadius);
309 bool showTopRight = showBottomRight || (topRightRadius != topLeftRadius);
311 radii.append(topLeftRadius);
313 radii.append(topRightRadius);
315 radii.append(bottomRightRadius);
317 radii.append(bottomLeftRadius);
319 return radii.size() == 1 && radii[0] == "0px";
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)
328 char opening[] = "inset(";
329 char separator[] = " ";
330 char cornersSeparator[] = "round";
331 StringBuilder result;
332 result.appendLiteral(opening);
334 bool showLeftArg = !left.isNull() && left != right;
335 bool showBottomArg = !bottom.isNull() && (bottom != top || showLeftArg);
336 bool showRightArg = !right.isNull() && (right != top || showBottomArg);
338 result.appendLiteral(separator);
339 result.append(right);
342 result.appendLiteral(separator);
343 result.append(bottom);
346 result.appendLiteral(separator);
350 if (!topLeftRadiusWidth.isNull() && !topLeftRadiusHeight.isNull()) {
351 Vector<String> horizontalRadii;
352 bool areDefaultCornerRadii = buildInsetRadii(horizontalRadii, topLeftRadiusWidth, topRightRadiusWidth, bottomRightRadiusWidth, bottomLeftRadiusWidth);
354 Vector<String> verticalRadii;
355 areDefaultCornerRadii &= buildInsetRadii(verticalRadii, topLeftRadiusHeight, topRightRadiusHeight, bottomRightRadiusHeight, bottomLeftRadiusHeight);
357 if (!areDefaultCornerRadii) {
358 result.appendLiteral(separator);
359 result.appendLiteral(cornersSeparator);
361 for (size_t i = 0; i < horizontalRadii.size(); ++i) {
362 result.appendLiteral(separator);
363 result.append(horizontalRadii[i]);
365 if (horizontalRadii != verticalRadii) {
366 result.appendLiteral(separator);
367 result.appendLiteral("/");
369 for (size_t i = 0; i < verticalRadii.size(); ++i) {
370 result.appendLiteral(separator);
371 result.append(verticalRadii[i]);
378 return result.toString();
381 static inline void updateCornerRadiusWidthAndHeight(CSSPrimitiveValue* corner, String& width, String& height)
386 Pair* radius = corner->getPairValue();
387 width = radius->first() ? radius->first()->cssText() : String("0");
388 if (radius->second())
389 height = radius->second()->cssText();
392 String CSSBasicShapeInset::cssText() const
394 String topLeftRadiusWidth;
395 String topLeftRadiusHeight;
396 String topRightRadiusWidth;
397 String topRightRadiusHeight;
398 String bottomRightRadiusWidth;
399 String bottomRightRadiusHeight;
400 String bottomLeftRadiusWidth;
401 String bottomLeftRadiusHeight;
403 updateCornerRadiusWidthAndHeight(topLeftRadius(), topLeftRadiusWidth, topLeftRadiusHeight);
404 updateCornerRadiusWidthAndHeight(topRightRadius(), topRightRadiusWidth, topRightRadiusHeight);
405 updateCornerRadiusWidthAndHeight(bottomRightRadius(), bottomRightRadiusWidth, bottomRightRadiusHeight);
406 updateCornerRadiusWidthAndHeight(bottomLeftRadius(), bottomLeftRadiusWidth, bottomLeftRadiusHeight);
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(),
415 topRightRadiusHeight,
416 bottomRightRadiusWidth,
417 bottomRightRadiusHeight,
418 bottomLeftRadiusWidth,
419 bottomLeftRadiusHeight);
422 bool CSSBasicShapeInset::equals(const CSSBasicShape& shape) const
424 if (shape.type() != CSSBasicShapeInsetType)
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);
438 void CSSBasicShapeInset::trace(Visitor* visitor)
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);