e5201ce13b5ae435a77beeb6b37e9baf7c426416
[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 <limits>
30 #include <wtf/MathExtras.h>
31 #include <wtf/text/AtomicString.h>
32 #include <wtf/text/StringBuilder.h>
33
34 namespace WebCore {
35
36 template <typename CharType>
37 static String stripLeadingAndTrailingHTMLSpaces(String string, CharType characters, unsigned length)
38 {
39     unsigned numLeadingSpaces = 0;
40     unsigned numTrailingSpaces = 0;
41
42     for (; numLeadingSpaces < length; ++numLeadingSpaces) {
43         if (isNotHTMLSpace(characters[numLeadingSpaces]))
44             break;
45     }
46
47     if (numLeadingSpaces == length)
48         return string.isNull() ? string : emptyAtom.string();
49
50     for (; numTrailingSpaces < length; ++numTrailingSpaces) {
51         if (isNotHTMLSpace(characters[length - numTrailingSpaces - 1]))
52             break;
53     }
54
55     ASSERT(numLeadingSpaces + numTrailingSpaces < length);
56
57     if (!(numLeadingSpaces | numTrailingSpaces))
58         return string;
59
60     return string.substring(numLeadingSpaces, length - (numLeadingSpaces + numTrailingSpaces));
61 }
62
63 String stripLeadingAndTrailingHTMLSpaces(const String& string)
64 {
65     unsigned length = string.length();
66
67     if (!length)
68         return string.isNull() ? string : emptyAtom.string();
69
70     if (string.is8Bit())
71         return stripLeadingAndTrailingHTMLSpaces(string, string.characters8(), length);
72
73     return stripLeadingAndTrailingHTMLSpaces(string, string.characters(), length);
74 }
75
76 String serializeForNumberType(const Decimal& number)
77 {
78     if (number.isZero()) {
79         // Decimal::toString appends exponent, e.g. "0e-18"
80         return number.isNegative() ? "-0" : "0";
81     }
82     return number.toString();
83 }
84
85 String serializeForNumberType(double number)
86 {
87     // According to HTML5, "the best representation of the number n as a floating
88     // point number" is a string produced by applying ToString() to n.
89     return String::numberToStringECMAScript(number);
90 }
91
92 Decimal parseToDecimalForNumberType(const String& string, const Decimal& fallbackValue)
93 {
94     // See HTML5 2.5.4.3 `Real numbers.' and parseToDoubleForNumberType
95
96     // String::toDouble() accepts leading + and whitespace characters, which are not valid here.
97     const UChar firstCharacter = string[0];
98     if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter))
99         return fallbackValue;
100
101     const Decimal value = Decimal::fromString(string);
102     if (!value.isFinite())
103         return fallbackValue;
104
105     // Numbers are considered finite IEEE 754 single-precision floating point values.
106     // See HTML5 2.5.4.3 `Real numbers.'
107     // FIXME: We should use numeric_limits<double>::max for number input type.
108     const Decimal floatMax = Decimal::fromDouble(std::numeric_limits<float>::max());
109     if (value < -floatMax || value > floatMax)
110         return fallbackValue;
111
112     // We return +0 for -0 case.
113     return value.isZero() ? Decimal(0) : value;
114 }
115
116 Decimal parseToDecimalForNumberType(const String& string)
117 {
118     return parseToDecimalForNumberType(string, Decimal::nan());
119 }
120
121 double parseToDoubleForNumberType(const String& string, double fallbackValue)
122 {
123     // See HTML5 2.5.4.3 `Real numbers.'
124
125     // String::toDouble() accepts leading + and whitespace characters, which are not valid here.
126     UChar firstCharacter = string[0];
127     if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter))
128         return fallbackValue;
129
130     bool valid = false;
131     double value = string.toDouble(&valid);
132     if (!valid)
133         return fallbackValue;
134
135     // NaN and infinity are considered valid by String::toDouble, but not valid here.
136     if (!isfinite(value))
137         return fallbackValue;
138
139     // Numbers are considered finite IEEE 754 single-precision floating point values.
140     // See HTML5 2.5.4.3 `Real numbers.'
141     if (-std::numeric_limits<float>::max() > value || value > std::numeric_limits<float>::max())
142         return fallbackValue;
143
144     // The following expression converts -0 to +0.
145     return value ? value : 0;
146 }
147
148 double parseToDoubleForNumberType(const String& string)
149 {
150     return parseToDoubleForNumberType(string, std::numeric_limits<double>::quiet_NaN());
151 }
152
153 template <typename CharacterType>
154 static bool parseHTMLIntegerInternal(const CharacterType* position, const CharacterType* end, int& value)
155 {
156     // Step 3
157     int sign = 1;
158
159     // Step 4
160     while (position < end) {
161         if (!isHTMLSpace(*position))
162             break;
163         ++position;
164     }
165
166     // Step 5
167     if (position == end)
168         return false;
169     ASSERT(position < end);
170
171     // Step 6
172     if (*position == '-') {
173         sign = -1;
174         ++position;
175     } else if (*position == '+')
176         ++position;
177     if (position == end)
178         return false;
179     ASSERT(position < end);
180
181     // Step 7
182     if (!isASCIIDigit(*position))
183         return false;
184
185     // Step 8
186     StringBuilder digits;
187     while (position < end) {
188         if (!isASCIIDigit(*position))
189             break;
190         digits.append(*position++);
191     }
192
193     // Step 9
194     bool ok;
195     if (digits.is8Bit())
196         value = sign * charactersToIntStrict(digits.characters8(), digits.length(), &ok);
197     else
198         value = sign * charactersToIntStrict(digits.characters16(), digits.length(), &ok);
199     return ok;
200 }
201
202 // http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-integers
203 bool parseHTMLInteger(const String& input, int& value)
204 {
205     // Step 1
206     // Step 2
207     unsigned length = input.length();
208     if (length && input.is8Bit()) {
209         const LChar* start = input.characters8();
210         return parseHTMLIntegerInternal(start, start + length, value);
211     }
212
213     const UChar* start = input.characters();
214     return parseHTMLIntegerInternal(start, start + length, value);
215 }
216
217 template <typename CharacterType>
218 static bool parseHTMLNonNegativeIntegerInternal(const CharacterType* position, const CharacterType* end, unsigned& value)
219 {
220     // Step 3
221     while (position < end) {
222         if (!isHTMLSpace(*position))
223             break;
224         ++position;
225     }
226
227     // Step 4
228     if (position == end)
229         return false;
230     ASSERT(position < end);
231
232     // Step 5
233     if (*position == '+')
234         ++position;
235
236     // Step 6
237     if (position == end)
238         return false;
239     ASSERT(position < end);
240
241     // Step 7
242     if (!isASCIIDigit(*position))
243         return false;
244
245     // Step 8
246     StringBuilder digits;
247     while (position < end) {
248         if (!isASCIIDigit(*position))
249             break;
250         digits.append(*position++);
251     }
252
253     // Step 9
254     bool ok;
255     if (digits.is8Bit())
256         value = charactersToUIntStrict(digits.characters8(), digits.length(), &ok);
257     else
258         value = charactersToUIntStrict(digits.characters16(), digits.length(), &ok);
259     return ok;
260 }
261
262
263 // http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-non-negative-integers
264 bool parseHTMLNonNegativeInteger(const String& input, unsigned& value)
265 {
266     // Step 1
267     // Step 2
268     unsigned length = input.length();
269     if (length && input.is8Bit()) {
270         const LChar* start = input.characters8();
271         return parseHTMLNonNegativeIntegerInternal(start, start + length, value);
272     }
273     
274     const UChar* start = input.characters();
275     return parseHTMLNonNegativeIntegerInternal(start, start + length, value);
276 }
277
278 }