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 <limits>
29#include <wtf/MathExtras.h>
30#include <wtf/dtoa.h>
31#include <wtf/text/AtomicString.h>
32
33namespace WebCore {
34
35String stripLeadingAndTrailingHTMLSpaces(const String& string)
36{
37    const UChar* characters = string.characters();
38    unsigned length = string.length();
39
40    unsigned numLeadingSpaces;
41    for (numLeadingSpaces = 0; numLeadingSpaces < length; ++numLeadingSpaces) {
42        if (isNotHTMLSpace(characters[numLeadingSpaces]))
43            break;
44    }
45
46    if (numLeadingSpaces == length)
47        return string.isNull() ? string : emptyAtom.string();
48
49    unsigned numTrailingSpaces;
50    for (numTrailingSpaces = 0; numTrailingSpaces < length; ++numTrailingSpaces) {
51        if (isNotHTMLSpace(characters[length - numTrailingSpaces - 1]))
52            break;
53    }
54
55    ASSERT(numLeadingSpaces + numTrailingSpaces < length);
56
57    return string.substring(numLeadingSpaces, length - (numLeadingSpaces + numTrailingSpaces));
58}
59
60String serializeForNumberType(double number)
61{
62    // According to HTML5, "the best representation of the number n as a floating
63    // point number" is a string produced by applying ToString() to n.
64    NumberToStringBuffer buffer;
65    unsigned length = numberToString(number, buffer);
66    return String(buffer, length);
67}
68
69bool parseToDoubleForNumberType(const String& string, double* result)
70{
71    // See HTML5 2.4.4.3 `Real numbers.'
72
73    // String::toDouble() accepts leading + and whitespace characters, which are not valid here.
74    UChar firstCharacter = string[0];
75    if (firstCharacter != '-' && !isASCIIDigit(firstCharacter))
76        return false;
77
78    bool valid = false;
79    double value = string.toDouble(&valid);
80    if (!valid)
81        return false;
82
83    // NaN and infinity are considered valid by String::toDouble, but not valid here.
84    if (!isfinite(value))
85        return false;
86
87    // Numbers are considered finite IEEE 754 single-precision floating point values.
88    // See HTML5 2.4.4.3 `Real numbers.'
89    if (-std::numeric_limits<float>::max() > value || value > std::numeric_limits<float>::max())
90        return false;
91
92    if (result) {
93        // The following expression converts -0 to +0.
94        *result = value ? value : 0;
95    }
96
97    return true;
98}
99
100bool parseToDoubleForNumberTypeWithDecimalPlaces(const String& string, double *result, unsigned *decimalPlaces)
101{
102    if (decimalPlaces)
103        *decimalPlaces = 0;
104
105    if (!parseToDoubleForNumberType(string, result))
106        return false;
107
108    if (!decimalPlaces)
109        return true;
110
111    size_t dotIndex = string.find('.');
112    size_t eIndex = string.find('e');
113    if (eIndex == notFound)
114        eIndex = string.find('E');
115
116    unsigned baseDecimalPlaces = 0;
117    if (dotIndex != notFound) {
118        if (eIndex == notFound)
119            baseDecimalPlaces = string.length() - dotIndex - 1;
120        else
121            baseDecimalPlaces = eIndex - dotIndex - 1;
122    }
123
124    int exponent = 0;
125    if (eIndex != notFound) {
126        unsigned cursor = eIndex + 1, cursorSaved;
127        int digit, exponentSign;
128        int32_t exponent32;
129        size_t length = string.length();
130
131        // Not using String.toInt() in order to perform the same computation as dtoa() does.
132        exponentSign = 0;
133        switch (digit = string[cursor]) {
134        case '-':
135            exponentSign = 1;
136        case '+':
137            digit = string[++cursor];
138        }
139        if (digit >= '0' && digit <= '9') {
140            while (cursor < length && digit == '0')
141                digit = string[++cursor];
142            if (digit > '0' && digit <= '9') {
143                exponent32 = digit - '0';
144                cursorSaved = cursor;
145                while (cursor < length && (digit = string[++cursor]) >= '0' && digit <= '9')
146                    exponent32 = (10 * exponent32) + digit - '0';
147                if (cursor - cursorSaved > 8 || exponent32 > 19999)
148                    /* Avoid confusion from exponents
149                     * so large that e might overflow.
150                     */
151                    exponent = 19999; /* safe for 16 bit ints */
152                else
153                    exponent = static_cast<int>(exponent32);
154                if (exponentSign)
155                    exponent = -exponent;
156            } else
157                exponent = 0;
158        }
159    }
160
161    int intDecimalPlaces = baseDecimalPlaces - exponent;
162    if (intDecimalPlaces < 0)
163        *decimalPlaces = 0;
164    else if (intDecimalPlaces > 19999)
165        *decimalPlaces = 19999;
166    else
167        *decimalPlaces = static_cast<unsigned>(intDecimalPlaces);
168
169    return true;
170}
171
172// http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-integers
173bool parseHTMLInteger(const String& input, int& value)
174{
175    // Step 1
176    // Step 2
177    const UChar* position = input.characters();
178    const UChar* end = position + input.length();
179
180    // Step 3
181    int sign = 1;
182
183    // Step 4
184    while (position < end) {
185        if (!isHTMLSpace(*position))
186            break;
187        ++position;
188    }
189
190    // Step 5
191    if (position == end)
192        return false;
193    ASSERT(position < end);
194
195    // Step 6
196    if (*position == '-') {
197        sign = -1;
198        ++position;
199    } else if (*position == '+')
200        ++position;
201    if (position == end)
202        return false;
203    ASSERT(position < end);
204
205    // Step 7
206    if (!isASCIIDigit(*position))
207        return false;
208
209    // Step 8
210    Vector<UChar, 16> digits;
211    while (position < end) {
212        if (!isASCIIDigit(*position))
213            break;
214        digits.append(*position++);
215    }
216
217    // Step 9
218    value = sign * charactersToIntStrict(digits.data(), digits.size());
219    return true;
220}
221
222}
223