0

Proxy GetLocaleInfo() & LocaleNameToLCID()

These functions in Windows may result in calls over the CSRSS ALPC
and allocations on CSRSS heaps, both of which are closed as part of
CSRSS lockdown, so they cannot be called from renderer processes if
this sandboxing mode is enabled.

This CL adds mojo support functions that proxy these calls, batching
together multiple calls from blink::LocaleWin where this makes sense.
The mojom calls are `[Sync]`. This is ok as they replace system calls
which themselves required a blocking IPC call, and because each call
is only needed to initialize cached members of Locale objects, which
are also cached by blink.

While initially supporting blink, these functions may support other
child processes than renderers so live in //content/browser.

As the calls to GetLocaleInfo() and LocaleNameToLCID() now happen
in the browser process, the mojom interface limits which properties
can be requested. Rather than allowing any LCTYPE the strings that
can be queried are restricted to those needed by blink.

The mojom implementation is guarded by WinSboxProxyLocale and calls
are only proxied if this feature is enabled.

Bug: 40408399
Change-Id: Ia34f8eff48d2e23e2c9a60825bbabd2ecd48caac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6108617
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org>
Commit-Queue: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1403046}
This commit is contained in:
Alex Gough
2025-01-07 09:12:57 -08:00
committed by Chromium LUCI CQ
parent 9341f7b5b7
commit a5a75196ef
20 changed files with 904 additions and 21 deletions

@ -2942,6 +2942,8 @@ source_set("browser") {
"renderer_host/legacy_render_widget_host_win.h",
"renderer_host/virtual_keyboard_controller_win.cc",
"renderer_host/virtual_keyboard_controller_win.h",
"sandbox_support_impl.h",
"sandbox_support_win_impl.cc",
"screenlock_monitor/screenlock_monitor_device_source_win.cc",
"speech/tts_win.cc",
"speech/tts_win_utils.cc",

@ -31,6 +31,8 @@
#if BUILDFLAG(IS_WIN)
#include "content/browser/renderer_host/dwrite_font_proxy_impl_win.h"
#include "content/browser/sandbox_support_impl.h"
#include "content/common/sandbox_support.mojom.h"
#include "content/public/common/font_cache_dispatcher_win.h"
#include "content/public/common/font_cache_win.mojom.h"
#endif
@ -90,7 +92,11 @@ void BrowserChildProcessHostImpl::BindHostReceiver(
FontCacheDispatcher::Create(std::move(r));
return;
}
if (auto r = receiver.As<mojom::SandboxSupport>()) {
static base::NoDestructor<SandboxSupportImpl> sandbox_support;
sandbox_support->BindReceiver(std::move(r));
return;
}
if (auto r = receiver.As<blink::mojom::DWriteFontProxy>()) {
base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_BLOCKING, base::MayBlock()})

