diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/text/layout_locale.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/text/layout_locale.cc | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/text/layout_locale.cc b/chromium/third_party/blink/renderer/platform/text/layout_locale.cc new file mode 100644 index 00000000000..34dd35f6e94 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/text/layout_locale.cc @@ -0,0 +1,276 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/text/layout_locale.h" + +#include "base/compiler_specific.h" +#include "third_party/blink/renderer/platform/language.h" +#include "third_party/blink/renderer/platform/text/hyphenation.h" +#include "third_party/blink/renderer/platform/text/icu_error.h" +#include "third_party/blink/renderer/platform/text/locale_to_script_mapping.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/thread_specific.h" + +#include <hb.h> +#include <unicode/locid.h> + +namespace blink { + +namespace { + +struct PerThreadData { + HashMap<AtomicString, scoped_refptr<LayoutLocale>, CaseFoldingHash> + locale_map; + const LayoutLocale* default_locale = nullptr; + const LayoutLocale* system_locale = nullptr; + const LayoutLocale* default_locale_for_han = nullptr; + bool default_locale_for_han_computed = false; + String current_accept_languages; +}; + +PerThreadData& GetPerThreadData() { + DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<PerThreadData>, data, ()); + return *data; +} + +} // namespace + +static hb_language_t ToHarfbuzLanguage(const AtomicString& locale) { + CString locale_as_latin1 = locale.Latin1(); + return hb_language_from_string(locale_as_latin1.data(), + locale_as_latin1.length()); +} + +// SkFontMgr requires script-based locale names, like "zh-Hant" and "zh-Hans", +// instead of "zh-CN" and "zh-TW". +static const char* ToSkFontMgrLocale(UScriptCode script) { + switch (script) { + case USCRIPT_KATAKANA_OR_HIRAGANA: + return "ja-JP"; + case USCRIPT_HANGUL: + return "ko-KR"; + case USCRIPT_SIMPLIFIED_HAN: + return "zh-Hans"; + case USCRIPT_TRADITIONAL_HAN: + return "zh-Hant"; + default: + return nullptr; + } +} + +const char* LayoutLocale::LocaleForSkFontMgr() const { + if (string_for_sk_font_mgr_.IsNull()) { + string_for_sk_font_mgr_ = ToSkFontMgrLocale(script_); + if (string_for_sk_font_mgr_.IsNull()) + string_for_sk_font_mgr_ = string_.Ascii(); + } + return string_for_sk_font_mgr_.data(); +} + +void LayoutLocale::ComputeScriptForHan() const { + if (IsUnambiguousHanScript(script_)) { + script_for_han_ = script_; + has_script_for_han_ = true; + return; + } + + script_for_han_ = ScriptCodeForHanFromSubtags(string_); + if (script_for_han_ == USCRIPT_COMMON) + script_for_han_ = USCRIPT_SIMPLIFIED_HAN; + else + has_script_for_han_ = true; + DCHECK(IsUnambiguousHanScript(script_for_han_)); +} + +UScriptCode LayoutLocale::GetScriptForHan() const { + if (script_for_han_ == USCRIPT_COMMON) + ComputeScriptForHan(); + return script_for_han_; +} + +bool LayoutLocale::HasScriptForHan() const { + if (script_for_han_ == USCRIPT_COMMON) + ComputeScriptForHan(); + return has_script_for_han_; +} + +// static +const LayoutLocale* LayoutLocale::LocaleForHan( + const LayoutLocale* content_locale) { + if (content_locale && content_locale->HasScriptForHan()) + return content_locale; + + PerThreadData& data = GetPerThreadData(); + if (UNLIKELY(!data.default_locale_for_han_computed)) { + // Use the first acceptLanguages that can disambiguate. + Vector<String> languages; + data.current_accept_languages.Split(',', languages); + for (String token : languages) { + token = token.StripWhiteSpace(); + const LayoutLocale* locale = LayoutLocale::Get(AtomicString(token)); + if (locale->HasScriptForHan()) { + data.default_locale_for_han = locale; + break; + } + } + if (!data.default_locale_for_han) { + const LayoutLocale& default_locale = GetDefault(); + if (default_locale.HasScriptForHan()) + data.default_locale_for_han = &default_locale; + } + if (!data.default_locale_for_han) { + const LayoutLocale& system_locale = GetSystem(); + if (system_locale.HasScriptForHan()) + data.default_locale_for_han = &system_locale; + } + data.default_locale_for_han_computed = true; + } + return data.default_locale_for_han; +} + +const char* LayoutLocale::LocaleForHanForSkFontMgr() const { + const char* locale = ToSkFontMgrLocale(GetScriptForHan()); + DCHECK(locale); + return locale; +} + +LayoutLocale::LayoutLocale(const AtomicString& locale) + : string_(locale), + harfbuzz_language_(ToHarfbuzLanguage(locale)), + script_(LocaleToScriptCodeForFontSelection(locale)), + script_for_han_(USCRIPT_COMMON), + has_script_for_han_(false), + hyphenation_computed_(false) {} + +// static +const LayoutLocale* LayoutLocale::Get(const AtomicString& locale) { + if (locale.IsNull()) + return nullptr; + + auto result = GetPerThreadData().locale_map.insert(locale, nullptr); + if (result.is_new_entry) + result.stored_value->value = base::AdoptRef(new LayoutLocale(locale)); + return result.stored_value->value.get(); +} + +// static +const LayoutLocale& LayoutLocale::GetDefault() { + PerThreadData& data = GetPerThreadData(); + if (UNLIKELY(!data.default_locale)) { + AtomicString language = DefaultLanguage(); + data.default_locale = + LayoutLocale::Get(!language.IsEmpty() ? language : "en"); + } + return *data.default_locale; +} + +// static +const LayoutLocale& LayoutLocale::GetSystem() { + PerThreadData& data = GetPerThreadData(); + if (UNLIKELY(!data.system_locale)) { + // Platforms such as Windows can give more information than the default + // locale, such as "en-JP" for English speakers in Japan. + String name = icu::Locale::getDefault().getName(); + data.system_locale = + LayoutLocale::Get(AtomicString(name.Replace('_', '-'))); + } + return *data.system_locale; +} + +scoped_refptr<LayoutLocale> LayoutLocale::CreateForTesting( + const AtomicString& locale) { + return base::AdoptRef(new LayoutLocale(locale)); +} + +Hyphenation* LayoutLocale::GetHyphenation() const { + if (hyphenation_computed_) + return hyphenation_.get(); + + hyphenation_computed_ = true; + hyphenation_ = Hyphenation::PlatformGetHyphenation(LocaleString()); + return hyphenation_.get(); +} + +void LayoutLocale::SetHyphenationForTesting( + const AtomicString& locale_string, + scoped_refptr<Hyphenation> hyphenation) { + const LayoutLocale& locale = ValueOrDefault(Get(locale_string)); + locale.hyphenation_computed_ = true; + locale.hyphenation_ = std::move(hyphenation); +} + +AtomicString LayoutLocale::LocaleWithBreakKeyword( + LineBreakIteratorMode mode) const { + if (string_.IsEmpty()) + return string_; + + // uloc_setKeywordValue_58 has a problem to handle "@" in the original + // string. crbug.com/697859 + if (string_.Contains('@')) + return string_; + + CString utf8_locale = string_.Utf8(); + Vector<char> buffer(utf8_locale.length() + 11, 0); + memcpy(buffer.data(), utf8_locale.data(), utf8_locale.length()); + + const char* keyword_value = nullptr; + switch (mode) { + default: + NOTREACHED(); + FALLTHROUGH; + case LineBreakIteratorMode::kDefault: + // nullptr will cause any existing values to be removed. + break; + case LineBreakIteratorMode::kNormal: + keyword_value = "normal"; + break; + case LineBreakIteratorMode::kStrict: + keyword_value = "strict"; + break; + case LineBreakIteratorMode::kLoose: + keyword_value = "loose"; + break; + } + + ICUError status; + int32_t length_needed = uloc_setKeywordValue( + "lb", keyword_value, buffer.data(), buffer.size(), &status); + if (U_SUCCESS(status)) + return AtomicString::FromUTF8(buffer.data(), length_needed); + + if (status == U_BUFFER_OVERFLOW_ERROR) { + buffer.Grow(length_needed + 1); + memset(buffer.data() + utf8_locale.length(), 0, + buffer.size() - utf8_locale.length()); + status = U_ZERO_ERROR; + int32_t length_needed2 = uloc_setKeywordValue( + "lb", keyword_value, buffer.data(), buffer.size(), &status); + DCHECK_EQ(length_needed, length_needed2); + if (U_SUCCESS(status) && length_needed == length_needed2) + return AtomicString::FromUTF8(buffer.data(), length_needed); + } + + NOTREACHED(); + return string_; +} + +// static +void LayoutLocale::AcceptLanguagesChanged(const String& accept_languages) { + PerThreadData& data = GetPerThreadData(); + if (data.current_accept_languages == accept_languages) + return; + + data.current_accept_languages = accept_languages; + data.default_locale_for_han = nullptr; + data.default_locale_for_han_computed = false; +} + +// static +void LayoutLocale::ClearForTesting() { + GetPerThreadData() = PerThreadData(); +} + +} // namespace blink |