Modernize some aspects of text codecs, eliminate WebKit use of strcasecmp
[WebKit-https.git] / Source / WebCore / platform / text / TextEncodingRegistry.cpp
index a5782cf..556dff3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
  * Copyright (C) 2007-2009 Torch Mobile, Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 #include "config.h"
 #include "TextEncodingRegistry.h"
 
+#include "TextCodecICU.h"
 #include "TextCodecLatin1.h"
-#include "TextCodecUserDefined.h"
+#include "TextCodecReplacement.h"
 #include "TextCodecUTF16.h"
 #include "TextCodecUTF8.h"
+#include "TextCodecUserDefined.h"
 #include "TextEncoding.h"
+#include <mutex>
 #include <wtf/ASCIICType.h>
+#include <wtf/CheckedArithmetic.h>
+#include <wtf/CurrentTime.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
+#include <wtf/Lock.h>
 #include <wtf/MainThread.h>
 #include <wtf/StdLibExtras.h>
-#include <wtf/StringExtras.h>
-
-#if USE(ICU_UNICODE)
-#include "TextCodecICU.h"
-#endif
-#if PLATFORM(MAC)
-#include "TextCodecMac.h"
-#endif
-#if OS(WINDOWS) && USE(WCHAR_UNICODE)
-#include "win/TextCodecWin.h"
-#endif
-
-#include <wtf/CurrentTime.h>
 #include <wtf/text/CString.h>
 
