| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/css/parser/CSSSupportsParser.h" |
| |
| #include "core/css/parser/CSSParserImpl.h" |
| |
| namespace blink { |
| |
| CSSSupportsParser::SupportsResult CSSSupportsParser::supportsCondition( |
| CSSParserTokenRange range, |
| CSSParserImpl& parser) { |
| // TODO(timloh): The spec allows leading whitespace in @supports but not |
| // CSS.supports, but major browser vendors allow it in CSS.supports also. |
| range.consumeWhitespace(); |
| return CSSSupportsParser(parser).consumeCondition(range); |
| } |
| |
| enum ClauseType { Unresolved, Conjunction, Disjunction }; |
| |
| CSSSupportsParser::SupportsResult CSSSupportsParser::consumeCondition( |
| CSSParserTokenRange range) { |
| if (range.peek().type() == IdentToken) |
| return consumeNegation(range); |
| |
| bool result; |
| ClauseType clauseType = Unresolved; |
| |
| while (true) { |
| SupportsResult nextResult = consumeConditionInParenthesis(range); |
| if (nextResult == Invalid) |
| return Invalid; |
| bool nextSupported = nextResult; |
| if (clauseType == Unresolved) |
| result = nextSupported; |
| else if (clauseType == Conjunction) |
| result &= nextSupported; |
| else |
| result |= nextSupported; |
| |
| if (range.atEnd()) |
| break; |
| if (range.consumeIncludingWhitespace().type() != WhitespaceToken) |
| return Invalid; |
| if (range.atEnd()) |
| break; |
| |
| const CSSParserToken& token = range.consume(); |
| if (token.type() != IdentToken) |
| return Invalid; |
| if (clauseType == Unresolved) |
| clauseType = token.value().length() == 3 ? Conjunction : Disjunction; |
| if ((clauseType == Conjunction && |
| !equalIgnoringASCIICase(token.value(), "and")) || |
| (clauseType == Disjunction && |
| !equalIgnoringASCIICase(token.value(), "or"))) |
| return Invalid; |
| |
| if (range.consumeIncludingWhitespace().type() != WhitespaceToken) |
| return Invalid; |
| } |
| return result ? Supported : Unsupported; |
| } |
| |
| CSSSupportsParser::SupportsResult CSSSupportsParser::consumeNegation( |
| CSSParserTokenRange range) { |
| ASSERT(range.peek().type() == IdentToken); |
| if (!equalIgnoringASCIICase(range.consume().value(), "not")) |
| return Invalid; |
| if (range.consumeIncludingWhitespace().type() != WhitespaceToken) |
| return Invalid; |
| SupportsResult result = consumeConditionInParenthesis(range); |
| range.consumeWhitespace(); |
| if (!range.atEnd() || result == Invalid) |
| return Invalid; |
| return result ? Unsupported : Supported; |
| } |
| |
| CSSSupportsParser::SupportsResult |
| CSSSupportsParser::consumeConditionInParenthesis(CSSParserTokenRange& range) { |
| if (range.peek().type() == FunctionToken) { |
| range.consumeComponentValue(); |
| return Unsupported; |
| } |
| if (range.peek().type() != LeftParenthesisToken) |
| return Invalid; |
| CSSParserTokenRange innerRange = range.consumeBlock(); |
| innerRange.consumeWhitespace(); |
| SupportsResult result = consumeCondition(innerRange); |
| if (result != Invalid) |
| return result; |
| return innerRange.peek().type() == IdentToken && |
| m_parser.supportsDeclaration(innerRange) |
| ? Supported |
| : Unsupported; |
| } |
| |
| } // namespace blink |