blob: 75df43e3cd2721a92c90c18154d53d5c203e2465 [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#ifndef CONTENT_BROWSER_RENDERER_HOST_TEXT_INPUT_MANAGER_H__
6#define CONTENT_BROWSER_RENDERER_HOST_TEXT_INPUT_MANAGER_H__
7
Arthur Sonzognic686e8f2024-01-11 08:36:378#include <optional>
Jan Wilken Dörriead587c32021-03-11 14:09:279#include <string>
ekaramadadd882292016-06-08 15:22:5610#include <unordered_map>
ekaramadfcce0882016-07-07 02:44:5111#include <utility>
ekaramadadd882292016-06-08 15:22:5612
Dave Tapuskac135bf12020-06-19 17:37:5313#include "base/i18n/rtl.h"
Keishi Hattori0e45c022021-11-27 09:25:5214#include "base/memory/raw_ptr.h"
ekaramadadd882292016-06-08 15:22:5615#include "base/observer_list.h"
16#include "content/common/content_export.h"
Adam Ettenberger23b345b2024-10-16 20:03:3917#include "third_party/blink/public/mojom/page/widget.mojom-forward.h"
Dave Tapuskac135bf12020-06-19 17:37:5318#include "ui/base/ime/mojom/text_input_state.mojom.h"
Jing Wang6554c1502021-05-05 02:04:0019#include "ui/base/ime/text_input_client.h"
ekaramadfcce0882016-07-07 02:44:5120#include "ui/gfx/geometry/rect.h"
ekaramad6f1786b2016-07-21 21:10:5821#include "ui/gfx/range/range.h"
ekaramadfcce0882016-07-07 02:44:5122#include "ui/gfx/selection_bound.h"
23
ekaramadadd882292016-06-08 15:22:5624namespace content {
ekaramadb8e23a96c2016-07-13 01:21:1525
ekaramad8cba78862016-06-24 19:42:3126class RenderWidgetHostImpl;
ekaramadadd882292016-06-08 15:22:5627class RenderWidgetHostViewBase;
ekaramadadd882292016-06-08 15:22:5628
29// A class which receives updates of TextInputState from multiple sources and
30// decides what the new TextInputState is. It also notifies the observers when
31// text input state is updated.
32class CONTENT_EXPORT TextInputManager {
33 public:
34 // The tab's top-level RWHV should be an observer of TextInputManager to get
35 // notifications about changes in TextInputState or other IME related state
36 // for child frames.
37 class CONTENT_EXPORT Observer {
38 public:
39 // Called when a view has called UpdateTextInputState on TextInputManager.
40 // If the call has led to a change in TextInputState, |did_update_state| is
41 // true. In some plaforms, we need this update even when the state has not
ekaramaddb49e172016-11-30 08:44:4042 // changed (e.g., Aura for updating IME). Also note that |updated_view| is
43 // the view which has most recently received an update in TextInputState.
44 // |updated_view| should not be used to obtain any IME state since this
45 // observer method might have been called in the process of unregistering
46 // |active_view_| from TextInputManager (which in turn is a result of either
47 // destroying |active_view_| or TextInputManager).
ekaramadadd882292016-06-08 15:22:5648 virtual void OnUpdateTextInputStateCalled(
49 TextInputManager* text_input_manager,
50 RenderWidgetHostViewBase* updated_view,
51 bool did_update_state) {}
ekaramad50ee2032016-06-29 02:18:2552 // Called when |updated_view| has called ImeCancelComposition on
53 // TextInputManager.
54 virtual void OnImeCancelComposition(
55 TextInputManager* text_input_manager,
56 RenderWidgetHostViewBase* updated_view) {}
ekaramadb8e23a96c2016-07-13 01:21:1557 // Called when |updated_view| has changed its SelectionRegion.
ekaramadfcce0882016-07-07 02:44:5158 virtual void OnSelectionBoundsChanged(
59 TextInputManager* text_input_manager,
60 RenderWidgetHostViewBase* updated_view) {}
ekaramadb8e23a96c2016-07-13 01:21:1561 // Called when |updated_view| has changed its CompositionRangeInfo.
Alex Mitra08747df2023-08-08 17:04:1062 // |character_bounds_changed| marks whether the current
63 // CompositionRangeInfo::character_bounds should be updated.
ekaramadb8e23a96c2016-07-13 01:21:1564 virtual void OnImeCompositionRangeChanged(
65 TextInputManager* text_input_manager,
Alex Mitra08747df2023-08-08 17:04:1066 RenderWidgetHostViewBase* updated_view,
Alex Mitra9b83bea92025-02-28 14:14:0467 bool character_bounds_changed) {}
ekaramad6f1786b2016-07-21 21:10:5868 // Called when the text selection for the |updated_view_| has changed.
69 virtual void OnTextSelectionChanged(
70 TextInputManager* text_input_manager,
71 RenderWidgetHostViewBase* updated_view) {}
72 };
73
ekaramad2f520092016-08-22 23:10:2474 // Text selection bounds.
75 struct SelectionRegion {
76 SelectionRegion();
77 SelectionRegion(const SelectionRegion& other);
Peter Kastingc6340732021-07-05 06:01:0078 SelectionRegion& operator=(const SelectionRegion& other);
ekaramad2f520092016-08-22 23:10:2479
ekaramad2f520092016-08-22 23:10:2480 // The begining of the selection region.
81 gfx::SelectionBound anchor;
82 // The end of the selection region (caret position).
83 gfx::SelectionBound focus;
84
Andrew Xua178f3a2021-04-13 00:54:1585 // The bounding box of the selection region.
86 gfx::Rect bounding_box;
87
ekaramad2f520092016-08-22 23:10:2488 // The following variables are only used on Mac platform.
89 // The current caret bounds.
90 gfx::Rect caret_rect;
91 // The current first selection bounds.
92 gfx::Rect first_selection_rect;
93 };
94
95 // Composition range information.
96 struct CompositionRangeInfo {
97 CompositionRangeInfo();
98 CompositionRangeInfo(const CompositionRangeInfo& other);
99 ~CompositionRangeInfo();
100
101 std::vector<gfx::Rect> character_bounds;
102 gfx::Range range;
103 };
104
ekaramad374b2662017-03-02 20:44:52105 // This class is used to store text selection information for views. The text
106 // selection information includes a range around the selected (highlighted)
107 // text which is defined by an offset from the beginning of the page/frame,
108 // a range for the selection, and the text including the selection which
109 // might include several characters before and after it.
110 class TextSelection {
111 public:
ekaramad6f1786b2016-07-21 21:10:58112 TextSelection();
113 TextSelection(const TextSelection& other);
114 ~TextSelection();
115
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58116 void SetSelection(const std::u16string& text,
ekaramad374b2662017-03-02 20:44:52117 size_t offset,
changwan44664cd2017-05-23 19:14:34118 const gfx::Range& range);
ekaramad65cf5592016-08-18 21:44:53119
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58120 const std::u16string& selected_text() const { return selected_text_; }
ekaramad374b2662017-03-02 20:44:52121 size_t offset() const { return offset_; }
122 const gfx::Range& range() const { return range_; }
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58123 const std::u16string& text() const { return text_; }
ekaramad374b2662017-03-02 20:44:52124
125 private:
ekaramad6f1786b2016-07-21 21:10:58126 // The offset of the text stored in |text| relative to the start of the web
127 // page.
changwan44664cd2017-05-23 19:14:34128 size_t offset_;
ekaramad6f1786b2016-07-21 21:10:58129
ekaramad374b2662017-03-02 20:44:52130 // The range of the selection in the page (highlighted text).
changwan44664cd2017-05-23 19:14:34131 gfx::Range range_;
ekaramad6f1786b2016-07-21 21:10:58132
ekaramad374b2662017-03-02 20:44:52133 // The highlighted text which is the portion of |text_| marked by |offset_|
134 // and |range_|. It will be an empty string if either |text_| or |range_|
135 // are empty of this selection information is invalid (i.e., |range_| does
136 // not cover any of |text_|.
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58137 std::u16string selected_text_;
ekaramad374b2662017-03-02 20:44:52138
139 // Part of the text on the page which includes the highlighted text plus
140 // possibly several characters before and after it.
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58141 std::u16string text_;
ekaramadadd882292016-06-08 15:22:56142 };
143
Anupam Snigdha915824a2024-03-05 17:55:30144 TextInputManager();
Peter Boström9b036532021-10-28 23:37:28145
146 TextInputManager(const TextInputManager&) = delete;
147 TextInputManager& operator=(const TextInputManager&) = delete;
148
ekaramadadd882292016-06-08 15:22:56149 ~TextInputManager();
150
ekaramad8cba78862016-06-24 19:42:31151 // Returns the currently active widget, i.e., the RWH which is associated with
152 // |active_view_|.
153 RenderWidgetHostImpl* GetActiveWidget() const;
ekaramadadd882292016-06-08 15:22:56154
ekaramadb8e23a96c2016-07-13 01:21:15155 // ---------------------------------------------------------------------------
156 // The following methods can be used to obtain information about IME-related
157 // state for the active RenderWidgetHost. If the active widget is nullptr, the
158 // methods below will return nullptr as well.
159 // Users of these methods should not hold on to the pointers as they become
160 // dangling if the TextInputManager or |active_view_| are destroyed.
161
ekaramaddb49e172016-11-30 08:44:40162 // Returns the currently stored TextInputState for |active_view_|. A state of
163 // nullptr can be interpreted as a ui::TextInputType of
164 // ui::TEXT_INPUT_TYPE_NONE.
Dave Tapuskac135bf12020-06-19 17:37:53165 const ui::mojom::TextInputState* GetTextInputState() const;
ekaramadadd882292016-06-08 15:22:56166
John Palmer75ce6c72021-01-21 05:15:00167 // Returns the current autocorrect range, or an empty range if no autocorrect
168 // range is currently present.
169 gfx::Range GetAutocorrectRange() const;
170
Jing Wang6554c1502021-05-05 02:04:00171 // Returns the grammar fragment which contains |range|. If non-existent,
Jing Wanga7c098c2022-05-24 01:28:20172 // returns nullopt.
Arthur Sonzognic686e8f2024-01-11 08:36:37173 std::optional<ui::GrammarFragment> GetGrammarFragment(gfx::Range range) const;
Jing Wang6554c1502021-05-05 02:04:00174
ekaramad2f520092016-08-22 23:10:24175 // Returns the selection bounds information for |view|. If |view| == nullptr,
176 // it will return the corresponding information for |active_view_| or nullptr
177 // if there are no active views.
178 const SelectionRegion* GetSelectionRegion(
179 RenderWidgetHostViewBase* view = nullptr) const;
ekaramadfcce0882016-07-07 02:44:51180
ekaramadd773ff42016-08-12 19:30:40181 // Returns the composition range and character bounds information for the
ekaramad79819af02017-04-10 21:10:26182 // |active_view_|. Returns nullptr If |active_view_| == nullptr.
183 const TextInputManager::CompositionRangeInfo* GetCompositionRangeInfo() const;
ekaramadb8e23a96c2016-07-13 01:21:15184
Adam Ettenberger23b345b2024-10-16 20:03:39185#if BUILDFLAG(IS_WIN)
186 // `ProximateCharacterRangeBounds` is provided by the renderer and contains a
187 // character offset range and associated character bounding boxes in widget
188 // coordinates for a subset of the actual character bounding boxes. This data
189 // enables StylusHandwritingWin gesture support for renderer content, but is
190 // a compromise since it only contains a limited subset of content for
191 // performance (CPU and memory) reasons, since computing and copying all of
192 // the character bounds from a renderer document may be costly and slow for
193 // large documents.
194 //
195 // For views content, since they exist in the browser process it's possible to
196 // retrieve accurate results without ProximateCharacterRangeBounds.
197 // ProximateCharacterRangeBounds will not be updated for views content, and
198 // shouldn't be relied upon for views use cases as it could be null or stale.
199 // Views that implement `ui::TextInputClient` should instead implement both
200 // `GetProximateCharacterBounds` and `GetProximateCharacterIndexFromPoint` to
201 // query their content directly. For example, `views::Textfield` could collect
202 // character bounds through `gfx::RenderText`, GetRenderText().
203 const blink::mojom::ProximateCharacterRangeBounds*
204 GetProximateCharacterBoundsInfo(const RenderWidgetHostViewBase& view) const;
205#endif // BUILDFLAG(IS_WIN)
206
ekaramad6f1786b2016-07-21 21:10:58207 // The following method returns the text selection state for the given |view|.
208 // If |view| == nullptr, it will assume |active_view_| and return its state.
209 // In the case of |active_view_| == nullptr, the method will return nullptr.
210 const TextSelection* GetTextSelection(
211 RenderWidgetHostViewBase* view = nullptr) const;
212
John Palmer2200b9a92021-10-27 04:39:02213 // Returns the bounds of the text control in the root frame.
Arthur Sonzognic686e8f2024-01-11 08:36:37214 const std::optional<gfx::Rect> GetTextControlBounds() const;
John Palmer2200b9a92021-10-27 04:39:02215
216 // Returns the bounds of the selected text in the root frame.
Arthur Sonzognic686e8f2024-01-11 08:36:37217 const std::optional<gfx::Rect> GetTextSelectionBounds() const;
John Palmer2200b9a92021-10-27 04:39:02218
ekaramad8cba78862016-06-24 19:42:31219 // ---------------------------------------------------------------------------
220 // The following methods are called by RWHVs on the tab to update their IME-
221 // related state.
222
ekaramadadd882292016-06-08 15:22:56223 // Updates the TextInputState for |view|.
224 void UpdateTextInputState(RenderWidgetHostViewBase* view,
Dave Tapuskac135bf12020-06-19 17:37:53225 const ui::mojom::TextInputState& state);
ekaramadadd882292016-06-08 15:22:56226
Adam Ettenberger23b345b2024-10-16 20:03:39227#if BUILDFLAG(IS_WIN)
228 // Takes ownership of `proximate_bounds` so it can be retrieved later by
229 // GetProximateCharacterBoundsInfo(). When populated by the renderer process,
230 // enables StylusHandwritingWin gesture support for renderer content.
231 // If `proximate_bounds` is null, removes `view` from the cache.
232 void UpdateProximateCharacterBounds(
233 RenderWidgetHostViewBase& view,
234 blink::mojom::ProximateCharacterRangeBoundsPtr proximate_bounds);
235#endif // BUILDFLAG(IS_WIN)
236
ekaramad50ee2032016-06-29 02:18:25237 // The current IME composition has been cancelled on the renderer side for
238 // the widget corresponding to |view|.
239 void ImeCancelComposition(RenderWidgetHostViewBase* view);
240
ekaramadfcce0882016-07-07 02:44:51241 // Updates the selection bounds for the |view|. In Aura, selection bounds are
242 // used to provide the InputMethod with the position of the caret, e.g., in
243 // setting the position of the ui::ImeWindow.
Dave Tapuskac135bf12020-06-19 17:37:53244 void SelectionBoundsChanged(RenderWidgetHostViewBase* view,
245 const gfx::Rect& anchor_rect,
246 base::i18n::TextDirection anchor_dir,
247 const gfx::Rect& focus_rect,
248 base::i18n::TextDirection focus_dir,
Andrew Xua178f3a2021-04-13 00:54:15249 const gfx::Rect& bounding_box,
Dave Tapuskac135bf12020-06-19 17:37:53250 bool is_anchor_first);
ekaramadfcce0882016-07-07 02:44:51251
Aaron Leventhalfe0c7ef2018-07-25 14:05:53252 // Notify observers that the selection bounds have been updated. This is also
253 // called when a view with a selection is reactivated.
254 void NotifySelectionBoundsChanged(RenderWidgetHostViewBase* view);
255
ekaramadb8e23a96c2016-07-13 01:21:15256 // Called when the composition range and/or character bounds have changed.
257 void ImeCompositionRangeChanged(
258 RenderWidgetHostViewBase* view,
259 const gfx::Range& range,
Alex Mitra9b83bea92025-02-28 14:14:04260 const std::optional<std::vector<gfx::Rect>>& character_bounds);
ekaramadb8e23a96c2016-07-13 01:21:15261
ekaramad6f1786b2016-07-21 21:10:58262 // Updates the new text selection information for the |view|.
263 void SelectionChanged(RenderWidgetHostViewBase* view,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58264 const std::u16string& text,
ekaramad6f1786b2016-07-21 21:10:58265 size_t offset,
changwan44664cd2017-05-23 19:14:34266 const gfx::Range& range);
ekaramad6f1786b2016-07-21 21:10:58267
ekaramadadd882292016-06-08 15:22:56268 // Registers the given |view| for tracking its TextInputState. This is called
269 // by any view which has updates in its TextInputState (whether tab's RWHV or
270 // that of a child frame). The |view| must unregister itself before being
271 // destroyed (i.e., call TextInputManager::Unregister).
272 void Register(RenderWidgetHostViewBase* view);
273
274 // Clears the TextInputState from the |view|. If |view == active_view_|, this
275 // call will lead to a TextInputState update since the TextInputState.type
276 // should be reset to none.
277 void Unregister(RenderWidgetHostViewBase* view);
278
279 // Returns true if |view| is already registered.
280 bool IsRegistered(RenderWidgetHostViewBase* view) const;
281
282 // Add and remove observers for notifications regarding updates in the
283 // TextInputState. Clients must be sure to remove themselves before they go
284 // away.
285 // Only the tab's RWHV should observer TextInputManager. In tests, however,
286 // in addition to the tab's RWHV, one or more test observers might observe
287 // TextInputManager.
288 void AddObserver(Observer* observer);
289 void RemoveObserver(Observer* observer);
ekaramadfd5f5a892016-08-12 04:33:10290 bool HasObserver(Observer* observer) const;
ekaramadadd882292016-06-08 15:22:56291
ekaramad8cba78862016-06-24 19:42:31292 RenderWidgetHostViewBase* active_view_for_testing() { return active_view_; }
ekaramad936536d2016-06-29 05:44:44293 size_t GetRegisteredViewsCountForTesting();
294 ui::TextInputType GetTextInputTypeForViewForTesting(
295 RenderWidgetHostViewBase* view);
ekaramad2c3c4872017-06-15 14:58:06296 const gfx::Range* GetCompositionRangeForTesting() const;
ekaramad8cba78862016-06-24 19:42:31297
ekaramadadd882292016-06-08 15:22:56298 private:
ekaramadfcce0882016-07-07 02:44:51299 // This class is used to create maps which hold specific IME state for a
300 // view.
301 template <class Value>
302 class ViewMap : public std::unordered_map<RenderWidgetHostViewBase*, Value> {
303 };
304
ekaramadadd882292016-06-08 15:22:56305 void NotifyObserversAboutInputStateUpdate(RenderWidgetHostViewBase* view,
306 bool did_update_state);
307
ekaramad8cba78862016-06-24 19:42:31308 // The view with active text input state, i.e., a focused <input> element.
309 // It will be nullptr if no such view exists. Note that the active view
310 // cannot have a |TextInputState.type| of ui::TEXT_INPUT_TYPE_NONE.
Keishi Hattori0e45c022021-11-27 09:25:52311 raw_ptr<RenderWidgetHostViewBase> active_view_;
ekaramadadd882292016-06-08 15:22:56312
ekaramad4cfc03e2016-07-21 17:34:21313 // The following maps track corresponding IME state for views. For each view,
314 // the values in the map are initialized and cleared in Register and
315 // Unregister methods, respectively.
My Nguyenca19c6d2020-08-05 11:32:42316 ViewMap<ui::mojom::TextInputStatePtr> text_input_state_map_;
ekaramadfcce0882016-07-07 02:44:51317 ViewMap<SelectionRegion> selection_region_map_;
ekaramadb8e23a96c2016-07-13 01:21:15318 ViewMap<CompositionRangeInfo> composition_range_info_map_;
ekaramad6f1786b2016-07-21 21:10:58319 ViewMap<TextSelection> text_selection_map_;
Adam Ettenberger23b345b2024-10-16 20:03:39320#if BUILDFLAG(IS_WIN)
321 ViewMap<blink::mojom::ProximateCharacterRangeBoundsPtr>
322 proximate_character_bounds_map_;
323#endif // BUILDFLAG(IS_WIN)
ekaramadadd882292016-06-08 15:22:56324
Trent Apteda250ec3ab2018-08-19 08:52:19325 base::ObserverList<Observer>::Unchecked observer_list_;
ekaramadadd882292016-06-08 15:22:56326};
327}
328
ekaramadb8e23a96c2016-07-13 01:21:15329#endif // CONTENT_BROWSER_RENDERER_HOST_TEXT_INPUT_MANAGER_H__