-using namespace WTF;
-
 namespace WebCore {
+using namespace WTF;
 
 const size_t maxEncodingNameLength = 63;
 
@@ -96,23 +88,10 @@ struct TextEncodingNameHash {
     static const bool safeToCompareToEmptyOrDeleted = false;
 };
 
-struct TextCodecFactory {
-    NewTextCodecFunction function;
-    const void* additionalData;
-    TextCodecFactory(NewTextCodecFunction f = 0, const void* d = 0) : function(f), additionalData(d) { }
-};
-
-typedef HashMap<const char*, const char*, TextEncodingNameHash> TextEncodingNameMap;
-typedef HashMap<const char*, TextCodecFactory> TextCodecMap;
+using TextEncodingNameMap = HashMap<const char*, const char*, TextEncodingNameHash>;
+using TextCodecMap = HashMap<const char*, NewTextCodecFunction>;
 
-static Mutex& encodingRegistryMutex()
-{
-    // We don't have to use AtomicallyInitializedStatic here because
-    // this function is called on the main thread for any page before
-    // it is used in worker threads.
-    DEFINE_STATIC_LOCAL(Mutex, mutex, ());
-    return mutex;
-}
+static StaticLock encodingRegistryMutex;
 
 static TextEncodingNameMap* textEncodingNameMap;
 static TextCodecMap* textCodecMap;
@@ -120,30 +99,7 @@ static bool didExtendTextCodecMaps;
 static HashSet<const char*>* japaneseEncodings;
 static HashSet<const char*>* nonBackslashEncodings;
 
-static const char* const textEncodingNameBlacklist[] = { "UTF-7" };
-
-#if ERROR_DISABLED
-
-static inline void checkExistingName(const char*, const char*) { }
-
-#else
-
-static void checkExistingName(const char* alias, const char* atomicName)
-{
-    const char* oldAtomicName = textEncodingNameMap->get(alias);
-    if (!oldAtomicName)
-        return;
-    if (oldAtomicName == atomicName)
-        return;
-    // Keep the warning silent about one case where we know this will happen.
-    if (strcmp(alias, "ISO-8859-8-I") == 0
-            && strcmp(oldAtomicName, "ISO-8859-8-I") == 0
-            && strcasecmp(atomicName, "iso-8859-8") == 0)
-        return;
-    LOG_ERROR("alias %s maps to %s already, but someone is trying to make it map to %s", alias, oldAtomicName, atomicName);
-}
-
-#endif
+static const char* const textEncodingNameBlacklist[] = { "UTF-7", "BOCU-1", "SCSU" };
 
 static bool isUndesiredAlias(const char* alias)
 {
@@ -168,43 +124,41 @@ static void addToTextEncodingNameMap(const char* alias, const char* name)
     ASSERT(strcmp(alias, name) == 0 || atomicName);
     if (!atomicName)
         atomicName = name;
-    checkExistingName(alias, atomicName);
+
+    ASSERT_WITH_MESSAGE(!textEncodingNameMap->get(alias), "Duplicate text encoding name %s for %s (previously registered as %s)", alias, atomicName, textEncodingNameMap->get(alias));
+
     textEncodingNameMap->add(alias, atomicName);
 }
 
-static void addToTextCodecMap(const char* name, NewTextCodecFunction function, const void* additionalData)
+static void addToTextCodecMap(const char* name, NewTextCodecFunction&& function)
 {
     const char* atomicName = textEncodingNameMap->get(name);
     ASSERT(atomicName);
-    textCodecMap->add(atomicName, TextCodecFactory(function, additionalData));
+    textCodecMap->add(atomicName, WTFMove(function));
 }
 
 static void pruneBlacklistedCodecs()
 {
-    for (size_t i = 0; i < WTF_ARRAY_LENGTH(textEncodingNameBlacklist); ++i) {
-        const char* atomicName = textEncodingNameMap->get(textEncodingNameBlacklist[i]);
+    for (auto& nameFromBlacklist : textEncodingNameBlacklist) {
+        auto* atomicName = textEncodingNameMap->get(nameFromBlacklist);
         if (!atomicName)
             continue;
 
         Vector<const char*> names;
-        TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin();
-        TextEncodingNameMap::const_iterator end = textEncodingNameMap->end();
-        for (; it != end; ++it) {
-            if (it->value == atomicName)
-                names.append(it->key);
+        for (auto& entry : *textEncodingNameMap) {
+            if (entry.value == atomicName)
+                names.append(entry.key);
         }
 
-        size_t length = names.size();
-        for (size_t j = 0; j < length; ++j)
-            textEncodingNameMap->remove(names[j]);
+        for (auto* name : names)
+            textEncodingNameMap->remove(name);
 
         textCodecMap->remove(atomicName);
     }
 }
 
-static void buildBaseTextCodecMaps()
+static void buildBaseTextCodecMaps(const std::lock_guard<StaticLock>&)
 {
-    ASSERT(isMainThread());
     ASSERT(!textCodecMap);
     ASSERT(!textEncodingNameMap);
 
@@ -279,63 +233,54 @@ bool shouldShowBackslashAsCurrencySymbolIn(const char* canonicalEncodingName)
 
 static void extendTextCodecMaps()
 {
-#if USE(ICU_UNICODE)
+    TextCodecReplacement::registerEncodingNames(addToTextEncodingNameMap);
+    TextCodecReplacement::registerCodecs(addToTextCodecMap);
+
     TextCodecICU::registerEncodingNames(addToTextEncodingNameMap);
     TextCodecICU::registerCodecs(addToTextCodecMap);
-#endif
-
-#if PLATFORM(MAC)
-    TextCodecMac::registerEncodingNames(addToTextEncodingNameMap);
-    TextCodecMac::registerCodecs(addToTextCodecMap);
-#endif
-
-#if OS(WINDOWS) && USE(WCHAR_UNICODE)
-    TextCodecWin::registerExtendedEncodingNames(addToTextEncodingNameMap);
-    TextCodecWin::registerExtendedCodecs(addToTextCodecMap);
-#endif
 
     pruneBlacklistedCodecs();
     buildQuirksSets();
 }
 
-PassOwnPtr<TextCodec> newTextCodec(const TextEncoding& encoding)
+std::unique_ptr<TextCodec> newTextCodec(const TextEncoding& encoding)
 {
-    MutexLocker lock(encodingRegistryMutex());
+    std::lock_guard<StaticLock> lock(encodingRegistryMutex);
 
     ASSERT(textCodecMap);
-    TextCodecFactory factory = textCodecMap->get(encoding.name());
-    ASSERT(factory.function);
-    return factory.function(encoding, factory.additionalData);
+    auto result = textCodecMap->find(encoding.name());
+    ASSERT(result != textCodecMap->end());
+    return result->value();
 }
 
 const char* atomicCanonicalTextEncodingName(const char* name)
 {
     if (!name || !name[0])
-        return 0;
-    if (!textEncodingNameMap)
-        buildBaseTextCodecMaps();
+        return nullptr;
 
-    MutexLocker lock(encodingRegistryMutex());
+    std::lock_guard<StaticLock> lock(encodingRegistryMutex);
+
+    if (!textEncodingNameMap)
+        buildBaseTextCodecMaps(lock);
 
     if (const char* atomicName = textEncodingNameMap->get(name))
         return atomicName;
     if (didExtendTextCodecMaps)
-        return 0;
+        return nullptr;
+
     extendTextCodecMaps();
     didExtendTextCodecMaps = true;
     return textEncodingNameMap->get(name);
 }
 
-template <typename CharacterType>
-const char* atomicCanonicalTextEncodingName(const CharacterType* characters, size_t length)
+template<typename CharacterType> static const char* atomicCanonicalTextEncodingName(const CharacterType* characters, size_t length)
 {
     char buffer[maxEncodingNameLength + 1];
     size_t j = 0;
     for (size_t i = 0; i < length; ++i) {
-        CharacterType c = characters[i];
         if (j == maxEncodingNameLength)
-            return 0;
-        buffer[j++] = c;
+            return nullptr;
+        buffer[j++] = characters[i];
     }
     buffer[j] = 0;
     return atomicCanonicalTextEncodingName(buffer);
@@ -343,13 +288,13 @@ const char* atomicCanonicalTextEncodingName(const CharacterType* characters, siz
 
 const char* atomicCanonicalTextEncodingName(const String& alias)
 {
-    if (!alias.length())
-        return 0;
+    if (alias.isEmpty() || !alias.isAllASCII())
+        return nullptr;
 
     if (alias.is8Bit())
-        return atomicCanonicalTextEncodingName<LChar>(alias.characters8(), alias.length());
+        return atomicCanonicalTextEncodingName(alias.characters8(), alias.length());
 
-    return atomicCanonicalTextEncodingName<UChar>(alias.characters(), alias.length());
+    return atomicCanonicalTextEncodingName(alias.characters16(), alias.length());
 }
 
 bool noExtendedTextEncodingNameUsed()
@@ -358,19 +303,28 @@ bool noExtendedTextEncodingNameUsed()
     return !didExtendTextCodecMaps;
 }
 
-#ifndef NDEBUG
-void dumpTextEncodingNameMap()
+String defaultTextEncodingNameForSystemLanguage()
 {
-    unsigned size = textEncodingNameMap->size();
-    fprintf(stderr, "Dumping %u entries in WebCore::textEncodingNameMap...\n", size);
-
-    MutexLocker lock(encodingRegistryMutex());
-
-    TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin();
-    TextEncodingNameMap::const_iterator end = textEncodingNameMap->end();
-    for (; it != end; ++it)
-        fprintf(stderr, "'%s' => '%s'\n", it->key, it->value);
-}
+#if PLATFORM(COCOA)
+    String systemEncodingName = CFStringConvertEncodingToIANACharSetName(webDefaultCFStringEncoding());
+
+    // CFStringConvertEncodingToIANACharSetName() returns cp949 for kTextEncodingDOSKorean AKA "extended EUC-KR" AKA windows-949.
+    // ICU uses this name for a different encoding, so we need to change the name to a value that actually gives us windows-949.
+    // In addition, this value must match what is used in Safari, see <rdar://problem/5579292>.
+    // On some OS versions, the result is CP949 (uppercase).
+    if (equalLettersIgnoringASCIICase(systemEncodingName, "cp949"))
+        systemEncodingName = ASCIILiteral("ks_c_5601-1987");
+
+    // CFStringConvertEncodingToIANACharSetName() returns cp874 for kTextEncodingDOSThai, AKA windows-874.
+    // Since "cp874" alias is not standard (https://encoding.spec.whatwg.org/#names-and-labels), map to
+    // "dos-874" instead.
+    if (equalLettersIgnoringASCIICase(systemEncodingName, "cp874"))
+        systemEncodingName = ASCIILiteral("dos-874");
+
+    return systemEncodingName;
+#else
+    return ASCIILiteral("ISO-8859-1");
 #endif
+}
 
 } // namespace WebCore