blob: 705a2ee24e463a65784a48844d7c9c26ae7b48db [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2016 The Chromium Authors
ekaramadadd882292016-06-08 15:22:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/renderer_host/text_input_manager.h"
6
Andrew Xua178f3a2021-04-13 00:54:157#include <algorithm>
8
Andrew Xue80a71912021-04-22 23:56:199#include "base/numerics/clamped_math.h"
David Sandersd4bf5eb2022-03-17 07:12:0510#include "base/observer_list.h"
Helmut Januschkaa965cc12024-05-07 07:11:0911#include "base/strings/string_number_conversions.h"
David Sandersc51d3b02025-06-13 22:22:1712#include "base/trace_event/trace_event.h"
Peter Kastingc6340732021-07-05 06:01:0013#include "build/build_config.h"
ekaramad8cba78862016-06-24 19:42:3114#include "content/browser/renderer_host/render_widget_host_impl.h"
ekaramadadd882292016-06-08 15:22:5615#include "content/browser/renderer_host/render_widget_host_view_base.h"
ekaramadb8e23a96c2016-07-13 01:21:1516#include "ui/gfx/geometry/rect.h"
ekaramad6f1786b2016-07-21 21:10:5817#include "ui/gfx/range/range.h"
ekaramadadd882292016-06-08 15:22:5618
19namespace content {
20
21namespace {
22
Dave Tapuskac135bf12020-06-19 17:37:5323bool ShouldUpdateTextInputState(const ui::mojom::TextInputState& old_state,
24 const ui::mojom::TextInputState& new_state) {
ekaramadadd882292016-06-08 15:22:5625#if defined(USE_AURA)
Darren Shen87552fe52021-04-14 18:31:0426 return old_state.node_id != new_state.node_id ||
27 old_state.type != new_state.type || old_state.mode != new_state.mode ||
ekaramadadd882292016-06-08 15:22:5628 old_state.flags != new_state.flags ||
29 old_state.can_compose_inline != new_state.can_compose_inline;
Dave Tapuskaa1cc3702023-02-03 20:43:2430#elif BUILDFLAG(IS_APPLE)
ekaramadc3ef49d2016-07-27 18:38:2531 return old_state.type != new_state.type ||
Sidney San Martínd743cc222019-07-20 23:11:1632 old_state.flags != new_state.flags ||
ekaramadc3ef49d2016-07-27 18:38:2533 old_state.can_compose_inline != new_state.can_compose_inline;
Xiaohan Wang7f8052e02022-01-14 18:44:2834#elif BUILDFLAG(IS_ANDROID)
ekaramad06b27712016-11-28 17:55:1035 // On Android, TextInputState update is sent only if there is some change in
36 // the state. So the new state is always different.
37 return true;
ekaramadadd882292016-06-08 15:22:5638#else
Peter Boströmfc7ddc182024-10-31 19:37:2139 NOTREACHED();
ekaramadadd882292016-06-08 15:22:5640#endif
41}
42
43} // namespace
44
Anupam Snigdha915824a2024-03-05 17:55:3045TextInputManager::TextInputManager() : active_view_(nullptr) {}
ekaramadadd882292016-06-08 15:22:5646
47TextInputManager::~TextInputManager() {
48 // If there is an active view, we should unregister it first so that the
49 // the tab's top-level RWHV will be notified about |TextInputState.type|
50 // resetting to none (i.e., we do not have an active RWHV anymore).
51 if (active_view_)
52 Unregister(active_view_);
53
54 // Unregister all the remaining views.
55 std::vector<RenderWidgetHostViewBase*> views;
My Nguyenca19c6d2020-08-05 11:32:4256 for (auto const& pair : text_input_state_map_)
ekaramadadd882292016-06-08 15:22:5657 views.push_back(pair.first);
58
vmpstr10e0d5f2016-07-21 23:46:0959 for (auto* view : views)
ekaramadadd882292016-06-08 15:22:5660 Unregister(view);
61}
62
ekaramad8cba78862016-06-24 19:42:3163RenderWidgetHostImpl* TextInputManager::GetActiveWidget() const {
64 return !!active_view_ ? static_cast<RenderWidgetHostImpl*>(
65 active_view_->GetRenderWidgetHost())
66 : nullptr;
ekaramadadd882292016-06-08 15:22:5667}
68
Dave Tapuskac135bf12020-06-19 17:37:5369const ui::mojom::TextInputState* TextInputManager::GetTextInputState() const {
My Nguyenca19c6d2020-08-05 11:32:4270 if (!active_view_) {
71 return nullptr;
72 }
73
74 return text_input_state_map_.at(active_view_).get();
ekaramadb8e23a96c2016-07-13 01:21:1575}
76
John Palmer75ce6c72021-01-21 05:15:0077gfx::Range TextInputManager::GetAutocorrectRange() const {
78 if (!active_view_)
79 return gfx::Range();
80
81 for (auto const& pair : text_input_state_map_) {
82 for (const auto& ime_text_span_info : pair.second->ime_text_spans_info) {
83 if (ime_text_span_info->span.type ==
84 ui::ImeTextSpan::Type::kAutocorrect) {
85 return gfx::Range(ime_text_span_info->span.start_offset,
86 ime_text_span_info->span.end_offset);
87 }
88 }
89 }
90 return gfx::Range();
91}
92
Arthur Sonzognic686e8f2024-01-11 08:36:3793std::optional<ui::GrammarFragment> TextInputManager::GetGrammarFragment(
Jing Wang6554c1502021-05-05 02:04:0094 gfx::Range range) const {
95 if (!active_view_)
Arthur Sonzognic686e8f2024-01-11 08:36:3796 return std::nullopt;
Jing Wang6554c1502021-05-05 02:04:0097
98 for (const auto& ime_text_span_info :
99 text_input_state_map_.at(active_view_)->ime_text_spans_info) {
100 if (ime_text_span_info->span.type ==
101 ui::ImeTextSpan::Type::kGrammarSuggestion &&
102 ime_text_span_info->span.suggestions.size() > 0) {
103 auto span_range = gfx::Range(ime_text_span_info->span.start_offset,
104 ime_text_span_info->span.end_offset);
105 if (span_range.Contains(range)) {
106 return ui::GrammarFragment(span_range,
107 ime_text_span_info->span.suggestions[0]);
108 }
109 }
110 }
Arthur Sonzognic686e8f2024-01-11 08:36:37111 return std::nullopt;
Jing Wang6554c1502021-05-05 02:04:00112}
113
ekaramad2f520092016-08-22 23:10:24114const TextInputManager::SelectionRegion* TextInputManager::GetSelectionRegion(
115 RenderWidgetHostViewBase* view) const {
116 DCHECK(!view || IsRegistered(view));
117 if (!view)
118 view = active_view_;
119 return view ? &selection_region_map_.at(view) : nullptr;
ekaramadfcce0882016-07-07 02:44:51120}
121
ekaramadd773ff42016-08-12 19:30:40122const TextInputManager::CompositionRangeInfo*
ekaramad79819af02017-04-10 21:10:26123TextInputManager::GetCompositionRangeInfo() const {
ekaramadd773ff42016-08-12 19:30:40124 return active_view_ ? &composition_range_info_map_.at(active_view_) : nullptr;
ekaramadb8e23a96c2016-07-13 01:21:15125}
126
Adam Ettenberger23b345b2024-10-16 20:03:39127#if BUILDFLAG(IS_WIN)
128const blink::mojom::ProximateCharacterRangeBounds*
129TextInputManager::GetProximateCharacterBoundsInfo(
130 const RenderWidgetHostViewBase& view) const {
131 // TODO(crbug.com/355578906): Remove const_cast<RenderWidgetHostViewBase*>,
132 // which is needed because TextInputManager::ViewMap has mutable
133 // `RenderWidgetHostViewBase*` keys and the two RenderWidgetHostViewAura
134 // callers are const methods passing (*this).
135 // - RenderWidgetHostViewAura::GetProximateCharacterBounds
136 // - RenderWidgetHostViewAura::GetProximateCharacterIndexFromPoint
137 const auto found = proximate_character_bounds_map_.find(
138 const_cast<RenderWidgetHostViewBase*>(&view));
139 return found != proximate_character_bounds_map_.end() ? found->second.get()
140 : nullptr;
141}
142#endif // BUILDFLAG(IS_WIN)
143
ekaramad6f1786b2016-07-21 21:10:58144const TextInputManager::TextSelection* TextInputManager::GetTextSelection(
145 RenderWidgetHostViewBase* view) const {
146 DCHECK(!view || IsRegistered(view));
147 if (!view)
148 view = active_view_;
xiaoyinheac75cf2017-06-26 23:48:45149 // A crash occurs when we end up here with an unregistered view.
150 // See crbug.com/735980
151 // TODO(ekaramad): Take a deeper look why this is happening.
152 return (view && IsRegistered(view)) ? &text_selection_map_.at(view) : nullptr;
ekaramad6f1786b2016-07-21 21:10:58153}
154
Arthur Sonzognic686e8f2024-01-11 08:36:37155const std::optional<gfx::Rect> TextInputManager::GetTextControlBounds() const {
John Palmer2200b9a92021-10-27 04:39:02156 const ui::mojom::TextInputState* state = GetTextInputState();
157 if (!active_view_ || !state || !state->edit_context_control_bounds)
Arthur Sonzognic686e8f2024-01-11 08:36:37158 return std::nullopt;
John Palmer2200b9a92021-10-27 04:39:02159
160 auto control_bounds = state->edit_context_control_bounds.value();
161 auto new_top_left =
162 active_view_->TransformPointToRootCoordSpace(control_bounds.origin());
Arthur Sonzognic686e8f2024-01-11 08:36:37163 return std::optional<gfx::Rect>(
John Palmer2200b9a92021-10-27 04:39:02164 gfx::Rect(new_top_left, control_bounds.size()));
165}
166
Arthur Sonzognic686e8f2024-01-11 08:36:37167const std::optional<gfx::Rect> TextInputManager::GetTextSelectionBounds()
John Palmer2200b9a92021-10-27 04:39:02168 const {
169 const ui::mojom::TextInputState* state = GetTextInputState();
170 if (!active_view_ || !state || !state->edit_context_selection_bounds)
Arthur Sonzognic686e8f2024-01-11 08:36:37171 return std::nullopt;
John Palmer2200b9a92021-10-27 04:39:02172
173 auto selection_bounds = state->edit_context_selection_bounds.value();
174 auto new_top_left =
175 active_view_->TransformPointToRootCoordSpace(selection_bounds.origin());
Arthur Sonzognic686e8f2024-01-11 08:36:37176 return std::optional<gfx::Rect>(
John Palmer2200b9a92021-10-27 04:39:02177 gfx::Rect(new_top_left, selection_bounds.size()));
178}
179
ekaramadadd882292016-06-08 15:22:56180void TextInputManager::UpdateTextInputState(
181 RenderWidgetHostViewBase* view,
Dave Tapuskac135bf12020-06-19 17:37:53182 const ui::mojom::TextInputState& text_input_state) {
ekaramadadd882292016-06-08 15:22:56183 DCHECK(IsRegistered(view));
184
ekaramad44c242982016-07-27 21:17:29185 if (text_input_state.type == ui::TEXT_INPUT_TYPE_NONE &&
186 active_view_ != view) {
187 // We reached here because an IPC is received to reset the TextInputState
188 // for |view|. But |view| != |active_view_|, which suggests that at least
189 // one other view has become active and we have received the corresponding
190 // IPC from their RenderWidget sooner than this one. That also means we have
191 // already synthesized the loss of TextInputState for the |view| before (see
192 // below). So we can forget about this method ever being called (no observer
193 // calls necessary).
changwan38c1f942017-03-10 18:47:38194 // NOTE: Android requires state to be returned even when the current state
195 // is/becomes NONE. Otherwise IME may become irresponsive.
Xiaohan Wang7f8052e02022-01-14 18:44:28196#if !BUILDFLAG(IS_ANDROID)
ekaramad44c242982016-07-27 21:17:29197 return;
changwan38c1f942017-03-10 18:47:38198#endif
ekaramad44c242982016-07-27 21:17:29199 }
200
201 // Since |view| is registered, we already have a previous value for its
ekaramadadd882292016-06-08 15:22:56202 // TextInputState.
My Nguyenca19c6d2020-08-05 11:32:42203 bool changed = ShouldUpdateTextInputState(*text_input_state_map_[view],
ekaramad06b27712016-11-28 17:55:10204 text_input_state);
Anupam Snigdhaf44f5e72020-12-07 23:15:01205 TRACE_EVENT2(
206 "ime", "TextInputManager::UpdateTextInputState", "changed", changed,
207 "text_input_state - type, selection, composition, "
208 "show_ime_if_needed, control_bounds",
Helmut Januschkaa965cc12024-05-07 07:11:09209 base::NumberToString(text_input_state.type) + ", " +
Anupam Snigdhaf44f5e72020-12-07 23:15:01210 text_input_state.selection.ToString() + ", " +
211 (text_input_state.composition.has_value()
212 ? text_input_state.composition->ToString()
213 : "") +
Helmut Januschkaa965cc12024-05-07 07:11:09214 ", " + base::NumberToString(text_input_state.show_ime_if_needed) +
215 ", " +
Anupam Snigdhaf44f5e72020-12-07 23:15:01216 (text_input_state.edit_context_control_bounds.has_value()
217 ? text_input_state.edit_context_control_bounds->ToString()
218 : ""));
My Nguyenca19c6d2020-08-05 11:32:42219 text_input_state_map_[view] = text_input_state.Clone();
220 for (const auto& ime_text_span_info :
221 text_input_state_map_[view]->ime_text_spans_info) {
222 const gfx::Rect& bounds = ime_text_span_info->bounds;
223 ime_text_span_info->bounds = gfx::Rect(
224 view->TransformPointToRootCoordSpace(bounds.origin()), bounds.size());
225 }
ekaramadadd882292016-06-08 15:22:56226
ekaramad44c242982016-07-27 21:17:29227 // If |view| is different from |active_view| and its |TextInputState.type| is
228 // not NONE, |active_view_| should change to |view|.
229 if (text_input_state.type != ui::TEXT_INPUT_TYPE_NONE &&
230 active_view_ != view) {
231 if (active_view_) {
232 // Ideally, we should always receive an IPC from |active_view_|'s
233 // RenderWidget to reset its |TextInputState.type| to NONE, before any
234 // other RenderWidget updates its TextInputState. But there is no
235 // guarantee in the order of IPCs from different RenderWidgets and another
236 // RenderWidget's IPC might arrive sooner and we reach here. To make the
237 // IME behavior identical to the non-OOPIF case, we have to manually reset
238 // the state for |active_view_|.
My Nguyenca19c6d2020-08-05 11:32:42239 text_input_state_map_[active_view_]->type = ui::TEXT_INPUT_TYPE_NONE;
ekaramad44c242982016-07-27 21:17:29240 RenderWidgetHostViewBase* active_view = active_view_;
241 active_view_ = nullptr;
242 NotifyObserversAboutInputStateUpdate(active_view, true);
243 }
ekaramadadd882292016-06-08 15:22:56244 active_view_ = view;
ekaramad44c242982016-07-27 21:17:29245 }
ekaramadadd882292016-06-08 15:22:56246
247 // If the state for |active_view_| is none, then we no longer have an
248 // |active_view_|.
249 if (active_view_ == view && text_input_state.type == ui::TEXT_INPUT_TYPE_NONE)
250 active_view_ = nullptr;
251
252 NotifyObserversAboutInputStateUpdate(view, changed);
253}
254
Adam Ettenberger23b345b2024-10-16 20:03:39255#if BUILDFLAG(IS_WIN)
256void TextInputManager::UpdateProximateCharacterBounds(
257 RenderWidgetHostViewBase& view,
258 blink::mojom::ProximateCharacterRangeBoundsPtr proximate_bounds) {
259 if (!proximate_bounds) {
260 proximate_character_bounds_map_.erase(&view);
261 return;
262 }
263 proximate_character_bounds_map_[&view] = std::move(proximate_bounds);
264}
265#endif // BUILDFLAG(IS_WIN)
266
ekaramad50ee2032016-06-29 02:18:25267void TextInputManager::ImeCancelComposition(RenderWidgetHostViewBase* view) {
268 DCHECK(IsRegistered(view));
ericwilligersde386342016-10-19 02:35:09269 for (auto& observer : observer_list_)
270 observer.OnImeCancelComposition(this, view);
ekaramad50ee2032016-06-29 02:18:25271}
272
ekaramadfcce0882016-07-07 02:44:51273void TextInputManager::SelectionBoundsChanged(
274 RenderWidgetHostViewBase* view,
Dave Tapuskac135bf12020-06-19 17:37:53275 const gfx::Rect& anchor_rect,
276 base::i18n::TextDirection anchor_dir,
277 const gfx::Rect& focus_rect,
278 base::i18n::TextDirection focus_dir,
Andrew Xua178f3a2021-04-13 00:54:15279 const gfx::Rect& bounding_box,
Dave Tapuskac135bf12020-06-19 17:37:53280 bool is_anchor_first) {
ekaramadfcce0882016-07-07 02:44:51281 DCHECK(IsRegistered(view));
ekaramad2f520092016-08-22 23:10:24282 // Converting the anchor point to root's coordinate space (for child frame
283 // views).
284 gfx::Point anchor_origin_transformed =
Dave Tapuskac135bf12020-06-19 17:37:53285 view->TransformPointToRootCoordSpace(anchor_rect.origin());
dmazzonid61b8fc2016-09-22 21:06:33286
ekaramadfcce0882016-07-07 02:44:51287 gfx::SelectionBound anchor_bound, focus_bound;
ekaramad2f520092016-08-22 23:10:24288
289 anchor_bound.SetEdge(gfx::PointF(anchor_origin_transformed),
ekaramadfcce0882016-07-07 02:44:51290 gfx::PointF(view->TransformPointToRootCoordSpace(
Dave Tapuskac135bf12020-06-19 17:37:53291 anchor_rect.bottom_left())));
292 focus_bound.SetEdge(
293 gfx::PointF(view->TransformPointToRootCoordSpace(focus_rect.origin())),
294 gfx::PointF(
295 view->TransformPointToRootCoordSpace(focus_rect.bottom_left())));
ekaramadfcce0882016-07-07 02:44:51296
Dave Tapuskac135bf12020-06-19 17:37:53297 if (anchor_rect == focus_rect) {
ekaramadfcce0882016-07-07 02:44:51298 anchor_bound.set_type(gfx::SelectionBound::CENTER);
299 focus_bound.set_type(gfx::SelectionBound::CENTER);
300 } else {
301 // Whether text is LTR at the anchor handle.
Dave Tapuskac135bf12020-06-19 17:37:53302 bool anchor_LTR = anchor_dir == base::i18n::LEFT_TO_RIGHT;
ekaramadfcce0882016-07-07 02:44:51303 // Whether text is LTR at the focus handle.
Dave Tapuskac135bf12020-06-19 17:37:53304 bool focus_LTR = focus_dir == base::i18n::LEFT_TO_RIGHT;
ekaramadfcce0882016-07-07 02:44:51305
Dave Tapuskac135bf12020-06-19 17:37:53306 if ((is_anchor_first && anchor_LTR) || (!is_anchor_first && !anchor_LTR)) {
ekaramadfcce0882016-07-07 02:44:51307 anchor_bound.set_type(gfx::SelectionBound::LEFT);
308 } else {
309 anchor_bound.set_type(gfx::SelectionBound::RIGHT);
310 }
Dave Tapuskac135bf12020-06-19 17:37:53311 if ((is_anchor_first && focus_LTR) || (!is_anchor_first && !focus_LTR)) {
ekaramadfcce0882016-07-07 02:44:51312 focus_bound.set_type(gfx::SelectionBound::RIGHT);
313 } else {
314 focus_bound.set_type(gfx::SelectionBound::LEFT);
315 }
316 }
317
Andrew Xua178f3a2021-04-13 00:54:15318 // Transform `bounding_box` to the top-level frame's coordinate space.
319 std::vector<gfx::Point> bounding_box_vertice = {
320 bounding_box.origin(), bounding_box.top_right(),
321 bounding_box.bottom_left(), bounding_box.bottom_right()};
322 std::vector<int> x_after_transform;
323 std::vector<int> y_after_transform;
324 for (const auto& vertex : bounding_box_vertice) {
325 const gfx::Point vertex_after_transform =
326 view->TransformPointToRootCoordSpace(vertex);
327 x_after_transform.push_back(vertex_after_transform.x());
328 y_after_transform.push_back(vertex_after_transform.y());
329 }
330
331 std::sort(x_after_transform.begin(), x_after_transform.end());
332 std::sort(y_after_transform.begin(), y_after_transform.end());
333
334 const gfx::Point bounding_box_origin_after_transform(x_after_transform[0],
335 y_after_transform[0]);
336 const gfx::Point bounding_box_bottom_right_after_transform(
337 x_after_transform.back(), y_after_transform.back());
338 const gfx::Rect bounding_box_transformed(
339 bounding_box_origin_after_transform,
Andrew Xue80a71912021-04-22 23:56:19340 gfx::Size(base::ClampSub(bounding_box_bottom_right_after_transform.x(),
341 bounding_box_origin_after_transform.x()),
342 base::ClampSub(bounding_box_bottom_right_after_transform.y(),
343 bounding_box_origin_after_transform.y())));
Andrew Xua178f3a2021-04-13 00:54:15344
ekaramadfcce0882016-07-07 02:44:51345 if (anchor_bound == selection_region_map_[view].anchor &&
Andrew Xua178f3a2021-04-13 00:54:15346 focus_bound == selection_region_map_[view].focus &&
347 bounding_box_transformed == selection_region_map_[view].bounding_box) {
ekaramadfcce0882016-07-07 02:44:51348 return;
Andrew Xua178f3a2021-04-13 00:54:15349 }
ekaramadfcce0882016-07-07 02:44:51350
351 selection_region_map_[view].anchor = anchor_bound;
352 selection_region_map_[view].focus = focus_bound;
Andrew Xua178f3a2021-04-13 00:54:15353 selection_region_map_[view].bounding_box = bounding_box_transformed;
dmazzonid61b8fc2016-09-22 21:06:33354
Dave Tapuskac135bf12020-06-19 17:37:53355 if (anchor_rect == focus_rect) {
ekaramad2f520092016-08-22 23:10:24356 selection_region_map_[view].caret_rect.set_origin(
357 anchor_origin_transformed);
Dave Tapuskac135bf12020-06-19 17:37:53358 selection_region_map_[view].caret_rect.set_size(anchor_rect.size());
ekaramad2f520092016-08-22 23:10:24359 }
360 selection_region_map_[view].first_selection_rect.set_origin(
361 anchor_origin_transformed);
Dave Tapuskac135bf12020-06-19 17:37:53362 selection_region_map_[view].first_selection_rect.set_size(anchor_rect.size());
dmazzonid61b8fc2016-09-22 21:06:33363
Aaron Leventhalfe0c7ef2018-07-25 14:05:53364 NotifySelectionBoundsChanged(view);
365}
366
367void TextInputManager::NotifySelectionBoundsChanged(
368 RenderWidgetHostViewBase* view) {
ericwilligersde386342016-10-19 02:35:09369 for (auto& observer : observer_list_)
370 observer.OnSelectionBoundsChanged(this, view);
ekaramadfcce0882016-07-07 02:44:51371}
372
ekaramadd773ff42016-08-12 19:30:40373// TODO(ekaramad): We use |range| only on Mac OS; but we still track its value
374// here for other platforms. See if there is a nice way around this with minimal
375// #ifdefs for platform specific code (https://crbug.com/602427).
ekaramadb8e23a96c2016-07-13 01:21:15376void TextInputManager::ImeCompositionRangeChanged(
377 RenderWidgetHostViewBase* view,
378 const gfx::Range& range,
Alex Mitra9b83bea92025-02-28 14:14:04379 const std::optional<std::vector<gfx::Rect>>& character_bounds) {
ekaramadb8e23a96c2016-07-13 01:21:15380 DCHECK(IsRegistered(view));
ekaramadb8e23a96c2016-07-13 01:21:15381
Alex Mitra08747df2023-08-08 17:04:10382 if (character_bounds.has_value()) {
383 composition_range_info_map_[view].character_bounds.clear();
384
385 // The values for the bounds should be converted to root view's coordinates
386 // before being stored.
387 for (auto& rect : character_bounds.value()) {
388 composition_range_info_map_[view].character_bounds.emplace_back(
389 view->TransformPointToRootCoordSpace(rect.origin()), rect.size());
390 }
391
392 composition_range_info_map_[view].range.set_start(range.start());
393 composition_range_info_map_[view].range.set_end(range.end());
394 }
ekaramadb8e23a96c2016-07-13 01:21:15395
Alex Mitra08747df2023-08-08 17:04:10396 for (auto& observer : observer_list_) {
Alex Mitra9b83bea92025-02-28 14:14:04397 observer.OnImeCompositionRangeChanged(this, view,
398 character_bounds.has_value());
Alex Mitra08747df2023-08-08 17:04:10399 }
ekaramadb8e23a96c2016-07-13 01:21:15400}
401
ekaramad6f1786b2016-07-21 21:10:58402void TextInputManager::SelectionChanged(RenderWidgetHostViewBase* view,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58403 const std::u16string& text,
ekaramad6f1786b2016-07-21 21:10:58404 size_t offset,
changwan44664cd2017-05-23 19:14:34405 const gfx::Range& range) {
ekaramad6f1786b2016-07-21 21:10:58406 DCHECK(IsRegistered(view));
changwan44664cd2017-05-23 19:14:34407 text_selection_map_[view].SetSelection(text, offset, range);
ericwilligersde386342016-10-19 02:35:09408 for (auto& observer : observer_list_)
409 observer.OnTextSelectionChanged(this, view);
ekaramad6f1786b2016-07-21 21:10:58410}
411
ekaramadadd882292016-06-08 15:22:56412void TextInputManager::Register(RenderWidgetHostViewBase* view) {
413 DCHECK(!IsRegistered(view));
My Nguyenca19c6d2020-08-05 11:32:42414 text_input_state_map_[view] = ui::mojom::TextInputState::New();
ekaramad4cfc03e2016-07-21 17:34:21415 selection_region_map_[view] = SelectionRegion();
416 composition_range_info_map_[view] = CompositionRangeInfo();
ekaramad6f1786b2016-07-21 21:10:58417 text_selection_map_[view] = TextSelection();
ekaramadadd882292016-06-08 15:22:56418}
419
420void TextInputManager::Unregister(RenderWidgetHostViewBase* view) {
421 DCHECK(IsRegistered(view));
422
423 text_input_state_map_.erase(view);
ekaramad4cfc03e2016-07-21 17:34:21424 selection_region_map_.erase(view);
425 composition_range_info_map_.erase(view);
ekaramad6f1786b2016-07-21 21:10:58426 text_selection_map_.erase(view);
Adam Ettenberger23b345b2024-10-16 20:03:39427#if BUILDFLAG(IS_WIN)
428 proximate_character_bounds_map_.erase(view);
429#endif // BUILDFLAG(IS_WIN)
ekaramad4cfc03e2016-07-21 17:34:21430
ekaramadadd882292016-06-08 15:22:56431 if (active_view_ == view) {
432 active_view_ = nullptr;
433 NotifyObserversAboutInputStateUpdate(view, true);
434 }
435 view->DidUnregisterFromTextInputManager(this);
436}
437
438bool TextInputManager::IsRegistered(RenderWidgetHostViewBase* view) const {
439 return text_input_state_map_.count(view) == 1;
440}
441
442void TextInputManager::AddObserver(Observer* observer) {
443 observer_list_.AddObserver(observer);
444}
445
446void TextInputManager::RemoveObserver(Observer* observer) {
447 observer_list_.RemoveObserver(observer);
448}
449
ekaramadfd5f5a892016-08-12 04:33:10450bool TextInputManager::HasObserver(Observer* observer) const {
451 return observer_list_.HasObserver(observer);
452}
453
ekaramad936536d2016-06-29 05:44:44454size_t TextInputManager::GetRegisteredViewsCountForTesting() {
455 return text_input_state_map_.size();
456}
457
458ui::TextInputType TextInputManager::GetTextInputTypeForViewForTesting(
459 RenderWidgetHostViewBase* view) {
460 DCHECK(IsRegistered(view));
My Nguyenca19c6d2020-08-05 11:32:42461 return text_input_state_map_[view]->type;
ekaramad936536d2016-06-29 05:44:44462}
463
ekaramad2c3c4872017-06-15 14:58:06464const gfx::Range* TextInputManager::GetCompositionRangeForTesting() const {
465 if (auto* info = GetCompositionRangeInfo())
466 return &info->range;
467 return nullptr;
468}
469
ekaramadadd882292016-06-08 15:22:56470void TextInputManager::NotifyObserversAboutInputStateUpdate(
471 RenderWidgetHostViewBase* updated_view,
472 bool did_update_state) {
ericwilligersde386342016-10-19 02:35:09473 for (auto& observer : observer_list_)
474 observer.OnUpdateTextInputStateCalled(this, updated_view, did_update_state);
ekaramadadd882292016-06-08 15:22:56475}
476
Peter Kastingc6340732021-07-05 06:01:00477TextInputManager::SelectionRegion::SelectionRegion() = default;
ekaramadfcce0882016-07-07 02:44:51478
479TextInputManager::SelectionRegion::SelectionRegion(
480 const SelectionRegion& other) = default;
481
Peter Kastingc6340732021-07-05 06:01:00482TextInputManager::SelectionRegion& TextInputManager::SelectionRegion::operator=(
483 const SelectionRegion& other) = default;
484
485TextInputManager::CompositionRangeInfo::CompositionRangeInfo() = default;
ekaramadb8e23a96c2016-07-13 01:21:15486
487TextInputManager::CompositionRangeInfo::CompositionRangeInfo(
488 const CompositionRangeInfo& other) = default;
489
Peter Kastingc6340732021-07-05 06:01:00490TextInputManager::CompositionRangeInfo::~CompositionRangeInfo() = default;
ekaramadb8e23a96c2016-07-13 01:21:15491
changwan44664cd2017-05-23 19:14:34492TextInputManager::TextSelection::TextSelection()
493 : offset_(0), range_(gfx::Range::InvalidRange()) {}
ekaramad6f1786b2016-07-21 21:10:58494
495TextInputManager::TextSelection::TextSelection(const TextSelection& other) =
496 default;
497
Peter Kastingc6340732021-07-05 06:01:00498TextInputManager::TextSelection::~TextSelection() = default;
ekaramad6f1786b2016-07-21 21:10:58499
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58500void TextInputManager::TextSelection::SetSelection(const std::u16string& text,
ekaramad374b2662017-03-02 20:44:52501 size_t offset,
changwan44664cd2017-05-23 19:14:34502 const gfx::Range& range) {
ekaramad374b2662017-03-02 20:44:52503 text_ = text;
504 range_.set_start(range.start());
505 range_.set_end(range.end());
506 offset_ = offset;
ekaramad65cf5592016-08-18 21:44:53507
ekaramad374b2662017-03-02 20:44:52508 // Update the selected text.
509 selected_text_.clear();
510 if (!text.empty() && !range.is_empty()) {
511 size_t pos = range.GetMin() - offset;
512 size_t n = range.length();
513 if (pos + n > text.length()) {
514 LOG(WARNING)
515 << "The text cannot fully cover range (selection's end point "
516 "exceeds text length).";
517 }
ekaramad65cf5592016-08-18 21:44:53518
ekaramad374b2662017-03-02 20:44:52519 if (pos >= text.length()) {
520 LOG(WARNING) << "The text cannot cover range (selection range's starting "
521 "point exceeds text length).";
522 } else {
523 selected_text_.append(text.substr(pos, n));
524 }
ekaramad65cf5592016-08-18 21:44:53525 }
ekaramad65cf5592016-08-18 21:44:53526}
527
ekaramad4cfc03e2016-07-21 17:34:21528} // namespace content