Rename "customFixedPositionRect" to "layoutViewportRect"
[WebKit-https.git] / Source / WebCore / css / parser / CSSVariableParser.cpp
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 "CSSVariableParser.h"
32
33 #include "CSSCustomPropertyValue.h"
34 #include "CSSParserContext.h"
35 #include "CSSParserTokenRange.h"
36
37 namespace WebCore {
38
39 bool CSSVariableParser::isValidVariableName(const CSSParserToken& token)
40 {
41     if (token.type() != IdentToken)
42         return false;
43
44     StringView value = token.value();
45     return value.length() >= 2 && value[0] == '-' && value[1] == '-';
46 }
47
48 bool CSSVariableParser::isValidVariableName(const String& string)
49 {
50     return string.length() >= 2 && string[0] == '-' && string[1] == '-';
51 }
52
53 static bool isValidConstantName(const CSSParserToken& token)
54 {
55     return token.type() == IdentToken;
56 }
57
58 bool isValidVariableReference(CSSParserTokenRange, bool& hasAtApplyRule, const CSSParserContext&);
59 bool isValidConstantReference(CSSParserTokenRange, bool& hasAtApplyRule, const CSSParserContext&);
60
61 static bool classifyBlock(CSSParserTokenRange range, bool& hasReferences, bool& hasAtApplyRule, const CSSParserContext& parserContext, bool isTopLevelBlock = true)
62 {
63     while (!range.atEnd()) {
64         if (range.peek().getBlockType() == CSSParserToken::BlockStart) {
65             const CSSParserToken& token = range.peek();
66             CSSParserTokenRange block = range.consumeBlock();
67             if (token.functionId() == CSSValueVar) {
68                 if (!isValidVariableReference(block, hasAtApplyRule, parserContext))
69                     return false; // Bail if any references are invalid
70                 hasReferences = true;
71                 continue;
72             }
73             if (token.functionId() == CSSValueEnv && parserContext.constantPropertiesEnabled) {
74                 if (!isValidConstantReference(block, hasAtApplyRule, parserContext))
75                     return false; // Bail if any references are invalid
76                 hasReferences = true;
77                 continue;
78             }
79             if (!classifyBlock(block, hasReferences, hasAtApplyRule, parserContext, false))
80                 return false;
81             continue;
82         }
83
84         ASSERT(range.peek().getBlockType() != CSSParserToken::BlockEnd);
85
86         const CSSParserToken& token = range.consume();
87         switch (token.type()) {
88         case AtKeywordToken: {
89             if (equalIgnoringASCIICase(token.value(), "apply")) {
90                 range.consumeWhitespace();
91                 const CSSParserToken& variableName = range.consumeIncludingWhitespace();
92                 if (!CSSVariableParser::isValidVariableName(variableName)
93                     || !(range.atEnd() || range.peek().type() == SemicolonToken || range.peek().type() == RightBraceToken))
94                     return false;
95                 hasAtApplyRule = true;
96             }
97             break;
98         }
99         case DelimiterToken: {
100             if (token.delimiter() == '!' && isTopLevelBlock)
101                 return false;
102             break;
103         }
104         case RightParenthesisToken:
105         case RightBraceToken:
106         case RightBracketToken:
107         case BadStringToken:
108         case BadUrlToken:
109             return false;
110         case SemicolonToken:
111             if (isTopLevelBlock)
112                 return false;
113             break;
114         default:
115             break;
116         }
117     }
118     return true;
119 }
120
121 bool isValidVariableReference(CSSParserTokenRange range, bool& hasAtApplyRule, const CSSParserContext& parserContext)
122 {
123     range.consumeWhitespace();
124     if (!CSSVariableParser::isValidVariableName(range.consumeIncludingWhitespace()))
125         return false;
126     if (range.atEnd())
127         return true;
128
129     if (range.consume().type() != CommaToken)
130         return false;
131     if (range.atEnd())
132         return false;
133
134     bool hasReferences = false;
135     return classifyBlock(range, hasReferences, hasAtApplyRule, parserContext);
136 }
137
138 bool isValidConstantReference(CSSParserTokenRange range, bool& hasAtApplyRule, const CSSParserContext& parserContext)
139 {
140     range.consumeWhitespace();
141     if (!isValidConstantName(range.consumeIncludingWhitespace()))
142         return false;
143     if (range.atEnd())
144         return true;
145
146     if (range.consume().type() != CommaToken)
147         return false;
148     if (range.atEnd())
149         return false;
150
151     bool hasReferences = false;
152     return classifyBlock(range, hasReferences, hasAtApplyRule, parserContext);
153 }
154
155 static CSSValueID classifyVariableRange(CSSParserTokenRange range, bool& hasReferences, bool& hasAtApplyRule, const CSSParserContext& parserContext)
156 {
157     hasReferences = false;
158     hasAtApplyRule = false;
159
160     range.consumeWhitespace();
161     if (range.peek().type() == IdentToken) {
162         CSSValueID id = range.consumeIncludingWhitespace().id();
163         if (range.atEnd() && (id == CSSValueInherit || id == CSSValueInitial || id == CSSValueUnset || id == CSSValueRevert))
164             return id;
165     }
166
167     if (classifyBlock(range, hasReferences, hasAtApplyRule, parserContext))
168         return CSSValueInternalVariableValue;
169     return CSSValueInvalid;
170 }
171
172 bool CSSVariableParser::containsValidVariableReferences(CSSParserTokenRange range, const CSSParserContext& parserContext)
173 {
174     bool hasReferences;
175     bool hasAtApplyRule;
176     CSSValueID type = classifyVariableRange(range, hasReferences, hasAtApplyRule, parserContext);
177     return type == CSSValueInternalVariableValue && hasReferences && !hasAtApplyRule;
178 }
179
180 RefPtr<CSSCustomPropertyValue> CSSVariableParser::parseDeclarationValue(const AtomString& variableName, CSSParserTokenRange range, const CSSParserContext& parserContext)
181 {
182     if (range.atEnd())
183         return nullptr;
184
185     bool hasReferences;
186     bool hasAtApplyRule;
187     CSSValueID type = classifyVariableRange(range, hasReferences, hasAtApplyRule, parserContext);
188
189     if (type == CSSValueInvalid)
190         return nullptr;
191     if (type == CSSValueInternalVariableValue)
192         return CSSCustomPropertyValue::createUnresolved(variableName, CSSVariableReferenceValue::create(range));
193     return CSSCustomPropertyValue::createUnresolved(variableName, type);
194 }
195
196 } // namespace WebCore