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