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 AtomicString& 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 |
197 | |