* platform/mac/GraphicsContextMac.mm: Fix one no-SVG compile problem by adding
[WebKit-https.git] / WebCore / platform / StreamingTextDecoder.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "StreamingTextDecoder.h"
28
29 #include <kxmlcore/Assertions.h>
30
31 using std::min;
32
33 namespace WebCore {
34
35 StreamingTextDecoder::StreamingTextDecoder(const TextEncoding& encoding)
36     : m_encoding(encoding)
37     , m_littleEndian(encoding.flags() & LittleEndian)
38     , m_atStart(true)
39     , m_error(false)
40     , m_numBufferedBytes(0)
41     , m_converterICU(0)
42 {
43 }
44
45 DeprecatedString StreamingTextDecoder::convertLatin1(const unsigned char* s, int length)
46 {
47     ASSERT(m_numBufferedBytes == 0);
48     return DeprecatedString(reinterpret_cast<const char *>(s), length);
49 }
50
51 static const UChar replacementCharacter = 0xFFFD;
52 static const UChar BOM = 0xFEFF;
53 static const int ConversionBufferSize = 16384;
54     
55 static UConverter* cachedConverterICU;
56 static TextEncodingID cachedConverterEncoding = InvalidEncoding;
57
58 StreamingTextDecoder::~StreamingTextDecoder()
59 {
60     if (m_converterICU) {
61         if (cachedConverterICU != 0)
62             ucnv_close(cachedConverterICU);
63         cachedConverterICU = m_converterICU;
64         cachedConverterEncoding = m_encoding.encodingID();
65     }
66 }
67
68 DeprecatedString StreamingTextDecoder::convertUTF16(const unsigned char *s, int length)
69 {
70     ASSERT(m_numBufferedBytes == 0 || m_numBufferedBytes == 1);
71
72     const unsigned char *p = s;
73     unsigned len = length;
74     
75     DeprecatedString result("");
76     
77     result.reserve(length / 2);
78
79     if (m_numBufferedBytes != 0 && len != 0) {
80         ASSERT(m_numBufferedBytes == 1);
81         UChar c;
82         if (m_littleEndian)
83             c = m_bufferedBytes[0] | (p[0] << 8);
84         else
85             c = (m_bufferedBytes[0] << 8) | p[0];
86
87         if (c)
88             result.append(reinterpret_cast<QChar *>(&c), 1);
89
90         m_numBufferedBytes = 0;
91         p += 1;
92         len -= 1;
93     }
94     
95     while (len > 1) {
96         UChar buffer[ConversionBufferSize];
97         int runLength = min(len / 2, (unsigned)(sizeof(buffer) / sizeof(buffer[0])));
98         int bufferLength = 0;
99         if (m_littleEndian) {
100             for (int i = 0; i < runLength; ++i) {
101                 UChar c = p[0] | (p[1] << 8);
102                 p += 2;
103                 if (c && c != BOM)
104                     buffer[bufferLength++] = c;
105             }
106         } else {
107             for (int i = 0; i < runLength; ++i) {
108                 UChar c = (p[0] << 8) | p[1];
109                 p += 2;
110                 if (c && c != BOM)
111                     buffer[bufferLength++] = c;
112             }
113         }
114         result.append(reinterpret_cast<QChar *>(buffer), bufferLength);
115         len -= runLength * 2;
116     }
117     
118     if (len) {
119         ASSERT(m_numBufferedBytes == 0);
120         m_numBufferedBytes = 1;
121         m_bufferedBytes[0] = p[0];
122     }
123     
124     return result;
125 }
126
127 static inline TextEncoding effectiveEncoding(const TextEncoding& encoding)
128 {
129     TextEncodingID id = encoding.encodingID();
130     if (id == Latin1Encoding || id == ASCIIEncoding)
131         id = WinLatin1Encoding;
132     return TextEncoding(id, encoding.flags());
133 }
134
135 UErrorCode StreamingTextDecoder::createICUConverter()
136 {
137     TextEncoding encoding = effectiveEncoding(m_encoding);
138     const char* encodingName = encoding.name();
139
140     bool cachedEncodingEqual = cachedConverterEncoding == encoding.encodingID();
141     cachedConverterEncoding = InvalidEncoding;
142
143     if (cachedEncodingEqual && cachedConverterICU) {
144         m_converterICU = cachedConverterICU;
145         cachedConverterICU = 0;
146     } else {    
147         UErrorCode err = U_ZERO_ERROR;
148         ASSERT(!m_converterICU);
149         m_converterICU = ucnv_open(encodingName, &err);
150         if (err == U_AMBIGUOUS_ALIAS_WARNING)
151             LOG_ERROR("ICU ambiguous alias warning for encoding: %s", encodingName);
152
153         if (!m_converterICU) {
154             LOG_ERROR("the ICU Converter won't convert from text encoding 0x%X, error %d", encoding.encodingID(), err);
155             return err;
156         }
157     }
158     
159     return U_ZERO_ERROR;
160 }
161
162 // We strip replacement characters because the ICU converter for UTF-8 converts
163 // invalid sequences into replacement characters, but other browsers discard them.
164 // We strip BOM characters because they can show up both at the start of content
165 // and inside content, and we never want them to end up in the decoded text.
166 static inline bool unwanted(UChar c)
167 {
168     return c == replacementCharacter || c == BOM;
169 }
170
171 void StreamingTextDecoder::appendOmittingUnwanted(DeprecatedString &s, const UChar *characters, int byteCount)
172 {
173     ASSERT(byteCount % sizeof(UChar) == 0);
174     int start = 0;
175     int characterCount = byteCount / sizeof(UChar);
176     for (int i = 0; i != characterCount; ++i) {
177         if (unwanted(characters[i])) {
178             if (start != i)
179                 s.append(reinterpret_cast<const QChar *>(&characters[start]), i - start);
180             start = i + 1;
181         }
182     }
183     if (start != characterCount)
184         s.append(reinterpret_cast<const QChar *>(&characters[start]), characterCount - start);
185 }
186
187 DeprecatedString StreamingTextDecoder::convertUsingICU(const unsigned char *chs, int len, bool flush)
188 {
189     // Get a converter for the passed-in encoding.
190     if (!m_converterICU && U_FAILURE(createICUConverter()))
191         return DeprecatedString();
192
193     ASSERT(m_converterICU);
194
195     DeprecatedString result("");
196     result.reserve(len);
197
198     UChar buffer[ConversionBufferSize];
199     const char *source = reinterpret_cast<const char *>(chs);
200     const char *sourceLimit = source + len;
201     int32_t *offsets = NULL;
202     UErrorCode err;
203     
204     do {
205         UChar *target = buffer;
206         const UChar *targetLimit = target + ConversionBufferSize;
207         err = U_ZERO_ERROR;
208         ucnv_toUnicode(m_converterICU, &target, targetLimit, &source, sourceLimit, offsets, flush, &err);
209         int count = target - buffer;
210         appendOmittingUnwanted(result, reinterpret_cast<const UChar *>(buffer), count * sizeof(UChar));
211     } while (err == U_BUFFER_OVERFLOW_ERROR);
212
213     if (U_FAILURE(err)) {
214         // flush the converter so it can be reused, and not be bothered by this error.
215         do {
216             UChar *target = buffer;
217             const UChar *targetLimit = target + ConversionBufferSize;
218             err = U_ZERO_ERROR;
219             ucnv_toUnicode(m_converterICU, &target, targetLimit, &source, sourceLimit, offsets, true, &err);
220         } while (source < sourceLimit);
221         LOG_ERROR("ICU conversion error");
222         return DeprecatedString();
223     }
224     
225     return result;
226 }
227
228 DeprecatedString StreamingTextDecoder::convert(const unsigned char *chs, int len, bool flush)
229 {
230     //#define PARTIAL_CHARACTER_HANDLING_TEST_CHUNK_SIZE 1000
231
232     switch (m_encoding.encodingID()) {
233     case Latin1Encoding:
234     case WinLatin1Encoding:
235         return convertLatin1(chs, len);
236
237     case UTF16Encoding:
238         return convertUTF16(chs, len);
239
240     default:
241 #if PARTIAL_CHARACTER_HANDLING_TEST_CHUNK_SIZE
242         DeprecatedString result;
243         int chunkSize;
244         for (int i = 0; i != len; i += chunkSize) {
245             chunkSize = len - i;
246             if (chunkSize > PARTIAL_CHARACTER_HANDLING_TEST_CHUNK_SIZE) {
247                 chunkSize = PARTIAL_CHARACTER_HANDLING_TEST_CHUNK_SIZE;
248             }
249             result += convertUsingICU(chs + i, chunkSize, flush && (i + chunkSize == len));
250         }
251         return result;
252 #else
253         return convertUsingICU(chs, len, flush);
254 #endif
255     }
256     ASSERT_NOT_REACHED();
257     return DeprecatedString();
258 }
259
260 DeprecatedString StreamingTextDecoder::toUnicode(const char *chs, int len, bool flush)
261 {
262     ASSERT_ARG(len, len >= 0);
263     
264     if (m_error || !chs)
265         return DeprecatedString();
266
267     if (len <= 0 && !flush)
268         return "";
269
270     // Handle normal case.
271     if (!m_atStart)
272         return convert(chs, len, flush);
273
274     // Check to see if we found a BOM.
275     int numBufferedBytes = m_numBufferedBytes;
276     int buf1Len = numBufferedBytes;
277     int buf2Len = len;
278     const unsigned char *buf1 = m_bufferedBytes;
279     const unsigned char *buf2 = reinterpret_cast<const unsigned char *>(chs);
280     unsigned char c1 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
281     unsigned char c2 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
282     unsigned char c3 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
283     int BOMLength = 0;
284     if (c1 == 0xFF && c2 == 0xFE) {
285         m_encoding = TextEncoding(UTF16Encoding, LittleEndian);
286         m_littleEndian = true;
287         BOMLength = 2;
288     } else if (c1 == 0xFE && c2 == 0xFF) {
289         m_encoding = TextEncoding(UTF16Encoding, BigEndian);
290         m_littleEndian = false;
291         BOMLength = 2;
292     } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) {
293         m_encoding = TextEncoding(UTF8Encoding);
294         BOMLength = 3;
295     }
296
297     // Handle case where we found a BOM.
298     if (BOMLength != 0) {
299         ASSERT(numBufferedBytes + len >= BOMLength);
300         int skip = BOMLength - numBufferedBytes;
301         m_numBufferedBytes = 0;
302         m_atStart = false;
303         return len == skip ? DeprecatedString("") : convert(chs + skip, len - skip, flush);
304     }
305
306     // Handle case where we know there is no BOM coming.
307     const int bufferSize = sizeof(m_bufferedBytes);
308     if (numBufferedBytes + len > bufferSize || flush) {
309         m_atStart = false;
310         if (numBufferedBytes == 0) {
311             return convert(chs, len, flush);
312         }
313         unsigned char bufferedBytes[sizeof(m_bufferedBytes)];
314         memcpy(bufferedBytes, m_bufferedBytes, numBufferedBytes);
315         m_numBufferedBytes = 0;
316         return convert(bufferedBytes, numBufferedBytes, false) + convert(chs, len, flush);
317     }
318
319     // Continue to look for the BOM.
320     memcpy(&m_bufferedBytes[numBufferedBytes], chs, len);
321     m_numBufferedBytes += len;
322     return "";
323 }
324     
325 } // namespace WebCore