8dfd1fd20b944d17e9acfb343dcbad3941e60aa4
[WebKit-https.git] / Source / WebCore / platform / text / TextEncodingRegistry.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2007-2009 Torch Mobile, Inc.
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 SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "TextEncodingRegistry.h"
29
30 #include "TextCodecLatin1.h"
31 #include "TextCodecUserDefined.h"
32 #include "TextCodecUTF16.h"
33 #include "TextCodecUTF8.h"
34 #include "TextEncoding.h"
35 #include <mutex>
36 #include <wtf/ASCIICType.h>
37 #include <wtf/HashMap.h>
38 #include <wtf/HashSet.h>
39 #include <wtf/MainThread.h>
40 #include <wtf/NeverDestroyed.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/StringExtras.h>
43
44 #if USE(ICU_UNICODE)
45 #include "TextCodecICU.h"
46 #endif
47 #if PLATFORM(MAC) && !PLATFORM(IOS)
48 #include "TextCodecMac.h"
49 #endif
50 #if OS(WINDOWS) && USE(WCHAR_UNICODE)
51 #include "win/TextCodecWin.h"
52 #endif
53
54 #include <wtf/CurrentTime.h>
55 #include <wtf/text/CString.h>
56
57 using namespace WTF;
58
59 namespace WebCore {
60
61 const size_t maxEncodingNameLength = 63;
62
63 // Hash for all-ASCII strings that does case folding.
64 struct TextEncodingNameHash {
65     static bool equal(const char* s1, const char* s2)
66     {
67         char c1;
68         char c2;
69         do {
70             c1 = *s1++;
71             c2 = *s2++;
72             if (toASCIILower(c1) != toASCIILower(c2))
73                 return false;
74         } while (c1 && c2);
75         return !c1 && !c2;
76     }
77
78     // This algorithm is the one-at-a-time hash from:
79     // http://burtleburtle.net/bob/hash/hashfaq.html
80     // http://burtleburtle.net/bob/hash/doobs.html
81     static unsigned hash(const char* s)
82     {
83         unsigned h = WTF::stringHashingStartValue;
84         for (;;) {
85             char c = *s++;
86             if (!c) {
87                 h += (h << 3);
88                 h ^= (h >> 11);
89                 h += (h << 15);
90                 return h;
91             }
92             h += toASCIILower(c);
93             h += (h << 10); 
94             h ^= (h >> 6); 
95         }
96     }
97
98     static const bool safeToCompareToEmptyOrDeleted = false;
99 };
100
101 struct TextCodecFactory {
102     NewTextCodecFunction function;
103     const void* additionalData;
104     TextCodecFactory(NewTextCodecFunction f = 0, const void* d = 0) : function(f), additionalData(d) { }
105 };
106
107 typedef HashMap<const char*, const char*, TextEncodingNameHash> TextEncodingNameMap;
108 typedef HashMap<const char*, TextCodecFactory> TextCodecMap;
109
110 static std::mutex& encodingRegistryMutex()
111 {
112     // We don't have to construct this mutex in a thread safe way because this function
113     // is called on the main thread for any page before it is used in worker threads.
114     static NeverDestroyed<std::mutex> mutex;
115
116     return mutex;
117 }
118
119 static TextEncodingNameMap* textEncodingNameMap;
120 static TextCodecMap* textCodecMap;
121 static bool didExtendTextCodecMaps;
122 static HashSet<const char*>* japaneseEncodings;
123 static HashSet<const char*>* nonBackslashEncodings;
124
125 static const char* const textEncodingNameBlacklist[] = { "UTF-7" };
126
127 #if ERROR_DISABLED
128
129 static inline void checkExistingName(const char*, const char*) { }
130
131 #else
132
133 static void checkExistingName(const char* alias, const char* atomicName)
134 {
135     const char* oldAtomicName = textEncodingNameMap->get(alias);
136     if (!oldAtomicName)
137         return;
138     if (oldAtomicName == atomicName)
139         return;
140     // Keep the warning silent about one case where we know this will happen.
141     if (strcmp(alias, "ISO-8859-8-I") == 0
142             && strcmp(oldAtomicName, "ISO-8859-8-I") == 0
143             && strcasecmp(atomicName, "iso-8859-8") == 0)
144         return;
145     LOG_ERROR("alias %s maps to %s already, but someone is trying to make it map to %s", alias, oldAtomicName, atomicName);
146 }
147
148 #endif
149
150 static bool isUndesiredAlias(const char* alias)
151 {
152     // Reject aliases with version numbers that are supported by some back-ends (such as "ISO_2022,locale=ja,version=0" in ICU).
153     for (const char* p = alias; *p; ++p) {
154         if (*p == ',')
155             return true;
156     }
157     // 8859_1 is known to (at least) ICU, but other browsers don't support this name - and having it caused a compatibility
158     // problem, see bug 43554.
159     if (0 == strcmp(alias, "8859_1"))
160         return true;
161     return false;
162 }
163
164 static void addToTextEncodingNameMap(const char* alias, const char* name)
165 {
166     ASSERT(strlen(alias) <= maxEncodingNameLength);
167     if (isUndesiredAlias(alias))
168         return;
169     const char* atomicName = textEncodingNameMap->get(name);
170     ASSERT(strcmp(alias, name) == 0 || atomicName);
171     if (!atomicName)
172         atomicName = name;
173     checkExistingName(alias, atomicName);
174     textEncodingNameMap->add(alias, atomicName);
175 }
176
177 static void addToTextCodecMap(const char* name, NewTextCodecFunction function, const void* additionalData)
178 {
179     const char* atomicName = textEncodingNameMap->get(name);
180     ASSERT(atomicName);
181     textCodecMap->add(atomicName, TextCodecFactory(function, additionalData));
182 }
183
184 static void pruneBlacklistedCodecs()
185 {
186     for (size_t i = 0; i < WTF_ARRAY_LENGTH(textEncodingNameBlacklist); ++i) {
187         const char* atomicName = textEncodingNameMap->get(textEncodingNameBlacklist[i]);
188         if (!atomicName)
189             continue;
190
191         Vector<const char*> names;
192         TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin();
193         TextEncodingNameMap::const_iterator end = textEncodingNameMap->end();
194         for (; it != end; ++it) {
195             if (it->value == atomicName)
196                 names.append(it->key);
197         }
198
199         size_t length = names.size();
200         for (size_t j = 0; j < length; ++j)
201             textEncodingNameMap->remove(names[j]);
202
203         textCodecMap->remove(atomicName);
204     }
205 }
206
207 static void buildBaseTextCodecMaps()
208 {
209     ASSERT(isMainThread());
210     ASSERT(!textCodecMap);
211     ASSERT(!textEncodingNameMap);
212
213     textCodecMap = new TextCodecMap;
214     textEncodingNameMap = new TextEncodingNameMap;
215
216     TextCodecLatin1::registerEncodingNames(addToTextEncodingNameMap);
217     TextCodecLatin1::registerCodecs(addToTextCodecMap);
218
219     TextCodecUTF8::registerEncodingNames(addToTextEncodingNameMap);
220     TextCodecUTF8::registerCodecs(addToTextCodecMap);
221
222     TextCodecUTF16::registerEncodingNames(addToTextEncodingNameMap);
223     TextCodecUTF16::registerCodecs(addToTextCodecMap);
224
225     TextCodecUserDefined::registerEncodingNames(addToTextEncodingNameMap);
226     TextCodecUserDefined::registerCodecs(addToTextCodecMap);
227 }
228
229 static void addEncodingName(HashSet<const char*>* set, const char* name)
230 {
231     // We must not use atomicCanonicalTextEncodingName() because this function is called in it.
232     const char* atomicName = textEncodingNameMap->get(name);
233     if (atomicName)
234         set->add(atomicName);
235 }
236
237 static void buildQuirksSets()
238 {
239     // FIXME: Having isJapaneseEncoding() and shouldShowBackslashAsCurrencySymbolIn()
240     // and initializing the sets for them in TextEncodingRegistry.cpp look strange.
241
242     ASSERT(!japaneseEncodings);
243     ASSERT(!nonBackslashEncodings);
244
245     japaneseEncodings = new HashSet<const char*>;
246     addEncodingName(japaneseEncodings, "EUC-JP");
247     addEncodingName(japaneseEncodings, "ISO-2022-JP");
248     addEncodingName(japaneseEncodings, "ISO-2022-JP-1");
249     addEncodingName(japaneseEncodings, "ISO-2022-JP-2");
250     addEncodingName(japaneseEncodings, "ISO-2022-JP-3");
251     addEncodingName(japaneseEncodings, "JIS_C6226-1978");
252     addEncodingName(japaneseEncodings, "JIS_X0201");
253     addEncodingName(japaneseEncodings, "JIS_X0208-1983");
254     addEncodingName(japaneseEncodings, "JIS_X0208-1990");
255     addEncodingName(japaneseEncodings, "JIS_X0212-1990");
256     addEncodingName(japaneseEncodings, "Shift_JIS");
257     addEncodingName(japaneseEncodings, "Shift_JIS_X0213-2000");
258     addEncodingName(japaneseEncodings, "cp932");
259     addEncodingName(japaneseEncodings, "x-mac-japanese");
260
261     nonBackslashEncodings = new HashSet<const char*>;
262     // The text encodings below treat backslash as a currency symbol for IE compatibility.
263     // See http://blogs.msdn.com/michkap/archive/2005/09/17/469941.aspx for more information.
264     addEncodingName(nonBackslashEncodings, "x-mac-japanese");
265     addEncodingName(nonBackslashEncodings, "ISO-2022-JP");
266     addEncodingName(nonBackslashEncodings, "EUC-JP");
267     // Shift_JIS_X0213-2000 is not the same encoding as Shift_JIS on Mac. We need to register both of them.
268     addEncodingName(nonBackslashEncodings, "Shift_JIS");
269     addEncodingName(nonBackslashEncodings, "Shift_JIS_X0213-2000");
270 }
271
272 bool isJapaneseEncoding(const char* canonicalEncodingName)
273 {
274     return canonicalEncodingName && japaneseEncodings && japaneseEncodings->contains(canonicalEncodingName);
275 }
276
277 bool shouldShowBackslashAsCurrencySymbolIn(const char* canonicalEncodingName)
278 {
279     return canonicalEncodingName && nonBackslashEncodings && nonBackslashEncodings->contains(canonicalEncodingName);
280 }
281
282 static void extendTextCodecMaps()
283 {
284 #if USE(ICU_UNICODE)
285     TextCodecICU::registerEncodingNames(addToTextEncodingNameMap);
286     TextCodecICU::registerCodecs(addToTextCodecMap);
287 #endif
288
289 #if PLATFORM(MAC) && !PLATFORM(IOS)
290     TextCodecMac::registerEncodingNames(addToTextEncodingNameMap);
291     TextCodecMac::registerCodecs(addToTextCodecMap);
292 #endif
293
294 #if OS(WINDOWS) && USE(WCHAR_UNICODE)
295     TextCodecWin::registerExtendedEncodingNames(addToTextEncodingNameMap);
296     TextCodecWin::registerExtendedCodecs(addToTextCodecMap);
297 #endif
298
299     pruneBlacklistedCodecs();
300     buildQuirksSets();
301 }
302
303 PassOwnPtr<TextCodec> newTextCodec(const TextEncoding& encoding)
304 {
305     std::lock_guard<std::mutex> lock(encodingRegistryMutex());
306
307     ASSERT(textCodecMap);
308     TextCodecFactory factory = textCodecMap->get(encoding.name());
309     ASSERT(factory.function);
310     return factory.function(encoding, factory.additionalData);
311 }
312
313 const char* atomicCanonicalTextEncodingName(const char* name)
314 {
315     if (!name || !name[0])
316         return nullptr;
317
318     if (!textEncodingNameMap)
319         buildBaseTextCodecMaps();
320
321     std::lock_guard<std::mutex> lock(encodingRegistryMutex());
322
323     if (const char* atomicName = textEncodingNameMap->get(name))
324         return atomicName;
325     if (didExtendTextCodecMaps)
326         return nullptr;
327
328     extendTextCodecMaps();
329     didExtendTextCodecMaps = true;
330     return textEncodingNameMap->get(name);
331 }
332
333 template <typename CharacterType>
334 const char* atomicCanonicalTextEncodingName(const CharacterType* characters, size_t length)
335 {
336     char buffer[maxEncodingNameLength + 1];
337     size_t j = 0;
338     for (size_t i = 0; i < length; ++i) {
339         CharacterType c = characters[i];
340         if (j == maxEncodingNameLength)
341             return 0;
342         buffer[j++] = c;
343     }
344     buffer[j] = 0;
345     return atomicCanonicalTextEncodingName(buffer);
346 }
347
348 const char* atomicCanonicalTextEncodingName(const String& alias)
349 {
350     if (!alias.length())
351         return 0;
352
353     if (alias.is8Bit())
354         return atomicCanonicalTextEncodingName<LChar>(alias.characters8(), alias.length());
355
356     return atomicCanonicalTextEncodingName<UChar>(alias.characters(), alias.length());
357 }
358
359 bool noExtendedTextEncodingNameUsed()
360 {
361     // If the calling thread did not use extended encoding names, it is fine for it to use a stale false value.
362     return !didExtendTextCodecMaps;
363 }
364
365 #ifndef NDEBUG
366 void dumpTextEncodingNameMap()
367 {
368     unsigned size = textEncodingNameMap->size();
369     fprintf(stderr, "Dumping %u entries in WebCore::textEncodingNameMap...\n", size);
370
371     std::lock_guard<std::mutex> lock(encodingRegistryMutex());
372
373     TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin();
374     TextEncodingNameMap::const_iterator end = textEncodingNameMap->end();
375     for (; it != end; ++it)
376         fprintf(stderr, "'%s' => '%s'\n", it->key, it->value);
377 }
378 #endif
379
380 } // namespace WebCore