WorkerNavigator is missing some attributes
[WebKit-https.git] / Source / WebCore / platform / Language.cpp
1 /*
2  * Copyright (C) 2010, 2013, 2016 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/PlatformUserPreferredLanguages.h>
32 #include <wtf/RetainPtr.h>
33 #include <wtf/text/WTFString.h>
34
35 #if USE(CF) && !PLATFORM(WIN)
36 #include <CoreFoundation/CoreFoundation.h>
37 #endif
38
39 namespace WebCore {
40
41 static StaticLock userPreferredLanguagesMutex;
42
43 static void registerLanguageDidChangeCallbackIfNecessary()
44 {
45     static std::once_flag once;
46     std::call_once(
47         once,
48         [] {
49             setPlatformUserPreferredLanguagesChangedCallback(languageDidChange);
50         });
51 }
52
53 typedef HashMap<void*, LanguageChangeObserverFunction> ObserverMap;
54 static ObserverMap& observerMap()
55 {
56     static NeverDestroyed<ObserverMap> map;
57     return map.get();
58 }
59
60 void addLanguageChangeObserver(void* context, LanguageChangeObserverFunction customObserver)
61 {
62     registerLanguageDidChangeCallbackIfNecessary();
63     observerMap().set(context, customObserver);
64 }
65
66 void removeLanguageChangeObserver(void* context)
67 {
68     ASSERT(observerMap().contains(context));
69     observerMap().remove(context);
70 }
71
72 void languageDidChange()
73 {
74     ObserverMap::iterator end = observerMap().end();
75     for (ObserverMap::iterator iter = observerMap().begin(); iter != end; ++iter)
76         iter->value(iter->key);
77 }
78
79 String defaultLanguage()
80 {
81     Vector<String> languages = userPreferredLanguages();
82     if (languages.size())
83         return languages[0];
84
85     return emptyString();
86 }
87
88 static Vector<String>& preferredLanguagesOverride()
89 {
90     static NeverDestroyed<Vector<String>> override;
91     return override;
92 }
93
94 Vector<String> userPreferredLanguagesOverride()
95 {
96     return preferredLanguagesOverride();
97 }
98
99 void overrideUserPreferredLanguages(const Vector<String>& override)
100 {
101     preferredLanguagesOverride() = override;
102     languageDidChange();
103 }
104
105 static Vector<String> isolatedCopy(const Vector<String>& strings)
106 {
107     Vector<String> copy;
108     copy.reserveInitialCapacity(strings.size());
109     for (auto& language : strings)
110         copy.uncheckedAppend(language.isolatedCopy());
111     return copy;
112 }
113
114 Vector<String> userPreferredLanguages()
115 {
116     {
117         std::lock_guard<StaticLock> lock(userPreferredLanguagesMutex);
118         Vector<String>& override = preferredLanguagesOverride();
119         if (!override.isEmpty())
120             return isolatedCopy(override);
121     }
122     
123     registerLanguageDidChangeCallbackIfNecessary();
124     return platformUserPreferredLanguages();
125 }
126
127 static String canonicalLanguageIdentifier(const String& languageCode)
128 {
129     String lowercaseLanguageCode = languageCode.convertToASCIILowercase();
130     
131     if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_')
132         lowercaseLanguageCode.replace(2, 1, "-");
133
134     return lowercaseLanguageCode;
135 }
136
137 size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList, bool& exactMatch)
138 {
139     String lowercaseLanguage = language.convertToASCIILowercase();
140     String languageWithoutLocaleMatch;
141     String languageMatchButNotLocale;
142     size_t languageWithoutLocaleMatchIndex = 0;
143     size_t languageMatchButNotLocaleMatchIndex = 0;
144     bool canMatchLanguageOnly = (lowercaseLanguage.length() == 2 || (lowercaseLanguage.length() >= 3 && lowercaseLanguage[2] == '-'));
145
146     for (size_t i = 0; i < languageList.size(); ++i) {
147         String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]);
148
149         if (lowercaseLanguage == canonicalizedLanguageFromList) {
150             exactMatch = true;
151             return i;
152         }
153
154         if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) {
155             if (lowercaseLanguage[0] == canonicalizedLanguageFromList[0] && lowercaseLanguage[1] == canonicalizedLanguageFromList[1]) {
156                 if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) {
157                     languageWithoutLocaleMatch = languageList[i];
158                     languageWithoutLocaleMatchIndex = i;
159                 }
160                 if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) {
161                     languageMatchButNotLocale = languageList[i];
162                     languageMatchButNotLocaleMatchIndex = i;
163                 }
164             }
165         }
166     }
167
168     exactMatch = false;
169
170     // If we have both a language-only match and a languge-but-not-locale match, return the
171     // languge-only match as is considered a "better" match. For example, if the list
172     // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en".
173     if (languageWithoutLocaleMatch.length())
174         return languageWithoutLocaleMatchIndex;
175
176     if (languageMatchButNotLocale.length())
177         return languageMatchButNotLocaleMatchIndex;
178
179     return languageList.size();
180 }
181
182 String displayNameForLanguageLocale(const String& localeName)
183 {
184 #if USE(CF) && !PLATFORM(WIN)
185     if (!localeName.isEmpty())
186         return adoptCF(CFLocaleCopyDisplayNameForPropertyValue(adoptCF(CFLocaleCopyCurrent()).get(), kCFLocaleIdentifier, localeName.createCFString().get())).get();
187 #endif
188     return localeName;
189 }
190
191 }