Extract WideToUTF16String/UTF16StringToWide to FML (#39006)
* Extract WideToUTF16String/UTF16StringToWide to FML
In third_party/accessibility, for string conversion, we use a mix of:
* FML
* third_party/accessibility base string utility functions
* static functions local to the translation unit itself
This moves all conversions between UTF16 and wide strings to FML. Note
that this implementation is only safe on platforms where:
* the size of wchar_t and char16_t are the same
* the encoding of wchar_t and char16_t are both UTF-16
which is the case for Windows, hence why these functions are implemented
in a Windows-specific translation unit (wstring_conversion).
Issue: https://github.com/flutter/flutter/issues/118811
* Migrate UTF16ToWide as well
diff --git a/fml/platform/win/wstring_conversion.cc b/fml/platform/win/wstring_conversion.cc
index 6e4e36e..ef87f60 100644
--- a/fml/platform/win/wstring_conversion.cc
+++ b/fml/platform/win/wstring_conversion.cc
@@ -23,4 +23,16 @@
return converter.from_bytes(str.data());
}
+std::u16string WideStringToUtf16(const std::wstring_view str) {
+ static_assert(sizeof(std::wstring::value_type) ==
+ sizeof(std::u16string::value_type));
+ return {begin(str), end(str)};
+}
+
+std::wstring Utf16ToWideString(const std::u16string_view str) {
+ static_assert(sizeof(std::wstring::value_type) ==
+ sizeof(std::u16string::value_type));
+ return {begin(str), end(str)};
+}
+
} // namespace fml
diff --git a/fml/platform/win/wstring_conversion.h b/fml/platform/win/wstring_conversion.h
index 4e7c110..cc5dbb6 100644
--- a/fml/platform/win/wstring_conversion.h
+++ b/fml/platform/win/wstring_conversion.h
@@ -16,6 +16,12 @@
// string.
std::wstring Utf8ToWideString(const std::string_view str);
+// Returns a UTF-16 encoded equivalent of a UTF-16 encoded wide string.
+std::u16string WideStringToUtf16(const std::wstring_view str);
+
+// Returns a UTF-16 encoded wide string equivalent of a UTF-16 string.
+std::wstring Utf16ToWideString(const std::u16string_view str);
+
} // namespace fml
#endif // FLUTTER_FML_PLATFORM_WIN_WSTRING_CONVERSION_H_
diff --git a/fml/platform/win/wstring_conversion_unittests.cc b/fml/platform/win/wstring_conversion_unittests.cc
index 81af259..1f72672 100644
--- a/fml/platform/win/wstring_conversion_unittests.cc
+++ b/fml/platform/win/wstring_conversion_unittests.cc
@@ -9,7 +9,7 @@
namespace fml {
namespace testing {
-TEST(StringConversion, Utf16ToWideStringEmpty) {
+TEST(StringConversion, Utf8ToWideStringEmpty) {
EXPECT_EQ(Utf8ToWideString(""), L"");
}
@@ -33,5 +33,29 @@
EXPECT_EQ(WideStringToUtf8(L"\x2603"), "\xe2\x98\x83");
}
+TEST(StringConversion, WideStringToUtf16Empty) {
+ EXPECT_EQ(WideStringToUtf16(L""), u"");
+}
+
+TEST(StringConversion, WideStringToUtf16Ascii) {
+ EXPECT_EQ(WideStringToUtf16(L"abc123"), u"abc123");
+}
+
+TEST(StringConversion, WideStringToUtf16Unicode) {
+ EXPECT_EQ(WideStringToUtf16(L"\xe2\x98\x83"), u"\xe2\x98\x83");
+}
+
+TEST(StringConversion, Utf16ToWideStringEmpty) {
+ EXPECT_EQ(Utf16ToWideString(u""), L"");
+}
+
+TEST(StringConversion, Utf16ToWideStringAscii) {
+ EXPECT_EQ(Utf16ToWideString(u"abc123"), L"abc123");
+}
+
+TEST(StringConversion, Utf16ToWideStringUtf8Unicode) {
+ EXPECT_EQ(Utf16ToWideString(u"\xe2\x98\x83"), L"\xe2\x98\x83");
+}
+
} // namespace testing
} // namespace fml
diff --git a/third_party/accessibility/ax/BUILD.gn b/third_party/accessibility/ax/BUILD.gn
index d03e3cd..fc07993 100644
--- a/third_party/accessibility/ax/BUILD.gn
+++ b/third_party/accessibility/ax/BUILD.gn
@@ -106,6 +106,7 @@
"oleacc.lib",
"uiautomationcore.lib",
]
+ deps = [ "//flutter/fml:string_conversion" ]
}
public_deps = [
diff --git a/third_party/accessibility/ax/ax_node_position_unittest.cc b/third_party/accessibility/ax/ax_node_position_unittest.cc
index 18677f3..b5b993d 100644
--- a/third_party/accessibility/ax/ax_node_position_unittest.cc
+++ b/third_party/accessibility/ax/ax_node_position_unittest.cc
@@ -19,6 +19,7 @@
#include "ax/ax_tree_id.h"
#include "ax/ax_tree_update.h"
#include "ax/test_ax_tree_manager.h"
+#include "flutter/fml/platform/win/wstring_conversion.h"
#include "gtest/gtest.h"
namespace ui {
@@ -28,10 +29,6 @@
namespace {
-std::u16string WideToUTF16(const std::wstring wide) {
- return std::u16string(wide.begin(), wide.end());
-}
-
constexpr AXNode::AXID ROOT_ID = 1;
constexpr AXNode::AXID BUTTON_ID = 2;
constexpr AXNode::AXID CHECK_BOX_ID = 3;
@@ -43,20 +40,20 @@
constexpr AXNode::AXID INLINE_BOX2_ID = 9;
// A group of basic and extended characters.
-constexpr const wchar_t* kGraphemeClusters[] = {
+constexpr const char16_t* kGraphemeClusters[] = {
// The English word "hey" consisting of four ASCII characters.
- L"h",
- L"e",
- L"y",
+ u"h",
+ u"e",
+ u"y",
// A Hindi word (which means "Hindi") consisting of two Devanagari
// grapheme clusters.
- L"\x0939\x093F",
- L"\x0928\x094D\x0926\x0940",
+ u"\x0939\x093F",
+ u"\x0928\x094D\x0926\x0940",
// A Thai word (which means "feel") consisting of three Thai grapheme
// clusters.
- L"\x0E23\x0E39\x0E49",
- L"\x0E2A\x0E36",
- L"\x0E01",
+ u"\x0E23\x0E39\x0E49",
+ u"\x0E2A\x0E36",
+ u"\x0E01",
};
class AXPositionTest : public testing::Test, public TestAXTreeManager {
@@ -419,7 +416,7 @@
std::u16string english_text;
for (int i = 0; i < 3; ++i) {
- std::u16string grapheme = WideToUTF16(kGraphemeClusters[i]);
+ std::u16string grapheme = kGraphemeClusters[i];
EXPECT_EQ(1u, grapheme.length())
<< "All English characters should be one UTF16 code unit in length.";
text_offsets->push_back(text_offsets->back() +
@@ -429,7 +426,7 @@
std::u16string hindi_text;
for (int i = 3; i < 5; ++i) {
- std::u16string grapheme = WideToUTF16(kGraphemeClusters[i]);
+ std::u16string grapheme = kGraphemeClusters[i];
EXPECT_LE(2u, grapheme.length()) << "All Hindi characters should be two "
"or more UTF16 code units in length.";
text_offsets->push_back(text_offsets->back() +
@@ -439,7 +436,7 @@
std::u16string thai_text;
for (int i = 5; i < 8; ++i) {
- std::u16string grapheme = WideToUTF16(kGraphemeClusters[i]);
+ std::u16string grapheme = kGraphemeClusters[i];
EXPECT_LT(0u, grapheme.length())
<< "One of the Thai characters should be one UTF16 code unit, "
"whilst others should be two or more.";
@@ -618,7 +615,7 @@
AXNodeData static_text_data_2;
static_text_data_2.id = 3;
static_text_data_2.role = ax::mojom::Role::kStaticText;
- static_text_data_2.SetName(WideToUTF16(L"\xfffc"));
+ static_text_data_2.SetName(u"\xfffc");
AXNodeData static_text_data_3;
static_text_data_3.id = 4;
@@ -876,7 +873,7 @@
TestPositionType text_position = AXNodePosition::CreateNullPosition();
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsNullPosition());
- ASSERT_EQ(WideToUTF16(L""), text_position->GetText());
+ ASSERT_EQ(u"", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromRoot) {
@@ -885,7 +882,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L"Line 1\nLine 2"), text_position->GetText());
+ ASSERT_EQ(u"Line 1\nLine 2", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromButton) {
@@ -894,7 +891,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L""), text_position->GetText());
+ ASSERT_EQ(u"", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromCheckbox) {
@@ -903,7 +900,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L""), text_position->GetText());
+ ASSERT_EQ(u"", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromTextField) {
@@ -912,7 +909,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L"Line 1\nLine 2"), text_position->GetText());
+ ASSERT_EQ(u"Line 1\nLine 2", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromStaticText) {
@@ -921,7 +918,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L"Line 1"), text_position->GetText());
+ ASSERT_EQ(u"Line 1", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromInlineTextBox) {
@@ -930,7 +927,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L"Line 1"), text_position->GetText());
+ ASSERT_EQ(u"Line 1", text_position->GetText());
}
TEST_F(AXPositionTest, GetTextFromLineBreak) {
@@ -939,7 +936,7 @@
ax::mojom::TextAffinity::kUpstream);
ASSERT_NE(nullptr, text_position);
ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_EQ(WideToUTF16(L"\n"), text_position->GetText());
+ ASSERT_EQ(u"\n", text_position->GetText());
}
TEST_F(AXPositionTest, GetMaxTextOffsetFromNullPosition) {
@@ -1106,7 +1103,7 @@
ASSERT_NE(nullptr, text_position);
EXPECT_TRUE(text_position->IsTextPosition());
EXPECT_EQ(38, text_position->MaxTextOffset());
- EXPECT_EQ(WideToUTF16(L"Placeholder from generated content3.14"),
+ EXPECT_EQ(u"Placeholder from generated content3.14",
text_position->GetText());
}
@@ -7712,10 +7709,10 @@
GetTreeID(), root_1.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
- expected_text = WideToUTF16(L"Hello ") + AXNodePosition::kEmbeddedCharacter +
- WideToUTF16(L" world3.14") +
- AXNodePosition::kEmbeddedCharacter + WideToUTF16(L"hey") +
- AXNodePosition::kEmbeddedCharacter;
+ expected_text =
+ std::u16string(u"Hello ") + AXNodePosition::kEmbeddedCharacter +
+ std::u16string(u" world3.14") + AXNodePosition::kEmbeddedCharacter +
+ std::u16string(u"hey") + AXNodePosition::kEmbeddedCharacter;
ASSERT_EQ(expected_text, position->GetText());
// MaxTextOffset() with an embedded object replacement character.
diff --git a/third_party/accessibility/ax/platform/ax_platform_node_textprovider_win_unittest.cc b/third_party/accessibility/ax/platform/ax_platform_node_textprovider_win_unittest.cc
index 2b3a846..b3d1c98 100644
--- a/third_party/accessibility/ax/platform/ax_platform_node_textprovider_win_unittest.cc
+++ b/third_party/accessibility/ax/platform/ax_platform_node_textprovider_win_unittest.cc
@@ -349,7 +349,7 @@
base::win::ScopedBstr text_content;
EXPECT_HRESULT_SUCCEEDED(
text_range_provider->GetText(-1, text_content.Receive()));
- EXPECT_EQ(base::WideToUTF16(text_content.Get()),
+ EXPECT_EQ(fml::WideStringToUtf16(text_content.Get()),
u"Dialog label.Dialog description." + kEmbeddedCharacterAsString +
u"ok.Some more detail " + u"about dialog.");
diff --git a/third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.cc b/third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.cc
index 2c7cca0..71f72d3 100644
--- a/third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.cc
@@ -13,6 +13,7 @@
#include "ax/platform/ax_platform_node_win.h"
#include "ax/platform/ax_platform_tree_manager.h"
#include "base/win/variant_vector.h"
+#include "flutter/fml/platform/win/wstring_conversion.h"
#define UIA_VALIDATE_TEXTRANGEPROVIDER_CALL() \
if (!GetOwner() || !GetOwner()->GetDelegate() || !start() || \
@@ -483,7 +484,7 @@
ScopedAXEmbeddedObjectBehaviorSetter ax_embedded_object_behavior(
AXEmbeddedObjectBehavior::kSuppressCharacter);
- std::u16string search_string = base::WideToUTF16(string);
+ std::u16string search_string = fml::WideStringToUtf16(string);
if (search_string.length() <= 0)
return E_INVALIDARG;
@@ -703,7 +704,7 @@
if (max_count < -1)
return E_INVALIDARG;
- std::wstring full_text = base::UTF16ToWide(GetString(max_count));
+ std::wstring full_text = fml::Utf16ToWideString(GetString(max_count));
if (!full_text.empty()) {
size_t length = full_text.length();
diff --git a/third_party/accessibility/ax/platform/ax_platform_node_win.cc b/third_party/accessibility/ax/platform/ax_platform_node_win.cc
index b00f5d2..fcc7b8b 100644
--- a/third_party/accessibility/ax/platform/ax_platform_node_win.cc
+++ b/third_party/accessibility/ax/platform/ax_platform_node_win.cc
@@ -38,6 +38,7 @@
#include "base/logging.h"
#include "base/win/atl_module.h"
#include "base/win/display.h"
+#include "flutter/fml/platform/win/wstring_conversion.h"
#include "gfx/geometry/rect_conversions.h"
// From ax.constants.mojom
@@ -963,7 +964,7 @@
return S_FALSE;
}
- *def_action = ::SysAllocString(base::UTF16ToWide(action_verb).c_str());
+ *def_action = ::SysAllocString(fml::Utf16ToWideString(action_verb).c_str());
BASE_DCHECK(def_action);
return S_OK;
}
@@ -1054,7 +1055,7 @@
if (name.empty() && !has_name)
return S_FALSE;
- *name_bstr = ::SysAllocString(base::UTF16ToWide(name).c_str());
+ *name_bstr = ::SysAllocString(fml::Utf16ToWideString(name).c_str());
return S_OK;
}
@@ -1749,7 +1750,7 @@
AXActionData data;
data.action = ax::mojom::Action::kSetValue;
- data.value = base::UTF16ToUTF8(base::WideToUTF16(value));
+ data.value = base::UTF16ToUTF8(fml::WideStringToUtf16(value));
if (GetDelegate()->AccessibilityPerformAction(data))
return S_OK;
return E_FAIL;
@@ -2132,20 +2133,20 @@
switch (property_id) {
case UIA_AriaPropertiesPropertyId:
result->vt = VT_BSTR;
- result->bstrVal =
- ::SysAllocString(base::UTF16ToWide(ComputeUIAProperties()).c_str());
+ result->bstrVal = ::SysAllocString(
+ fml::Utf16ToWideString(ComputeUIAProperties()).c_str());
break;
case UIA_AriaRolePropertyId:
result->vt = VT_BSTR;
result->bstrVal =
- ::SysAllocString(base::UTF16ToWide(UIAAriaRole()).c_str());
+ ::SysAllocString(fml::Utf16ToWideString(UIAAriaRole()).c_str());
break;
case UIA_AutomationIdPropertyId:
V_VT(result) = VT_BSTR;
V_BSTR(result) = ::SysAllocString(
- base::UTF16ToWide(GetDelegate()->GetAuthorUniqueId()).c_str());
+ fml::Utf16ToWideString(GetDelegate()->GetAuthorUniqueId()).c_str());
break;
case UIA_ClassNamePropertyId:
@@ -2314,8 +2315,8 @@
std::u16string localized_control_type = GetRoleDescription();
if (!localized_control_type.empty()) {
result->vt = VT_BSTR;
- result->bstrVal =
- ::SysAllocString(base::UTF16ToWide(localized_control_type).c_str());
+ result->bstrVal = ::SysAllocString(
+ fml::Utf16ToWideString(localized_control_type).c_str());
}
// If a role description has not been provided, leave as VT_EMPTY.
// UIA core handles Localized Control type for some built-in types and
@@ -2466,7 +2467,7 @@
if (!localized_landmark_type.empty()) {
result->vt = VT_BSTR;
result->bstrVal = ::SysAllocString(
- base::UTF16ToWide(localized_landmark_type).c_str());
+ fml::Utf16ToWideString(localized_landmark_type).c_str());
}
break;
}
@@ -2525,7 +2526,7 @@
// convention here and when we fire events via ::NotifyWinEvent().
result->vt = VT_BSTR;
result->bstrVal = ::SysAllocString(
- base::UTF16ToWide(base::NumberToString16(-GetUniqueId())).c_str());
+ fml::Utf16ToWideString(base::NumberToString16(-GetUniqueId())).c_str());
}
return S_OK;
@@ -5314,7 +5315,7 @@
value_text = base::NumberToString16(red * 100 / 255) + u"% red " +
base::NumberToString16(green * 100 / 255) + u"% green " +
base::NumberToString16(blue * 100 / 255) + u"% blue";
- BSTR value = ::SysAllocString(base::UTF16ToWide(value_text).c_str());
+ BSTR value = ::SysAllocString(fml::Utf16ToWideString(value_text).c_str());
BASE_DCHECK(value);
return value;
}
@@ -5325,7 +5326,7 @@
if (target->GetData().role == ax::mojom::Role::kRootWebArea ||
target->GetData().role == ax::mojom::Role::kWebArea) {
result = base::UTF8ToUTF16(target->GetDelegate()->GetTreeData().url);
- BSTR value = ::SysAllocString(base::UTF16ToWide(result).c_str());
+ BSTR value = ::SysAllocString(fml::Utf16ToWideString(result).c_str());
BASE_DCHECK(value);
return value;
}
@@ -5335,7 +5336,7 @@
//
if (target->GetData().role == ax::mojom::Role::kLink) {
result = target->GetString16Attribute(ax::mojom::StringAttribute::kUrl);
- BSTR value = ::SysAllocString(base::UTF16ToWide(result).c_str());
+ BSTR value = ::SysAllocString(fml::Utf16ToWideString(result).c_str());
BASE_DCHECK(value);
return value;
}
@@ -5350,7 +5351,7 @@
if (target->GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange,
&fval)) {
result = base::NumberToString16(fval);
- BSTR value = ::SysAllocString(base::UTF16ToWide(result).c_str());
+ BSTR value = ::SysAllocString(fml::Utf16ToWideString(result).c_str());
BASE_DCHECK(value);
return value;
}
@@ -5359,7 +5360,7 @@
if (result.empty() && target->IsRichTextField())
result = target->GetInnerText();
- BSTR value = ::SysAllocString(base::UTF16ToWide(result).c_str());
+ BSTR value = ::SysAllocString(fml::Utf16ToWideString(result).c_str());
BASE_DCHECK(value);
return value;
}
@@ -5372,7 +5373,7 @@
if (!GetString16Attribute(attribute, &str))
return S_FALSE;
- *value_bstr = ::SysAllocString(base::UTF16ToWide(str).c_str());
+ *value_bstr = ::SysAllocString(fml::Utf16ToWideString(str).c_str());
BASE_DCHECK(*value_bstr);
return S_OK;
@@ -5380,7 +5381,7 @@
HRESULT AXPlatformNodeWin::GetNameAsBstr(BSTR* value_bstr) const {
std::u16string str = GetNameAsString16();
- *value_bstr = ::SysAllocString(base::UTF16ToWide(str).c_str());
+ *value_bstr = ::SysAllocString(fml::Utf16ToWideString(str).c_str());
BASE_DCHECK(*value_bstr);
return S_OK;
}
@@ -5490,14 +5491,14 @@
const std::u16string string =
GetInheritedString16Attribute(ax::mojom::StringAttribute::kFontFamily);
- return ::SysAllocString(base::UTF16ToWide(string).c_str());
+ return ::SysAllocString(fml::Utf16ToWideString(string).c_str());
}
BSTR AXPlatformNodeWin::GetStyleNameAttributeAsBSTR() const {
std::u16string style_name =
GetDelegate()->GetStyleNameAttributeAsLocalizedString();
- return ::SysAllocString(base::UTF16ToWide(style_name).c_str());
+ return ::SysAllocString(fml::Utf16ToWideString(style_name).c_str());
}
TextDecorationLineStyle AXPlatformNodeWin::GetUIATextDecorationStyle(
diff --git a/third_party/accessibility/base/string_utils.cc b/third_party/accessibility/base/string_utils.cc
index 61e0a4a..963f7d5 100644
--- a/third_party/accessibility/base/string_utils.cc
+++ b/third_party/accessibility/base/string_utils.cc
@@ -72,14 +72,6 @@
return fml::Utf16ToUtf8(src);
}
-std::u16string WideToUTF16(const std::wstring& src) {
- return std::u16string(src.begin(), src.end());
-}
-
-std::wstring UTF16ToWide(const std::u16string& src) {
- return std::wstring(src.begin(), src.end());
-}
-
std::u16string NumberToString16(float number) {
return ASCIIToUTF16(NumberToString(number));
}
diff --git a/third_party/accessibility/base/string_utils.h b/third_party/accessibility/base/string_utils.h
index 6e3c6c6..ceddfbf 100644
--- a/third_party/accessibility/base/string_utils.h
+++ b/third_party/accessibility/base/string_utils.h
@@ -26,8 +26,6 @@
std::u16string ASCIIToUTF16(std::string src);
std::u16string UTF8ToUTF16(std::string src);
std::string UTF16ToUTF8(std::u16string src);
-std::u16string WideToUTF16(const std::wstring& src);
-std::wstring UTF16ToWide(const std::u16string& src);
std::u16string NumberToString16(unsigned int number);
std::u16string NumberToString16(int32_t number);