Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / text / win / TextCodecWin.cpp
1 /*
2  * Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved.
3  * Copyright (C) 2010-2012 Patrick Gansterer <paroga@paroga.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  *  This library is distributed in the hope that i will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Library General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Library General Public License
20  *  along with this library; see the file COPYING.LIB.  If not, write to
21  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301, USA.
23  */
24
25 #include "config.h"
26 #include "TextCodecWin.h"
27
28 #include "COMPtr.h"
29 #include <mlang.h>
30 #include <windows.h>
31 #include <wtf/HashMap.h>
32 #include <wtf/HashSet.h>
33 #include <wtf/text/CString.h>
34 #include <wtf/text/StringHash.h>
35 #include <wtf/text/WTFString.h>
36
37 namespace WebCore {
38
39 struct CharsetInfo {
40     CString m_name;
41     String m_friendlyName;
42     UINT m_codePage;
43     Vector<CString> m_aliases;
44 };
45
46 class LanguageManager {
47 private:
48     LanguageManager();
49
50     friend LanguageManager& languageManager();
51 };
52
53 // Usage: a lookup table used to get CharsetInfo with code page ID.
54 // Key: code page ID. Value: charset information.
55 static HashMap<UINT, CString>& codePageCharsets()
56 {
57     static HashMap<UINT, CString> cc;
58     return cc;
59 }
60
61 static HashMap<String, CharsetInfo>& knownCharsets()
62 {
63     static HashMap<String, CharsetInfo> kc;
64     return kc;
65 }
66
67 // Usage: a map that stores charsets that are supported by system. Sorted by name.
68 // Key: charset. Value: code page ID.
69 typedef HashSet<String> CharsetSet;
70 static CharsetSet& supportedCharsets()
71 {
72     static CharsetSet sl;
73     return sl;
74 }
75
76 static LanguageManager& languageManager()
77 {
78     static LanguageManager lm;
79     return lm;
80 }
81
82 LanguageManager::LanguageManager()
83 {
84     COMPtr<IMultiLanguage> multiLanguage;
85     if (FAILED(::CoCreateInstance(CLSID_CMultiLanguage, 0, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, reinterpret_cast<LPVOID*>(&multiLanguage))))
86         return;
87
88     COMPtr<IEnumCodePage> enumInterface;
89     if (FAILED(multiLanguage->EnumCodePages(MIMECONTF_BROWSER, &enumInterface)))
90         return;
91
92     MIMECPINFO cpInfo;
93     ULONG ccpInfo;
94     while (SUCCEEDED(enumInterface->Next(1, &cpInfo, &ccpInfo)) && ccpInfo) {
95         if (!IsValidCodePage(cpInfo.uiCodePage))
96             continue;
97
98         HashMap<UINT, CString>::iterator i = codePageCharsets().find(cpInfo.uiCodePage);
99
100         CString name(String(cpInfo.wszWebCharset).latin1());
101         if (i == codePageCharsets().end()) {
102             CharsetInfo info;
103             info.m_codePage = cpInfo.uiCodePage;
104             knownCharsets().set(name.data(), info);
105             i = codePageCharsets().set(cpInfo.uiCodePage, name).iterator;
106         }
107         if (i != codePageCharsets().end()) {
108             HashMap<String, CharsetInfo>::iterator j = knownCharsets().find(String(i->value.data(), i->value.length()));
109             ASSERT(j != knownCharsets().end());
110             CharsetInfo& info = j->value;
111             info.m_name = i->value.data();
112             info.m_friendlyName = cpInfo.wszDescription;
113             info.m_aliases.append(name);
114             info.m_aliases.append(String(cpInfo.wszHeaderCharset).latin1());
115             info.m_aliases.append(String(cpInfo.wszBodyCharset).latin1());
116             String cpName = "cp" + String::number(cpInfo.uiCodePage);
117             info.m_aliases.append(cpName.latin1());
118             supportedCharsets().add(i->value.data());
119         }
120     }
121 }
122
123 static UINT getCodePage(const char* name)
124 {
125     // Explicitly use a "const" reference to fix the silly VS build error
126     // saying "==" is not found for const_iterator and iterator
127     const HashMap<String, CharsetInfo>& charsets = knownCharsets();
128     HashMap<String, CharsetInfo>::const_iterator i = charsets.find(name);
129     return i == charsets.end() ? CP_ACP : i->value.m_codePage;
130 }
131
132 static std::unique_ptr<TextCodec> newTextCodecWin(const TextEncoding& encoding, const void*)
133 {
134     return std::make_unique<TextCodecWin>(getCodePage(encoding.name()));
135 }
136
137 TextCodecWin::TextCodecWin(UINT codePage)
138     : m_codePage(codePage)
139 {
140 }
141
142 TextCodecWin::~TextCodecWin() = default;
143
144 void TextCodecWin::registerExtendedEncodingNames(EncodingNameRegistrar registrar)
145 {
146     languageManager();
147     for (CharsetSet::iterator i = supportedCharsets().begin(); i != supportedCharsets().end(); ++i) {
148         HashMap<String, CharsetInfo>::iterator j = knownCharsets().find(*i);
149         if (j != knownCharsets().end()) {
150             registrar(j->value.m_name.data(), j->value.m_name.data());
151             for (Vector<CString>::const_iterator alias = j->value.m_aliases.begin(); alias != j->value.m_aliases.end(); ++alias)
152                 registrar(alias->data(), j->value.m_name.data());
153         }
154     }
155 }
156
157 void TextCodecWin::registerExtendedCodecs(TextCodecRegistrar registrar)
158 {
159     languageManager();
160     for (CharsetSet::iterator i = supportedCharsets().begin(); i != supportedCharsets().end(); ++i) {
161         HashMap<String, CharsetInfo>::iterator j = knownCharsets().find(*i);
162         if (j != knownCharsets().end())
163             registrar(j->value.m_name.data(), newTextCodecWin, 0);
164     }
165 }
166
167 static DWORD getCodePageFlags(UINT codePage)
168 {
169     if (codePage == 42) // Symbol
170         return 0;
171
172     // Microsoft says the flag must be 0 for the following code pages
173     if (codePage > 50000) {
174         if ((codePage >= 50220 && codePage <= 50222)
175             || codePage == 50225
176             || codePage == 50227
177             || codePage == 50229
178             || codePage == 52936
179             || codePage == 54936
180             || (codePage >= 57002 && codePage <= 57001)
181             || codePage == 65000 // UTF-7
182             )
183             return 0;
184     }
185
186     return MB_PRECOMPOSED | MB_ERR_INVALID_CHARS;
187 }
188
189 static inline const char* findFirstNonAsciiCharacter(const char* bytes, size_t length)
190 {
191     for (const char* bytesEnd = bytes + length; bytes < bytesEnd; ++bytes) {
192         if (*bytes & 0x80)
193             break;
194     }
195     return bytes;
196 }
197
198 static void decodeInternal(Vector<UChar, 8192>& result, UINT codePage, const char* bytes, size_t length, size_t* left)
199 {
200     *left = length;
201     if (!bytes || !length)
202         return;
203
204     DWORD flags = getCodePageFlags(codePage);
205
206     int testLength = length;
207     int untestedLength = length;
208     for (;;) {
209         int resultLength = MultiByteToWideChar(codePage, flags, bytes, testLength, 0, 0);
210
211         if (resultLength > 0) {
212             int oldSize = result.size();
213             result.resize(oldSize + resultLength);
214
215             MultiByteToWideChar(codePage, flags, bytes, testLength, result.data() + oldSize, resultLength);
216
217             if (testLength == untestedLength) {
218                 *left = length - testLength;
219                 break;
220             }
221             untestedLength -= testLength;
222             length -= testLength;
223             bytes += testLength;
224         } else {
225             untestedLength = testLength - 1;
226             if (!untestedLength) {
227                 *left = length;
228                 break;
229             }
230         }
231         testLength = (untestedLength + 1) / 2;
232     }
233 }
234
235 String TextCodecWin::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError)
236 {
237     if (!m_decodeBuffer.isEmpty()) {
238         m_decodeBuffer.append(bytes, length);
239         bytes = m_decodeBuffer.data();
240         length = m_decodeBuffer.size();
241     }
242
243     size_t left;
244     Vector<UChar, 8192> result;
245     for (;;) {
246         decodeInternal(result, m_codePage, bytes, length, &left);
247         if (!left)
248             break;
249
250         if (!flush && left < 16)
251             break;
252
253         result.append(L'?');
254         sawError = true;
255         if (stopOnError)
256             return String::adopt(result);
257
258         if (left == 1)
259             break;
260
261         bytes += length - left + 1;
262         length = left - 1;
263     }
264     if (left && !flush) {
265         if (m_decodeBuffer.isEmpty())
266             m_decodeBuffer.append(bytes + length - left, left);
267         else {
268             memmove(m_decodeBuffer.data(), bytes + length - left, left);
269             m_decodeBuffer.resize(left);
270         }
271     } else
272         m_decodeBuffer.clear();
273
274     return String::adopt(result);
275 }
276
277 CString TextCodecWin::encode(const UChar* characters, size_t length, UnencodableHandling)
278 {
279     if (!characters || !length)
280         return CString();
281
282     int resultLength = WideCharToMultiByte(m_codePage, WC_COMPOSITECHECK, characters, length, 0, 0, 0, 0);
283
284     // FIXME: We need to implement UnencodableHandling: QuestionMarksForUnencodables, EntitiesForUnencodables, and URLEncodedEntitiesForUnencodables.
285
286     if (resultLength <= 0)
287         return "?";
288
289     char* characterBuffer;
290     CString result = CString::newUninitialized(resultLength, characterBuffer);
291
292     WideCharToMultiByte(m_codePage, WC_COMPOSITECHECK, characters, length, characterBuffer, resultLength, 0, 0);
293
294     return result;
295 }
296
297 void TextCodecWin::enumerateSupportedEncodings(EncodingReceiver& receiver)
298 {
299     languageManager();
300     for (CharsetSet::iterator i = supportedCharsets().begin(); i != supportedCharsets().end(); ++i) {
301         HashMap<String, CharsetInfo>::iterator j = knownCharsets().find(*i);
302         if (j != knownCharsets().end() && !receiver.receive(j->value.m_name.data(), j->value.m_friendlyName.charactersWithNullTermination().data(), j->value.m_codePage))
303             break;
304     }
305 }
306
307 } // namespace WebCore