Update ANGLE
[WebKit-https.git] / Source / WebCore / platform / Language.cpp
index 6cf2597..ec4b349 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "config.h"
 #include "Language.h"
 
-#include "PlatformString.h"
 #include <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/PlatformUserPreferredLanguages.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/text/WTFString.h>
+
+#if USE(CF) && !PLATFORM(WIN)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
 
 namespace WebCore {
 
+static StaticLock userPreferredLanguagesMutex;
+
+static void registerLanguageDidChangeCallbackIfNecessary()
+{
+    static std::once_flag once;
+    std::call_once(
+        once,
+        [] {
+            setPlatformUserPreferredLanguagesChangedCallback(languageDidChange);
+        });
+}
+
 typedef HashMap<void*, LanguageChangeObserverFunction> ObserverMap;
 static ObserverMap& observerMap()
 {
-    DEFINE_STATIC_LOCAL(ObserverMap, map, ());
-    return map;
+    static NeverDestroyed<ObserverMap> map;
+    return map.get();
 }
 
 void addLanguageChangeObserver(void* context, LanguageChangeObserverFunction customObserver)
 {
+    registerLanguageDidChangeCallbackIfNecessary();
     observerMap().set(context, customObserver);
 }
 
@@ -53,27 +73,119 @@ void languageDidChange()
 {
     ObserverMap::iterator end = observerMap().end();
     for (ObserverMap::iterator iter = observerMap().begin(); iter != end; ++iter)
-        iter->second(iter->first);
+        iter->value(iter->key);
 }
 
-static String& languageOverride()
+String defaultLanguage()
 {
-    DEFINE_STATIC_LOCAL(String, override, ());
+    Vector<String> languages = userPreferredLanguages();
+    if (languages.size())
+        return languages[0];
+
+    return emptyString();
+}
+
+static Vector<String>& preferredLanguagesOverride()
+{
+    static NeverDestroyed<Vector<String>> override;
     return override;
 }
 
-String defaultLanguage()
+Vector<String> userPreferredLanguagesOverride()
+{
+    return preferredLanguagesOverride();
+}
+
+void overrideUserPreferredLanguages(const Vector<String>& override)
+{
+    preferredLanguagesOverride() = override;
+    languageDidChange();
+}
+
+static Vector<String> isolatedCopy(const Vector<String>& strings)
 {
-    const String& override = languageOverride();
-    if (!override.isNull())
-        return override;
+    Vector<String> copy;
+    copy.reserveInitialCapacity(strings.size());
+    for (auto& language : strings)
+        copy.uncheckedAppend(language.isolatedCopy());
+    return copy;
+}
+
+Vector<String> userPreferredLanguages()
+{
+    {
+        std::lock_guard<StaticLock> lock(userPreferredLanguagesMutex);
+        Vector<String>& override = preferredLanguagesOverride();
+        if (!override.isEmpty())
+            return isolatedCopy(override);
+    }
+    
+    registerLanguageDidChangeCallbackIfNecessary();
+    return platformUserPreferredLanguages();
+}
+
+static String canonicalLanguageIdentifier(const String& languageCode)
+{
+    String lowercaseLanguageCode = languageCode.convertToASCIILowercase();
+    
+    if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_')
+        lowercaseLanguageCode.replace(2, 1, "-");
+
+    return lowercaseLanguageCode;
+}
+
+size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList, bool& exactMatch)
+{
+    String lowercaseLanguage = language.convertToASCIILowercase();
+    String languageWithoutLocaleMatch;
+    String languageMatchButNotLocale;
+    size_t languageWithoutLocaleMatchIndex = 0;
+    size_t languageMatchButNotLocaleMatchIndex = 0;
+    bool canMatchLanguageOnly = (lowercaseLanguage.length() == 2 || (lowercaseLanguage.length() >= 3 && lowercaseLanguage[2] == '-'));
+
+    for (size_t i = 0; i < languageList.size(); ++i) {
+        String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]);
+
+        if (lowercaseLanguage == canonicalizedLanguageFromList) {
+            exactMatch = true;
+            return i;
+        }
+
+        if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) {
+            if (lowercaseLanguage[0] == canonicalizedLanguageFromList[0] && lowercaseLanguage[1] == canonicalizedLanguageFromList[1]) {
+                if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) {
+                    languageWithoutLocaleMatch = languageList[i];
+                    languageWithoutLocaleMatchIndex = i;
+                }
+                if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) {
+                    languageMatchButNotLocale = languageList[i];
+                    languageMatchButNotLocaleMatchIndex = i;
+                }
+            }
+        }
+    }
+
+    exactMatch = false;
+
+    // If we have both a language-only match and a languge-but-not-locale match, return the
+    // languge-only match as is considered a "better" match. For example, if the list
+    // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en".
+    if (languageWithoutLocaleMatch.length())
+        return languageWithoutLocaleMatchIndex;
+
+    if (languageMatchButNotLocale.length())
+        return languageMatchButNotLocaleMatchIndex;
 
-    return platformDefaultLanguage();
+    return languageList.size();
 }
 
-void overrideDefaultLanguage(const String& override)
+String displayNameForLanguageLocale(const String& localeName)
 {
-    languageOverride() = override;
+#if USE(CF) && !PLATFORM(WIN)
+    if (!localeName.isEmpty())
+        return adoptCF(CFLocaleCopyDisplayNameForPropertyValue(adoptCF(CFLocaleCopyCurrent()).get(), kCFLocaleIdentifier, localeName.createCFString().get())).get();
+#endif
+    return localeName;
 }
 
 }