CSSCalculationValue.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. * Copyright (C) 2011, 2012 Google Inc. 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 are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include "config.h"
  31. #include "CSSCalculationValue.h"
  32. #include "CSSValueList.h"
  33. #include "Length.h"
  34. #include "StyleResolver.h"
  35. #include <wtf/OwnPtr.h>
  36. #include <wtf/PassOwnPtr.h>
  37. #include <wtf/text/StringBuilder.h>
  38. static const int maxExpressionDepth = 100;
  39. enum ParseState {
  40. OK,
  41. TooDeep,
  42. NoMoreTokens
  43. };
  44. namespace WebCore {
  45. static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
  46. {
  47. switch (type) {
  48. case CSSPrimitiveValue::CSS_NUMBER:
  49. case CSSPrimitiveValue::CSS_PARSER_INTEGER:
  50. return CalcNumber;
  51. case CSSPrimitiveValue::CSS_PERCENTAGE:
  52. return CalcPercent;
  53. case CSSPrimitiveValue::CSS_EMS:
  54. case CSSPrimitiveValue::CSS_EXS:
  55. case CSSPrimitiveValue::CSS_PX:
  56. case CSSPrimitiveValue::CSS_CM:
  57. case CSSPrimitiveValue::CSS_MM:
  58. case CSSPrimitiveValue::CSS_IN:
  59. case CSSPrimitiveValue::CSS_PT:
  60. case CSSPrimitiveValue::CSS_PC:
  61. case CSSPrimitiveValue::CSS_REMS:
  62. case CSSPrimitiveValue::CSS_CHS:
  63. return CalcLength;
  64. #if ENABLE(CSS_VARIABLES)
  65. case CSSPrimitiveValue::CSS_VARIABLE_NAME:
  66. return CalcVariable;
  67. #endif
  68. default:
  69. return CalcOther;
  70. }
  71. }
  72. static String buildCssText(const String& expression)
  73. {
  74. StringBuilder result;
  75. result.append("calc");
  76. bool expressionHasSingleTerm = expression[0] != '(';
  77. if (expressionHasSingleTerm)
  78. result.append('(');
  79. result.append(expression);
  80. if (expressionHasSingleTerm)
  81. result.append(')');
  82. return result.toString();
  83. }
  84. String CSSCalcValue::customCssText() const
  85. {
  86. return buildCssText(m_expression->customCssText());
  87. }
  88. bool CSSCalcValue::equals(const CSSCalcValue& other) const
  89. {
  90. return compareCSSValuePtr(m_expression, other.m_expression);
  91. }
  92. #if ENABLE(CSS_VARIABLES)
  93. String CSSCalcValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
  94. {
  95. return buildCssText(m_expression->serializeResolvingVariables(variables));
  96. }
  97. bool CSSCalcValue::hasVariableReference() const
  98. {
  99. return m_expression->hasVariableReference();
  100. }
  101. #endif
  102. double CSSCalcValue::clampToPermittedRange(double value) const
  103. {
  104. return m_nonNegative && value < 0 ? 0 : value;
  105. }
  106. double CSSCalcValue::doubleValue() const
  107. {
  108. return clampToPermittedRange(m_expression->doubleValue());
  109. }
  110. double CSSCalcValue::computeLengthPx(const RenderStyle* currentStyle, const RenderStyle* rootStyle, double multiplier, bool computingFontSize) const
  111. {
  112. return clampToPermittedRange(m_expression->computeLengthPx(currentStyle, rootStyle, multiplier, computingFontSize));
  113. }
  114. CSSCalcExpressionNode::~CSSCalcExpressionNode()
  115. {
  116. }
  117. class CSSCalcPrimitiveValue : public CSSCalcExpressionNode {
  118. WTF_MAKE_FAST_ALLOCATED;
  119. public:
  120. static PassRefPtr<CSSCalcPrimitiveValue> create(CSSPrimitiveValue* value, bool isInteger)
  121. {
  122. return adoptRef(new CSSCalcPrimitiveValue(value, isInteger));
  123. }
  124. virtual bool isZero() const
  125. {
  126. return !m_value->getDoubleValue();
  127. }
  128. virtual String customCssText() const
  129. {
  130. return m_value->cssText();
  131. }
  132. #if ENABLE(CSS_VARIABLES)
  133. virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
  134. {
  135. return m_value->customSerializeResolvingVariables(variables);
  136. }
  137. virtual bool hasVariableReference() const
  138. {
  139. return m_value->isVariableName();
  140. }
  141. #endif
  142. virtual PassOwnPtr<CalcExpressionNode> toCalcValue(const RenderStyle* style, const RenderStyle* rootStyle, double zoom) const
  143. {
  144. switch (m_category) {
  145. case CalcNumber:
  146. return adoptPtr(new CalcExpressionNumber(m_value->getFloatValue()));
  147. case CalcLength:
  148. return adoptPtr(new CalcExpressionNumber(m_value->computeLength<float>(style, rootStyle, zoom)));
  149. case CalcPercent:
  150. case CalcPercentLength:
  151. return adoptPtr(new CalcExpressionLength(StyleResolver::convertToFloatLength(m_value.get(), style, rootStyle, zoom)));
  152. // Only types that could be part of a Length expression can be converted
  153. // to a CalcExpressionNode. CalcPercentNumber makes no sense as a Length.
  154. case CalcPercentNumber:
  155. #if ENABLE(CSS_VARIABLES)
  156. case CalcVariable:
  157. #endif
  158. case CalcOther:
  159. ASSERT_NOT_REACHED();
  160. }
  161. return nullptr;
  162. }
  163. virtual double doubleValue() const
  164. {
  165. switch (m_category) {
  166. case CalcNumber:
  167. case CalcPercent:
  168. return m_value->getDoubleValue();
  169. case CalcLength:
  170. case CalcPercentLength:
  171. case CalcPercentNumber:
  172. #if ENABLE(CSS_VARIABLES)
  173. case CalcVariable:
  174. #endif
  175. case CalcOther:
  176. ASSERT_NOT_REACHED();
  177. break;
  178. }
  179. return 0;
  180. }
  181. virtual double computeLengthPx(const RenderStyle* currentStyle, const RenderStyle* rootStyle, double multiplier, bool computingFontSize) const
  182. {
  183. switch (m_category) {
  184. case CalcLength:
  185. return m_value->computeLength<double>(currentStyle, rootStyle, multiplier, computingFontSize);
  186. case CalcPercent:
  187. case CalcNumber:
  188. return m_value->getDoubleValue();
  189. case CalcPercentLength:
  190. case CalcPercentNumber:
  191. #if ENABLE(CSS_VARIABLES)
  192. case CalcVariable:
  193. #endif
  194. case CalcOther:
  195. ASSERT_NOT_REACHED();
  196. break;
  197. }
  198. return 0;
  199. }
  200. virtual bool equals(const CSSCalcExpressionNode& other) const
  201. {
  202. if (type() != other.type())
  203. return false;
  204. return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
  205. }
  206. virtual Type type() const { return CssCalcPrimitiveValue; }
  207. private:
  208. explicit CSSCalcPrimitiveValue(CSSPrimitiveValue* value, bool isInteger)
  209. : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
  210. , m_value(value)
  211. {
  212. }
  213. RefPtr<CSSPrimitiveValue> m_value;
  214. };
  215. static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
  216. { CalcNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther },
  217. { CalcOther, CalcLength, CalcPercentLength, CalcOther, CalcPercentLength },
  218. { CalcPercentNumber, CalcPercentLength, CalcPercent, CalcPercentNumber, CalcPercentLength },
  219. { CalcPercentNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther },
  220. { CalcOther, CalcPercentLength, CalcPercentLength, CalcOther, CalcPercentLength },
  221. };
  222. static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
  223. {
  224. CalculationCategory leftCategory = leftSide.category();
  225. CalculationCategory rightCategory = rightSide.category();
  226. if (leftCategory == CalcOther || rightCategory == CalcOther)
  227. return CalcOther;
  228. #if ENABLE(CSS_VARIABLES)
  229. if (leftCategory == CalcVariable || rightCategory == CalcVariable)
  230. return CalcVariable;
  231. #endif
  232. switch (op) {
  233. case CalcAdd:
  234. case CalcSubtract:
  235. return addSubtractResult[leftCategory][rightCategory];
  236. case CalcMultiply:
  237. if (leftCategory != CalcNumber && rightCategory != CalcNumber)
  238. return CalcOther;
  239. return leftCategory == CalcNumber ? rightCategory : leftCategory;
  240. case CalcDivide:
  241. if (rightCategory != CalcNumber || rightSide.isZero())
  242. return CalcOther;
  243. return leftCategory;
  244. }
  245. ASSERT_NOT_REACHED();
  246. return CalcOther;
  247. }
  248. class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
  249. public:
  250. static PassRefPtr<CSSCalcBinaryOperation> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
  251. {
  252. ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther);
  253. CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
  254. if (newCategory == CalcOther)
  255. return 0;
  256. return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
  257. }
  258. virtual bool isZero() const
  259. {
  260. return !doubleValue();
  261. }
  262. virtual PassOwnPtr<CalcExpressionNode> toCalcValue(const RenderStyle* style, const RenderStyle* rootStyle, double zoom) const
  263. {
  264. OwnPtr<CalcExpressionNode> left(m_leftSide->toCalcValue(style, rootStyle, zoom));
  265. if (!left)
  266. return nullptr;
  267. OwnPtr<CalcExpressionNode> right(m_rightSide->toCalcValue(style, rootStyle, zoom));
  268. if (!right)
  269. return nullptr;
  270. return adoptPtr(new CalcExpressionBinaryOperation(left.release(), right.release(), m_operator));
  271. }
  272. virtual double doubleValue() const
  273. {
  274. return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
  275. }
  276. virtual double computeLengthPx(const RenderStyle* currentStyle, const RenderStyle* rootStyle, double multiplier, bool computingFontSize) const
  277. {
  278. const double leftValue = m_leftSide->computeLengthPx(currentStyle, rootStyle, multiplier, computingFontSize);
  279. const double rightValue = m_rightSide->computeLengthPx(currentStyle, rootStyle, multiplier, computingFontSize);
  280. return evaluate(leftValue, rightValue);
  281. }
  282. static String buildCssText(const String& leftExpression, const String& rightExpression, CalcOperator op)
  283. {
  284. StringBuilder result;
  285. result.append('(');
  286. result.append(leftExpression);
  287. result.append(' ');
  288. result.append(static_cast<char>(op));
  289. result.append(' ');
  290. result.append(rightExpression);
  291. result.append(')');
  292. return result.toString();
  293. }
  294. virtual String customCssText() const
  295. {
  296. return buildCssText(m_leftSide->customCssText(), m_rightSide->customCssText(), m_operator);
  297. }
  298. #if ENABLE(CSS_VARIABLES)
  299. virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
  300. {
  301. return buildCssText(m_leftSide->serializeResolvingVariables(variables), m_rightSide->serializeResolvingVariables(variables), m_operator);
  302. }
  303. virtual bool hasVariableReference() const
  304. {
  305. return m_leftSide->hasVariableReference() || m_rightSide->hasVariableReference();
  306. }
  307. #endif
  308. virtual bool equals(const CSSCalcExpressionNode& exp) const
  309. {
  310. if (type() != exp.type())
  311. return false;
  312. const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
  313. return compareCSSValuePtr(m_leftSide, other.m_leftSide)
  314. && compareCSSValuePtr(m_rightSide, other.m_rightSide)
  315. && m_operator == other.m_operator;
  316. }
  317. virtual Type type() const { return CssCalcBinaryOperation; }
  318. private:
  319. CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
  320. : CSSCalcExpressionNode(category, leftSide->isInteger() && rightSide->isInteger())
  321. , m_leftSide(leftSide)
  322. , m_rightSide(rightSide)
  323. , m_operator(op)
  324. {
  325. }
  326. double evaluate(double leftValue, double rightValue) const
  327. {
  328. switch (m_operator) {
  329. case CalcAdd:
  330. return leftValue + rightValue;
  331. case CalcSubtract:
  332. return leftValue - rightValue;
  333. case CalcMultiply:
  334. return leftValue * rightValue;
  335. case CalcDivide:
  336. if (rightValue)
  337. return leftValue / rightValue;
  338. return std::numeric_limits<double>::quiet_NaN();
  339. }
  340. return 0;
  341. }
  342. const RefPtr<CSSCalcExpressionNode> m_leftSide;
  343. const RefPtr<CSSCalcExpressionNode> m_rightSide;
  344. const CalcOperator m_operator;
  345. };
  346. static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
  347. {
  348. (*depth)++;
  349. if (*depth > maxExpressionDepth)
  350. return TooDeep;
  351. if (index >= tokens->size())
  352. return NoMoreTokens;
  353. return OK;
  354. }
  355. class CSSCalcExpressionNodeParser {
  356. public:
  357. PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
  358. {
  359. unsigned index = 0;
  360. Value result;
  361. bool ok = parseValueExpression(tokens, 0, &index, &result);
  362. ASSERT_WITH_SECURITY_IMPLICATION(index <= tokens->size());
  363. if (!ok || index != tokens->size())
  364. return 0;
  365. return result.value;
  366. }
  367. private:
  368. struct Value {
  369. RefPtr<CSSCalcExpressionNode> value;
  370. };
  371. char operatorValue(CSSParserValueList* tokens, unsigned index)
  372. {
  373. if (index >= tokens->size())
  374. return 0;
  375. CSSParserValue* value = tokens->valueAt(index);
  376. if (value->unit != CSSParserValue::Operator)
  377. return 0;
  378. return value->iValue;
  379. }
  380. bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
  381. {
  382. CSSParserValue* parserValue = tokens->valueAt(*index);
  383. if (parserValue->unit == CSSParserValue::Operator || parserValue->unit == CSSParserValue::Function)
  384. return false;
  385. RefPtr<CSSValue> value = parserValue->createCSSValue();
  386. if (!value || !value->isPrimitiveValue())
  387. return false;
  388. CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
  389. result->value = CSSCalcPrimitiveValue::create(primitiveValue, parserValue->isInt);
  390. ++*index;
  391. return true;
  392. }
  393. bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
  394. {
  395. if (checkDepthAndIndex(&depth, *index, tokens) != OK)
  396. return false;
  397. if (operatorValue(tokens, *index) == '(') {
  398. unsigned currentIndex = *index + 1;
  399. if (!parseValueExpression(tokens, depth, &currentIndex, result))
  400. return false;
  401. if (operatorValue(tokens, currentIndex) != ')')
  402. return false;
  403. *index = currentIndex + 1;
  404. return true;
  405. }
  406. return parseValue(tokens, index, result);
  407. }
  408. bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
  409. {
  410. if (checkDepthAndIndex(&depth, *index, tokens) != OK)
  411. return false;
  412. if (!parseValueTerm(tokens, depth, index, result))
  413. return false;
  414. while (*index < tokens->size() - 1) {
  415. char operatorCharacter = operatorValue(tokens, *index);
  416. if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide)
  417. break;
  418. ++*index;
  419. Value rhs;
  420. if (!parseValueTerm(tokens, depth, index, &rhs))
  421. return false;
  422. result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
  423. if (!result->value)
  424. return false;
  425. }
  426. ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
  427. return true;
  428. }
  429. bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
  430. {
  431. if (checkDepthAndIndex(&depth, *index, tokens) != OK)
  432. return false;
  433. if (!parseValueMultiplicativeExpression(tokens, depth, index, result))
  434. return false;
  435. while (*index < tokens->size() - 1) {
  436. char operatorCharacter = operatorValue(tokens, *index);
  437. if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract)
  438. break;
  439. ++*index;
  440. Value rhs;
  441. if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs))
  442. return false;
  443. result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
  444. if (!result->value)
  445. return false;
  446. }
  447. ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
  448. return true;
  449. }
  450. bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
  451. {
  452. return parseAdditiveValueExpression(tokens, depth, index, result);
  453. }
  454. };
  455. PassRefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList, CalculationPermittedValueRange range)
  456. {
  457. CSSCalcExpressionNodeParser parser;
  458. RefPtr<CSSCalcExpressionNode> expression;
  459. if (equalIgnoringCase(name, "calc(") || equalIgnoringCase(name, "-webkit-calc("))
  460. expression = parser.parseCalc(parserValueList);
  461. // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
  462. return expression ? adoptRef(new CSSCalcValue(expression, range)) : 0;
  463. }
  464. } // namespace WebCore