summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/parser/SizesCalcParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/css/parser/SizesCalcParser.cpp')
-rw-r--r--Source/WebCore/css/parser/SizesCalcParser.cpp259
1 files changed, 259 insertions, 0 deletions
diff --git a/Source/WebCore/css/parser/SizesCalcParser.cpp b/Source/WebCore/css/parser/SizesCalcParser.cpp
new file mode 100644
index 000000000..4c36c740a
--- /dev/null
+++ b/Source/WebCore/css/parser/SizesCalcParser.cpp
@@ -0,0 +1,259 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "SizesCalcParser.h"
+
+#include "CSSParserToken.h"
+#include "RenderView.h"
+#include "SizesAttributeParser.h"
+
+namespace WebCore {
+
+SizesCalcParser::SizesCalcParser(CSSParserTokenRange range, const Document& document)
+ : m_result(0)
+ , m_document(document)
+{
+ m_isValid = calcToReversePolishNotation(range) && calculate();
+}
+
+float SizesCalcParser::result() const
+{
+ ASSERT(m_isValid);
+ return m_result;
+}
+
+static bool operatorPriority(UChar cc, bool& highPriority)
+{
+ if (cc == '+' || cc == '-')
+ highPriority = false;
+ else if (cc == '*' || cc == '/')
+ highPriority = true;
+ else
+ return false;
+ return true;
+}
+
+bool SizesCalcParser::handleOperator(Vector<CSSParserToken>& stack, const CSSParserToken& token)
+{
+ // If the token is an operator, o1, then:
+ // while there is an operator token, o2, at the top of the stack, and
+ // either o1 is left-associative and its precedence is equal to that of o2,
+ // or o1 has precedence less than that of o2,
+ // pop o2 off the stack, onto the output queue;
+ // push o1 onto the stack.
+ bool stackOperatorPriority;
+ bool incomingOperatorPriority;
+
+ if (!operatorPriority(token.delimiter(), incomingOperatorPriority))
+ return false;
+ if (!stack.isEmpty() && stack.last().type() == DelimiterToken) {
+ if (!operatorPriority(stack.last().delimiter(), stackOperatorPriority))
+ return false;
+ if (!incomingOperatorPriority || stackOperatorPriority) {
+ appendOperator(stack.last());
+ stack.removeLast();
+ }
+ }
+ stack.append(token);
+ return true;
+}
+
+void SizesCalcParser::appendNumber(const CSSParserToken& token)
+{
+ SizesCalcValue value;
+ value.value = token.numericValue();
+ m_valueList.append(value);
+}
+
+bool SizesCalcParser::appendLength(const CSSParserToken& token)
+{
+ SizesCalcValue value;
+ double result = SizesAttributeParser::computeLength(token.numericValue(), token.unitType(), m_document);
+ value.value = result;
+ value.isLength = true;
+ m_valueList.append(value);
+ return true;
+}
+
+void SizesCalcParser::appendOperator(const CSSParserToken& token)
+{
+ SizesCalcValue value;
+ value.operation = token.delimiter();
+ m_valueList.append(value);
+}
+
+bool SizesCalcParser::calcToReversePolishNotation(CSSParserTokenRange range)
+{
+ // This method implements the shunting yard algorithm, to turn the calc syntax into a reverse polish notation.
+ // http://en.wikipedia.org/wiki/Shunting-yard_algorithm
+
+ Vector<CSSParserToken> stack;
+ while (!range.atEnd()) {
+ const CSSParserToken& token = range.consume();
+ switch (token.type()) {
+ case NumberToken:
+ appendNumber(token);
+ break;
+ case DimensionToken:
+ if (!CSSPrimitiveValue::isLength(token.unitType()) || !appendLength(token))
+ return false;
+ break;
+ case DelimiterToken:
+ if (!handleOperator(stack, token))
+ return false;
+ break;
+ case FunctionToken:
+ if (!equalIgnoringASCIICase(token.value(), "calc"))
+ return false;
+ // "calc(" is the same as "("
+ FALLTHROUGH;
+ case LeftParenthesisToken:
+ // If the token is a left parenthesis, then push it onto the stack.
+ stack.append(token);
+ break;
+ case RightParenthesisToken:
+ // If the token is a right parenthesis:
+ // Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
+ while (!stack.isEmpty() && stack.last().type() != LeftParenthesisToken && stack.last().type() != FunctionToken) {
+ appendOperator(stack.last());
+ stack.removeLast();
+ }
+ // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
+ if (stack.isEmpty())
+ return false;
+ // Pop the left parenthesis from the stack, but not onto the output queue.
+ stack.removeLast();
+ break;
+ case WhitespaceToken:
+ case EOFToken:
+ break;
+ case CDOToken:
+ case CDCToken:
+ case AtKeywordToken:
+ case HashToken:
+ case UrlToken:
+ case BadUrlToken:
+ case PercentageToken:
+ case IncludeMatchToken:
+ case DashMatchToken:
+ case PrefixMatchToken:
+ case SuffixMatchToken:
+ case SubstringMatchToken:
+ case ColumnToken:
+ case UnicodeRangeToken:
+ case IdentToken:
+ case CommaToken:
+ case ColonToken:
+ case SemicolonToken:
+ case LeftBraceToken:
+ case LeftBracketToken:
+ case RightBraceToken:
+ case RightBracketToken:
+ case StringToken:
+ case BadStringToken:
+ return false;
+ case CommentToken:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+
+ // When there are no more tokens to read:
+ // While there are still operator tokens in the stack:
+ while (!stack.isEmpty()) {
+ // If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
+ CSSParserTokenType type = stack.last().type();
+ if (type == LeftParenthesisToken || type == FunctionToken)
+ return false;
+ // Pop the operator onto the output queue.
+ appendOperator(stack.last());
+ stack.removeLast();
+ }
+ return true;
+}
+
+static bool operateOnStack(Vector<SizesCalcValue>& stack, UChar operation)
+{
+ if (stack.size() < 2)
+ return false;
+ SizesCalcValue rightOperand = stack.last();
+ stack.removeLast();
+ SizesCalcValue leftOperand = stack.last();
+ stack.removeLast();
+ bool isLength;
+ switch (operation) {
+ case '+':
+ if (rightOperand.isLength != leftOperand.isLength)
+ return false;
+ isLength = (rightOperand.isLength && leftOperand.isLength);
+ stack.append(SizesCalcValue(leftOperand.value + rightOperand.value, isLength));
+ break;
+ case '-':
+ if (rightOperand.isLength != leftOperand.isLength)
+ return false;
+ isLength = (rightOperand.isLength && leftOperand.isLength);
+ stack.append(SizesCalcValue(leftOperand.value - rightOperand.value, isLength));
+ break;
+ case '*':
+ if (rightOperand.isLength && leftOperand.isLength)
+ return false;
+ isLength = (rightOperand.isLength || leftOperand.isLength);
+ stack.append(SizesCalcValue(leftOperand.value * rightOperand.value, isLength));
+ break;
+ case '/':
+ if (rightOperand.isLength || !rightOperand.value)
+ return false;
+ stack.append(SizesCalcValue(leftOperand.value / rightOperand.value, leftOperand.isLength));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool SizesCalcParser::calculate()
+{
+ Vector<SizesCalcValue> stack;
+ for (const auto& value : m_valueList) {
+ if (!value.operation)
+ stack.append(value);
+ else {
+ if (!operateOnStack(stack, value.operation))
+ return false;
+ }
+ }
+ if (stack.size() == 1 && stack.last().isLength) {
+ m_result = std::max(clampTo<float>(stack.last().value), (float)0.0);
+ return true;
+ }
+ return false;
+}
+
+} // namespace WebCore