PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/core/css/CSSBasicShapes.cpp

https://repo.or.cz/blink.git
C++ | 426 lines | 337 code | 59 blank | 30 comment | 108 complexity | 0e16d1a92331301f8e96e4ecf1e8004e MD5 | raw file
Possible License(s): BSD-3-Clause, Unlicense, AGPL-1.0, Apache-2.0
  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. #include "config.h"
  30. #include "core/css/CSSBasicShapes.h"
  31. #include "core/css/CSSValuePair.h"
  32. #include "core/css/CSSValuePool.h"
  33. #include "platform/Length.h"
  34. #include "wtf/text/StringBuilder.h"
  35. using namespace WTF;
  36. namespace blink {
  37. DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CSSBasicShape)
  38. static String buildCircleString(const String& radius, const String& centerX, const String& centerY)
  39. {
  40. char at[] = "at";
  41. char separator[] = " ";
  42. StringBuilder result;
  43. result.appendLiteral("circle(");
  44. if (!radius.isNull())
  45. result.append(radius);
  46. if (!centerX.isNull() || !centerY.isNull()) {
  47. if (!radius.isNull())
  48. result.appendLiteral(separator);
  49. result.append(at);
  50. result.appendLiteral(separator);
  51. result.append(centerX);
  52. result.appendLiteral(separator);
  53. result.append(centerY);
  54. }
  55. result.append(')');
  56. return result.toString();
  57. }
  58. static String serializePositionOffset(const CSSValuePair& offset, const CSSValuePair& other)
  59. {
  60. if ((toCSSPrimitiveValue(offset.first()).getValueID() == CSSValueLeft && toCSSPrimitiveValue(other.first()).getValueID() == CSSValueTop)
  61. || (toCSSPrimitiveValue(offset.first()).getValueID() == CSSValueTop && toCSSPrimitiveValue(other.first()).getValueID() == CSSValueLeft))
  62. return offset.second().cssText();
  63. return offset.cssText();
  64. }
  65. static PassRefPtrWillBeRawPtr<CSSValuePair> buildSerializablePositionOffset(PassRefPtrWillBeRawPtr<CSSValue> offset, CSSValueID defaultSide)
  66. {
  67. CSSValueID side = defaultSide;
  68. RefPtrWillBeRawPtr<CSSPrimitiveValue> amount = nullptr;
  69. if (!offset) {
  70. side = CSSValueCenter;
  71. } else if (offset->isPrimitiveValue() && toCSSPrimitiveValue(offset.get())->isValueID()) {
  72. side = toCSSPrimitiveValue(offset.get())->getValueID();
  73. } else if (offset->isValuePair()) {
  74. side = toCSSPrimitiveValue(toCSSValuePair(*offset).first()).getValueID();
  75. amount = &toCSSPrimitiveValue(toCSSValuePair(*offset).second());
  76. } else {
  77. amount = toCSSPrimitiveValue(offset.get());
  78. }
  79. if (side == CSSValueCenter) {
  80. side = defaultSide;
  81. amount = cssValuePool().createValue(50, CSSPrimitiveValue::UnitType::Percentage);
  82. } else if ((side == CSSValueRight || side == CSSValueBottom)
  83. && amount->isPercentage()) {
  84. side = defaultSide;
  85. amount = cssValuePool().createValue(100 - amount->getFloatValue(), CSSPrimitiveValue::UnitType::Percentage);
  86. } else if (amount->isLength() && !amount->getFloatValue()) {
  87. if (side == CSSValueRight || side == CSSValueBottom)
  88. amount = cssValuePool().createValue(100, CSSPrimitiveValue::UnitType::Percentage);
  89. else
  90. amount = cssValuePool().createValue(0, CSSPrimitiveValue::UnitType::Percentage);
  91. side = defaultSide;
  92. }
  93. return CSSValuePair::create(cssValuePool().createValue(side), amount.release(), CSSValuePair::KeepIdenticalValues);
  94. }
  95. String CSSBasicShapeCircle::cssText() const
  96. {
  97. RefPtrWillBeRawPtr<CSSValuePair> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
  98. RefPtrWillBeRawPtr<CSSValuePair> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
  99. String radius;
  100. if (m_radius && m_radius->getValueID() != CSSValueClosestSide)
  101. radius = m_radius->cssText();
  102. return buildCircleString(radius,
  103. serializePositionOffset(*normalizedCX, *normalizedCY),
  104. serializePositionOffset(*normalizedCY, *normalizedCX));
  105. }
  106. bool CSSBasicShapeCircle::equals(const CSSBasicShape& shape) const
  107. {
  108. if (shape.type() != CSSBasicShapeCircleType)
  109. return false;
  110. const CSSBasicShapeCircle& other = toCSSBasicShapeCircle(shape);
  111. return compareCSSValuePtr(m_centerX, other.m_centerX)
  112. && compareCSSValuePtr(m_centerY, other.m_centerY)
  113. && compareCSSValuePtr(m_radius, other.m_radius);
  114. }
  115. DEFINE_TRACE(CSSBasicShapeCircle)
  116. {
  117. visitor->trace(m_centerX);
  118. visitor->trace(m_centerY);
  119. visitor->trace(m_radius);
  120. CSSBasicShape::trace(visitor);
  121. }
  122. static String buildEllipseString(const String& radiusX, const String& radiusY, const String& centerX, const String& centerY)
  123. {
  124. char at[] = "at";
  125. char separator[] = " ";
  126. StringBuilder result;
  127. result.appendLiteral("ellipse(");
  128. bool needsSeparator = false;
  129. if (!radiusX.isNull()) {
  130. result.append(radiusX);
  131. needsSeparator = true;
  132. }
  133. if (!radiusY.isNull()) {
  134. if (needsSeparator)
  135. result.appendLiteral(separator);
  136. result.append(radiusY);
  137. needsSeparator = true;
  138. }
  139. if (!centerX.isNull() || !centerY.isNull()) {
  140. if (needsSeparator)
  141. result.appendLiteral(separator);
  142. result.appendLiteral(at);
  143. result.appendLiteral(separator);
  144. result.append(centerX);
  145. result.appendLiteral(separator);
  146. result.append(centerY);
  147. }
  148. result.append(')');
  149. return result.toString();
  150. }
  151. String CSSBasicShapeEllipse::cssText() const
  152. {
  153. RefPtrWillBeRawPtr<CSSValuePair> normalizedCX = buildSerializablePositionOffset(m_centerX, CSSValueLeft);
  154. RefPtrWillBeRawPtr<CSSValuePair> normalizedCY = buildSerializablePositionOffset(m_centerY, CSSValueTop);
  155. String radiusX;
  156. String radiusY;
  157. if (m_radiusX) {
  158. bool shouldSerializeRadiusXValue = m_radiusX->getValueID() != CSSValueClosestSide;
  159. bool shouldSerializeRadiusYValue = false;
  160. if (m_radiusY) {
  161. shouldSerializeRadiusYValue = m_radiusY->getValueID() != CSSValueClosestSide;
  162. if (shouldSerializeRadiusYValue)
  163. radiusY = m_radiusY->cssText();
  164. }
  165. if (shouldSerializeRadiusXValue || (!shouldSerializeRadiusXValue && shouldSerializeRadiusYValue))
  166. radiusX = m_radiusX->cssText();
  167. }
  168. return buildEllipseString(radiusX, radiusY,
  169. serializePositionOffset(*normalizedCX, *normalizedCY),
  170. serializePositionOffset(*normalizedCY, *normalizedCX));
  171. }
  172. bool CSSBasicShapeEllipse::equals(const CSSBasicShape& shape) const
  173. {
  174. if (shape.type() != CSSBasicShapeEllipseType)
  175. return false;
  176. const CSSBasicShapeEllipse& other = toCSSBasicShapeEllipse(shape);
  177. return compareCSSValuePtr(m_centerX, other.m_centerX)
  178. && compareCSSValuePtr(m_centerY, other.m_centerY)
  179. && compareCSSValuePtr(m_radiusX, other.m_radiusX)
  180. && compareCSSValuePtr(m_radiusY, other.m_radiusY);
  181. }
  182. DEFINE_TRACE(CSSBasicShapeEllipse)
  183. {
  184. visitor->trace(m_centerX);
  185. visitor->trace(m_centerY);
  186. visitor->trace(m_radiusX);
  187. visitor->trace(m_radiusY);
  188. CSSBasicShape::trace(visitor);
  189. }
  190. static String buildPolygonString(const WindRule& windRule, const Vector<String>& points)
  191. {
  192. ASSERT(!(points.size() % 2));
  193. StringBuilder result;
  194. const char evenOddOpening[] = "polygon(evenodd, ";
  195. const char nonZeroOpening[] = "polygon(";
  196. const char commaSeparator[] = ", ";
  197. static_assert(sizeof(evenOddOpening) > sizeof(nonZeroOpening), "polygon string openings should be the same length");
  198. // Compute the required capacity in advance to reduce allocations.
  199. size_t length = sizeof(evenOddOpening) - 1;
  200. for (size_t i = 0; i < points.size(); i += 2) {
  201. if (i)
  202. length += (sizeof(commaSeparator) - 1);
  203. // add length of two strings, plus one for the space separator.
  204. length += points[i].length() + 1 + points[i + 1].length();
  205. }
  206. result.reserveCapacity(length);
  207. if (windRule == RULE_EVENODD)
  208. result.appendLiteral(evenOddOpening);
  209. else
  210. result.appendLiteral(nonZeroOpening);
  211. for (size_t i = 0; i < points.size(); i += 2) {
  212. if (i)
  213. result.appendLiteral(commaSeparator);
  214. result.append(points[i]);
  215. result.append(' ');
  216. result.append(points[i + 1]);
  217. }
  218. result.append(')');
  219. return result.toString();
  220. }
  221. String CSSBasicShapePolygon::cssText() const
  222. {
  223. Vector<String> points;
  224. points.reserveInitialCapacity(m_values.size());
  225. for (size_t i = 0; i < m_values.size(); ++i)
  226. points.append(m_values.at(i)->cssText());
  227. return buildPolygonString(m_windRule, points);
  228. }
  229. bool CSSBasicShapePolygon::equals(const CSSBasicShape& shape) const
  230. {
  231. if (shape.type() != CSSBasicShapePolygonType)
  232. return false;
  233. const CSSBasicShapePolygon& rhs = toCSSBasicShapePolygon(shape);
  234. return compareCSSValueVector(m_values, rhs.m_values);
  235. }
  236. DEFINE_TRACE(CSSBasicShapePolygon)
  237. {
  238. visitor->trace(m_values);
  239. CSSBasicShape::trace(visitor);
  240. }
  241. static bool buildInsetRadii(Vector<String> &radii, const String& topLeftRadius, const String& topRightRadius, const String& bottomRightRadius, const String& bottomLeftRadius)
  242. {
  243. bool showBottomLeft = topRightRadius != bottomLeftRadius;
  244. bool showBottomRight = showBottomLeft || (bottomRightRadius != topLeftRadius);
  245. bool showTopRight = showBottomRight || (topRightRadius != topLeftRadius);
  246. radii.append(topLeftRadius);
  247. if (showTopRight)
  248. radii.append(topRightRadius);
  249. if (showBottomRight)
  250. radii.append(bottomRightRadius);
  251. if (showBottomLeft)
  252. radii.append(bottomLeftRadius);
  253. return radii.size() == 1 && radii[0] == "0px";
  254. }
  255. static String buildInsetString(const String& top, const String& right, const String& bottom, const String& left,
  256. const String& topLeftRadiusWidth, const String& topLeftRadiusHeight,
  257. const String& topRightRadiusWidth, const String& topRightRadiusHeight,
  258. const String& bottomRightRadiusWidth, const String& bottomRightRadiusHeight,
  259. const String& bottomLeftRadiusWidth, const String& bottomLeftRadiusHeight)
  260. {
  261. char opening[] = "inset(";
  262. char separator[] = " ";
  263. char cornersSeparator[] = "round";
  264. StringBuilder result;
  265. result.appendLiteral(opening);
  266. result.append(top);
  267. bool showLeftArg = !left.isNull() && left != right;
  268. bool showBottomArg = !bottom.isNull() && (bottom != top || showLeftArg);
  269. bool showRightArg = !right.isNull() && (right != top || showBottomArg);
  270. if (showRightArg) {
  271. result.appendLiteral(separator);
  272. result.append(right);
  273. }
  274. if (showBottomArg) {
  275. result.appendLiteral(separator);
  276. result.append(bottom);
  277. }
  278. if (showLeftArg) {
  279. result.appendLiteral(separator);
  280. result.append(left);
  281. }
  282. if (!topLeftRadiusWidth.isNull() && !topLeftRadiusHeight.isNull()) {
  283. Vector<String> horizontalRadii;
  284. bool areDefaultCornerRadii = buildInsetRadii(horizontalRadii, topLeftRadiusWidth, topRightRadiusWidth, bottomRightRadiusWidth, bottomLeftRadiusWidth);
  285. Vector<String> verticalRadii;
  286. areDefaultCornerRadii &= buildInsetRadii(verticalRadii, topLeftRadiusHeight, topRightRadiusHeight, bottomRightRadiusHeight, bottomLeftRadiusHeight);
  287. if (!areDefaultCornerRadii) {
  288. result.appendLiteral(separator);
  289. result.appendLiteral(cornersSeparator);
  290. for (size_t i = 0; i < horizontalRadii.size(); ++i) {
  291. result.appendLiteral(separator);
  292. result.append(horizontalRadii[i]);
  293. }
  294. if (horizontalRadii != verticalRadii) {
  295. result.appendLiteral(separator);
  296. result.appendLiteral("/");
  297. for (size_t i = 0; i < verticalRadii.size(); ++i) {
  298. result.appendLiteral(separator);
  299. result.append(verticalRadii[i]);
  300. }
  301. }
  302. }
  303. }
  304. result.append(')');
  305. return result.toString();
  306. }
  307. static inline void updateCornerRadiusWidthAndHeight(const CSSValuePair* cornerRadius, String& width, String& height)
  308. {
  309. if (!cornerRadius)
  310. return;
  311. width = cornerRadius->first().cssText();
  312. height = cornerRadius->second().cssText();
  313. }
  314. String CSSBasicShapeInset::cssText() const
  315. {
  316. String topLeftRadiusWidth;
  317. String topLeftRadiusHeight;
  318. String topRightRadiusWidth;
  319. String topRightRadiusHeight;
  320. String bottomRightRadiusWidth;
  321. String bottomRightRadiusHeight;
  322. String bottomLeftRadiusWidth;
  323. String bottomLeftRadiusHeight;
  324. updateCornerRadiusWidthAndHeight(topLeftRadius(), topLeftRadiusWidth, topLeftRadiusHeight);
  325. updateCornerRadiusWidthAndHeight(topRightRadius(), topRightRadiusWidth, topRightRadiusHeight);
  326. updateCornerRadiusWidthAndHeight(bottomRightRadius(), bottomRightRadiusWidth, bottomRightRadiusHeight);
  327. updateCornerRadiusWidthAndHeight(bottomLeftRadius(), bottomLeftRadiusWidth, bottomLeftRadiusHeight);
  328. return buildInsetString(m_top ? m_top->cssText() : String(),
  329. m_right ? m_right->cssText() : String(),
  330. m_bottom ? m_bottom->cssText() : String(),
  331. m_left ? m_left->cssText() : String(),
  332. topLeftRadiusWidth,
  333. topLeftRadiusHeight,
  334. topRightRadiusWidth,
  335. topRightRadiusHeight,
  336. bottomRightRadiusWidth,
  337. bottomRightRadiusHeight,
  338. bottomLeftRadiusWidth,
  339. bottomLeftRadiusHeight);
  340. }
  341. bool CSSBasicShapeInset::equals(const CSSBasicShape& shape) const
  342. {
  343. if (shape.type() != CSSBasicShapeInsetType)
  344. return false;
  345. const CSSBasicShapeInset& other = toCSSBasicShapeInset(shape);
  346. return compareCSSValuePtr(m_top, other.m_top)
  347. && compareCSSValuePtr(m_right, other.m_right)
  348. && compareCSSValuePtr(m_bottom, other.m_bottom)
  349. && compareCSSValuePtr(m_left, other.m_left)
  350. && compareCSSValuePtr(m_topLeftRadius, other.m_topLeftRadius)
  351. && compareCSSValuePtr(m_topRightRadius, other.m_topRightRadius)
  352. && compareCSSValuePtr(m_bottomRightRadius, other.m_bottomRightRadius)
  353. && compareCSSValuePtr(m_bottomLeftRadius, other.m_bottomLeftRadius);
  354. }
  355. DEFINE_TRACE(CSSBasicShapeInset)
  356. {
  357. visitor->trace(m_top);
  358. visitor->trace(m_right);
  359. visitor->trace(m_bottom);
  360. visitor->trace(m_left);
  361. visitor->trace(m_topLeftRadius);
  362. visitor->trace(m_topRightRadius);
  363. visitor->trace(m_bottomRightRadius);
  364. visitor->trace(m_bottomLeftRadius);
  365. CSSBasicShape::trace(visitor);
  366. }
  367. } // namespace blink