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
33 namespace WebCore {
34
stripLeadingAndTrailingHTMLSpaces(const String & string)35 String 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
serializeForNumberType(double number)60 String 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
parseToDoubleForNumberType(const String & string,double * result)69 bool 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
parseToDoubleForNumberTypeWithDecimalPlaces(const String & string,double * result,unsigned * decimalPlaces)100 bool 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
parseHTMLInteger(const String & input,int & value)173 bool 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