@ -0,0 +1,205 @@
// 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 "base/functional/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "content/browser/sandbox_support_impl.h"
#include "content/common/sandbox_support.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/functions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
using LcTypeString = mojom::SandboxSupport::LcTypeString;
using LcTypeStrings = mojom::SandboxSupport::LcTypeStrings;
namespace {
inline constexpr uint32_t kSunday = 0;
// LCIDs - See https://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
inline constexpr uint32_t kEnglishUS = 0x409; // en-us
inline constexpr uint32_t kSpanishMexico = 0x080A; // es-MX
inline constexpr uint32_t kKoreanKorea = 0x0412; // ko-KR
// Test that the mojo interface calls the expected Windows APIs.
class SandboxSupportWinUnitTest : public testing::Test {
public:
SandboxSupportWinUnitTest() {
features_.InitFromCommandLine("WinSboxProxyLocale", "");
impl_.BindReceiver(sandbox_support_.BindNewPipeAndPassReceiver());
}
void SetUp() override {
mojo::SetDefaultProcessErrorHandler(base::BindRepeating(
&SandboxSupportWinUnitTest::OnProcessError, base::Unretained(this)));
}
content::mojom::SandboxSupport& sandbox_support() {
return *sandbox_support_;
}
bool had_error() { return had_error_; }
void OnProcessError(const std::string& error) { had_error_ = true; }
bool had_error_ = false;
base::test::ScopedFeatureList features_;
base::test::TaskEnvironment task_environment_;
mojo::Remote<content::mojom::SandboxSupport> sandbox_support_;
SandboxSupportImpl impl_;
};
} // namespace
TEST_F(SandboxSupportWinUnitTest, BasicEnUs) {
uint32_t lcid;
uint32_t first_day;
EXPECT_TRUE(sandbox_support().LcidAndFirstDayOfWeek(u"en-us", u"en-us", true,
&lcid, &first_day));
EXPECT_EQ(first_day, kSunday);
// This depends on the system configuration.
EXPECT_TRUE(lcid == LOCALE_USER_DEFAULT || lcid == kEnglishUS);
uint32_t digit_sub;
std::u16string digits;
std::u16string decimal;
std::u16string thousand;
std::u16string negative_sign;
uint32_t negnumber;
EXPECT_TRUE(sandbox_support().DigitsAndSigns(lcid, true, &digit_sub, &digits,
&decimal, &thousand,
&negative_sign, &negnumber));
EXPECT_EQ(digit_sub, 1u);
EXPECT_TRUE(digits.empty());
EXPECT_EQ(decimal, u".");
EXPECT_EQ(thousand, u",");
EXPECT_EQ(negative_sign, u"-");
EXPECT_EQ(negnumber, 1u);
std::u16string tmp_string;
EXPECT_TRUE(sandbox_support().LocaleString(
lcid, true, LcTypeString::kYearMonth, &tmp_string));
EXPECT_EQ(tmp_string, u"MMMM yyyy");
std::vector<std::u16string> tmp_strings;
EXPECT_TRUE(sandbox_support().LocaleStrings(lcid, true, LcTypeStrings::kAmPm,
&tmp_strings));
EXPECT_EQ(tmp_strings.size(), 2u);
EXPECT_EQ(tmp_strings.at(1), u"PM");
}
TEST_F(SandboxSupportWinUnitTest, Locales) {
uint32_t lcid;
uint32_t first_day;
EXPECT_TRUE(sandbox_support().LcidAndFirstDayOfWeek(u"es-MX", u"es-MX", true,
&lcid, &first_day));
EXPECT_EQ(lcid, kSpanishMexico);
EXPECT_EQ(first_day, kSunday);
EXPECT_TRUE(sandbox_support().LcidAndFirstDayOfWeek(u"ko-KR", u"ko-KR", true,
&lcid, &first_day));
EXPECT_EQ(lcid, kKoreanKorea);
EXPECT_EQ(first_day, kSunday);
}
TEST_F(SandboxSupportWinUnitTest, NonDefault) {
// Chrome actually calls these with `force_defaults=false`. We cannot test
// return values as the test machine may have custom locales or settings.
uint32_t lcid;
uint32_t first_day;
EXPECT_TRUE(sandbox_support().LcidAndFirstDayOfWeek(u"en-us", u"en-us", false,
&lcid, &first_day));
EXPECT_TRUE(lcid == LOCALE_USER_DEFAULT || lcid == kEnglishUS);
uint32_t digit_sub;
std::u16string digits;
std::u16string decimal;
std::u16string thousand;
std::u16string negative_sign;
uint32_t negnumber;
EXPECT_TRUE(sandbox_support().DigitsAndSigns(lcid, true, &digit_sub, &digits,
&decimal, &thousand,
&negative_sign, &negnumber));
std::u16string tmp_string;
EXPECT_TRUE(sandbox_support().LocaleString(
lcid, true, LcTypeString::kYearMonth, &tmp_string));
EXPECT_EQ(tmp_string, u"MMMM yyyy");
std::vector<std::u16string> tmp_strings;
EXPECT_TRUE(sandbox_support().LocaleStrings(lcid, true, LcTypeStrings::kAmPm,
&tmp_strings));
}
TEST_F(SandboxSupportWinUnitTest, MultiStrings) {
// Collection => expected value of 2nd element. (kAmPm is shortest with two.)
std::vector<std::pair<LcTypeStrings, std::u16string>> expected = {
{LcTypeStrings::kMonths, u"February"},
{LcTypeStrings::kShortMonths, u"Feb"},
{LcTypeStrings::kShortWeekDays, u"Mo"},
{LcTypeStrings::kAmPm, u"PM"}};
uint32_t lcid;
uint32_t first_day;
EXPECT_TRUE(sandbox_support().LcidAndFirstDayOfWeek(u"en-us", u"en-us", false,
&lcid, &first_day));
EXPECT_TRUE(lcid == LOCALE_USER_DEFAULT || lcid == kEnglishUS);
for (const auto& val : expected) {
std::vector<std::u16string> tmp_strings;
EXPECT_TRUE(
sandbox_support().LocaleStrings(lcid, true, val.first, &tmp_strings));
EXPECT_EQ(val.second, tmp_strings.at(1));
}
}
TEST_F(SandboxSupportWinUnitTest, Strings) {
// Collection => expected value of item.
std::vector<std::pair<LcTypeString, std::u16string>> expected = {
{LcTypeString::kShortDate, u"M/d/yyyy"},
{LcTypeString::kYearMonth, u"MMMM yyyy"},
{LcTypeString::kTimeFormat, u"h:mm:ss tt"},
{LcTypeString::kShortTime, u"h:mm tt"}};
uint32_t lcid;
uint32_t first_day;
EXPECT_TRUE(sandbox_support().LcidAndFirstDayOfWeek(u"en-us", u"en-us", false,
&lcid, &first_day));
EXPECT_TRUE(lcid == LOCALE_USER_DEFAULT || lcid == kEnglishUS);
for (const auto& val : expected) {
std::u16string tmp_string;
EXPECT_TRUE(
sandbox_support().LocaleString(lcid, true, val.first, &tmp_string));
EXPECT_EQ(val.second, tmp_string);
}
}
TEST_F(SandboxSupportWinUnitTest, LimitsLanguage) {
uint32_t lcid;
uint32_t first_day;
// https://learn.microsoft.com/en-us/windows/win32/intl/locale-name-constants
// LOCALE_NAME_MAX_LENGTH at time of writing was 85.
static_assert(LOCALE_NAME_MAX_LENGTH <= 85);
EXPECT_FALSE(sandbox_support().LcidAndFirstDayOfWeek(
u"0123456789012345678901234567890123456789"
u"0123456789012345678901234567890123456789tooooooooolong-"
u"toolongtoolong",
u"en-us", false, &lcid, &first_day));
EXPECT_TRUE(had_error());
}
TEST_F(SandboxSupportWinUnitTest, LimitsDefaultLanguage) {
uint32_t lcid;
uint32_t first_day;
// https://learn.microsoft.com/en-us/windows/win32/intl/locale-name-constants
// LOCALE_NAME_MAX_LENGTH at time of writing was 85.
static_assert(LOCALE_NAME_MAX_LENGTH <= 85);
EXPECT_FALSE(sandbox_support().LcidAndFirstDayOfWeek(
u"en-us",
u"0123456789012345678901234567890123456789"
u"0123456789012345678901234567890123456789tooooooooolong-"
u"toolongtoolong",
false, &lcid, &first_day));
EXPECT_TRUE(had_error());
}
} // namespace content

