// Copyright 2025 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <windows.h> #include <string> #include <string_view> #include "base/strings/string_util.h" #include "base/strings/string_util_win.h" #include "content/browser/sandbox_support_impl.h" namespace content { namespace { LCID CallLocaleNameToLCID(std::u16string_view locale) { CHECK(locale.size() < LOCALE_NAME_MAX_LENGTH); return ::LocaleNameToLCID(base::as_wcstr(locale), 0); } std::u16string GetLocaleInfoString(LCID lcid, LCTYPE type, bool force_defaults) { if (force_defaults) { type = type | LOCALE_NOUSEROVERRIDE; } int size_with_nul = ::GetLocaleInfo(lcid, type, 0, 0); if (size_with_nul <= 0) { return std::u16string(); } std::u16string buffer; // basic_string guarantees that `buffer` can be indexed from // [0, size], with the requirement that buffer[size] is only set // to NUL. buffer.resize(size_with_nul - 1); ::GetLocaleInfo(lcid, type, base::as_writable_wcstr(buffer.data()), size_with_nul); return buffer; } std::vector<std::u16string> GetLocaleInfoStrings(LCID lcid, base::span<const LCTYPE> types, bool force_defaults, bool allow_empty) { std::vector<std::u16string> strings; for (const auto& type : types) { auto str = GetLocaleInfoString(lcid, type, force_defaults); if (str.empty() && !allow_empty) { return {}; } strings.push_back(std::move(str)); } return strings; } std::optional<DWORD> GetLocaleInfoDWORD(LCID lcid, LCTYPE type, bool force_defaults) { DWORD result = 0; if (force_defaults) { type = type | LOCALE_NOUSEROVERRIDE; } if (::GetLocaleInfo(lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR)) > 0) { return result; } return std::nullopt; } std::u16string_view LanguageCode(const std::u16string_view locale) { size_t dash_pos = locale.find('-'); if (dash_pos == std::u16string::npos) { return locale; } return locale.substr(0, dash_pos); } LCID LCIDFromLocale(const std::u16string& locale, bool force_defaults) { auto default_language_code = GetLocaleInfoString( LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, force_defaults); auto locale_language_code = LanguageCode(locale); if (base::EqualsCaseInsensitiveASCII(locale_language_code, default_language_code)) { return LOCALE_USER_DEFAULT; } return CallLocaleNameToLCID(locale); } } // namespace SandboxSupportImpl::SandboxSupportImpl() = default; SandboxSupportImpl::~SandboxSupportImpl() = default; void SandboxSupportImpl::BindReceiver( mojo::PendingReceiver<mojom::SandboxSupport> receiver) { receivers_.Add(this, std::move(receiver)); } void SandboxSupportImpl::LcidAndFirstDayOfWeek( const std::u16string& locale, const std::u16string& default_language, bool force_defaults, LcidAndFirstDayOfWeekCallback callback) { if (locale.size() > LOCALE_NAME_MAX_LENGTH || default_language.size() > LOCALE_NAME_MAX_LENGTH) { receivers_.ReportBadMessage("Locale larger than LOCALE_NAME_MAX_LENGTH."); return; } LCID lcid = LCIDFromLocale(locale, force_defaults); if (!lcid) { lcid = LCIDFromLocale(default_language, force_defaults); } auto first_day = GetLocaleInfoDWORD(lcid, LOCALE_IFIRSTDAYOFWEEK, force_defaults); int first_day_of_week = ((first_day.has_value() ? *first_day : 0) + 1) % 7; std::move(callback).Run(lcid, first_day_of_week); } // https://learn.microsoft.com/en-us/windows/win32/intl/locale-idigitsubstitution constexpr inline DWORD kDigitSubstitution0to9 = 1; // https://learn.microsoft.com/en-us/windows/win32/intl/locale-ineg-constants constexpr inline DWORD kNegNumberSignPrefix = 1; void SandboxSupportImpl::DigitsAndSigns(uint32_t lcid, bool force_defaults, DigitsAndSignsCallback callback) { DWORD digit_substitution = GetLocaleInfoDWORD(lcid, LOCALE_IDIGITSUBSTITUTION, force_defaults) .value_or(kDigitSubstitution0to9); std::u16string digits; if (digit_substitution != kDigitSubstitution0to9) { digits = GetLocaleInfoString(lcid, LOCALE_SNATIVEDIGITS, force_defaults); } auto decimal = GetLocaleInfoString(lcid, LOCALE_SDECIMAL, force_defaults); auto thousand = GetLocaleInfoString(lcid, LOCALE_STHOUSAND, force_defaults); auto negative_sign = GetLocaleInfoString(lcid, LOCALE_SNEGATIVESIGN, force_defaults); DWORD negnumber = GetLocaleInfoDWORD(lcid, LOCALE_INEGNUMBER, force_defaults) .value_or(kNegNumberSignPrefix); std::move(callback).Run(digit_substitution, digits, decimal, thousand, negative_sign, negnumber); } void SandboxSupportImpl::LocaleStrings(uint32_t lcid, bool force_defaults, LcTypeStrings collection, LocaleStringsCallback callback) { std::vector<std::u16string> result; switch (collection) { case LcTypeStrings::kMonths: { static constexpr LCTYPE kTypes[12] = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12, }; result = GetLocaleInfoStrings(lcid, kTypes, force_defaults, false); break; } case LcTypeStrings::kShortMonths: { static constexpr LCTYPE kTypes[12] = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3, LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6, LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9, LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12, }; result = GetLocaleInfoStrings(lcid, kTypes, force_defaults, false); break; } case LcTypeStrings::kShortWeekDays: { static constexpr LCTYPE kTypes[7] = { // Numbered 1 (Monday) - 7 (Sunday), so do 7, then 1-6 LOCALE_SSHORTESTDAYNAME7, LOCALE_SSHORTESTDAYNAME1, LOCALE_SSHORTESTDAYNAME2, LOCALE_SSHORTESTDAYNAME3, LOCALE_SSHORTESTDAYNAME4, LOCALE_SSHORTESTDAYNAME5, LOCALE_SSHORTESTDAYNAME6}; result = GetLocaleInfoStrings(lcid, kTypes, force_defaults, false); break; } case LcTypeStrings::kAmPm: { static constexpr LCTYPE kTypes[2] = { LOCALE_S1159, LOCALE_S2359, }; result = GetLocaleInfoStrings(lcid, kTypes, force_defaults, true); break; } } std::move(callback).Run(result); } void SandboxSupportImpl::LocaleString(uint32_t lcid, bool force_defaults, LcTypeString type, LocaleStringCallback callback) { LCTYPE lctype; switch (type) { case LcTypeString::kShortDate: lctype = LOCALE_SSHORTDATE; break; case LcTypeString::kYearMonth: lctype = LOCALE_SYEARMONTH; break; case LcTypeString::kTimeFormat: lctype = LOCALE_STIMEFORMAT; break; case LcTypeString::kShortTime: lctype = LOCALE_SSHORTTIME; break; } auto result = GetLocaleInfoString(lcid, lctype, force_defaults); std::move(callback).Run(result); } } // namespace content