| /* | 
 |  * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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 PROFITS; OR BUSINESS | 
 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | 
 |  * THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "platform/Language.h" | 
 |  | 
 | #include "platform/text/PlatformLocale.h" | 
 | #include "platform/wtf/text/WTFString.h" | 
 | #include "public/platform/Platform.h" | 
 |  | 
 | namespace blink { | 
 |  | 
 | namespace { | 
 |  | 
 | static String CanonicalizeLanguageIdentifier(const String& language_code) { | 
 |   String copied_code = language_code; | 
 |   // Platform::defaultLocale() might provide a language code with '_'. | 
 |   copied_code.Replace('_', '-'); | 
 |   return copied_code; | 
 | } | 
 |  | 
 | // Main thread static AtomicString. This can be safely shared across threads. | 
 | const AtomicString* g_platform_language = nullptr; | 
 |  | 
 | const AtomicString& PlatformLanguage() { | 
 |   DCHECK(g_platform_language->Impl()->IsStatic()) | 
 |       << "global language string is used from multiple threads, and must be " | 
 |          "static"; | 
 |   return *g_platform_language; | 
 | } | 
 |  | 
 | Vector<AtomicString>& PreferredLanguagesOverride() { | 
 |   DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Vector<AtomicString>>, | 
 |                                   thread_specific_languages, ()); | 
 |   return *thread_specific_languages; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | void InitializePlatformLanguage() { | 
 |   DCHECK(IsMainThread()); | 
 |   DEFINE_STATIC_LOCAL( | 
 |       // We add the platform language as a static string for two reasons: | 
 |       // 1. it can be shared across threads. | 
 |       // 2. since this is done very early on, we don't want to accidentally | 
 |       //    collide with a hard coded static string (like "fr" on SVG). | 
 |       const AtomicString, platform_language, (([]() { | 
 |         String canonicalized = CanonicalizeLanguageIdentifier( | 
 |             Platform::Current()->DefaultLocale()); | 
 |         if (!canonicalized.IsEmpty()) { | 
 |           StringImpl* impl = StringImpl::CreateStatic( | 
 |               reinterpret_cast<const char*>(canonicalized.Characters8()), | 
 |               canonicalized.length(), | 
 |               StringHasher::ComputeHashAndMaskTop8Bits( | 
 |                   canonicalized.Characters8(), canonicalized.length())); | 
 |  | 
 |           return AtomicString(impl); | 
 |         } | 
 |         return AtomicString(); | 
 |       })())); | 
 |  | 
 |   g_platform_language = &platform_language; | 
 | } | 
 |  | 
 | void OverrideUserPreferredLanguagesForTesting( | 
 |     const Vector<AtomicString>& override) { | 
 |   Vector<AtomicString>& canonicalized = PreferredLanguagesOverride(); | 
 |   canonicalized.resize(0); | 
 |   canonicalized.ReserveCapacity(override.size()); | 
 |   for (const auto& lang : override) | 
 |     canonicalized.push_back(CanonicalizeLanguageIdentifier(lang)); | 
 |   Locale::ResetDefaultLocale(); | 
 | } | 
 |  | 
 | AtomicString DefaultLanguage() { | 
 |   Vector<AtomicString>& override = PreferredLanguagesOverride(); | 
 |   if (!override.IsEmpty()) | 
 |     return override[0]; | 
 |   return PlatformLanguage(); | 
 | } | 
 |  | 
 | Vector<AtomicString> UserPreferredLanguages() { | 
 |   Vector<AtomicString>& override = PreferredLanguagesOverride(); | 
 |   if (!override.IsEmpty()) | 
 |     return override; | 
 |  | 
 |   Vector<AtomicString> languages; | 
 |   languages.ReserveInitialCapacity(1); | 
 |   languages.push_back(PlatformLanguage()); | 
 |   return languages; | 
 | } | 
 |  | 
 | size_t IndexOfBestMatchingLanguageInList( | 
 |     const AtomicString& language, | 
 |     const Vector<AtomicString>& language_list) { | 
 |   AtomicString language_without_locale_match; | 
 |   AtomicString language_match_but_not_locale; | 
 |   size_t language_without_locale_match_index = 0; | 
 |   size_t language_match_but_not_locale_match_index = 0; | 
 |   bool can_match_language_only = | 
 |       (language.length() == 2 || | 
 |        (language.length() >= 3 && language[2] == '-')); | 
 |  | 
 |   for (size_t i = 0; i < language_list.size(); ++i) { | 
 |     String canonicalized_language_from_list = | 
 |         CanonicalizeLanguageIdentifier(language_list[i]); | 
 |  | 
 |     if (language == canonicalized_language_from_list) | 
 |       return i; | 
 |  | 
 |     if (can_match_language_only && | 
 |         canonicalized_language_from_list.length() >= 2) { | 
 |       if (language[0] == canonicalized_language_from_list[0] && | 
 |           language[1] == canonicalized_language_from_list[1]) { | 
 |         if (!language_without_locale_match.length() && | 
 |             canonicalized_language_from_list.length() == 2) { | 
 |           language_without_locale_match = language_list[i]; | 
 |           language_without_locale_match_index = i; | 
 |         } | 
 |         if (!language_match_but_not_locale.length() && | 
 |             canonicalized_language_from_list.length() >= 3) { | 
 |           language_match_but_not_locale = language_list[i]; | 
 |           language_match_but_not_locale_match_index = i; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // 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 (language_without_locale_match.length()) | 
 |     return language_without_locale_match_index; | 
 |  | 
 |   if (language_match_but_not_locale.length()) | 
 |     return language_match_but_not_locale_match_index; | 
 |  | 
 |   return language_list.size(); | 
 | } | 
 |  | 
 | }  // namespace blink |