From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Orko Garai <orko@igalia.com>
Date: Fri, 29 Nov 2024 16:17:04 +0000
Subject: Wayland IME: Underline composition text fallback

At this time text-input-v3 does not provide any styling information.
As a quality-of-life improvement, ensure that a default composition
style is applied so that the composition text is underlined.

This will also ensure that the user experience is consistent with
ozone/x11.

Bug: 355238629
Change-Id: I8d4bce5e5700510d72f114bb57171f43646be098
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5741768
Commit-Queue: Orko Garai <orko@igalia.com>
Reviewed-by: Darren Shen <shend@chromium.org>
Reviewed-by: Kramer Ge <fangzhoug@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1389833}

diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
index 71a51ce25b808ee5dc5359ce99becac3da3869bc..1985765618f228102f411282f9476f04a450b4e1 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
@@ -598,6 +598,7 @@ void WaylandInputMethodContext::OnPreeditString(
     const gfx::Range& preedit_cursor) {
   CompositionText composition_text;
   composition_text.text = base::UTF8ToUTF16(text);
+  bool has_composition_style = false;
   for (const auto& span : spans) {
     auto start_offset = OffsetFromUTF8Offset(text, span.index);
     if (!start_offset)
@@ -608,9 +609,18 @@ void WaylandInputMethodContext::OnPreeditString(
     const auto& style = span.style;
     if (!style.has_value())
       continue;
+    if (style->type == ImeTextSpan::Type::kComposition) {
+      has_composition_style = true;
+    }
     composition_text.ime_text_spans.emplace_back(style->type, *start_offset,
                                                  *end_offset, style->thickness);
   }
+  if (!composition_text.text.empty() && !has_composition_style) {
+    // If no explicit composition style is specified, add default composition
+    // style to the composition text.
+    composition_text.ime_text_spans.emplace_back(
+        ImeTextSpan::Type::kComposition, 0, composition_text.text.length());
+  }
   if (!preedit_cursor.IsValid()) {
     // This is the case if a preceding preedit_cursor event in text-input-v1 was
     // not received or an explicit negative value was requested to hide the
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
index 41a3f5de13b506871f00780b7803bd65a169c22c..22d7a4b82a2e9183dbe1556a73ec236b4cac5048 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
@@ -1324,6 +1324,34 @@ TEST_P(WaylandInputMethodContextTest, SetInputTypeAfterFocus) {
   });
 }
 
+TEST_P(WaylandInputMethodContextTest, OnPreeditChangedDefaultCompositionStyle) {
+  constexpr std::string_view kPreeditString("PreeditString");
+  constexpr gfx::Range kSelection{7, 13};
+  input_method_context_->OnPreeditString(
+      kPreeditString,
+      // No composition style provided.
+      {{1,
+        3,
+        {{ImeTextSpan::Type::kMisspellingSuggestion,
+          ImeTextSpan::Thickness::kNone}}}},
+      kSelection);
+  EXPECT_TRUE(input_method_context_delegate_->was_on_preedit_changed_called());
+  EXPECT_EQ(input_method_context_delegate_->last_preedit()->ime_text_spans,
+            (ImeTextSpans{ImeTextSpan(ImeTextSpan::Type::kMisspellingSuggestion,
+                                      1, 4, ImeTextSpan::Thickness::kNone),
+                          // Default composition should be applied.
+                          ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
+                                      kPreeditString.size(),
+                                      ImeTextSpan::Thickness::kThin)}));
+  EXPECT_EQ(
+      input_method_context_->predicted_state_for_testing().surrounding_text,
+      u"PreeditString");
+  EXPECT_EQ(input_method_context_->predicted_state_for_testing().composition,
+            gfx::Range(0, kPreeditString.size()));
+  EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
+            kSelection);
+}
+
 TEST_P(WaylandInputMethodContextTest, OnPreeditChanged) {
   constexpr std::string_view kPreeditString("PreeditString");
   constexpr gfx::Range kSelection{7, 13};
@@ -1331,13 +1359,19 @@ TEST_P(WaylandInputMethodContextTest, OnPreeditChanged) {
       kPreeditString,
       {{0,
         static_cast<uint32_t>(kPreeditString.size()),
-        {{ImeTextSpan::Type::kComposition, ImeTextSpan::Thickness::kThin}}}},
+        {{ImeTextSpan::Type::kComposition, ImeTextSpan::Thickness::kThick}}},
+       {1,
+        3,
+        {{ImeTextSpan::Type::kMisspellingSuggestion,
+          ImeTextSpan::Thickness::kNone}}}},
       kSelection);
   EXPECT_TRUE(input_method_context_delegate_->was_on_preedit_changed_called());
   EXPECT_EQ(input_method_context_delegate_->last_preedit()->ime_text_spans,
-            ImeTextSpans{ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
-                                     kPreeditString.size(),
-                                     ImeTextSpan::Thickness::kThin)});
+            (ImeTextSpans{ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
+                                      kPreeditString.size(),
+                                      ImeTextSpan::Thickness::kThick),
+                          ImeTextSpan(ImeTextSpan::Type::kMisspellingSuggestion,
+                                      1, 4, ImeTextSpan::Thickness::kNone)}));
   EXPECT_EQ(
       input_method_context_->predicted_state_for_testing().surrounding_text,
       u"PreeditString");
