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