@ -66,6 +66,8 @@
#if BUILDFLAG(IS_WIN)
#include "components/services/font_data/font_data_service_impl.h"
#include "content/browser/renderer_host/dwrite_font_proxy_impl_win.h"
#include "content/browser/sandbox_support_impl.h"
#include "content/common/sandbox_support.mojom.h"
#include "content/public/common/font_cache_dispatcher_win.h"
#include "content/public/common/font_cache_win.mojom.h"
#endif
@ -379,7 +381,7 @@ void RenderProcessHostImpl::IOThreadHostImpl::BindHostReceiver(
}
#endif
#if BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
if (auto r = receiver.As<mojom::SandboxSupport>()) {
static base::NoDestructor<SandboxSupportImpl> sandbox_support;
sandbox_support->BindReceiver(std::move(r));

@ -5,6 +5,8 @@
#ifndef CONTENT_BROWSER_SANDBOX_SUPPORT_IMPL_H_
#define CONTENT_BROWSER_SANDBOX_SUPPORT_IMPL_H_
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "content/common/sandbox_support.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
@ -16,7 +18,7 @@ namespace content {
// renderer. However all child process types have access to this interface.
// This class lives on the IO thread and is owned by the Mojo interface
// registry.
class SandboxSupportImpl : public mojom::SandboxSupport {
class CONTENT_EXPORT SandboxSupportImpl : public mojom::SandboxSupport {
public:
SandboxSupportImpl();
@ -28,7 +30,27 @@ class SandboxSupportImpl : public mojom::SandboxSupport {
void BindReceiver(mojo::PendingReceiver<mojom::SandboxSupport> receiver);
// content::mojom::SandboxSupport:
#if BUILDFLAG(IS_MAC)
void GetSystemColors(GetSystemColorsCallback callback) override;
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN)
void LcidAndFirstDayOfWeek(const std::u16string& locale,
const std::u16string& default_language,
bool force_defaults,
LcidAndFirstDayOfWeekCallback callback) override;
void DigitsAndSigns(uint32_t lcid,
bool force_defaults,
DigitsAndSignsCallback callback) override;
void LocaleStrings(uint32_t lcid,
bool force_defaults,
LcTypeStrings collection,
LocaleStringsCallback callback) override;
void LocaleString(uint32_t lcid,
bool force_defaults,
LcTypeString type,
LocaleStringCallback callback) override;
#endif // BUILDFLAG(IS_WIN)
private:
mojo::ReceiverSet<mojom::SandboxSupport> receivers_;

@ -0,0 +1,219 @@
// 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

@ -136,6 +136,8 @@ target(link_target_type, "child") {
if (is_win) {
sources += [
"child_process_sandbox_support_impl_win.cc",
"child_process_sandbox_support_impl_win.h",
"dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc",
"dwrite_font_proxy/dwrite_font_proxy_init_impl_win.h",
"dwrite_font_proxy/dwrite_font_proxy_win.cc",

@ -0,0 +1,135 @@
// 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 "content/child/child_process_sandbox_support_impl_win.h"
#include <string>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/notreached.h"
#include "content/public/child/child_thread.h"
namespace content {
WebSandboxSupportWin::WebSandboxSupportWin() {
// Not available in some browser tests.
auto* child_thread = ChildThread::Get();
if (child_thread) {
child_thread->BindHostReceiver(
sandbox_support_.BindNewPipeAndPassReceiver());
}
}
WebSandboxSupportWin::~WebSandboxSupportWin() = default;
bool WebSandboxSupportWin::IsLocaleProxyEnabled() {
return base::FeatureList::IsEnabled(content::mojom::kWinSboxProxyLocale);
}
std::pair<LCID, unsigned> WebSandboxSupportWin::LcidAndFirstDayOfWeek(
blink::WebString locale,
blink::WebString default_language,
bool force_defaults) {
uint32_t lcid;
uint32_t first_day_of_week;
CHECK(sandbox_support_->LcidAndFirstDayOfWeek(
locale.Utf16(), default_language.Utf16(), force_defaults, &lcid,
&first_day_of_week));
return {lcid, first_day_of_week};
}
std::unique_ptr<blink::WebSandboxSupport::LocaleInitData>
WebSandboxSupportWin::DigitsAndSigns(LCID lcid, bool force_defaults) {
auto init_data = std::make_unique<blink::WebSandboxSupport::LocaleInitData>();
uint32_t digit_substitution = 0;
std::u16string digits;
std::u16string decimal;
std::u16string thousand;
std::u16string negative_sign;
uint32_t negnumber = 0;
CHECK(sandbox_support_->DigitsAndSigns(
lcid, force_defaults, &digit_substitution, &digits, &decimal, &thousand,
&negative_sign, &negnumber));
init_data->digit_substitution = digit_substitution;
init_data->digits = blink::WebString(digits);
init_data->decimal = blink::WebString(decimal);
init_data->thousand = blink::WebString(thousand);
init_data->negative_sign = blink::WebString(negative_sign);
init_data->negnumber = negnumber;
return init_data;
}
blink::WebVector<blink::WebString> WebSandboxSupportWin::LocaleStrings(
LCID lcid,
bool force_defaults,
mojom::SandboxSupport::LcTypeStrings collection) {
std::vector<std::u16string> results;
CHECK(sandbox_support_->LocaleStrings(lcid, force_defaults, collection,
&results));
blink::WebVector<blink::WebString> ret;
ret.reserve(results.size());
for (const auto& str : results) {
ret.push_back(blink::WebString(str));
}
return ret;
}
blink::WebVector<blink::WebString> WebSandboxSupportWin::MonthLabels(
LCID lcid,
bool force_defaults) {
return LocaleStrings(lcid, force_defaults,
mojom::SandboxSupport::LcTypeStrings::kMonths);
}
blink::WebVector<blink::WebString> WebSandboxSupportWin::WeekDayShortLabels(
LCID lcid,
bool force_defaults) {
return LocaleStrings(lcid, force_defaults,
mojom::SandboxSupport::LcTypeStrings::kShortWeekDays);
}
blink::WebVector<blink::WebString> WebSandboxSupportWin::ShortMonthLabels(
LCID lcid,
bool force_defaults) {
return LocaleStrings(lcid, force_defaults,
mojom::SandboxSupport::LcTypeStrings::kShortMonths);
}
blink::WebVector<blink::WebString> WebSandboxSupportWin::AmPmLabels(
LCID lcid,
bool force_defaults) {
return LocaleStrings(lcid, force_defaults,
mojom::SandboxSupport::LcTypeStrings::kAmPm);
}
blink::WebString WebSandboxSupportWin::LocaleString(LCID lcid,
LCTYPE type,
bool force_defaults) {
mojom::SandboxSupport::LcTypeString wanted;
switch (type) {
case LOCALE_SSHORTDATE:
wanted = mojom::SandboxSupport::LcTypeString::kShortDate;
break;
case LOCALE_SYEARMONTH:
wanted = mojom::SandboxSupport::LcTypeString::kYearMonth;
break;
case LOCALE_STIMEFORMAT:
wanted = mojom::SandboxSupport::LcTypeString::kTimeFormat;
break;
case LOCALE_SSHORTTIME:
wanted = mojom::SandboxSupport::LcTypeString::kShortTime;
break;
default:
NOTREACHED();
}
std::u16string str;
CHECK(sandbox_support_->LocaleString(lcid, force_defaults, wanted, &str));
return blink::WebString(std::move(str));
}
} // namespace content

@ -0,0 +1,61 @@
// 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.
#ifndef CONTENT_CHILD_CHILD_PROCESS_SANDBOX_SUPPORT_IMPL_WIN_H_
#define CONTENT_CHILD_CHILD_PROCESS_SANDBOX_SUPPORT_IMPL_WIN_H_
#include "content/common/sandbox_support.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/win/web_sandbox_support.h"
namespace content {
// Implementation of the interface used by Blink to upcall to the privileged
// process (browser) for handling requests for data that are not allowed within
// the sandbox.
class WebSandboxSupportWin : public blink::WebSandboxSupport {
public:
WebSandboxSupportWin();
WebSandboxSupportWin(const WebSandboxSupportWin&) = delete;
WebSandboxSupportWin& operator=(const WebSandboxSupportWin&) = delete;
~WebSandboxSupportWin() override;
bool IsLocaleProxyEnabled() override;
std::pair<LCID, unsigned> LcidAndFirstDayOfWeek(
blink::WebString locale,
blink::WebString default_language,
bool force_defaults) override;
std::unique_ptr<blink::WebSandboxSupport::LocaleInitData> DigitsAndSigns(
LCID lcid,
bool force_defaults) override;
blink::WebVector<blink::WebString> MonthLabels(LCID lcid,
bool force_defaults) override;
blink::WebVector<blink::WebString> WeekDayShortLabels(
LCID lcid,
bool force_defaults) override;
blink::WebVector<blink::WebString> ShortMonthLabels(
LCID lcid,
bool force_defaults) override;
blink::WebVector<blink::WebString> AmPmLabels(LCID lcid,
bool force_defaults) override;
blink::WebString LocaleString(LCID lcid,
LCTYPE type,
bool force_defaults) override;
private:
blink::WebVector<blink::WebString> LocaleStrings(
LCID lcid,
bool force_defaults,
mojom::SandboxSupport::LcTypeStrings collection);
mojo::Remote<mojom::SandboxSupport> sandbox_support_;
};
} // namespace content
#endif // CONTENT_CHILD_CHILD_PROCESS_SANDBOX_SUPPORT_IMPL_WIN_H_

@ -507,6 +507,10 @@ mojom("mojo_bindings") {
sources += [ "gin_java_bridge.mojom" ]
}
if (is_win) {
sources += [ "sandbox_support.mojom" ]
}
enabled_features = []
if (enable_ipc_logging) {
enabled_features += [ "ipc_logging" ]

@ -4,12 +4,82 @@
module content.mojom;
[EnableIf=is_mac]
import "mojo/public/mojom/base/shared_memory.mojom";
[EnableIf=is_win]
import "mojo/public/mojom/base/string16.mojom";
[EnableIf=is_win]
feature kWinSboxProxyLocale {
const string name = "WinSboxProxyLocale";
const bool enabled_state = false;
};
// Interface for a sandboxed child process to request services of
// the browser.
interface SandboxSupport {
// Mac:
// Returns the shared memory region containing system theme color
// information.
[EnableIf=is_mac]
GetSystemColors() => (mojo_base.mojom.ReadOnlySharedMemoryRegion region);
// Windows: Various GetLocale*() proxies. These replace wrapped syscalls
// so have to be [Sync].
// Returns the LCID and first day of week corresponding to `locale` with a
// fallback to `default_language`. If `force_defaults` is true then user
// customization is ignored. Both string arguments have a max length of
// LOCALE_NAME_MAX_LENGTH.
[Sync, EnableIf=is_win, RuntimeFeature=kWinSboxProxyLocale]
LcidAndFirstDayOfWeek(mojo_base.mojom.String16 locale,
mojo_base.mojom.String16 default_language,
bool defaults)
=> (uint32 lcid, uint32 first_day_of_week);
// Returns digit substitutions and negative sign prefix and suffix for `lcid`.
// Ignores user customization if `defaults` is true. Backs
// InitializeLocaleData(). `digits` are not fetched if `digit_substitution` is
// 0to9.
[Sync, EnableIf=is_win, RuntimeFeature=kWinSboxProxyLocale]
DigitsAndSigns(uint32 lcid, bool defaults)
=> (uint32 digit_substitution,
mojo_base.mojom.String16 digits,
mojo_base.mojom.String16 decimal,
mojo_base.mojom.String16 thousand,
mojo_base.mojom.String16 negative_sign,
uint32 negnumber);
// Single strings that can be directly requested.
[EnableIf=is_win]
enum LcTypeString {
kShortDate,
kYearMonth,
kTimeFormat,
kShortTime,
};
// Returns a single string, selected by `type`.
[Sync, EnableIf=is_win, RuntimeFeature=kWinSboxProxyLocale]
LocaleString(uint32 lcid, bool defaults, LcTypeString type)
=> (mojo_base.mojom.String16 str);
// Collections of locale strings.
[EnableIf=is_win]
enum LcTypeStrings {
// MONTHNAME*
kMonths,
// ABBREVMONTHNAME*
kShortMonths,
// SHORTESTDAYNAME* - in blink order Sunday - Monday.
kShortWeekDays,
// 1159,2359
kAmPm,
};
// Returns a collection of strings, selected by `collection`.
[Sync, EnableIf=is_win, RuntimeFeature=kWinSboxProxyLocale]
LocaleStrings(uint32 lcid, bool defaults, LcTypeStrings collection)
=> (array<mojo_base.mojom.String16> strings);
};

@ -122,6 +122,7 @@
#include "url/origin.h"
#if BUILDFLAG(IS_WIN)
#include "content/child/child_process_sandbox_support_impl_win.h"
#include "content/renderer/font_data/font_data_manager.h"
#include "skia/ext/font_utils.h"
#include "third_party/blink/public/web/win/web_font_rendering.h"
@ -224,10 +225,13 @@ RendererBlinkPlatformImpl::RendererBlinkPlatformImpl(
#endif
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
BUILDFLAG(IS_WIN)
if (sandboxEnabled()) {
#if BUILDFLAG(IS_MAC)
sandbox_support_ = std::make_unique<WebSandboxSupportMac>();
#elif BUILDFLAG(IS_WIN)
sandbox_support_ = std::make_unique<WebSandboxSupportWin>();
#else
sandbox_support_ = std::make_unique<WebSandboxSupportLinux>(font_loader);
#endif
@ -285,7 +289,8 @@ void RendererBlinkPlatformImpl::SetThreadType(base::PlatformThreadId thread_id,
#endif
blink::WebSandboxSupport* RendererBlinkPlatformImpl::GetSandboxSupport() {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
BUILDFLAG(IS_WIN)
return sandbox_support_.get();
#else
// These platforms do not require sandbox support.

@ -256,7 +256,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl {
void Collect3DContextInformation(blink::Platform::GraphicsInfo* gl_info,
const gpu::GPUInfo& gpu_info) const;
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
BUILDFLAG(IS_WIN)
std::unique_ptr<blink::WebSandboxSupport> sandbox_support_;
#endif

@ -3299,6 +3299,7 @@ test("content_unittests") {
}
if (is_win) {
sources += [
"../browser/child_process_sandbox_support_win_unittest.cc",
"../browser/device_posture/device_posture_registry_watcher_win_unittest.cc",
"../browser/renderer_host/direct_manipulation_test_helper_win.cc",
"../browser/renderer_host/direct_manipulation_test_helper_win.h",

@ -11,6 +11,8 @@
#include "content/child/child_process_sandbox_support_impl_mac.h"
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "content/child/child_process_sandbox_support_impl_linux.h"
#elif BUILDFLAG(IS_WIN)
#include "content/child/child_process_sandbox_support_impl_win.h"
#endif
namespace content {
@ -27,6 +29,8 @@ UtilityBlinkPlatformWithSandboxSupportImpl::
sandbox_support_ = std::make_unique<WebSandboxSupportLinux>(font_loader);
#elif BUILDFLAG(IS_MAC)
sandbox_support_ = std::make_unique<WebSandboxSupportMac>();
#elif BUILDFLAG(IS_WIN)
sandbox_support_ = std::make_unique<WebSandboxSupportWin>();
#endif
}
@ -35,7 +39,8 @@ UtilityBlinkPlatformWithSandboxSupportImpl::
blink::WebSandboxSupport*
UtilityBlinkPlatformWithSandboxSupportImpl::GetSandboxSupport() {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
BUILDFLAG(IS_WIN)
return sandbox_support_.get();
#else
return nullptr;

@ -33,7 +33,8 @@ class UtilityBlinkPlatformWithSandboxSupportImpl : public blink::Platform {
blink::WebSandboxSupport* GetSandboxSupport() override;
private:
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
BUILDFLAG(IS_WIN)
std::unique_ptr<blink::WebSandboxSupport> sandbox_support_;
#endif
};

@ -411,6 +411,7 @@ source_set("blink_headers") {
sources += [
"platform/web_font_prewarmer.h",
"platform/web_font_rendering_client.h",
"platform/win/web_sandbox_support.h",
"web/win/web_font_family_names.h",
"web/win/web_font_rendering.h",
]

@ -0,0 +1,3 @@
include_rules = [
"+base/win/windows_types.h",
]

@ -0,0 +1,73 @@
// 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.
#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WIN_WEB_SANDBOX_SUPPORT_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WIN_WEB_SANDBOX_SUPPORT_H_
#include <memory>
#include <utility>
#include "base/win/windows_types.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
// Repeat some Windows typedefs used by the locale functions that
// are not in base/win/windows_types.h
using LCID = DWORD;
using LCTYPE = DWORD;
namespace blink {
// Put methods here that are required due to sandbox restrictions.
class WebSandboxSupport {
public:
// Bundles the result of several calls to locale functions.
struct LocaleInitData {
// LOCALE_IDIGITSUBSTITUTION.
DWORD digit_substitution;
// LOCALE_SNATIVEDIGITS. Empty if digit_substitution == 1 (0to9).
WebString digits;
// LOCALE_SDECIMAL.
WebString decimal;
// LOCALE_STHOUSAND.
WebString thousand;
// LOCALE_SNEGATIVESIGN.
WebString negative_sign;
// LOCALE_INEGNUMBER.
DWORD negnumber;
};
virtual ~WebSandboxSupport() {}
// Can locale-related calls be proxied?
virtual bool IsLocaleProxyEnabled() = 0;
// Returns locale information. Fails unless IsLocaleProxyEnabled().
virtual std::pair<LCID, unsigned> LcidAndFirstDayOfWeek(
WebString locale,
WebString default_language,
bool force_defaults) = 0;
// Returns digit symbols and sign prefixes and suffixes. Fails unless
// IsLocaleProxyEnabled().
virtual std::unique_ptr<LocaleInitData> DigitsAndSigns(
LCID lcid,
bool force_defaults) = 0;
virtual WebVector<WebString> MonthLabels(LCID lcid, bool force_defaults) = 0;
virtual WebVector<WebString> WeekDayShortLabels(LCID lcid,
bool force_defaults) = 0;
virtual WebVector<WebString> ShortMonthLabels(LCID lcid,
bool force_defaults) = 0;
virtual WebVector<WebString> AmPmLabels(LCID lcid, bool force_defaults) = 0;
// Note: only specified allowed types can be provided for `type`.
virtual WebString LocaleString(LCID lcid,
LCTYPE type,
bool force_defaults) = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WIN_WEB_SANDBOX_SUPPORT_H_

@ -39,6 +39,8 @@
#include "base/memory/ptr_util.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/win/web_sandbox_support.h"
#include "third_party/blink/renderer/platform/language.h"
#include "third_party/blink/renderer/platform/text/date_components.h"
#include "third_party/blink/renderer/platform/text/date_time_format.h"
@ -151,11 +153,22 @@ LCID LCIDFromLocale(const String& locale, bool defaults_for_locale) {
std::pair<LCID, unsigned> GetLcidAndFirstDayOfWeek(const String& locale,
bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return proxy->LcidAndFirstDayOfWeek(locale, DefaultLanguage(),
defaults_for_locale);
}
LCID lcid = LCIDFromLocale(locale, defaults_for_locale);
return {lcid, GetFirstDayOfWeek(lcid, defaults_for_locale)};
}
Vector<String> GetMonthLabels(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return Vector<String>(proxy->MonthLabels(lcid, defaults_for_locale));
}
static constexpr LCTYPE kTypes[12] = {
LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
@ -166,6 +179,11 @@ Vector<String> GetMonthLabels(LCID lcid, bool defaults_for_locale) {
}
Vector<String> GetWeekDayShortLabels(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return Vector<String>(proxy->WeekDayShortLabels(lcid, defaults_for_locale));
}
static constexpr LCTYPE kTypes[7] = {
// Numbered 1 (Monday) - 7 (Sunday), so do 7, then 1-6
LOCALE_SSHORTESTDAYNAME7, LOCALE_SSHORTESTDAYNAME1,
@ -176,6 +194,11 @@ Vector<String> GetWeekDayShortLabels(LCID lcid, bool defaults_for_locale) {
}
Vector<String> GetShortMonthLabels(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return Vector<String>(proxy->ShortMonthLabels(lcid, defaults_for_locale));
}
static constexpr LCTYPE kTypes[12] = {
LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2,
LOCALE_SABBREVMONTHNAME3, LOCALE_SABBREVMONTHNAME4,
@ -188,6 +211,11 @@ Vector<String> GetShortMonthLabels(LCID lcid, bool defaults_for_locale) {
}
Vector<String> GetTimeAMPMLabels(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return Vector<String>(proxy->AmPmLabels(lcid, defaults_for_locale));
}
static constexpr LCTYPE kTypes[2] = {
LOCALE_S1159,
LOCALE_S2359,
@ -196,18 +224,34 @@ Vector<String> GetTimeAMPMLabels(LCID lcid, bool defaults_for_locale) {
}
String GetShortDate(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return proxy->LocaleString(lcid, LOCALE_SSHORTDATE, defaults_for_locale);
}
return GetLocaleInfoString(lcid, LOCALE_SSHORTDATE, defaults_for_locale);
}
String GetYearMonth(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return proxy->LocaleString(lcid, LOCALE_SYEARMONTH, defaults_for_locale);
}
return GetLocaleInfoString(lcid, LOCALE_SYEARMONTH, defaults_for_locale);
}
String GetTimeFormat(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return proxy->LocaleString(lcid, LOCALE_STIMEFORMAT, defaults_for_locale);
}
return GetLocaleInfoString(lcid, LOCALE_STIMEFORMAT, defaults_for_locale);
}
String GetShortTime(LCID lcid, bool defaults_for_locale) {
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
return proxy->LocaleString(lcid, LOCALE_SSHORTTIME, defaults_for_locale);
}
return GetLocaleInfoString(lcid, LOCALE_SSHORTTIME, defaults_for_locale);
}
@ -499,35 +543,56 @@ void LocaleWin::InitializeLocaleData() {
kNegativeFormatSpaceSignSuffix = 4,
};
DWORD digit_substitution = CallGetLocaleInfoDWORD(
lcid_, LOCALE_IDIGITSUBSTITUTION, kDigitSubstitution0to9);
DWORD digit_substitution;
String digits;
String decimal;
String thousand;
String negative_sign;
DWORD negnumber;
WebSandboxSupport* proxy = Platform::Current()->GetSandboxSupport();
if (proxy && proxy->IsLocaleProxyEnabled()) {
auto init_data = proxy->DigitsAndSigns(lcid_, defaults_for_locale_);
digit_substitution = init_data->digit_substitution;
digits = std::move(init_data->digits);
decimal = std::move(init_data->decimal);
thousand = std::move(init_data->thousand);
negative_sign = std::move(init_data->negative_sign);
negnumber = init_data->negnumber;
} else {
digit_substitution = CallGetLocaleInfoDWORD(
lcid_, LOCALE_IDIGITSUBSTITUTION, kDigitSubstitution0to9);
if (digit_substitution != kDigitSubstitution0to9) {
digits = GetLocaleInfoString(lcid_, LOCALE_SNATIVEDIGITS,
defaults_for_locale_);
}
decimal = GetLocaleInfoString(lcid_, LOCALE_SDECIMAL, defaults_for_locale_);
thousand =
GetLocaleInfoString(lcid_, LOCALE_STHOUSAND, defaults_for_locale_);
negative_sign =
GetLocaleInfoString(lcid_, LOCALE_SNEGATIVESIGN, defaults_for_locale_);
negnumber = CallGetLocaleInfoDWORD(lcid_, LOCALE_INEGNUMBER,
kNegativeFormatSignPrefix);
}
// No Locale calls after this point.
Vector<String, kDecimalSymbolsSize> symbols;
if (digit_substitution == kDigitSubstitution0to9) {
symbols = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
} else {
String digits =
GetLocaleInfoString(lcid_, LOCALE_SNATIVEDIGITS, defaults_for_locale_);
DCHECK_GE(digits.length(), 10u);
for (wtf_size_t i = 0; i < 10; ++i) {
symbols.push_back(digits.Substring(i, 1));
}
}
DCHECK_EQ(symbols.size(), kDecimalSeparatorIndex);
symbols.push_back(
GetLocaleInfoString(lcid_, LOCALE_SDECIMAL, defaults_for_locale_));
symbols.push_back(decimal);
DCHECK_EQ(symbols.size(), kGroupSeparatorIndex);
symbols.push_back(
GetLocaleInfoString(lcid_, LOCALE_STHOUSAND, defaults_for_locale_));
symbols.push_back(thousand);
DCHECK_EQ(symbols.size(), kDecimalSymbolsSize);
String negative_sign =
GetLocaleInfoString(lcid_, LOCALE_SNEGATIVESIGN, defaults_for_locale_);
DWORD negative_format = CallGetLocaleInfoDWORD(lcid_, LOCALE_INEGNUMBER,
kNegativeFormatSignPrefix);
String negative_prefix = g_empty_string;
String negative_suffix = g_empty_string;
switch (negative_format) {
switch (negnumber) {
case kNegativeFormatParenthesis:
negative_prefix = "(";
negative_suffix = ")";