Remove constant() in favor of env()
[WebKit-https.git] / Source / WebCore / css / CSSVariableData.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 "CSSVariableData.h"
32
33 #include "CSSCustomPropertyValue.h"
34 #include "CSSParserTokenRange.h"
35 #include <wtf/text/AtomicStringHash.h>
36 #include <wtf/text/StringBuilder.h>
37 #include <wtf/text/StringView.h>
38
39 namespace WebCore {
40
41 template<typename CharacterType> void CSSVariableData::updateTokens(const CSSParserTokenRange& range)
42 {
43     const CharacterType* currentOffset = m_backingString.characters<CharacterType>();
44     for (const CSSParserToken& token : range) {
45         if (token.hasStringBacking()) {
46             unsigned length = token.value().length();
47             StringView string(currentOffset, length);
48             m_tokens.append(token.copyWithUpdatedString(string));
49             currentOffset += length;
50         } else
51             m_tokens.append(token);
52     }
53     ASSERT(currentOffset == m_backingString.characters<CharacterType>() + m_backingString.length());
54 }
55
56 bool CSSVariableData::operator==(const CSSVariableData& other) const
57 {
58     return tokens() == other.tokens();
59 }
60
61 void CSSVariableData::consumeAndUpdateTokens(const CSSParserTokenRange& range)
62 {
63     StringBuilder stringBuilder;
64     CSSParserTokenRange localRange = range;
65
66     while (!localRange.atEnd()) {
67         CSSParserToken token = localRange.consume();
68         if (token.hasStringBacking())
69             stringBuilder.append(token.value());
70     }
71     m_backingString = stringBuilder.toString();
72     if (m_backingString.is8Bit())
73         updateTokens<LChar>(range);
74     else
75         updateTokens<UChar>(range);
76 }
77
78 CSSVariableData::CSSVariableData(const CSSParserTokenRange& range, bool needsVariableResolution)
79     : m_needsVariableResolution(needsVariableResolution)
80 {
81     ASSERT(!range.atEnd());
82     consumeAndUpdateTokens(range);
83 }
84
85 bool CSSVariableData::checkVariablesForCycles(const AtomicString& name, CustomPropertyValueMap& customProperties, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
86 {
87     if (invalidProperties.contains(name))
88         return false;
89     
90     HashSet<AtomicString> newSeenProperties = seenProperties;
91     newSeenProperties.add(name);
92     
93     bool valid = checkVariablesForCyclesWithRange(m_tokens, customProperties, newSeenProperties, invalidProperties);
94     if (!valid)
95         invalidProperties.add(name);
96     
97     return valid;
98 }
99     
100 bool CSSVariableData::checkVariablesForCyclesWithRange(CSSParserTokenRange range, CustomPropertyValueMap& customProperties, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
101 {
102     while (!range.atEnd()) {
103         if (range.peek().functionId() == CSSValueVar || range.peek().functionId() == CSSValueEnv) {
104             CSSParserTokenRange block = range.consumeBlock();
105             
106             block.consumeWhitespace();
107             ASSERT(block.peek().type() == IdentToken);
108             AtomicString variableName = block.consumeIncludingWhitespace().value().toAtomicString();
109             ASSERT(block.atEnd() || block.peek().type() == CommaToken);
110             if (seenProperties.contains(variableName))
111                 return false;
112
113             RefPtr<CSSCustomPropertyValue> value = customProperties.get(variableName);
114             if (value && value->containsVariables() && !value->checkVariablesForCycles(variableName, customProperties, seenProperties, invalidProperties))
115                 return false;
116
117             if (range.peek().type() == CommaToken) {
118                 // Fallback.
119                 range.consume();
120                 return checkVariablesForCyclesWithRange(block, customProperties, seenProperties, invalidProperties);
121             }
122         } else
123             range.consume();
124     }
125     return true;
126 }
127
128 bool CSSVariableData::resolveVariableFallback(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
129 {
130     if (range.atEnd())
131         return false;
132     ASSERT(range.peek().type() == CommaToken);
133     range.consume();
134     return resolveTokenRange(customProperties, range, result);
135 }
136     
137 bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
138 {
139     range.consumeWhitespace();
140     ASSERT(range.peek().type() == IdentToken);
141     AtomicString variableName = range.consumeIncludingWhitespace().value().toAtomicString();
142     ASSERT(range.atEnd() || (range.peek().type() == CommaToken));
143     
144     RefPtr<CSSCustomPropertyValue> property = customProperties.get(variableName);
145     if (!property || !property->value())
146         return resolveVariableFallback(customProperties, range, result);
147     
148     if (property->containsVariables()) {
149         // FIXME: Avoid doing this work more than once.
150         RefPtr<CSSVariableData> resolvedData = property->value()->resolveVariableReferences(customProperties);
151         if (!resolvedData)
152             return false;
153         result.appendVector(resolvedData->tokens());
154     } else
155         result.appendVector(property->value()->tokens());
156     
157     return true;
158 }
159
160 RefPtr<CSSVariableData> CSSVariableData::resolveVariableReferences(const CustomPropertyValueMap& customProperties) const
161 {
162     Vector<CSSParserToken> resolvedTokens;
163     CSSParserTokenRange range = m_tokens;
164     if (!resolveTokenRange(customProperties, range, resolvedTokens))
165         return nullptr;
166     return CSSVariableData::createResolved(resolvedTokens, *this);
167 }
168     
169 bool CSSVariableData::resolveTokenRange(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
170 {
171     bool success = true;
172     while (!range.atEnd()) {
173         if (range.peek().functionId() == CSSValueVar || range.peek().functionId() == CSSValueEnv)
174             success &= resolveVariableReference(customProperties, range.consumeBlock(), result);
175         else
176             result.append(range.consume());
177     }
178     return success;
179 }
180
181 } // namespace WebCore