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