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