[webcore/html] remove extra header includes from cpp files.
[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 "URL.h"
30 #include <limits>
31 #include <wtf/MathExtras.h>
32 #include <wtf/text/AtomicString.h>
33 #include <wtf/text/StringBuilder.h>
34
35 namespace WebCore {
36
37 template <typename CharType>
38 static 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
64 String 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.characters(), length);
75 }
76
77 String 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
86 String 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
93 Decimal 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
117 Decimal parseToDecimalForNumberType(const String& string)
118 {
119     return parseToDecimalForNumberType(string, Decimal::nan());
120 }
121
122 double 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
149 double parseToDoubleForNumberType(const String& string)
150 {
151     return parseToDoubleForNumberType(string, std::numeric_limits<double>::quiet_NaN());
152 }
153
154 template <typename CharacterType>
155 static 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
204 bool 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
218 template <typename CharacterType>
219 static 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
265 bool 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.characters();
276     return parseHTMLNonNegativeIntegerInternal(start, start + length, value);
277 }
278
279 static 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 equalNonNull(a, b);
286 }
287
288 bool threadSafeMatch(const QualifiedName& a, const QualifiedName& b)
289 {
290     return threadSafeEqual(a.localName().impl(), b.localName().impl());
291 }
292
293 #if ENABLE(THREADED_HTML_PARSER)
294 bool threadSafeMatch(const HTMLIdentifier& localName, const QualifiedName& qName)
295 {
296     return threadSafeEqual(localName.asStringImpl(), qName.localName().impl());
297 }
298 #endif
299
300 struct ImageWithScale {
301     unsigned imageURLStart;
302     unsigned imageURLLength;
303     float scaleFactor;
304
305     ImageWithScale()
306         : imageURLStart(0)
307         , imageURLLength(0)
308         , scaleFactor(1)
309     { 
310     }
311
312     bool hasImageURL() const
313     {
314         return imageURLLength;
315     }
316 };
317 typedef Vector<ImageWithScale> ImageCandidates;
318
319 static inline bool compareByScaleFactor(const ImageWithScale& first, const ImageWithScale& second)
320 {
321     return first.scaleFactor < second.scaleFactor;
322 }
323
324 static inline bool isHTMLSpaceOrComma(UChar character)
325 {
326     return isHTMLSpace(character) || character == ',';
327 }
328
329 // See the specifications for more details about the algorithm to follow.
330 // http://www.w3.org/TR/2013/WD-html-srcset-20130228/#processing-the-image-candidates.
331 static void parseImagesWithScaleFromSrcsetAttribute(const String& srcsetAttribute, ImageCandidates& imageCandidates)
332 {
333     ASSERT(imageCandidates.isEmpty());
334
335     size_t imageCandidateStart = 0;
336     unsigned srcsetAttributeLength = srcsetAttribute.length();
337
338     while (imageCandidateStart < srcsetAttributeLength) {
339         float imageScaleFactor = 1;
340         size_t separator;
341
342         // 4. Splitting loop: Skip whitespace.
343         size_t imageURLStart = srcsetAttribute.find(isNotHTMLSpace, imageCandidateStart);
344         if (imageURLStart == notFound)
345             break;
346         // If The current candidate is either totally empty or only contains space, skipping.
347         if (srcsetAttribute[imageURLStart] == ',') {
348             imageCandidateStart = imageURLStart + 1;
349             continue;
350         }
351         // 5. Collect a sequence of characters that are not space characters, and let that be url.
352         size_t imageURLEnd = srcsetAttribute.find(isHTMLSpace, imageURLStart + 1);
353         if (imageURLEnd == notFound) {
354             imageURLEnd = srcsetAttributeLength;
355             separator = srcsetAttributeLength;
356         } else if (srcsetAttribute[imageURLEnd - 1] == ',') {
357             --imageURLEnd;
358             separator = imageURLEnd;
359         } else {
360             // 7. Collect a sequence of characters that are not "," (U+002C) characters, and let that be descriptors.
361             size_t imageScaleStart = srcsetAttribute.find(isNotHTMLSpace, imageURLEnd + 1);
362             if (imageScaleStart == notFound)
363                 separator = srcsetAttributeLength;
364             else if (srcsetAttribute[imageScaleStart] == ',')
365                 separator = imageScaleStart;
366             else {
367                 // This part differs from the spec as the current implementation only supports pixel density descriptors for now.
368                 size_t imageScaleEnd = srcsetAttribute.find(isHTMLSpaceOrComma, imageScaleStart + 1);
369                 imageScaleEnd = (imageScaleEnd == notFound) ? srcsetAttributeLength : imageScaleEnd;
370                 size_t commaPosition = imageScaleEnd;
371                 // Make sure there are no other descriptors.
372                 while ((commaPosition < srcsetAttributeLength - 1) && isHTMLSpace(srcsetAttribute[commaPosition]))
373                     ++commaPosition;
374                 // If the first not html space character after the scale modifier is not a comma,
375                 // the current candidate is an invalid input.
376                 if ((commaPosition < srcsetAttributeLength - 1) && srcsetAttribute[commaPosition] != ',') {
377                     // Find the nearest comma and skip the input.
378                     commaPosition = srcsetAttribute.find(',', commaPosition + 1);
379                     if (commaPosition == notFound)
380                         break;
381                     imageCandidateStart = commaPosition + 1;
382                     continue;
383                 }
384                 separator = commaPosition;
385                 if (srcsetAttribute[imageScaleEnd - 1] != 'x') {
386                     imageCandidateStart = separator + 1;
387                     continue;
388                 }
389                 bool validScaleFactor = false;
390                 size_t scaleFactorLengthWithoutUnit = imageScaleEnd - imageScaleStart - 1;
391                 imageScaleFactor = charactersToFloat(srcsetAttribute.characters() + imageScaleStart, scaleFactorLengthWithoutUnit, &validScaleFactor);
392
393                 if (!validScaleFactor) {
394                     imageCandidateStart = separator + 1;
395                     continue;
396                 }
397             }
398         }
399         ImageWithScale image;
400         image.imageURLStart = imageURLStart;
401         image.imageURLLength = imageURLEnd - imageURLStart;
402         image.scaleFactor = imageScaleFactor;
403
404         imageCandidates.append(image);
405         // 11. Return to the step labeled splitting loop.
406         imageCandidateStart = separator + 1;
407     }
408 }
409
410 String bestFitSourceForImageAttributes(float deviceScaleFactor, const String& srcAttribute, const String& srcsetAttribute)
411 {
412     ImageCandidates imageCandidates;
413
414     parseImagesWithScaleFromSrcsetAttribute(srcsetAttribute, imageCandidates);
415
416     if (!srcAttribute.isEmpty()) {
417         ImageWithScale srcPlaceholderImage;
418         imageCandidates.append(srcPlaceholderImage);
419     }
420
421     if (imageCandidates.isEmpty())
422         return String();
423
424     std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByScaleFactor);
425
426     for (size_t i = 0; i < imageCandidates.size() - 1; ++i) {
427         if (imageCandidates[i].scaleFactor >= deviceScaleFactor)
428             return imageCandidates[i].hasImageURL() ? srcsetAttribute.substringSharingImpl(imageCandidates[i].imageURLStart, imageCandidates[i].imageURLLength) : srcAttribute;
429     }
430     const ImageWithScale& lastCandidate = imageCandidates.last();
431     return lastCandidate.hasImageURL() ? srcsetAttribute.substringSharingImpl(lastCandidate.imageURLStart, lastCandidate.imageURLLength) : srcAttribute;
432 }
433
434 }