1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Copyright (C) 2016 Apple 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 "CSSSupportsParser.h"
32
33#include "CSSParserImpl.h"
34
35namespace WebCore {
36
37CSSSupportsParser::SupportsResult CSSSupportsParser::supportsCondition(CSSParserTokenRange range, CSSParserImpl& parser, SupportsParsingMode mode)
38{
39 // FIXME: The spec allows leading whitespace in @supports but not CSS.supports,
40 // but major browser vendors allow it in CSS.supports also.
41 range.consumeWhitespace();
42 CSSSupportsParser supportsParser(parser);
43 auto result = supportsParser.consumeCondition(range);
44 if (mode != ForWindowCSS || result != Invalid)
45 return result;
46 // window.CSS.supports requires parsing as-if the condition was wrapped in
47 // parenthesis. The only productions that wouldn't have parsed above are the
48 // declaration condition or the general enclosed productions.
49 return supportsParser.consumeDeclarationConditionOrGeneralEnclosed(range);
50}
51
52enum ClauseType { Unresolved, Conjunction, Disjunction };
53
54CSSSupportsParser::SupportsResult CSSSupportsParser::consumeCondition(CSSParserTokenRange range)
55{
56 if (range.peek().type() == IdentToken || range.peek().type() == FunctionToken)
57 return consumeNegation(range);
58
59 bool result;
60 ClauseType clauseType = Unresolved;
61
62 auto previousTokenType = IdentToken;
63
64 while (true) {
65 SupportsResult nextResult = consumeConditionInParenthesis(range, previousTokenType);
66 if (nextResult == Invalid)
67 return Invalid;
68 bool nextSupported = nextResult;
69 if (clauseType == Unresolved)
70 result = nextSupported;
71 else if (clauseType == Conjunction)
72 result &= nextSupported;
73 else
74 result |= nextSupported;
75
76 if (range.atEnd())
77 break;
78 range.consumeWhitespace();
79 if (range.atEnd())
80 break;
81
82 const CSSParserToken& token = range.peek();
83 if (token.type() != IdentToken && token.type() != FunctionToken)
84 return Invalid;
85
86 previousTokenType = token.type();
87
88 if (clauseType == Unresolved)
89 clauseType = token.value().length() == 3 ? Conjunction : Disjunction;
90 if ((clauseType == Conjunction && !equalIgnoringASCIICase(token.value(), "and"))
91 || (clauseType == Disjunction && !equalIgnoringASCIICase(token.value(), "or")))
92 return Invalid;
93
94 if (token.type() == IdentToken)
95 range.consumeIncludingWhitespace();
96 }
97 return result ? Supported : Unsupported;
98}
99
100CSSSupportsParser::SupportsResult CSSSupportsParser::consumeNegation(CSSParserTokenRange range)
101{
102 ASSERT(range.peek().type() == IdentToken || range.peek().type() == FunctionToken);
103 auto tokenType = range.peek().type();
104 if (!equalIgnoringASCIICase(range.peek().value(), "not"))
105 return Invalid;
106 if (range.peek().type() == IdentToken)
107 range.consumeIncludingWhitespace();
108 SupportsResult result = consumeConditionInParenthesis(range, tokenType);
109 range.consumeWhitespace();
110 if (!range.atEnd() || result == Invalid)
111 return Invalid;
112 return result ? Unsupported : Supported;
113}
114
115CSSSupportsParser::SupportsResult CSSSupportsParser::consumeDeclarationConditionOrGeneralEnclosed(CSSParserTokenRange& range)
116{
117 if (range.peek().type() == FunctionToken) {
118 range.consumeComponentValue();
119 return Unsupported;
120 }
121
122 return range.peek().type() == IdentToken && m_parser.supportsDeclaration(range) ? Supported : Unsupported;
123}
124
125CSSSupportsParser::SupportsResult CSSSupportsParser::consumeConditionInParenthesis(CSSParserTokenRange& range, CSSParserTokenType startTokenType)
126{
127 if (startTokenType == IdentToken && range.peek().type() != LeftParenthesisToken)
128 return Invalid;
129
130 CSSParserTokenRange innerRange = range.consumeBlock();
131 innerRange.consumeWhitespace();
132 SupportsResult result = consumeCondition(innerRange);
133 if (result != Invalid)
134 return result;
135 return consumeDeclarationConditionOrGeneralEnclosed(innerRange);
136}
137
138} // namespace WebCore
139