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