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/CSSValuePair.h"
34 #include "core/css/CSSValuePool.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
)
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
);
63 return result
.toString();
66 static String
serializePositionOffset(const CSSValuePair
& offset
, const CSSValuePair
& other
)
68 if ((toCSSPrimitiveValue(offset
.first()).getValueID() == CSSValueLeft
&& toCSSPrimitiveValue(other
.first()).getValueID() == CSSValueTop
)
69 || (toCSSPrimitiveValue(offset
.first()).getValueID() == CSSValueTop
&& toCSSPrimitiveValue(other
.first()).getValueID() == CSSValueLeft
))
70 return offset
.second().cssText();
71 return offset
.cssText();
74 static PassRefPtrWillBeRawPtr
<CSSValuePair
> buildSerializablePositionOffset(PassRefPtrWillBeRawPtr
<CSSValue
> offset
, CSSValueID defaultSide
)
76 CSSValueID side
= defaultSide
;
77 RefPtrWillBeRawPtr
<CSSPrimitiveValue
> amount
= nullptr;
80 side
= CSSValueCenter
;
81 } else if (offset
->isPrimitiveValue() && toCSSPrimitiveValue(offset
.get())->isValueID()) {
82 side
= toCSSPrimitiveValue(offset
.get())->getValueID();
83 } else if (offset
->isValuePair()) {
84 side
= toCSSPrimitiveValue(toCSSValuePair(*offset
).first()).getValueID();
85 amount
= &toCSSPrimitiveValue(toCSSValuePair(*offset
).second());
87 amount
= toCSSPrimitiveValue(offset
.get());
90 if (side
== CSSValueCenter
) {
92 amount
= cssValuePool().createValue(50, CSSPrimitiveValue::UnitType::Percentage
);
93 } else if ((side
== CSSValueRight
|| side
== CSSValueBottom
)
94 && amount
->isPercentage()) {
96 amount
= cssValuePool().createValue(100 - amount
->getFloatValue(), CSSPrimitiveValue::UnitType::Percentage
);
97 } else if (amount
->isLength() && !amount
->getFloatValue()) {
98 if (side
== CSSValueRight
|| side
== CSSValueBottom
)
99 amount
= cssValuePool().createValue(100, CSSPrimitiveValue::UnitType::Percentage
);
101 amount
= cssValuePool().createValue(0, CSSPrimitiveValue::UnitType::Percentage
);
105 return CSSValuePair::create(cssValuePool().createValue(side
), amount
.release(), CSSValuePair::KeepIdenticalValues
);
108 String
CSSBasicShapeCircle::cssText() const
110 RefPtrWillBeRawPtr
<CSSValuePair
> normalizedCX
= buildSerializablePositionOffset(m_centerX
, CSSValueLeft
);
111 RefPtrWillBeRawPtr
<CSSValuePair
> normalizedCY
= buildSerializablePositionOffset(m_centerY
, CSSValueTop
);
114 if (m_radius
&& m_radius
->getValueID() != CSSValueClosestSide
)
115 radius
= m_radius
->cssText();
117 return buildCircleString(radius
,
118 serializePositionOffset(*normalizedCX
, *normalizedCY
),
119 serializePositionOffset(*normalizedCY
, *normalizedCX
));
122 bool CSSBasicShapeCircle::equals(const CSSBasicShape
& shape
) const
124 if (shape
.type() != CSSBasicShapeCircleType
)
127 const CSSBasicShapeCircle
& other
= toCSSBasicShapeCircle(shape
);
128 return compareCSSValuePtr(m_centerX
, other
.m_centerX
)
129 && compareCSSValuePtr(m_centerY
, other
.m_centerY
)
130 && compareCSSValuePtr(m_radius
, other
.m_radius
);
133 DEFINE_TRACE(CSSBasicShapeCircle
)
135 visitor
->trace(m_centerX
);
136 visitor
->trace(m_centerY
);
137 visitor
->trace(m_radius
);
138 CSSBasicShape::trace(visitor
);
141 static String
buildEllipseString(const String
& radiusX
, const String
& radiusY
, const String
& centerX
, const String
& centerY
)
144 char separator
[] = " ";
145 StringBuilder result
;
146 result
.appendLiteral("ellipse(");
147 bool needsSeparator
= false;
148 if (!radiusX
.isNull()) {
149 result
.append(radiusX
);
150 needsSeparator
= true;
152 if (!radiusY
.isNull()) {
154 result
.appendLiteral(separator
);
155 result
.append(radiusY
);
156 needsSeparator
= true;
159 if (!centerX
.isNull() || !centerY
.isNull()) {
161 result
.appendLiteral(separator
);
162 result
.appendLiteral(at
);
163 result
.appendLiteral(separator
);
164 result
.append(centerX
);
165 result
.appendLiteral(separator
);
166 result
.append(centerY
);
169 return result
.toString();
172 String
CSSBasicShapeEllipse::cssText() const
174 RefPtrWillBeRawPtr
<CSSValuePair
> normalizedCX
= buildSerializablePositionOffset(m_centerX
, CSSValueLeft
);
175 RefPtrWillBeRawPtr
<CSSValuePair
> normalizedCY
= buildSerializablePositionOffset(m_centerY
, CSSValueTop
);
180 bool shouldSerializeRadiusXValue
= m_radiusX
->getValueID() != CSSValueClosestSide
;
181 bool shouldSerializeRadiusYValue
= false;
184 shouldSerializeRadiusYValue
= m_radiusY
->getValueID() != CSSValueClosestSide
;
185 if (shouldSerializeRadiusYValue
)
186 radiusY
= m_radiusY
->cssText();
188 if (shouldSerializeRadiusXValue
|| (!shouldSerializeRadiusXValue
&& shouldSerializeRadiusYValue
))
189 radiusX
= m_radiusX
->cssText();
192 return buildEllipseString(radiusX
, radiusY
,
193 serializePositionOffset(*normalizedCX
, *normalizedCY
),
194 serializePositionOffset(*normalizedCY
, *normalizedCX
));
197 bool CSSBasicShapeEllipse::equals(const CSSBasicShape
& shape
) const
199 if (shape
.type() != CSSBasicShapeEllipseType
)
202 const CSSBasicShapeEllipse
& other
= toCSSBasicShapeEllipse(shape
);
203 return compareCSSValuePtr(m_centerX
, other
.m_centerX
)
204 && compareCSSValuePtr(m_centerY
, other
.m_centerY
)
205 && compareCSSValuePtr(m_radiusX
, other
.m_radiusX
)
206 && compareCSSValuePtr(m_radiusY
, other
.m_radiusY
);
209 DEFINE_TRACE(CSSBasicShapeEllipse
)
211 visitor
->trace(m_centerX
);
212 visitor
->trace(m_centerY
);
213 visitor
->trace(m_radiusX
);
214 visitor
->trace(m_radiusY
);
215 CSSBasicShape::trace(visitor
);
218 static String
buildPolygonString(const WindRule
& windRule
, const Vector
<String
>& points
)
220 ASSERT(!(points
.size() % 2));
222 StringBuilder result
;
223 const char evenOddOpening
[] = "polygon(evenodd, ";
224 const char nonZeroOpening
[] = "polygon(";
225 const char commaSeparator
[] = ", ";
226 static_assert(sizeof(evenOddOpening
) > sizeof(nonZeroOpening
), "polygon string openings should be the same length");
228 // Compute the required capacity in advance to reduce allocations.
229 size_t length
= sizeof(evenOddOpening
) - 1;
230 for (size_t i
= 0; i
< points
.size(); i
+= 2) {
232 length
+= (sizeof(commaSeparator
) - 1);
233 // add length of two strings, plus one for the space separator.
234 length
+= points
[i
].length() + 1 + points
[i
+ 1].length();
236 result
.reserveCapacity(length
);
238 if (windRule
== RULE_EVENODD
)
239 result
.appendLiteral(evenOddOpening
);
241 result
.appendLiteral(nonZeroOpening
);
243 for (size_t i
= 0; i
< points
.size(); i
+= 2) {
245 result
.appendLiteral(commaSeparator
);
246 result
.append(points
[i
]);
248 result
.append(points
[i
+ 1]);
252 return result
.toString();
255 String
CSSBasicShapePolygon::cssText() const
257 Vector
<String
> points
;
258 points
.reserveInitialCapacity(m_values
.size());
260 for (size_t i
= 0; i
< m_values
.size(); ++i
)
261 points
.append(m_values
.at(i
)->cssText());
263 return buildPolygonString(m_windRule
, points
);
266 bool CSSBasicShapePolygon::equals(const CSSBasicShape
& shape
) const
268 if (shape
.type() != CSSBasicShapePolygonType
)
271 const CSSBasicShapePolygon
& rhs
= toCSSBasicShapePolygon(shape
);
272 return compareCSSValueVector(m_values
, rhs
.m_values
);
275 DEFINE_TRACE(CSSBasicShapePolygon
)
277 visitor
->trace(m_values
);
278 CSSBasicShape::trace(visitor
);
281 static bool buildInsetRadii(Vector
<String
> &radii
, const String
& topLeftRadius
, const String
& topRightRadius
, const String
& bottomRightRadius
, const String
& bottomLeftRadius
)
283 bool showBottomLeft
= topRightRadius
!= bottomLeftRadius
;
284 bool showBottomRight
= showBottomLeft
|| (bottomRightRadius
!= topLeftRadius
);
285 bool showTopRight
= showBottomRight
|| (topRightRadius
!= topLeftRadius
);
287 radii
.append(topLeftRadius
);
289 radii
.append(topRightRadius
);
291 radii
.append(bottomRightRadius
);
293 radii
.append(bottomLeftRadius
);
295 return radii
.size() == 1 && radii
[0] == "0px";
298 static String
buildInsetString(const String
& top
, const String
& right
, const String
& bottom
, const String
& left
,
299 const String
& topLeftRadiusWidth
, const String
& topLeftRadiusHeight
,
300 const String
& topRightRadiusWidth
, const String
& topRightRadiusHeight
,
301 const String
& bottomRightRadiusWidth
, const String
& bottomRightRadiusHeight
,
302 const String
& bottomLeftRadiusWidth
, const String
& bottomLeftRadiusHeight
)
304 char opening
[] = "inset(";
305 char separator
[] = " ";
306 char cornersSeparator
[] = "round";
307 StringBuilder result
;
308 result
.appendLiteral(opening
);
310 bool showLeftArg
= !left
.isNull() && left
!= right
;
311 bool showBottomArg
= !bottom
.isNull() && (bottom
!= top
|| showLeftArg
);
312 bool showRightArg
= !right
.isNull() && (right
!= top
|| showBottomArg
);
314 result
.appendLiteral(separator
);
315 result
.append(right
);
318 result
.appendLiteral(separator
);
319 result
.append(bottom
);
322 result
.appendLiteral(separator
);
326 if (!topLeftRadiusWidth
.isNull() && !topLeftRadiusHeight
.isNull()) {
327 Vector
<String
> horizontalRadii
;
328 bool areDefaultCornerRadii
= buildInsetRadii(horizontalRadii
, topLeftRadiusWidth
, topRightRadiusWidth
, bottomRightRadiusWidth
, bottomLeftRadiusWidth
);
330 Vector
<String
> verticalRadii
;
331 areDefaultCornerRadii
&= buildInsetRadii(verticalRadii
, topLeftRadiusHeight
, topRightRadiusHeight
, bottomRightRadiusHeight
, bottomLeftRadiusHeight
);
333 if (!areDefaultCornerRadii
) {
334 result
.appendLiteral(separator
);
335 result
.appendLiteral(cornersSeparator
);
337 for (size_t i
= 0; i
< horizontalRadii
.size(); ++i
) {
338 result
.appendLiteral(separator
);
339 result
.append(horizontalRadii
[i
]);
341 if (horizontalRadii
!= verticalRadii
) {
342 result
.appendLiteral(separator
);
343 result
.appendLiteral("/");
345 for (size_t i
= 0; i
< verticalRadii
.size(); ++i
) {
346 result
.appendLiteral(separator
);
347 result
.append(verticalRadii
[i
]);
354 return result
.toString();
357 static inline void updateCornerRadiusWidthAndHeight(const CSSValuePair
* cornerRadius
, String
& width
, String
& height
)
362 width
= cornerRadius
->first().cssText();
363 height
= cornerRadius
->second().cssText();
366 String
CSSBasicShapeInset::cssText() const
368 String topLeftRadiusWidth
;
369 String topLeftRadiusHeight
;
370 String topRightRadiusWidth
;
371 String topRightRadiusHeight
;
372 String bottomRightRadiusWidth
;
373 String bottomRightRadiusHeight
;
374 String bottomLeftRadiusWidth
;
375 String bottomLeftRadiusHeight
;
377 updateCornerRadiusWidthAndHeight(topLeftRadius(), topLeftRadiusWidth
, topLeftRadiusHeight
);
378 updateCornerRadiusWidthAndHeight(topRightRadius(), topRightRadiusWidth
, topRightRadiusHeight
);
379 updateCornerRadiusWidthAndHeight(bottomRightRadius(), bottomRightRadiusWidth
, bottomRightRadiusHeight
);
380 updateCornerRadiusWidthAndHeight(bottomLeftRadius(), bottomLeftRadiusWidth
, bottomLeftRadiusHeight
);
382 return buildInsetString(m_top
? m_top
->cssText() : String(),
383 m_right
? m_right
->cssText() : String(),
384 m_bottom
? m_bottom
->cssText() : String(),
385 m_left
? m_left
->cssText() : String(),
389 topRightRadiusHeight
,
390 bottomRightRadiusWidth
,
391 bottomRightRadiusHeight
,
392 bottomLeftRadiusWidth
,
393 bottomLeftRadiusHeight
);
396 bool CSSBasicShapeInset::equals(const CSSBasicShape
& shape
) const
398 if (shape
.type() != CSSBasicShapeInsetType
)
401 const CSSBasicShapeInset
& other
= toCSSBasicShapeInset(shape
);
402 return compareCSSValuePtr(m_top
, other
.m_top
)
403 && compareCSSValuePtr(m_right
, other
.m_right
)
404 && compareCSSValuePtr(m_bottom
, other
.m_bottom
)
405 && compareCSSValuePtr(m_left
, other
.m_left
)
406 && compareCSSValuePtr(m_topLeftRadius
, other
.m_topLeftRadius
)
407 && compareCSSValuePtr(m_topRightRadius
, other
.m_topRightRadius
)
408 && compareCSSValuePtr(m_bottomRightRadius
, other
.m_bottomRightRadius
)
409 && compareCSSValuePtr(m_bottomLeftRadius
, other
.m_bottomLeftRadius
);
412 DEFINE_TRACE(CSSBasicShapeInset
)
414 visitor
->trace(m_top
);
415 visitor
->trace(m_right
);
416 visitor
->trace(m_bottom
);
417 visitor
->trace(m_left
);
418 visitor
->trace(m_topLeftRadius
);
419 visitor
->trace(m_topRightRadius
);
420 visitor
->trace(m_bottomRightRadius
);
421 visitor
->trace(m_bottomLeftRadius
);
422 CSSBasicShape::trace(visitor
);