1/*
2    Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18*/
19
20#include "config.h"
21#include "MIMESniffing.h"
22
23#include <cstring>
24#include <stdint.h>
25#include <wtf/StdLibExtras.h>
26
27using namespace std;
28
29// MIME type sniffing implementation based on http://tools.ietf.org/html/draft-abarth-mime-sniff-06
30
31namespace {
32
33static inline bool isTextInList(const char* text, size_t size, const char** data)
34{
35    for (size_t i = 0; i < size; ++i) {
36        if (!strcmp(text, data[i]))
37            return true;
38    }
39    return false;
40
41}
42
43// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-6
44const char* textTypes[] = {
45    "text/plain",
46    "text/plain; charset=ISO-8859-1",
47    "text/plain; charset=iso-8859-1",
48    "text/plain; charset=UTF-8"
49};
50const size_t textTypesSize = sizeof(textTypes) / sizeof(textTypes[0]);
51
52static inline bool isTextOrBinaryType(const char* type)
53{
54    return isTextInList(type, textTypesSize, textTypes);
55}
56
57// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-6
58const char* unknownTypes[] = {
59    "",
60    "unknown/unknown",
61    "application/unknown",
62    "*/*"
63};
64const size_t unknownTypesSize = sizeof(unknownTypes) / sizeof(unknownTypes[0]);
65
66static inline bool isUnknownType(const char* type)
67{
68    if (isTextInList(type, unknownTypesSize, unknownTypes))
69        return true;
70    if (!strchr(type, '/')) {
71        // Firefox/Chrome rejects a mime type if it does not contain a slash.
72        return true;
73    }
74    return false;
75}
76
77const char* xmlTypes[] = {
78    "text/xml",
79    "application/xml"
80};
81const size_t xmlTypesSize = sizeof(xmlTypes) / sizeof(xmlTypes[0]);
82
83const char xmlSuffix[] = "+xml";
84
85static inline bool isXMLType(const char* type)
86{
87    const size_t xmlSuffixSize = sizeof(xmlSuffix) - 1;
88    size_t typeSize = strlen(type);
89    if (typeSize >= xmlSuffixSize && !memcmp(type + typeSize - xmlSuffixSize, xmlSuffix, xmlSuffixSize))
90        return true;
91
92    return isTextInList(type, xmlTypesSize, xmlTypes);
93}
94
95// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-8
96const char binaryFlags[256] = {
97    1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1,
98    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
99    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
113};
114
115static inline bool isBinaryChar(unsigned char data)
116{
117    return binaryFlags[data];
118}
119
120static inline bool isBinaryData(const char* data, size_t size)
121{
122    for (size_t i = 0; i < size; ++i) {
123        if (isBinaryChar(data[i]))
124            return true;
125    }
126    return false;
127}
128
129// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-11
130const char whiteSpaceChars[256] = {
131    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
132    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
134    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
147};
148
149static inline bool isWhiteSpace(unsigned char data)
150{
151    return whiteSpaceChars[data];
152}
153
154static inline void skipWhiteSpace(const char* data, size_t& pos, size_t dataSize)
155{
156    while (pos < dataSize && isWhiteSpace(data[pos]))
157        ++pos;
158}
159
160enum {
161    SkipWhiteSpace = 1,
162    TrailingSpaceOrBracket = 2
163};
164
165struct MagicNumbers {
166    const char* pattern;
167    const char* mask;
168    const char* mimeType;
169    size_t size;
170    int flags;
171};
172
173#define MAGIC_NUMBERS_MASKED(pattern, mask, mimeType, flags) {(pattern), (mask), (mimeType), sizeof(pattern) - 1, (flags)}
174#define MAGIC_NUMBERS_SIMPLE(pattern, mimeType) {(pattern), 0, (mimeType), sizeof(pattern) - 1, 0}
175
176// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-12
177const MagicNumbers securityConstrainedTypes[] = {
178    MAGIC_NUMBERS_MASKED("<!DOCTYPE HTML", "\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
179    MAGIC_NUMBERS_MASKED("<HTML", "\xFF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
180    MAGIC_NUMBERS_MASKED("<HEAD", "\xFF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
181    MAGIC_NUMBERS_MASKED("<SCRIPT", "\xFF\xDF\xDF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
182    MAGIC_NUMBERS_MASKED("<IFRAME", "\xFF\xDF\xDF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
183    MAGIC_NUMBERS_MASKED("<H1", "\xFF\xDF\xFF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
184    MAGIC_NUMBERS_MASKED("<DIV", "\xFF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
185    MAGIC_NUMBERS_MASKED("<FONT", "\xFF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
186    MAGIC_NUMBERS_MASKED("<TABLE", "\xFF\xDF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
187    MAGIC_NUMBERS_MASKED("<A", "\xFF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
188    MAGIC_NUMBERS_MASKED("<STYLE", "\xFF\xDF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
189    MAGIC_NUMBERS_MASKED("<TITLE", "\xFF\xDF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
190    MAGIC_NUMBERS_MASKED("<B", "\xFF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
191    MAGIC_NUMBERS_MASKED("<BODY", "\xFF\xDF\xDF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
192    MAGIC_NUMBERS_MASKED("<BR", "\xFF\xDF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
193    MAGIC_NUMBERS_MASKED("<P", "\xFF\xDF", "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
194    MAGIC_NUMBERS_MASKED("<!--", 0, "text/html", SkipWhiteSpace | TrailingSpaceOrBracket),
195    MAGIC_NUMBERS_MASKED("<?xml", 0, "text/xml", SkipWhiteSpace),
196    MAGIC_NUMBERS_SIMPLE("%PDF-", "application/pdf")
197};
198const size_t securityConstrainedTypesSize = sizeof(securityConstrainedTypes) / sizeof(securityConstrainedTypes[0]);
199
200// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-8
201const MagicNumbers bomTypes[] = {
202    MAGIC_NUMBERS_SIMPLE("\xFE\xFF", "text/plain"), // UTF-16BE BOM
203    MAGIC_NUMBERS_SIMPLE("\xFF\xFE", "text/plain"), // UTF-16LE BOM
204    MAGIC_NUMBERS_SIMPLE("\xEF\xBB\xBF", "text/plain") // UTF-8 BOM
205};
206const size_t bomTypesSize = sizeof(bomTypes) / sizeof(bomTypes[0]);
207
208// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-13
209const MagicNumbers safeTypes[] = {
210    MAGIC_NUMBERS_SIMPLE("%!PS-Adobe-", "application/postscript"),
211    MAGIC_NUMBERS_SIMPLE("\x4F\x67\x67\x53\x00", "application/ogg"), // An Ogg Vorbis audio or video signature.
212    MAGIC_NUMBERS_MASKED("RIFF\x00\x00\x00\x00WAVE", "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF", "audio/x-wave", 0), // "RIFF" followed by four bytes, followed by "WAVE".
213    MAGIC_NUMBERS_SIMPLE("\x1A\x45\xDF\xA3", "video/webm"), // The WebM signature.
214    MAGIC_NUMBERS_SIMPLE("Rar!\x1A\x07\x00", "application/x-rar-compressed"), // A RAR archive.
215    MAGIC_NUMBERS_SIMPLE("\x50\x4B\x03\x04", "application/zip"), // A ZIP archive.
216    MAGIC_NUMBERS_SIMPLE("\x1F\x8B\x08", "application/x-gzip") // A GZIP archive.
217};
218const size_t safeTypesSize = sizeof(safeTypes) / sizeof(safeTypes[0]);
219
220// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-16
221const MagicNumbers imageTypes[] = {
222    MAGIC_NUMBERS_MASKED("RIFF\x00\x00\x00\x00WEBPVP", "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF", "image/webp", 0), // "RIFF" followed by four bytes, followed by "WEBPVP".
223    MAGIC_NUMBERS_SIMPLE("GIF87a", "image/gif"),
224    MAGIC_NUMBERS_SIMPLE("GIF89a", "image/gif"),
225    MAGIC_NUMBERS_SIMPLE("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", "image/png"),
226    MAGIC_NUMBERS_SIMPLE("\xFF\xD8\xFF", "image/jpeg"),
227    MAGIC_NUMBERS_SIMPLE("BM", "image/bmp"),
228    MAGIC_NUMBERS_SIMPLE("\x00\x00\x01\x00", "image/vnd.microsoft.icon") // A Windows Icon signature.
229};
230const size_t imageTypesSize = sizeof(imageTypes) / sizeof(imageTypes[0]);
231
232static inline size_t dataSizeNeededForImageSniffing()
233{
234    size_t result = 0;
235    for (size_t i = 0; i < imageTypesSize; ++i) {
236        if (imageTypes[i].size > result)
237            result = imageTypes[i].size;
238    }
239    return result;
240}
241
242static inline bool maskedCompareSlowCase(const MagicNumbers& info, const char* data)
243{
244    const char* pattern = reinterpret_cast<const char*>(info.pattern);
245    const char* mask = reinterpret_cast<const char*>(info.mask);
246
247    size_t count = info.size;
248
249    for (size_t i = 0; i < count; ++i) {
250        if ((*data++ & *mask++) != *pattern++)
251            return false;
252    }
253    return true;
254}
255
256static inline bool maskedCompare(const MagicNumbers& info, const char* data, size_t dataSize)
257{
258    if (dataSize < info.size)
259        return false;
260
261    if (!isPointerTypeAlignmentOkay(static_cast<const uint32_t*>(static_cast<const void*>(data))))
262        return maskedCompareSlowCase(info, data);
263
264    const uint32_t* pattern32 = reinterpret_cast_ptr<const uint32_t*>(info.pattern);
265    const uint32_t* mask32 = reinterpret_cast_ptr<const uint32_t*>(info.mask);
266    const uint32_t* data32 = reinterpret_cast_ptr<const uint32_t*>(data);
267
268    size_t count = info.size >> 2;
269
270    for (size_t i = 0; i < count; ++i) {
271        if ((*data32++ & *mask32++) != *pattern32++)
272            return false;
273    }
274
275    const char* p = reinterpret_cast<const char*>(pattern32);
276    const char* m = reinterpret_cast<const char*>(mask32);
277    const char* d = reinterpret_cast<const char*>(data32);
278
279    count = info.size & 3;
280
281    for (size_t i = 0; i < count; ++i) {
282        if ((*d++ & *m++) != *p++)
283            return false;
284    }
285
286    return true;
287}
288
289// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-11
290static inline bool checkSpaceOrBracket(const char* data)
291{
292    return isWhiteSpace(*data) || *data == 0x3E;
293}
294
295static inline bool compare(const MagicNumbers& info, const char* data, size_t dataSize)
296{
297    if (info.flags & SkipWhiteSpace) {
298        size_t pos = 0;
299        skipWhiteSpace(data, pos, dataSize);
300        data += pos;
301        dataSize -= pos;
302    }
303
304    bool result;
305    if (info.mask)
306        result = maskedCompare(info, data, info.size);
307    else
308        result = dataSize >= info.size && !memcmp(data, info.pattern, info.size);
309
310    return result && (!(info.flags & TrailingSpaceOrBracket) || checkSpaceOrBracket(data + info.size));
311}
312
313static inline const char* findMIMEType(const char* data, size_t dataSize, const MagicNumbers* types, size_t typesCount)
314{
315    for (size_t i = 0; i < typesCount; ++i) {
316        if (compare(types[i], data, dataSize))
317            return types[i].mimeType;
318    }
319    return 0;
320}
321
322static inline const char* findSimpleMIMEType(const char* data, size_t dataSize, const MagicNumbers* types, size_t typesCount)
323{
324    for (size_t i = 0; i < typesCount; ++i) {
325        ASSERT(!types[i].mask);
326        ASSERT(!types[i].flags);
327
328        if (dataSize >= types[i].size && !memcmp(data, types[i].pattern, types[i].size))
329            return types[i].mimeType;
330    }
331    return 0;
332}
333
334bool isTypeInList(const char* type, const MagicNumbers* types, size_t typesCount)
335{
336    for (size_t i = 0; i < typesCount; ++i) {
337        if (!strcmp(type, types[i].mimeType))
338            return true;
339    }
340    return false;
341}
342
343// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-8
344static const char* internalTextOrBinaryTypeSniffingProcedure(const char* data, size_t dataSize)
345{
346    const char* mimeType = 0;
347
348    mimeType = findSimpleMIMEType(data, dataSize, bomTypes, bomTypesSize);
349    if (mimeType)
350        return mimeType;
351
352    if (!isBinaryData(data, dataSize))
353        return "text/plain";
354
355    mimeType = findMIMEType(data, dataSize, safeTypes, safeTypesSize);
356    if (mimeType)
357        return mimeType;
358
359    mimeType = findMIMEType(data, dataSize, imageTypes, imageTypesSize);
360    if (mimeType)
361        return mimeType;
362
363    return "application/octet-stream";
364}
365
366static const char* textOrBinaryTypeSniffingProcedure(const char* data, size_t dataSize)
367{
368    const char* result = internalTextOrBinaryTypeSniffingProcedure(data, dataSize);
369    ASSERT(!isTypeInList(result, securityConstrainedTypes, securityConstrainedTypesSize));
370    return result;
371}
372
373// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-10
374static const char* unknownTypeSniffingProcedure(const char* data, size_t dataSize)
375{
376    const char* mimeType = 0;
377
378    mimeType = findMIMEType(data, dataSize, securityConstrainedTypes, securityConstrainedTypesSize);
379    if (mimeType)
380        return mimeType;
381
382    mimeType = findSimpleMIMEType(data, dataSize, bomTypes, bomTypesSize);
383    if (mimeType)
384        return mimeType;
385
386    mimeType = findMIMEType(data, dataSize, safeTypes, safeTypesSize);
387    if (mimeType)
388        return mimeType;
389
390    mimeType = findMIMEType(data, dataSize, imageTypes, imageTypesSize);
391    if (mimeType)
392        return mimeType;
393
394    if (!isBinaryData(data, dataSize))
395        return "text/plain";
396
397    return "application/octet-stream";
398}
399
400// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-16
401static const char* imageTypeSniffingProcedure(const char* data, size_t dataSize)
402{
403    return findMIMEType(data, dataSize, imageTypes, imageTypesSize);
404}
405
406static inline bool checkText(const char* data, size_t& pos, size_t dataSize, const char* text, size_t textSize)
407{
408    if (dataSize - pos < textSize || memcmp(data + pos, text, textSize))
409        return false;
410
411    pos += textSize;
412    return true;
413}
414
415const char rssUrl[] = "http://purl.org/rss/1.0";
416const char rdfUrl[] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
417
418static inline const char* checkRDF(const char* data, size_t pos, size_t dataSize)
419{
420    bool isRDF = false;
421    bool isRSS = false;
422
423    while (pos <= dataSize) {
424        if (checkText(data, pos, dataSize, rssUrl, sizeof(rssUrl) - 1)) {
425            isRSS = true;
426            continue;
427        }
428
429        if (checkText(data, pos, dataSize, rdfUrl, sizeof(rdfUrl) - 1)) {
430            isRDF = true;
431            continue;
432        }
433
434        ++pos;
435
436        if (isRSS && isRDF)
437            return "application/rdf+xml";
438    }
439
440    return 0;
441}
442
443static inline bool skipTag(const char*& data, size_t& pos, size_t dataSize, const char* tag, size_t tagSize, const char* tagEnd, size_t tagEndSize)
444{
445    if (!checkText(data, pos, dataSize, tag, tagSize))
446        return false;
447
448    while (pos < dataSize && !checkText(data, pos, dataSize, tagEnd, tagEndSize))
449        ++pos;
450
451    return true;
452}
453
454// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-17
455static const char* feedTypeSniffingProcedure(const char* data, size_t dataSize)
456{
457    size_t pos = 0;
458
459    if (dataSize >= 3 && !memcmp(data, "\xEF\xBB\xBF", 3))
460        pos += 3;
461
462    while (pos < dataSize) {
463        skipWhiteSpace(data, pos, dataSize);
464
465        if (!skipTag(data, pos, dataSize, "<!--", 4, "-->", 3) && !skipTag(data, pos, dataSize, "<!", 2, "!>", 2) && !skipTag(data, pos, dataSize, "<?", 2, "?>", 2))
466            break;
467    }
468
469    if (checkText(data, pos, dataSize, "<rss", 4))
470        return "application/rss+xml";
471
472    if (checkText(data, pos, dataSize, "<feed", 5))
473        return "application/atom+xml";
474
475    if (checkText(data, pos, dataSize, "<rdf:RDF", 8))
476        return checkRDF(data, pos, dataSize);
477
478    return 0;
479}
480
481}
482
483// http://tools.ietf.org/html/draft-abarth-mime-sniff-06#page-6
484MIMESniffer::MIMESniffer(const char* advertisedMIMEType, bool isSupportedImageType)
485    : m_dataSize(0)
486    , m_function(0)
487{
488    if (!advertisedMIMEType) {
489        m_dataSize = 512;
490        m_function = &unknownTypeSniffingProcedure;
491        return;
492    }
493
494    if (isTextOrBinaryType(advertisedMIMEType)) {
495        m_dataSize = 512;
496        m_function = &textOrBinaryTypeSniffingProcedure;
497        return;
498    }
499
500    if (isUnknownType(advertisedMIMEType)) {
501        m_dataSize = 512;
502        m_function = &unknownTypeSniffingProcedure;
503        return;
504    }
505
506    if (isXMLType(advertisedMIMEType))
507        return;
508
509    if (isSupportedImageType) {
510        static const size_t dataSize = dataSizeNeededForImageSniffing();
511        m_dataSize = dataSize;
512        m_function = &imageTypeSniffingProcedure;
513        return;
514    }
515
516    if (!strcmp(advertisedMIMEType, "text/html")) {
517        m_dataSize = 512;
518        m_function = &feedTypeSniffingProcedure;
519        return;
520    }
521}
522