LayoutTests:
[WebKit-https.git] / WebCore / platform / TextEncoding.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 "TextEncoding.h"
28
29 #include "CharsetNames.h"
30 #include <wtf/Assertions.h>
31 #include <wtf/HashSet.h>
32 #include "StreamingTextDecoder.h"
33 #include <unicode/unorm.h>
34
35 namespace WebCore {
36
37 TextEncoding::TextEncoding(const char* name, bool eightBitOnly)
38 {
39     m_encodingID = textEncodingIDFromCharsetName(name, &m_flags);
40     if (eightBitOnly && m_encodingID == UTF16Encoding)
41         m_encodingID = UTF8Encoding;
42 }
43
44 const char* TextEncoding::name() const
45 {
46     return charsetNameFromTextEncodingID(m_encodingID);
47 }
48 UChar TextEncoding::backslashAsCurrencySymbol() const
49 {
50     if (m_flags & BackslashIsYen)
51         return 0x00A5; // yen sign
52  
53     return '\\';
54 }
55
56 DeprecatedString TextEncoding::toUnicode(const char *chs, int len) const
57 {
58     return StreamingTextDecoder(*this).toUnicode(chs, len, true);
59 }
60
61 DeprecatedString TextEncoding::toUnicode(const DeprecatedByteArray &qba, int len) const
62 {
63     return StreamingTextDecoder(*this).toUnicode(qba, len, true);
64 }
65
66 // We'd like to use ICU for this on OS X as well eventually, but we need to make sure
67 // it covers all the encodings that we need
68 #ifndef __APPLE__
69
70 static UConverter* cachedConverter;
71 static TextEncodingID cachedConverterEncoding = InvalidEncoding;
72
73 static const int ConversionBufferSize = 16384;
74
75 static inline UConverter* getConverter(TextEncodingID encoding, UErrorCode* status)
76 {
77     if (cachedConverter && encoding == cachedConverterEncoding) {
78         UConverter* conv = cachedConverter;
79         cachedConverter = 0;
80         return conv;
81     }
82
83     const char* encodingName = charsetNameFromTextEncodingID(encoding);
84     UErrorCode err = U_ZERO_ERROR;
85     UConverter* conv = ucnv_open(encodingName, &err);
86     if (err == U_AMBIGUOUS_ALIAS_WARNING)
87         LOG_ERROR("ICU ambiguous alias warning for encoding: %s", encodingName);
88
89     if (!conv) {
90         LOG_ERROR("the ICU Converter won't convert to text encoding 0x%X, error %d", encoding, err);
91         *status = err;
92         return 0;
93     }
94
95     return conv;
96 }
97
98 static inline void cacheConverter(TextEncodingID id, UConverter* conv)
99 {
100     if (conv) {
101         if (cachedConverter)
102             ucnv_close(cachedConverter);
103         cachedConverter = conv;
104         cachedConverterEncoding = id;
105     }
106 }
107
108 static inline TextEncodingID effectiveEncoding(TextEncodingID encoding)
109 {
110     if (encoding == Latin1Encoding || encoding == ASCIIEncoding)
111         return WinLatin1Encoding;
112     return encoding;
113 }
114
115 DeprecatedCString TextEncoding::fromUnicode(const DeprecatedString &qcs, bool allowEntities) const
116 {
117     TextEncodingID encoding = effectiveEncoding(m_encodingID);
118
119     if (encoding == WinLatin1Encoding && qcs.isAllLatin1())
120         return qcs.latin1();
121
122     if ((encoding == WinLatin1Encoding || encoding == UTF8Encoding || encoding == ASCIIEncoding) 
123         && qcs.isAllASCII())
124         return qcs.ascii();
125
126     // FIXME: We should see if there is "force ASCII range" mode in ICU;
127     // until then, we change the backslash into a yen sign.
128     // Encoding will change the yen sign back into a backslash.
129     DeprecatedString copy = qcs;
130     copy.replace('\\', backslashAsCurrencySymbol());
131
132     UErrorCode err = U_ZERO_ERROR;
133     UConverter* conv = getConverter(encoding, &err);
134     if (!conv && U_FAILURE(err))
135         return DeprecatedCString();
136
137     ASSERT(conv);
138
139     // FIXME: when DeprecatedString buffer is latin1, it would be nice to
140     // convert from that w/o having to allocate a unicode buffer
141
142     char buffer[ConversionBufferSize];
143     const UChar* source = reinterpret_cast<const UChar*>(copy.unicode());
144     const UChar* sourceLimit = source + copy.length();
145
146     DeprecatedString normalizedString;
147     if (UNORM_YES != unorm_quickCheck(source, copy.length(), UNORM_NFC, &err)) {
148         normalizedString.truncate(copy.length()); // normalization to NFC rarely increases the length, so this first attempt will usually succeed
149         
150         int32_t normalizedLength = unorm_normalize(source, copy.length(), UNORM_NFC, 0, reinterpret_cast<UChar*>(const_cast<QChar*>(normalizedString.unicode())), copy.length(), &err);
151         if (err == U_BUFFER_OVERFLOW_ERROR) {
152             err = U_ZERO_ERROR;
153             normalizedString.truncate(normalizedLength);
154             normalizedLength = unorm_normalize(source, copy.length(), UNORM_NFC, 0, reinterpret_cast<UChar*>(const_cast<QChar*>(normalizedString.unicode())), normalizedLength, &err);
155         }
156         
157         source = reinterpret_cast<const UChar*>(normalizedString.unicode());
158         sourceLimit = source + normalizedLength;
159     }
160
161     DeprecatedCString result(1); // for trailing zero
162
163     if (allowEntities)
164         ucnv_setFromUCallBack(conv, UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_DEC, 0, 0, &err);
165     else {
166         ucnv_setSubstChars(conv, "?", 1, &err);
167         ucnv_setFromUCallBack(conv, UCNV_FROM_U_CALLBACK_SUBSTITUTE, 0, 0, 0, &err);
168     }
169
170     ASSERT(U_SUCCESS(err));
171     if (U_FAILURE(err))
172         return DeprecatedCString();
173
174     do {
175         char* target = buffer;
176         char* targetLimit = target + ConversionBufferSize;
177         err = U_ZERO_ERROR;
178         ucnv_fromUnicode(conv, &target, targetLimit, &source, sourceLimit, 0, true,  &err);
179         int count = target - buffer;
180         buffer[count] = 0;
181         result.append(buffer);
182     } while (err == U_BUFFER_OVERFLOW_ERROR);
183
184     cacheConverter(encoding, conv);
185
186     return result;
187 }
188 #endif
189
190
191 } // namespace WebCore