BackgroundHTMLParser should be able to atomize well-known strings
[WebKit-https.git] / Source / WebCore / html / parser / HTMLParserIdioms.cpp
1 /*
2  * Copyright (C) 2010 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
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "HTMLParserIdioms.h"
27
28 #include "Decimal.h"
29 #include "HTMLIdentifier.h"
30 #include "QualifiedName.h"
31 #include <limits>
32 #include <wtf/MathExtras.h>
33 #include <wtf/text/AtomicString.h>
34 #include <wtf/text/StringBuilder.h>
35 #include <wtf/text/StringHash.h>
36
37 namespace WebCore {
38
39 template <typename CharType>
40 static String stripLeadingAndTrailingHTMLSpaces(String string, CharType characters, unsigned length)
41 {
42     unsigned numLeadingSpaces = 0;
43     unsigned numTrailingSpaces = 0;
44
45     for (; numLeadingSpaces < length; ++numLeadingSpaces) {
46         if (isNotHTMLSpace(characters[numLeadingSpaces]))
47             break;
48     }
49
50     if (numLeadingSpaces == length)
51         return string.isNull() ? string : emptyAtom.string();
52
53     for (; numTrailingSpaces < length; ++numTrailingSpaces) {
54         if (isNotHTMLSpace(characters[length - numTrailingSpaces - 1]))
55             break;
56     }
57
58     ASSERT(numLeadingSpaces + numTrailingSpaces < length);
59
60     if (!(numLeadingSpaces | numTrailingSpaces))
61         return string;
62
63     return string.substring(numLeadingSpaces, length - (numLeadingSpaces + numTrailingSpaces));
64 }
65
66 String stripLeadingAndTrailingHTMLSpaces(const String& string)
67 {
68     unsigned length = string.length();
69
70     if (!length)
71         return string.isNull() ? string : emptyAtom.string();
72
73     if (string.is8Bit())
74         return stripLeadingAndTrailingHTMLSpaces(string, string.characters8(), length);
75
76     return stripLeadingAndTrailingHTMLSpaces(string, string.characters(), length);
77 }
78
79 String serializeForNumberType(const Decimal& number)
80 {
81     if (number.isZero()) {
82         // Decimal::toString appends exponent, e.g. "0e-18"
83         return number.isNegative() ? "-0" : "0";
84     }
85     return number.toString();
86 }
87
88 String serializeForNumberType(double number)
89 {
90     // According to HTML5, "the best representation of the number n as a floating
91     // point number" is a string produced by applying ToString() to n.
92     return String::numberToStringECMAScript(number);
93 }
94
95 Decimal parseToDecimalForNumberType(const String& string, const Decimal& fallbackValue)
96 {
97     // See HTML5 2.5.4.3 `Real numbers.' and parseToDoubleForNumberType
98
99     // String::toDouble() accepts leading + and whitespace characters, which are not valid here.
100     const UChar firstCharacter = string[0];
101     if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter))
102         return fallbackValue;
103
104     const Decimal value = Decimal::fromString(string);
105     if (!value.isFinite())
106         return fallbackValue;
107
108     // Numbers are considered finite IEEE 754 single-precision floating point values.
109     // See HTML5 2.5.4.3 `Real numbers.'
110     // FIXME: We should use numeric_limits<double>::max for number input type.
111     const Decimal floatMax = Decimal::fromDouble(std::numeric_limits<float>::max());
112     if (value < -floatMax || value > floatMax)
113         return fallbackValue;
114
115     // We return +0 for -0 case.
116     return value.isZero() ? Decimal(0) : value;
117 }
118
119 Decimal parseToDecimalForNumberType(const String& string)
120 {
121     return parseToDecimalForNumberType(string, Decimal::nan());
122 }
123
124 double parseToDoubleForNumberType(const String& string, double fallbackValue)
125 {
126     // See HTML5 2.5.4.3 `Real numbers.'
127
128     // String::toDouble() accepts leading + and whitespace characters, which are not valid here.
129     UChar firstCharacter = string[0];
130     if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter))
131         return fallbackValue;
132
133     bool valid = false;
134     double value = string.toDouble(&valid);
135     if (!valid)
136         return fallbackValue;
137
138     // NaN and infinity are considered valid by String::toDouble, but not valid here.
139     if (!std::isfinite(value))
140         return fallbackValue;
141
142     // Numbers are considered finite IEEE 754 single-precision floating point values.
143     // See HTML5 2.5.4.3 `Real numbers.'
144     if (-std::numeric_limits<float>::max() > value || value > std::numeric_limits<float>::max())
145         return fallbackValue;
146
147     // The following expression converts -0 to +0.
148     return value ? value : 0;
149 }
150
151 double parseToDoubleForNumberType(const String& string)
152 {
153     return parseToDoubleForNumberType(string, std::numeric_limits<double>::quiet_NaN());
154 }
155
156 template <typename CharacterType>
157 static bool parseHTMLIntegerInternal(const CharacterType* position, const CharacterType* end, int& value)
158 {
159     // Step 3
160     int sign = 1;
161
162     // Step 4
163     while (position < end) {
164         if (!isHTMLSpace(*position))
165             break;
166         ++position;
167     }
168
169     // Step 5
170     if (position == end)
171         return false;
172     ASSERT(position < end);
173
174     // Step 6
175     if (*position == '-') {
176         sign = -1;
177         ++position;
178     } else if (*position == '+')
179         ++position;
180     if (position == end)
181         return false;
182     ASSERT(position < end);
183
184     // Step 7
185     if (!isASCIIDigit(*position))
186         return false;
187
188     // Step 8
189     StringBuilder digits;
190     while (position < end) {
191         if (!isASCIIDigit(*position))
192             break;
193         digits.append(*position++);
194     }
195
196     // Step 9
197     bool ok;
198     if (digits.is8Bit())
199         value = sign * charactersToIntStrict(digits.characters8(), digits.length(), &ok);
200     else
201         value = sign * charactersToIntStrict(digits.characters16(), digits.length(), &ok);
202     return ok;
203 }
204
205 // http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-integers
206 bool parseHTMLInteger(const String& input, int& value)
207 {
208     // Step 1
209     // Step 2
210     unsigned length = input.length();
211     if (length && input.is8Bit()) {
212         const LChar* start = input.characters8();
213         return parseHTMLIntegerInternal(start, start + length, value);
214     }
215
216     const UChar* start = input.characters();
217     return parseHTMLIntegerInternal(start, start + length, value);
218 }
219
220 template <typename CharacterType>
221 static bool parseHTMLNonNegativeIntegerInternal(const CharacterType* position, const CharacterType* end, unsigned& value)
222 {
223     // Step 3
224     while (position < end) {
225         if (!isHTMLSpace(*position))
226             break;
227         ++position;
228     }
229
230     // Step 4
231     if (position == end)
232         return false;
233     ASSERT(position < end);
234
235     // Step 5
236     if (*position == '+')
237         ++position;
238
239     // Step 6
240     if (position == end)
241         return false;
242     ASSERT(position < end);
243
244     // Step 7
245     if (!isASCIIDigit(*position))
246         return false;
247
248     // Step 8
249     StringBuilder digits;
250     while (position < end) {
251         if (!isASCIIDigit(*position))
252             break;
253         digits.append(*position++);
254     }
255
256     // Step 9
257     bool ok;
258     if (digits.is8Bit())
259         value = charactersToUIntStrict(digits.characters8(), digits.length(), &ok);
260     else
261         value = charactersToUIntStrict(digits.characters16(), digits.length(), &ok);
262     return ok;
263 }
264
265
266 // http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-non-negative-integers
267 bool parseHTMLNonNegativeInteger(const String& input, unsigned& value)
268 {
269     // Step 1
270     // Step 2
271     unsigned length = input.length();
272     if (length && input.is8Bit()) {
273         const LChar* start = input.characters8();
274         return parseHTMLNonNegativeIntegerInternal(start, start + length, value);
275     }
276     
277     const UChar* start = input.characters();
278     return parseHTMLNonNegativeIntegerInternal(start, start + length, value);
279 }
280
281 static bool threadSafeEqual(const StringImpl* a, const StringImpl* b)
282 {
283     if (a == b)
284         return true;
285     if (a->hash() != b->hash())
286         return false;
287     return StringHash::equal(a, b);
288 }
289
290 bool threadSafeMatch(const QualifiedName& a, const QualifiedName& b)
291 {
292     return threadSafeEqual(a.localName().impl(), b.localName().impl());
293 }
294
295 #if ENABLE(THREADED_HTML_PARSER)
296 bool threadSafeMatch(const HTMLIdentifier& localName, const QualifiedName& qName)
297 {
298     return threadSafeEqual(localName.asStringImpl(), qName.localName().impl());
299 }
300 #endif
301
302 }