Unreviewed, rolling out r234489.
[WebKit-https.git] / Source / WebCore / css / parser / CSSParserToken.cpp
1 // Copyright 2014 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 "CSSParserToken.h"
32
33 #include "CSSMarkup.h"
34 #include "CSSPrimitiveValue.h"
35 #include "CSSPropertyParser.h"
36 #include <limits.h>
37 #include <wtf/text/StringBuilder.h>
38
39 namespace WebCore {
40
41 template<typename CharacterType>
42 CSSPrimitiveValue::UnitType cssPrimitiveValueUnitFromTrie(const CharacterType* data, unsigned length)
43 {
44     ASSERT(data);
45     ASSERT(length);
46     switch (length) {
47     case 1:
48         switch (toASCIILower(data[0])) {
49         case 's':
50             return CSSPrimitiveValue::UnitType::CSS_S;
51         }
52         break;
53     case 2:
54         switch (toASCIILower(data[0])) {
55         case 'c':
56             switch (toASCIILower(data[1])) {
57             case 'h':
58                 return CSSPrimitiveValue::UnitType::CSS_CHS;
59             case 'm':
60                 return CSSPrimitiveValue::UnitType::CSS_CM;
61             }
62             break;
63         case 'e':
64             switch (toASCIILower(data[1])) {
65             case 'm':
66                 return CSSPrimitiveValue::UnitType::CSS_EMS;
67             case 'x':
68                 return CSSPrimitiveValue::UnitType::CSS_EXS;
69             }
70             break;
71         case 'f':
72             if (toASCIILower(data[1]) == 'r')
73                 return CSSPrimitiveValue::UnitType::CSS_FR;
74             break;
75         case 'h':
76             if (toASCIILower(data[1]) == 'z')
77                 return CSSPrimitiveValue::UnitType::CSS_HZ;
78             break;
79         case 'i':
80             if (toASCIILower(data[1]) == 'n')
81                 return CSSPrimitiveValue::UnitType::CSS_IN;
82             break;
83         case 'm':
84             switch (toASCIILower(data[1])) {
85             case 'm':
86                 return CSSPrimitiveValue::UnitType::CSS_MM;
87             case 's':
88                 return CSSPrimitiveValue::UnitType::CSS_MS;
89             }
90             break;
91         case 'p':
92             switch (toASCIILower(data[1])) {
93             case 'c':
94                 return CSSPrimitiveValue::UnitType::CSS_PC;
95             case 't':
96                 return CSSPrimitiveValue::UnitType::CSS_PT;
97             case 'x':
98                 return CSSPrimitiveValue::UnitType::CSS_PX;
99             }
100             break;
101         case 'v':
102             switch (toASCIILower(data[1])) {
103             case 'h':
104                 return CSSPrimitiveValue::UnitType::CSS_VH;
105             case 'w':
106                 return CSSPrimitiveValue::UnitType::CSS_VW;
107             }
108             break;
109         }
110         break;
111     case 3:
112         switch (toASCIILower(data[0])) {
113         case 'd':
114             switch (toASCIILower(data[1])) {
115             case 'e':
116                 if (toASCIILower(data[2]) == 'g')
117                     return CSSPrimitiveValue::UnitType::CSS_DEG;
118                 break;
119             case 'p':
120                 if (toASCIILower(data[2]) == 'i')
121                     return CSSPrimitiveValue::UnitType::CSS_DPI;
122                 break;
123             }
124         break;
125         case 'k':
126             if (toASCIILower(data[1]) == 'h' && toASCIILower(data[2]) == 'z')
127                 return CSSPrimitiveValue::UnitType::CSS_KHZ;
128             break;
129         case 'r':
130             switch (toASCIILower(data[1])) {
131             case 'a':
132                 if (toASCIILower(data[2]) == 'd')
133                     return CSSPrimitiveValue::UnitType::CSS_RAD;
134                 break;
135             case 'e':
136                 if (toASCIILower(data[2]) == 'm')
137                     return CSSPrimitiveValue::UnitType::CSS_REMS;
138                 break;
139             }
140         break;
141     }
142     break;
143     case 4:
144         switch (toASCIILower(data[0])) {
145         case 'd':
146             switch (toASCIILower(data[1])) {
147             case 'p':
148                 switch (toASCIILower(data[2])) {
149                 case 'c':
150                     if (toASCIILower(data[3]) == 'm')
151                         return CSSPrimitiveValue::UnitType::CSS_DPCM;
152                     break;
153                 case 'p':
154                     if (toASCIILower(data[3]) == 'x')
155                         return CSSPrimitiveValue::UnitType::CSS_DPPX;
156                     break;
157                 }
158             break;
159         }
160         break;
161         case 'g':
162             if (toASCIILower(data[1]) == 'r' && toASCIILower(data[2]) == 'a' && toASCIILower(data[3]) == 'd')
163                 return CSSPrimitiveValue::UnitType::CSS_GRAD;
164             break;
165         case 't':
166             if (toASCIILower(data[1]) == 'u' && toASCIILower(data[2]) == 'r' && toASCIILower(data[3]) == 'n')
167                 return CSSPrimitiveValue::UnitType::CSS_TURN;
168             break;
169         case 'v':
170             switch (toASCIILower(data[1])) {
171             case 'm':
172                 switch (toASCIILower(data[2])) {
173                 case 'a':
174                     if (toASCIILower(data[3]) == 'x')
175                         return CSSPrimitiveValue::UnitType::CSS_VMAX;
176                     break;
177                 case 'i':
178                     if (toASCIILower(data[3]) == 'n')
179                         return CSSPrimitiveValue::UnitType::CSS_VMIN;
180                     break;
181                 }
182                 break;
183             }
184             break;
185         }
186         break;
187     case 5:
188         switch (toASCIILower(data[0])) {
189         case '_':
190             if (toASCIILower(data[1]) == '_' && toASCIILower(data[2]) == 'q' && toASCIILower(data[3]) == 'e' && toASCIILower(data[4]) == 'm')
191                 return CSSPrimitiveValue::UnitType::CSS_QUIRKY_EMS;
192             break;
193         }
194         break;
195     }
196     return CSSPrimitiveValue::UnitType::CSS_UNKNOWN;
197 }
198
199 static CSSPrimitiveValue::UnitType stringToUnitType(StringView stringView)
200 {
201     if (stringView.is8Bit())
202         return cssPrimitiveValueUnitFromTrie(stringView.characters8(), stringView.length());
203     return cssPrimitiveValueUnitFromTrie(stringView.characters16(), stringView.length());
204 }
205
206 CSSParserToken::CSSParserToken(CSSParserTokenType type, BlockType blockType)
207     : m_type(type)
208     , m_blockType(blockType)
209 {
210 }
211
212 // Just a helper used for Delimiter tokens.
213 CSSParserToken::CSSParserToken(CSSParserTokenType type, UChar c)
214     : m_type(type)
215     , m_blockType(NotBlock)
216     , m_delimiter(c)
217 {
218     ASSERT(m_type == DelimiterToken);
219 }
220
221 CSSParserToken::CSSParserToken(CSSParserTokenType type, StringView value, BlockType blockType)
222     : m_type(type)
223     , m_blockType(blockType)
224 {
225     initValueFromStringView(value);
226     m_id = -1;
227 }
228
229 CSSParserToken::CSSParserToken(CSSParserTokenType type, double numericValue, NumericValueType numericValueType, NumericSign sign)
230     : m_type(type)
231     , m_blockType(NotBlock)
232     , m_numericValueType(numericValueType)
233     , m_numericSign(sign)
234     , m_unit(static_cast<unsigned>(CSSPrimitiveValue::UnitType::CSS_NUMBER))
235 {
236     ASSERT(type == NumberToken);
237     m_numericValue = numericValue;
238 }
239
240 CSSParserToken::CSSParserToken(CSSParserTokenType type, UChar32 start, UChar32 end)
241     : m_type(UnicodeRangeToken)
242     , m_blockType(NotBlock)
243 {
244     ASSERT_UNUSED(type, type == UnicodeRangeToken);
245     m_unicodeRange.start = start;
246     m_unicodeRange.end = end;
247 }
248
249 CSSParserToken::CSSParserToken(HashTokenType type, StringView value)
250     : m_type(HashToken)
251     , m_blockType(NotBlock)
252     , m_hashTokenType(type)
253 {
254     initValueFromStringView(value);
255 }
256
257 void CSSParserToken::convertToDimensionWithUnit(StringView unit)
258 {
259     ASSERT(m_type == NumberToken);
260     m_type = DimensionToken;
261     initValueFromStringView(unit);
262     m_unit = static_cast<unsigned>(stringToUnitType(unit));
263 }
264
265 void CSSParserToken::convertToPercentage()
266 {
267     ASSERT(m_type == NumberToken);
268     m_type = PercentageToken;
269     m_unit = static_cast<unsigned>(CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
270 }
271
272 UChar CSSParserToken::delimiter() const
273 {
274     ASSERT(m_type == DelimiterToken);
275     return m_delimiter;
276 }
277
278 NumericSign CSSParserToken::numericSign() const
279 {
280     // This is valid for DimensionToken and PercentageToken, but only used
281     // in <an+b> parsing on NumberTokens.
282     ASSERT(m_type == NumberToken);
283     return static_cast<NumericSign>(m_numericSign);
284 }
285
286 NumericValueType CSSParserToken::numericValueType() const
287 {
288     ASSERT(m_type == NumberToken || m_type == PercentageToken || m_type == DimensionToken);
289     return static_cast<NumericValueType>(m_numericValueType);
290 }
291
292 double CSSParserToken::numericValue() const
293 {
294     ASSERT(m_type == NumberToken || m_type == PercentageToken || m_type == DimensionToken);
295     return m_numericValue;
296 }
297
298 CSSPropertyID CSSParserToken::parseAsCSSPropertyID() const
299 {
300     ASSERT(m_type == IdentToken);
301     return cssPropertyID(value());
302 }
303
304 CSSValueID CSSParserToken::id() const
305 {
306     if (m_type != IdentToken)
307         return CSSValueInvalid;
308     if (m_id < 0)
309         m_id = cssValueKeywordID(value());
310     return static_cast<CSSValueID>(m_id);
311 }
312
313 CSSValueID CSSParserToken::functionId() const
314 {
315     if (m_type != FunctionToken)
316         return CSSValueInvalid;
317     if (m_id < 0)
318         m_id = cssValueKeywordID(value());
319     return static_cast<CSSValueID>(m_id);
320 }
321
322 bool CSSParserToken::hasStringBacking() const
323 {
324     CSSParserTokenType tokenType = type();
325     return tokenType == IdentToken
326         || tokenType == FunctionToken
327         || tokenType == AtKeywordToken
328         || tokenType == HashToken
329         || tokenType == UrlToken
330         || tokenType == DimensionToken
331         || tokenType == StringToken;
332 }
333
334 CSSParserToken CSSParserToken::copyWithUpdatedString(const StringView& string) const
335 {
336     CSSParserToken copy(*this);
337     copy.initValueFromStringView(string);
338     return copy;
339 }
340
341 bool CSSParserToken::valueDataCharRawEqual(const CSSParserToken& other) const
342 {
343     if (m_valueLength != other.m_valueLength)
344         return false;
345
346     if (m_valueDataCharRaw == other.m_valueDataCharRaw && m_valueIs8Bit == other.m_valueIs8Bit)
347         return true;
348
349     if (m_valueIs8Bit)
350         return other.m_valueIs8Bit ? equal(static_cast<const LChar*>(m_valueDataCharRaw), static_cast<const LChar*>(other.m_valueDataCharRaw), m_valueLength) : equal(static_cast<const LChar*>(m_valueDataCharRaw), static_cast<const UChar*>(other.m_valueDataCharRaw), m_valueLength);
351     
352     return other.m_valueIs8Bit ? equal(static_cast<const UChar*>(m_valueDataCharRaw), static_cast<const LChar*>(other.m_valueDataCharRaw), m_valueLength) : equal(static_cast<const UChar*>(m_valueDataCharRaw), static_cast<const UChar*>(other.m_valueDataCharRaw), m_valueLength);
353 }
354
355 bool CSSParserToken::operator==(const CSSParserToken& other) const
356 {
357     if (m_type != other.m_type)
358         return false;
359     switch (m_type) {
360     case DelimiterToken:
361         return delimiter() == other.delimiter();
362     case HashToken:
363         if (m_hashTokenType != other.m_hashTokenType)
364             return false;
365         FALLTHROUGH;
366     case IdentToken:
367     case FunctionToken:
368     case StringToken:
369     case UrlToken:
370         return valueDataCharRawEqual(other);
371     case DimensionToken:
372         if (!valueDataCharRawEqual(other))
373             return false;
374         FALLTHROUGH;
375     case NumberToken:
376     case PercentageToken:
377         return m_numericSign == other.m_numericSign && m_numericValue == other.m_numericValue && m_numericValueType == other.m_numericValueType;
378     case UnicodeRangeToken:
379         return m_unicodeRange.start == other.m_unicodeRange.start && m_unicodeRange.end == other.m_unicodeRange.end;
380     default:
381         return true;
382     }
383 }
384
385 void CSSParserToken::serialize(StringBuilder& builder) const
386 {
387     // This is currently only used for @supports CSSOM. To keep our implementation
388     // simple we handle some of the edge cases incorrectly (see comments below).
389     switch (type()) {
390     case IdentToken:
391         serializeIdentifier(value().toString(), builder);
392         break;
393     case FunctionToken:
394         serializeIdentifier(value().toString(), builder);
395         return builder.append('(');
396     case AtKeywordToken:
397         builder.append('@');
398         serializeIdentifier(value().toString(), builder);
399         break;
400     case HashToken:
401         builder.append('#');
402         serializeIdentifier(value().toString(), builder, (getHashTokenType() == HashTokenUnrestricted));
403         break;
404     case UrlToken:
405         builder.append("url(");
406         serializeIdentifier(value().toString(), builder);
407         return builder.append(')');
408     case DelimiterToken:
409         if (delimiter() == '\\')
410             return builder.append("\\\n");
411         return builder.append(delimiter());
412     case NumberToken:
413         // These won't properly preserve the NumericValueType flag
414         if (m_numericSign == PlusSign)
415             builder.append('+');
416         return builder.appendNumber(numericValue());
417     case PercentageToken:
418         builder.appendNumber(numericValue());
419         return builder.append('%');
420     case DimensionToken:
421         // This will incorrectly serialize e.g. 4e3e2 as 4000e2
422         builder.appendNumber(numericValue());
423         serializeIdentifier(value().toString(), builder);
424         break;
425     case UnicodeRangeToken:
426         return builder.append(String::format("U+%X-%X", unicodeRangeStart(), unicodeRangeEnd()));
427     case StringToken:
428         return serializeString(value().toString(), builder);
429
430     case IncludeMatchToken:
431         return builder.append("~=");
432     case DashMatchToken:
433         return builder.append("|=");
434     case PrefixMatchToken:
435         return builder.append("^=");
436     case SuffixMatchToken:
437         return builder.append("$=");
438     case SubstringMatchToken:
439         return builder.append("*=");
440     case ColumnToken:
441         return builder.append("||");
442     case CDOToken:
443         return builder.append("<!--");
444     case CDCToken:
445         return builder.append("-->");
446     case BadStringToken:
447         return builder.append("'\n");
448     case BadUrlToken:
449         return builder.append("url(()");
450     case WhitespaceToken:
451         return builder.append(' ');
452     case ColonToken:
453         return builder.append(':');
454     case SemicolonToken:
455         return builder.append(';');
456     case CommaToken:
457         return builder.append(',');
458     case LeftParenthesisToken:
459         return builder.append('(');
460     case RightParenthesisToken:
461         return builder.append(')');
462     case LeftBracketToken:
463         return builder.append('[');
464     case RightBracketToken:
465         return builder.append(']');
466     case LeftBraceToken:
467         return builder.append('{');
468     case RightBraceToken:
469         return builder.append('}');
470
471     case EOFToken:
472     case CommentToken:
473         ASSERT_NOT_REACHED();
474         return;
475     }
476 }
477
478 } // namespace WebCore