Use NeverDestroyed instead of DEPRECATED_DEFINE_STATIC_LOCAL
[WebKit-https.git] / Source / WebCore / platform / Language.cpp
1 /*
2  * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "Language.h"
28
29 #include <wtf/HashMap.h>
30 #include <wtf/NeverDestroyed.h>
31 #include <wtf/RetainPtr.h>
32 #include <wtf/text/WTFString.h>
33
34 #if USE(CF) && !PLATFORM(WIN)
35 #include <CoreFoundation/CoreFoundation.h>
36 #endif
37
38 namespace WebCore {
39
40 typedef HashMap<void*, LanguageChangeObserverFunction> ObserverMap;
41 static ObserverMap& observerMap()
42 {
43     static NeverDestroyed<ObserverMap> map;
44     return map.get();
45 }
46
47 void addLanguageChangeObserver(void* context, LanguageChangeObserverFunction customObserver)
48 {
49     observerMap().set(context, customObserver);
50 }
51
52 void removeLanguageChangeObserver(void* context)
53 {
54     ASSERT(observerMap().contains(context));
55     observerMap().remove(context);
56 }
57
58 void languageDidChange()
59 {
60     ObserverMap::iterator end = observerMap().end();
61     for (ObserverMap::iterator iter = observerMap().begin(); iter != end; ++iter)
62         iter->value(iter->key);
63 }
64
65 String defaultLanguage()
66 {
67     Vector<String> languages = userPreferredLanguages();
68     if (languages.size())
69         return languages[0];
70
71     return emptyString();
72 }
73
74 static Vector<String>& preferredLanguagesOverride()
75 {
76     static NeverDestroyed<Vector<String>> override;
77     return override;
78 }
79
80 Vector<String> userPreferredLanguagesOverride()
81 {
82     return preferredLanguagesOverride();
83 }
84
85 void overrideUserPreferredLanguages(const Vector<String>& override)
86 {
87     preferredLanguagesOverride() = override;
88     languageDidChange();
89 }
90     
91 Vector<String> userPreferredLanguages()
92 {
93     Vector<String>& override = preferredLanguagesOverride();
94     if (!override.isEmpty())
95         return override;
96     
97     return platformUserPreferredLanguages();
98 }
99
100 static String canonicalLanguageIdentifier(const String& languageCode)
101 {
102     String lowercaseLanguageCode = languageCode.lower();
103     
104     if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_')
105         lowercaseLanguageCode.replace(2, 1, "-");
106
107     return lowercaseLanguageCode;
108 }
109
110 size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList, bool& exactMatch)
111 {
112     String lowercaseLanguage = language.lower();
113     String languageWithoutLocaleMatch;
114     String languageMatchButNotLocale;
115     size_t languageWithoutLocaleMatchIndex = 0;
116     size_t languageMatchButNotLocaleMatchIndex = 0;
117     bool canMatchLanguageOnly = (lowercaseLanguage.length() == 2 || (lowercaseLanguage.length() >= 3 && lowercaseLanguage[2] == '-'));
118
119     for (size_t i = 0; i < languageList.size(); ++i) {
120         String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]);
121
122         if (lowercaseLanguage == canonicalizedLanguageFromList) {
123             exactMatch = true;
124             return i;
125         }
126
127         if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) {
128             if (lowercaseLanguage[0] == canonicalizedLanguageFromList[0] && lowercaseLanguage[1] == canonicalizedLanguageFromList[1]) {
129                 if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) {
130                     languageWithoutLocaleMatch = languageList[i];
131                     languageWithoutLocaleMatchIndex = i;
132                 }
133                 if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) {
134                     languageMatchButNotLocale = languageList[i];
135                     languageMatchButNotLocaleMatchIndex = i;
136                 }
137             }
138         }
139     }
140
141     exactMatch = false;
142
143     // If we have both a language-only match and a languge-but-not-locale match, return the
144     // languge-only match as is considered a "better" match. For example, if the list
145     // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en".
146     if (languageWithoutLocaleMatch.length())
147         return languageWithoutLocaleMatchIndex;
148
149     if (languageMatchButNotLocale.length())
150         return languageMatchButNotLocaleMatchIndex;
151
152     return languageList.size();
153 }
154
155 String displayNameForLanguageLocale(const String& localeName)
156 {
157 #if USE(CF) && !PLATFORM(WIN)
158     if (!localeName.isEmpty())
159         return adoptCF(CFLocaleCopyDisplayNameForPropertyValue(adoptCF(CFLocaleCopyCurrent()).get(), kCFLocaleIdentifier, localeName.createCFString().get())).get();
160 #endif
161     return localeName;
162 }
163
164 }