2009-03-26 Jungshik Shin <jshin@chromium.org>
[WebKit-https.git] / WebCore / loader / TextResourceDecoder.cpp
1 /*
2     Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4     Copyright (C) 2005, 2006, 2007 Alexey Proskuryakov (ap@nypop.com)
5
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Library General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Library General Public License for more details.
15
16     You should have received a copy of the GNU Library General Public License
17     along with this library; see the file COPYING.LIB.  If not, write to
18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19     Boston, MA 02110-1301, USA.
20 */
21
22
23 #include "config.h"
24 #include "TextResourceDecoder.h"
25
26 #include "DOMImplementation.h"
27 #include "HTMLNames.h"
28 #include "TextCodec.h"
29 #include "TextEncoding.h"
30 #include "TextEncodingDetector.h"
31 #include "TextEncodingRegistry.h"
32 #include <wtf/ASCIICType.h>
33 #include <wtf/StringExtras.h>
34
35 using namespace WTF;
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
41 // You might think we should put these find functions elsewhere, perhaps with the
42 // similar functions that operate on UChar, but arguably only the decoder has
43 // a reason to process strings of char rather than UChar.
44
45 static int find(const char* subject, size_t subjectLength, const char* target)
46 {
47     size_t targetLength = strlen(target);
48     if (targetLength > subjectLength)
49         return -1;
50     for (size_t i = 0; i <= subjectLength - targetLength; ++i) {
51         bool match = true;
52         for (size_t j = 0; j < targetLength; ++j) {
53             if (subject[i + j] != target[j]) {
54                 match = false;
55                 break;
56             }
57         }
58         if (match)
59             return i;
60     }
61     return -1;
62 }
63
64 static int findIgnoringCase(const char* subject, size_t subjectLength, const char* target)
65 {
66     size_t targetLength = strlen(target);
67     if (targetLength > subjectLength)
68         return -1;
69 #ifndef NDEBUG
70     for (size_t i = 0; i < targetLength; ++i)
71         ASSERT(isASCIILower(target[i]));
72 #endif
73     for (size_t i = 0; i <= subjectLength - targetLength; ++i) {
74         bool match = true;
75         for (size_t j = 0; j < targetLength; ++j) {
76             if (toASCIILower(subject[i + j]) != target[j]) {
77                 match = false;
78                 break;
79             }
80         }
81         if (match)
82             return i;
83     }
84     return -1;
85 }
86
87 static TextEncoding findTextEncoding(const char* encodingName, int length)
88 {
89     Vector<char, 64> buffer(length + 1);
90     memcpy(buffer.data(), encodingName, length);
91     buffer[length] = '\0';
92     return buffer.data();
93 }
94
95 class KanjiCode {
96 public:
97     enum Type { ASCII, JIS, EUC, SJIS, UTF16, UTF8 };
98     static enum Type judge(const char* str, int length);
99     static const int ESC = 0x1b;
100     static const unsigned char sjisMap[256];
101     static int ISkanji(int code)
102     {
103         if (code >= 0x100)
104             return 0;
105         return sjisMap[code & 0xff] & 1;
106     }
107     static int ISkana(int code)
108     {
109         if (code >= 0x100)
110             return 0;
111         return sjisMap[code & 0xff] & 2;
112     }
113 };
114
115 const unsigned char KanjiCode::sjisMap[256] = {
116     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
125     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
126     0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
127     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
128     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
129     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
130     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
131     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
132 };
133
134 /*
135  * EUC-JP is
136  *     [0xa1 - 0xfe][0xa1 - 0xfe]
137  *     0x8e[0xa1 - 0xfe](SS2)
138  *     0x8f[0xa1 - 0xfe][0xa1 - 0xfe](SS3)
139  *
140  * Shift_Jis is
141  *     [0x81 - 0x9f, 0xe0 - 0xef(0xfe?)][0x40 - 0x7e, 0x80 - 0xfc]
142  *
143  * Shift_Jis Hankaku Kana is
144  *     [0xa1 - 0xdf]
145  */
146
147 /*
148  * KanjiCode::judge() is based on judge_jcode() from jvim
149  *     http://hp.vector.co.jp/authors/VA003457/vim/
150  *
151  * Special Thanks to Kenichi Tsuchida
152  */
153
154 enum KanjiCode::Type KanjiCode::judge(const char* str, int size)
155 {
156     enum Type code;
157     int i;
158     int bfr = false;            /* Kana Moji */
159     int bfk = 0;                /* EUC Kana */
160     int sjis = 0;
161     int euc = 0;
162
163     const unsigned char* ptr = reinterpret_cast<const unsigned char*>(str);
164
165     code = ASCII;
166
167     i = 0;
168     while (i < size) {
169         if (ptr[i] == ESC && (size - i >= 3)) {
170             if ((ptr[i + 1] == '$' && ptr[i + 2] == 'B')
171             || (ptr[i + 1] == '(' && ptr[i + 2] == 'B')) {
172                 code = JIS;
173                 goto breakBreak;
174             } else if ((ptr[i + 1] == '$' && ptr[i + 2] == '@')
175                     || (ptr[i + 1] == '(' && ptr[i + 2] == 'J')) {
176                 code = JIS;
177                 goto breakBreak;
178             } else if (ptr[i + 1] == '(' && ptr[i + 2] == 'I') {
179                 code = JIS;
180                 i += 3;
181             } else if (ptr[i + 1] == ')' && ptr[i + 2] == 'I') {
182                 code = JIS;
183                 i += 3;
184             } else {
185                 i++;
186             }
187             bfr = false;
188             bfk = 0;
189         } else {
190             if (ptr[i] < 0x20) {
191                 bfr = false;
192                 bfk = 0;
193                 /* ?? check kudokuten ?? && ?? hiragana ?? */
194                 if ((i >= 2) && (ptr[i - 2] == 0x81)
195                         && (0x41 <= ptr[i - 1] && ptr[i - 1] <= 0x49)) {
196                     code = SJIS;
197                     sjis += 100;        /* kudokuten */
198                 } else if ((i >= 2) && (ptr[i - 2] == 0xa1)
199                         && (0xa2 <= ptr[i - 1] && ptr[i - 1] <= 0xaa)) {
200                     code = EUC;
201                     euc += 100;         /* kudokuten */
202                 } else if ((i >= 2) && (ptr[i - 2] == 0x82) && (0xa0 <= ptr[i - 1])) {
203                     sjis += 40;         /* hiragana */
204                 } else if ((i >= 2) && (ptr[i - 2] == 0xa4) && (0xa0 <= ptr[i - 1])) {
205                     euc += 40;          /* hiragana */
206                 }
207             } else {
208                 /* ?? check hiragana or katana ?? */
209                 if ((size - i > 1) && (ptr[i] == 0x82) && (0xa0 <= ptr[i + 1])) {
210                     sjis++;     /* hiragana */
211                 } else if ((size - i > 1) && (ptr[i] == 0x83)
212                          && (0x40 <= ptr[i + 1] && ptr[i + 1] <= 0x9f)) {
213                     sjis++;     /* katakana */
214                 } else if ((size - i > 1) && (ptr[i] == 0xa4) && (0xa0 <= ptr[i + 1])) {
215                     euc++;      /* hiragana */
216                 } else if ((size - i > 1) && (ptr[i] == 0xa5) && (0xa0 <= ptr[i + 1])) {
217                     euc++;      /* katakana */
218                 }
219                 if (bfr) {
220                     if ((i >= 1) && (0x40 <= ptr[i] && ptr[i] <= 0xa0) && ISkanji(ptr[i - 1])) {
221                         code = SJIS;
222                         goto breakBreak;
223                     } else if ((i >= 1) && (0x81 <= ptr[i - 1] && ptr[i - 1] <= 0x9f) && ((0x40 <= ptr[i] && ptr[i] < 0x7e) || (0x7e < ptr[i] && ptr[i] <= 0xfc))) {
224                         code = SJIS;
225                         goto breakBreak;
226                     } else if ((i >= 1) && (0xfd <= ptr[i] && ptr[i] <= 0xfe) && (0xa1 <= ptr[i - 1] && ptr[i - 1] <= 0xfe)) {
227                         code = EUC;
228                         goto breakBreak;
229                     } else if ((i >= 1) && (0xfd <= ptr[i - 1] && ptr[i - 1] <= 0xfe) && (0xa1 <= ptr[i] && ptr[i] <= 0xfe)) {
230                         code = EUC;
231                         goto breakBreak;
232                     } else if ((i >= 1) && (ptr[i] < 0xa0 || 0xdf < ptr[i]) && (0x8e == ptr[i - 1])) {
233                         code = SJIS;
234                         goto breakBreak;
235                     } else if (ptr[i] <= 0x7f) {
236                         code = SJIS;
237                         goto breakBreak;
238                     } else {
239                         if (0xa1 <= ptr[i] && ptr[i] <= 0xa6) {
240                             euc++;      /* sjis hankaku kana kigo */
241                         } else if (0xa1 <= ptr[i] && ptr[i] <= 0xdf) {
242                             ;           /* sjis hankaku kana */
243                         } else if (0xa1 <= ptr[i] && ptr[i] <= 0xfe) {
244                             euc++;
245                         } else if (0x8e == ptr[i]) {
246                             euc++;
247                         } else if (0x20 <= ptr[i] && ptr[i] <= 0x7f) {
248                             sjis++;
249                         }
250                         bfr = false;
251                         bfk = 0;
252                     }
253                 } else if (0x8e == ptr[i]) {
254                     if (size - i <= 1) {
255                         ;
256                     } else if (0xa1 <= ptr[i + 1] && ptr[i + 1] <= 0xdf) {
257                         /* EUC KANA or SJIS KANJI */
258                         if (bfk == 1) {
259                             euc += 100;
260                         }
261                         bfk++;
262                         i++;
263                     } else {
264                         /* SJIS only */
265                         code = SJIS;
266                         goto breakBreak;
267                     }
268                 } else if (0x81 <= ptr[i] && ptr[i] <= 0x9f) {
269                     /* SJIS only */
270                     code = SJIS;
271                     if ((size - i >= 1)
272                             && ((0x40 <= ptr[i + 1] && ptr[i + 1] <= 0x7e)
273                             || (0x80 <= ptr[i + 1] && ptr[i + 1] <= 0xfc))) {
274                         goto breakBreak;
275                     }
276                 } else if (0xfd <= ptr[i] && ptr[i] <= 0xfe) {
277                     /* EUC only */
278                     code = EUC;
279                     if ((size - i >= 1)
280                             && (0xa1 <= ptr[i + 1] && ptr[i + 1] <= 0xfe)) {
281                         goto breakBreak;
282                     }
283                 } else if (ptr[i] <= 0x7f) {
284                     ;
285                 } else {
286                     bfr = true;
287                     bfk = 0;
288                 }
289             }
290             i++;
291         }
292     }
293     if (code == ASCII) {
294         if (sjis > euc) {
295             code = SJIS;
296         } else if (sjis < euc) {
297             code = EUC;
298         }
299     }
300 breakBreak:
301     return (code);
302 }
303
304 TextResourceDecoder::ContentType TextResourceDecoder::determineContentType(const String& mimeType)
305 {
306     if (equalIgnoringCase(mimeType, "text/css"))
307         return CSS;
308     if (equalIgnoringCase(mimeType, "text/html"))
309         return HTML;
310     if (DOMImplementation::isXMLMIMEType(mimeType))
311         return XML;
312     return PlainText;
313 }
314
315 const TextEncoding& TextResourceDecoder::defaultEncoding(ContentType contentType, const TextEncoding& specifiedDefaultEncoding)
316 {
317     // Despite 8.5 "Text/xml with Omitted Charset" of RFC 3023, we assume UTF-8 instead of US-ASCII 
318     // for text/xml. This matches Firefox.
319     if (contentType == XML)
320         return UTF8Encoding();
321     if (!specifiedDefaultEncoding.isValid())
322         return Latin1Encoding();
323     return specifiedDefaultEncoding;
324 }
325
326 TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncoding& specifiedDefaultEncoding, bool usesEncodingDetector)
327     : m_contentType(determineContentType(mimeType))
328     , m_encoding(defaultEncoding(m_contentType, specifiedDefaultEncoding))
329     , m_source(DefaultEncoding)
330     , m_hintEncoding(0)
331     , m_checkedForBOM(false)
332     , m_checkedForCSSCharset(false)
333     , m_checkedForHeadCharset(false)
334     , m_useLenientXMLDecoding(false)
335     , m_sawError(false)
336     , m_usesEncodingDetector(usesEncodingDetector)
337 {
338 }
339
340 TextResourceDecoder::~TextResourceDecoder()
341 {
342 }
343
344 void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSource source)
345 {
346     // In case the encoding didn't exist, we keep the old one (helps some sites specifying invalid encodings).
347     if (!encoding.isValid())
348         return;
349
350     // When encoding comes from meta tag (i.e. it cannot be XML files sent via XHR),
351     // treat x-user-defined as windows-1252 (bug 18270)
352     if (source == EncodingFromMetaTag && strcasecmp(encoding.name(), "x-user-defined") == 0)
353         m_encoding = "windows-1252";
354     else if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset)        
355         m_encoding = encoding.closestByteBasedEquivalent();
356     else
357         m_encoding = encoding;
358
359     m_codec.clear();
360     m_source = source;
361 }
362
363 // Returns the position of the encoding string.
364 static int findXMLEncoding(const char* str, int len, int& encodingLength)
365 {
366     int pos = find(str, len, "encoding");
367     if (pos == -1)
368         return -1;
369     pos += 8;
370     
371     // Skip spaces and stray control characters.
372     while (pos < len && str[pos] <= ' ')
373         ++pos;
374
375     // Skip equals sign.
376     if (pos >= len || str[pos] != '=')
377         return -1;
378     ++pos;
379
380     // Skip spaces and stray control characters.
381     while (pos < len && str[pos] <= ' ')
382         ++pos;
383
384     // Skip quotation mark.
385     if (pos >= len)
386         return - 1;
387     char quoteMark = str[pos];
388     if (quoteMark != '"' && quoteMark != '\'')
389         return -1;
390     ++pos;
391
392     // Find the trailing quotation mark.
393     int end = pos;
394     while (end < len && str[end] != quoteMark)
395         ++end;
396     if (end >= len)
397         return -1;
398
399     encodingLength = end - pos;
400     return pos;
401 }
402
403 // true if there is more to parse
404 static inline bool skipWhitespace(const char*& pos, const char* dataEnd)
405 {
406     while (pos < dataEnd && (*pos == '\t' || *pos == ' '))
407         ++pos;
408     return pos != dataEnd;
409 }
410
411 size_t TextResourceDecoder::checkForBOM(const char* data, size_t len)
412 {
413     // Check for UTF-16/32 or UTF-8 BOM mark at the beginning, which is a sure sign of a Unicode encoding.
414     // We let it override even a user-chosen encoding.
415     ASSERT(!m_checkedForBOM);
416
417     size_t lengthOfBOM = 0;
418
419     size_t bufferLength = m_buffer.size();
420
421     size_t buf1Len = bufferLength;
422     size_t buf2Len = len;
423     const unsigned char* buf1 = reinterpret_cast<const unsigned char*>(m_buffer.data());
424     const unsigned char* buf2 = reinterpret_cast<const unsigned char*>(data);
425     unsigned char c1 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
426     unsigned char c2 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
427     unsigned char c3 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
428     unsigned char c4 = buf2Len ? (--buf2Len, *buf2++) : 0;
429
430     // Check for the BOM.
431     if (c1 == 0xFF && c2 == 0xFE) {
432         if (c3 != 0 || c4 != 0) {
433             setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding);
434             lengthOfBOM = 2;
435         } else {
436             setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding);
437             lengthOfBOM = 4;
438         }
439     } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) {
440         setEncoding(UTF8Encoding(), AutoDetectedEncoding);
441         lengthOfBOM = 3;
442     } else if (c1 == 0xFE && c2 == 0xFF) {
443         setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding);
444         lengthOfBOM = 2;
445     } else if (c1 == 0 && c2 == 0 && c3 == 0xFE && c4 == 0xFF) {
446         setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding);
447         lengthOfBOM = 4;
448     }
449
450     if (lengthOfBOM || bufferLength + len >= 4)
451         m_checkedForBOM = true;
452
453     return lengthOfBOM;
454 }
455
456 bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& movedDataToBuffer)
457 {
458     if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) {
459         m_checkedForCSSCharset = true;
460         return true;
461     }
462
463     size_t oldSize = m_buffer.size();
464     m_buffer.grow(oldSize + len);
465     memcpy(m_buffer.data() + oldSize, data, len);
466
467     movedDataToBuffer = true;
468
469     if (m_buffer.size() > 8) { // strlen("@charset") == 8
470         const char* dataStart = m_buffer.data();
471         const char* dataEnd = dataStart + m_buffer.size();
472
473         if (dataStart[0] == '@' && dataStart[1] == 'c' && dataStart[2] == 'h' && dataStart[3] == 'a' && dataStart[4] == 'r' && 
474             dataStart[5] == 's' && dataStart[6] == 'e' && dataStart[7] == 't') {
475     
476             dataStart += 8;
477             const char* pos = dataStart;
478             if (!skipWhitespace(pos, dataEnd))
479                 return false;
480
481             if (*pos == '"' || *pos == '\'') {
482                 char quotationMark = *pos;
483                 ++pos;
484                 dataStart = pos;
485             
486                 while (pos < dataEnd && *pos != quotationMark)
487                     ++pos;
488                 if (pos == dataEnd)
489                     return false;
490
491                 int encodingNameLength = pos - dataStart + 1;
492                 
493                 ++pos;
494                 if (!skipWhitespace(pos, dataEnd))
495                     return false;
496
497                 if (*pos == ';')
498                     setEncoding(findTextEncoding(dataStart, encodingNameLength), EncodingFromCSSCharset);
499             }
500         }
501         m_checkedForCSSCharset = true;
502         return true;
503     }
504     return false;
505 }
506
507 // Other browsers allow comments in the head section, so we need to also.
508 // It's important not to look for tags inside the comments.
509 static inline void skipComment(const char*& ptr, const char* pEnd)
510 {
511     const char* p = ptr;
512     // Allow <!-->; other browsers do.
513     if (*p == '>') {
514         p++;
515     } else {
516         while (p != pEnd) {
517             if (*p == '-') {
518                 // This is the real end of comment, "-->".
519                 if (p[1] == '-' && p[2] == '>') {
520                     p += 3;
521                     break;
522                 }
523                 // This is the incorrect end of comment that other browsers allow, "--!>".
524                 if (p[1] == '-' && p[2] == '!' && p[3] == '>') {
525                     p += 4;
526                     break;
527                 }
528             }
529             p++;
530         }
531     }
532     ptr = p;
533 }
534
535 const int bytesToCheckUnconditionally = 1024; // That many input bytes will be checked for meta charset even if <head> section is over.
536
537 bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool& movedDataToBuffer)
538 {
539     if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) {
540         m_checkedForHeadCharset = true;
541         return true;
542     }
543
544     // This is not completely efficient, since the function might go
545     // through the HTML head several times.
546
547     size_t oldSize = m_buffer.size();
548     m_buffer.grow(oldSize + len);
549     memcpy(m_buffer.data() + oldSize, data, len);
550
551     movedDataToBuffer = true;
552
553     const char* ptr = m_buffer.data();
554     const char* pEnd = ptr + m_buffer.size();
555
556     // Is there enough data available to check for XML declaration?
557     if (m_buffer.size() < 8)
558         return false;
559
560     // Handle XML declaration, which can have encoding in it. This encoding is honored even for HTML documents.
561     // It is an error for an XML declaration not to be at the start of an XML document, and it is ignored in HTML documents in such case.
562     if (ptr[0] == '<' && ptr[1] == '?' && ptr[2] == 'x' && ptr[3] == 'm' && ptr[4] == 'l') {
563         const char* xmlDeclarationEnd = ptr;
564         while (xmlDeclarationEnd != pEnd && *xmlDeclarationEnd != '>')
565             ++xmlDeclarationEnd;
566         if (xmlDeclarationEnd == pEnd)
567             return false;
568         // No need for +1, because we have an extra "?" to lose at the end of XML declaration.
569         int len;
570         int pos = findXMLEncoding(ptr, xmlDeclarationEnd - ptr, len);
571         if (pos != -1)
572             setEncoding(findTextEncoding(ptr + pos, len), EncodingFromXMLHeader);
573         // continue looking for a charset - it may be specified in an HTTP-Equiv meta
574     } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == '?' && ptr[3] == 0 && ptr[4] == 'x' && ptr[5] == 0) {
575         setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding);
576         return true;
577     } else if (ptr[0] == 0 && ptr[1] == '<' && ptr[2] == 0 && ptr[3] == '?' && ptr[4] == 0 && ptr[5] == 'x') {
578         setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding);
579         return true;
580     } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0 && ptr[4] == '?' && ptr[5] == 0 && ptr[6] == 0 && ptr[7] == 0) {
581         setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding);
582         return true;
583     } else if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == '<' && ptr[4] == 0 && ptr[5] == 0 && ptr[6] == 0 && ptr[7] == '?') {
584         setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding);
585         return true;
586     }
587
588     // we still don't have an encoding, and are in the head
589     // the following tags are allowed in <head>:
590     // SCRIPT|STYLE|META|LINK|OBJECT|TITLE|BASE
591     
592     // We stop scanning when a tag that is not permitted in <head>
593     // is seen, rather when </head> is seen, because that more closely
594     // matches behavior in other browsers; more details in
595     // <http://bugs.webkit.org/show_bug.cgi?id=3590>.
596     
597     // Additionally, we ignore things that looks like tags in <title>, <script> and <noscript>; see
598     // <http://bugs.webkit.org/show_bug.cgi?id=4560>, <http://bugs.webkit.org/show_bug.cgi?id=12165>
599     // and <http://bugs.webkit.org/show_bug.cgi?id=12389>.
600
601     // Since many sites have charset declarations after <body> or other tags that are disallowed in <head>,
602     // we don't bail out until we've checked at least bytesToCheckUnconditionally bytes of input.
603
604     AtomicStringImpl* enclosingTagName = 0;
605     bool inHeadSection = true; // Becomes false when </head> or any tag not allowed in head is encountered.
606
607     // the HTTP-EQUIV meta has no effect on XHTML
608     if (m_contentType == XML)
609         return true;
610
611     while (ptr + 3 < pEnd) { // +3 guarantees that "<!--" fits in the buffer - and certainly we aren't going to lose any "charset" that way.
612         if (*ptr == '<') {
613             bool end = false;
614             ptr++;
615
616             // Handle comments.
617             if (ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') {
618                 ptr += 3;
619                 skipComment(ptr, pEnd);
620                 if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) {
621                     // Some pages that test bandwidth from within the browser do it by having
622                     // huge comments and measuring the time they take to load. Repeatedly scanning
623                     // these comments can take a lot of CPU time.
624                     m_checkedForHeadCharset = true;
625                     return true;
626                 }
627                 continue;
628             }
629
630             if (*ptr == '/') {
631                 ++ptr;
632                 end = true;
633             }
634
635             // Grab the tag name, but mostly ignore namespaces.
636             bool sawNamespace = false;
637             char tagBuffer[20];
638             int len = 0;
639             while (len < 19) {
640                 if (ptr == pEnd)
641                     return false;
642                 char c = *ptr;
643                 if (c == ':') {
644                     len = 0;
645                     sawNamespace = true;
646                     ptr++;
647                     continue;
648                 }
649                 if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
650                     ;
651                 else if (c >= 'A' && c <= 'Z')
652                     c += 'a' - 'A';
653                 else
654                     break;
655                 tagBuffer[len++] = c;
656                 ptr++;
657             }
658             tagBuffer[len] = 0;
659             AtomicString tag(tagBuffer);
660             
661             if (enclosingTagName) {
662                 if (end && tag.impl() == enclosingTagName)
663                     enclosingTagName = 0;
664             } else {
665                 if (tag == titleTag)
666                     enclosingTagName = titleTag.localName().impl();
667                 else if (tag == scriptTag)
668                     enclosingTagName = scriptTag.localName().impl();
669                 else if (tag == noscriptTag)
670                     enclosingTagName = noscriptTag.localName().impl();
671             }
672             
673             // Find where the opening tag ends.
674             const char* tagContentStart = ptr;
675             if (!end) {
676                 while (ptr != pEnd && *ptr != '>') {
677                     if (*ptr == '\'' || *ptr == '"') {
678                         char quoteMark = *ptr;
679                         ++ptr;
680                         while (ptr != pEnd && *ptr != quoteMark)
681                             ++ptr;
682                         if (ptr == pEnd)
683                             return false;
684                     }
685                     ++ptr;
686                 }
687                 if (ptr == pEnd)
688                     return false;
689                 ++ptr;
690             }
691             
692             if (!end && tag == metaTag && !sawNamespace) {
693                 const char* str = tagContentStart;
694                 int length = ptr - tagContentStart;
695                 int pos = 0;
696                 while (pos < length) {
697                     int charsetPos = findIgnoringCase(str + pos, length - pos, "charset");
698                     if (charsetPos == -1)
699                         break;
700                     pos += charsetPos + 7;
701                     // skip whitespace
702                     while (pos < length && str[pos] <= ' ')
703                         pos++;
704                     if (pos == length)
705                         break;
706                     if (str[pos++] != '=')
707                         continue;
708                     while ((pos < length) &&
709                             (str[pos] <= ' ' || str[pos] == '=' || str[pos] == '"' || str[pos] == '\''))
710                         pos++;
711
712                     // end ?
713                     if (pos == length)
714                         break;
715                     int end = pos;
716                     while (end < length &&
717                            str[end] != ' ' && str[end] != '"' && str[end] != '\'' &&
718                            str[end] != ';' && str[end] != '>')
719                         end++;
720                     setEncoding(findTextEncoding(str + pos, end - pos), EncodingFromMetaTag);
721                     if (m_source == EncodingFromMetaTag)
722                         return true;
723
724                     if (end >= length || str[end] == '/' || str[end] == '>')
725                         break;
726
727                     pos = end + 1;
728                 }
729             } else {
730                 if (!enclosingTagName && tag != scriptTag && tag != noscriptTag && tag != styleTag
731                     && tag != linkTag && tag != metaTag && tag != objectTag && tag != titleTag && tag != baseTag
732                     && (end || tag != htmlTag) && (end || tag != headTag) && isASCIIAlpha(tagBuffer[0])) {
733                     inHeadSection = false;
734                 }
735
736                 if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) {
737                     m_checkedForHeadCharset = true;
738                     return true;
739                 }
740             }
741         } else
742             ++ptr;
743     }
744     return false;
745 }
746
747 void TextResourceDecoder::detectJapaneseEncoding(const char* data, size_t len)
748 {
749     switch (KanjiCode::judge(data, len)) {
750         case KanjiCode::JIS:
751             setEncoding("ISO-2022-JP", AutoDetectedEncoding);
752             break;
753         case KanjiCode::EUC:
754             setEncoding("EUC-JP", AutoDetectedEncoding);
755             break;
756         case KanjiCode::SJIS:
757             setEncoding("Shift_JIS", AutoDetectedEncoding);
758             break;
759         case KanjiCode::ASCII:
760         case KanjiCode::UTF16:
761         case KanjiCode::UTF8:
762             break;
763     }
764 }
765
766 // We use the encoding detector in two cases:
767 //   1. Encoding detector is turned ON and no other encoding source is
768 //      available (that is, it's DefaultEncoding).
769 //   2. Encoding detector is turned ON and the encoding is set to
770 //      the encoding of the parent frame, which is also auto-detected.
771 //   Note that condition #2 is NOT satisfied unless parent-child frame
772 //   relationship is compliant to the same-origin policy. If they're from
773 //   different domains, |m_source| would not be set to EncodingFromParentFrame
774 //   in the first place. 
775 bool TextResourceDecoder::shouldAutoDetect() const
776 {
777     // Just checking m_hintEncoding suffices here because it's only set
778     // in setHintEncoding when the source is AutoDetectedEncoding.
779     return m_usesEncodingDetector
780         && (m_source == DefaultEncoding || (m_source == EncodingFromParentFrame && m_hintEncoding)); 
781 }
782
783 String TextResourceDecoder::decode(const char* data, size_t len)
784 {
785     size_t lengthOfBOM = 0;
786     if (!m_checkedForBOM)
787         lengthOfBOM = checkForBOM(data, len);
788
789     bool movedDataToBuffer = false;
790
791     if (m_contentType == CSS && !m_checkedForCSSCharset)
792         if (!checkForCSSCharset(data, len, movedDataToBuffer))
793             return "";
794
795     if ((m_contentType == HTML || m_contentType == XML) && !m_checkedForHeadCharset) // HTML and XML
796         if (!checkForHeadCharset(data, len, movedDataToBuffer))
797             return "";
798
799     // FIXME: It seems wrong to change our encoding downstream after
800     // we have already done some decoding. However, it's not possible
801     // to avoid in a sense in two cases below because triggering conditions
802     // for both cases depend on the information that won't be available
803     // until we do partial read. 
804     // The first case had better be removed altogether (see bug 21990)
805     // or at least be made to be invoked only when the encoding detection
806     // is turned on. 
807     // Do the auto-detect 1) using Japanese detector if our default encoding is
808     // one of the Japanese detector or 2) using detectTextEncoding if encoding
809     // detection is turned on.
810     if (m_source != UserChosenEncoding && m_source != AutoDetectedEncoding && m_encoding.isJapanese())
811         detectJapaneseEncoding(data, len);
812     else if (shouldAutoDetect()) {
813         TextEncoding detectedEncoding;
814         if (detectTextEncoding(data, len, m_hintEncoding, &detectedEncoding))
815             setEncoding(detectedEncoding, AutoDetectedEncoding);
816     }
817
818     ASSERT(m_encoding.isValid());
819
820     if (!m_codec)
821         m_codec.set(newTextCodec(m_encoding).release());
822
823     if (m_buffer.isEmpty())
824         return m_codec->decode(data + lengthOfBOM, len - lengthOfBOM, false, m_contentType == XML, m_sawError);
825
826     if (!movedDataToBuffer) {
827         size_t oldSize = m_buffer.size();
828         m_buffer.grow(oldSize + len);
829         memcpy(m_buffer.data() + oldSize, data, len);
830     }
831
832     String result = m_codec->decode(m_buffer.data() + lengthOfBOM, m_buffer.size() - lengthOfBOM, false, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError);
833     m_buffer.clear();
834     return result;
835 }
836
837 String TextResourceDecoder::flush()
838 {
839    // If we can not identify the encoding even after a document is completely
840    // loaded, we need to detect the encoding if other conditions for
841    // autodetection is satisfied.
842     if (m_buffer.size() && shouldAutoDetect()
843         && ((!m_checkedForHeadCharset && (m_contentType == HTML || m_contentType == XML)) || (!m_checkedForCSSCharset && (m_contentType == CSS)))) {
844          TextEncoding detectedEncoding;
845          if (detectTextEncoding(m_buffer.data(), m_buffer.size(),
846                                 m_hintEncoding, &detectedEncoding))
847              setEncoding(detectedEncoding, AutoDetectedEncoding);
848     }
849
850     if (!m_codec)
851         m_codec.set(newTextCodec(m_encoding).release());
852
853     String result = m_codec->decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError);
854     m_buffer.clear();
855     m_codec.clear();
856     m_checkedForBOM = false; // Skip BOM again when re-decoding.
857     return result;
858 }
859
860 }