Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
danakj | 89f4708 | 2020-09-02 17:53:43 | [diff] [blame] | 5 | #include "content/web_test/renderer/text_input_controller.h" |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 6 | |
Dave Tapuska | 13da016a | 2021-02-04 21:51:58 | [diff] [blame] | 7 | #include "content/web_test/renderer/web_frame_test_proxy.h" |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 8 | #include "gin/arguments.h" |
| 9 | #include "gin/handle.h" |
| 10 | #include "gin/object_template_builder.h" |
| 11 | #include "gin/wrappable.h" |
Dave Tapuska | 0dd120b | 2020-05-08 17:06:52 | [diff] [blame] | 12 | #include "third_party/blink/public/common/input/web_coalesced_input_event.h" |
Dave Tapuska | 129cef8 | 2019-12-19 16:36:48 | [diff] [blame] | 13 | #include "third_party/blink/public/common/input/web_keyboard_event.h" |
Dave Tapuska | d1aaf8a | 2023-09-12 14:50:34 | [diff] [blame] | 14 | #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h" |
Blink Reformat | a30d423 | 2018-04-07 15:31:06 | [diff] [blame] | 15 | #include "third_party/blink/public/platform/web_input_event_result.h" |
Blink Reformat | a30d423 | 2018-04-07 15:31:06 | [diff] [blame] | 16 | #include "third_party/blink/public/web/web_frame_widget.h" |
Blink Reformat | a30d423 | 2018-04-07 15:31:06 | [diff] [blame] | 17 | #include "third_party/blink/public/web/web_input_method_controller.h" |
| 18 | #include "third_party/blink/public/web/web_local_frame.h" |
| 19 | #include "third_party/blink/public/web/web_range.h" |
| 20 | #include "third_party/blink/public/web/web_view.h" |
changwan | 9bdc18f0 | 2015-11-20 02:43:52 | [diff] [blame] | 21 | #include "third_party/skia/include/core/SkColor.h" |
Dave Tapuska | 7fa7521 | 2020-06-04 17:46:11 | [diff] [blame] | 22 | #include "ui/base/ime/ime_text_span.h" |
dtapuska | 899ac22 | 2017-01-03 18:09:16 | [diff] [blame] | 23 | #include "ui/events/base_event_utils.h" |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 24 | #include "v8/include/v8.h" |
| 25 | |
danakj | 741848a | 2020-04-07 22:48:06 | [diff] [blame] | 26 | namespace content { |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 27 | |
| 28 | class TextInputControllerBindings |
| 29 | : public gin::Wrappable<TextInputControllerBindings> { |
| 30 | public: |
| 31 | static gin::WrapperInfo kWrapperInfo; |
| 32 | |
Peter Boström | 9b03653 | 2021-10-28 23:37:28 | [diff] [blame] | 33 | TextInputControllerBindings(const TextInputControllerBindings&) = delete; |
| 34 | TextInputControllerBindings& operator=(const TextInputControllerBindings&) = |
| 35 | delete; |
| 36 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 37 | static void Install(base::WeakPtr<TextInputController> controller, |
lukasza | 8b6d5f3 | 2016-04-22 16:56:31 | [diff] [blame] | 38 | blink::WebLocalFrame* frame); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 39 | |
| 40 | private: |
| 41 | explicit TextInputControllerBindings( |
| 42 | base::WeakPtr<TextInputController> controller); |
dcheng | e933b3e | 2014-10-21 11:44:09 | [diff] [blame] | 43 | ~TextInputControllerBindings() override; |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 44 | |
| 45 | // gin::Wrappable: |
dcheng | e933b3e | 2014-10-21 11:44:09 | [diff] [blame] | 46 | gin::ObjectTemplateBuilder GetObjectTemplateBuilder( |
anand.ratn | 449f39a4 | 2014-10-06 13:45:57 | [diff] [blame] | 47 | v8::Isolate* isolate) override; |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 48 | |
| 49 | void InsertText(const std::string& text); |
| 50 | void UnmarkText(); |
Ryan Landay | 7b5dbd5 | 2018-01-11 19:05:59 | [diff] [blame] | 51 | void UnmarkAndUnselectText(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 52 | void DoCommand(const std::string& text); |
Ryan Landay | 9c680942 | 2018-01-17 07:04:15 | [diff] [blame] | 53 | void ExtendSelectionAndDelete(int before, int after); |
Ryan Landay | 6d163ae | 2018-01-17 06:58:06 | [diff] [blame] | 54 | void DeleteSurroundingText(int before, int after); |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 55 | void SetMarkedText(const std::string& text, uint32_t start, uint32_t length); |
| 56 | void SetMarkedTextFromExistingText(uint32_t start, uint32_t end); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 57 | bool HasMarkedText(); |
| 58 | std::vector<int> MarkedRange(); |
| 59 | std::vector<int> SelectedRange(); |
Peter Kasting | 7064abb | 2022-08-11 18:11:38 | [diff] [blame] | 60 | std::vector<int> FirstRectForCharacterRange(uint32_t location, |
| 61 | uint32_t length); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 62 | void SetComposition(const std::string& text); |
Alex Keng | d1e6590d | 2022-06-28 10:31:12 | [diff] [blame] | 63 | void SetCompositionWithReplacementRange(const std::string& text, |
| 64 | int replacement_start, |
| 65 | int replacement_end); |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 66 | void ForceTextInputStateUpdate(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 67 | |
| 68 | base::WeakPtr<TextInputController> controller_; |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 69 | }; |
| 70 | |
| 71 | gin::WrapperInfo TextInputControllerBindings::kWrapperInfo = { |
| 72 | gin::kEmbedderNativeGin}; |
| 73 | |
| 74 | // static |
| 75 | void TextInputControllerBindings::Install( |
| 76 | base::WeakPtr<TextInputController> controller, |
lukasza | 8b6d5f3 | 2016-04-22 16:56:31 | [diff] [blame] | 77 | blink::WebLocalFrame* frame) { |
Dave Tapuska | d1aaf8a | 2023-09-12 14:50:34 | [diff] [blame] | 78 | v8::Isolate* isolate = frame->GetAgentGroupScheduler()->Isolate(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 79 | v8::HandleScope handle_scope(isolate); |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 80 | v8::Local<v8::Context> context = frame->MainWorldScriptContext(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 81 | if (context.IsEmpty()) |
| 82 | return; |
| 83 | |
| 84 | v8::Context::Scope context_scope(context); |
| 85 | |
| 86 | gin::Handle<TextInputControllerBindings> bindings = |
| 87 | gin::CreateHandle(isolate, new TextInputControllerBindings(controller)); |
[email protected] | ad4d203 | 2014-04-28 13:50:59 | [diff] [blame] | 88 | if (bindings.IsEmpty()) |
| 89 | return; |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 90 | v8::Local<v8::Object> global = context->Global(); |
Dan Elphick | a83be51 | 2019-02-05 15:57:23 | [diff] [blame] | 91 | global |
| 92 | ->Set(context, gin::StringToV8(isolate, "textInputController"), |
| 93 | bindings.ToV8()) |
| 94 | .Check(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | TextInputControllerBindings::TextInputControllerBindings( |
| 98 | base::WeakPtr<TextInputController> controller) |
| 99 | : controller_(controller) {} |
| 100 | |
| 101 | TextInputControllerBindings::~TextInputControllerBindings() {} |
| 102 | |
| 103 | gin::ObjectTemplateBuilder |
| 104 | TextInputControllerBindings::GetObjectTemplateBuilder(v8::Isolate* isolate) { |
| 105 | return gin::Wrappable<TextInputControllerBindings>::GetObjectTemplateBuilder( |
| 106 | isolate) |
| 107 | .SetMethod("insertText", &TextInputControllerBindings::InsertText) |
| 108 | .SetMethod("unmarkText", &TextInputControllerBindings::UnmarkText) |
Ryan Landay | 7b5dbd5 | 2018-01-11 19:05:59 | [diff] [blame] | 109 | .SetMethod("unmarkAndUnselectText", |
| 110 | &TextInputControllerBindings::UnmarkAndUnselectText) |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 111 | .SetMethod("doCommand", &TextInputControllerBindings::DoCommand) |
Ryan Landay | 9c680942 | 2018-01-17 07:04:15 | [diff] [blame] | 112 | .SetMethod("extendSelectionAndDelete", |
| 113 | &TextInputControllerBindings::ExtendSelectionAndDelete) |
Ryan Landay | 6d163ae | 2018-01-17 06:58:06 | [diff] [blame] | 114 | .SetMethod("deleteSurroundingText", |
| 115 | &TextInputControllerBindings::DeleteSurroundingText) |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 116 | .SetMethod("setMarkedText", &TextInputControllerBindings::SetMarkedText) |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 117 | .SetMethod("setMarkedTextFromExistingText", |
| 118 | &TextInputControllerBindings::SetMarkedTextFromExistingText) |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 119 | .SetMethod("hasMarkedText", &TextInputControllerBindings::HasMarkedText) |
| 120 | .SetMethod("markedRange", &TextInputControllerBindings::MarkedRange) |
| 121 | .SetMethod("selectedRange", &TextInputControllerBindings::SelectedRange) |
| 122 | .SetMethod("firstRectForCharacterRange", |
| 123 | &TextInputControllerBindings::FirstRectForCharacterRange) |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 124 | .SetMethod("setComposition", &TextInputControllerBindings::SetComposition) |
Alex Keng | d1e6590d | 2022-06-28 10:31:12 | [diff] [blame] | 125 | .SetMethod( |
| 126 | "setCompositionWithReplacementRange", |
| 127 | &TextInputControllerBindings::SetCompositionWithReplacementRange) |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 128 | .SetMethod("forceTextInputStateUpdate", |
| 129 | &TextInputControllerBindings::ForceTextInputStateUpdate); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | void TextInputControllerBindings::InsertText(const std::string& text) { |
| 133 | if (controller_) |
| 134 | controller_->InsertText(text); |
| 135 | } |
| 136 | |
| 137 | void TextInputControllerBindings::UnmarkText() { |
| 138 | if (controller_) |
| 139 | controller_->UnmarkText(); |
| 140 | } |
| 141 | |
Ryan Landay | 7b5dbd5 | 2018-01-11 19:05:59 | [diff] [blame] | 142 | void TextInputControllerBindings::UnmarkAndUnselectText() { |
| 143 | if (controller_) |
| 144 | controller_->UnmarkAndUnselectText(); |
| 145 | } |
| 146 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 147 | void TextInputControllerBindings::DoCommand(const std::string& text) { |
| 148 | if (controller_) |
| 149 | controller_->DoCommand(text); |
| 150 | } |
| 151 | |
Ryan Landay | 9c680942 | 2018-01-17 07:04:15 | [diff] [blame] | 152 | void TextInputControllerBindings::ExtendSelectionAndDelete(int before, |
| 153 | int after) { |
| 154 | if (controller_) |
| 155 | controller_->ExtendSelectionAndDelete(before, after); |
| 156 | } |
| 157 | |
Ryan Landay | 6d163ae | 2018-01-17 06:58:06 | [diff] [blame] | 158 | void TextInputControllerBindings::DeleteSurroundingText(int before, int after) { |
| 159 | if (controller_) |
| 160 | controller_->DeleteSurroundingText(before, after); |
| 161 | } |
| 162 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 163 | void TextInputControllerBindings::SetMarkedText(const std::string& text, |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 164 | uint32_t start, |
| 165 | uint32_t length) { |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 166 | if (controller_) |
| 167 | controller_->SetMarkedText(text, start, length); |
| 168 | } |
| 169 | |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 170 | void TextInputControllerBindings::SetMarkedTextFromExistingText(uint32_t start, |
| 171 | uint32_t end) { |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 172 | if (controller_) |
| 173 | controller_->SetMarkedTextFromExistingText(start, end); |
| 174 | } |
| 175 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 176 | bool TextInputControllerBindings::HasMarkedText() { |
| 177 | return controller_ ? controller_->HasMarkedText() : false; |
| 178 | } |
| 179 | |
| 180 | std::vector<int> TextInputControllerBindings::MarkedRange() { |
| 181 | return controller_ ? controller_->MarkedRange() : std::vector<int>(); |
| 182 | } |
| 183 | |
| 184 | std::vector<int> TextInputControllerBindings::SelectedRange() { |
| 185 | return controller_ ? controller_->SelectedRange() : std::vector<int>(); |
| 186 | } |
| 187 | |
| 188 | std::vector<int> TextInputControllerBindings::FirstRectForCharacterRange( |
Peter Kasting | 7064abb | 2022-08-11 18:11:38 | [diff] [blame] | 189 | uint32_t location, |
| 190 | uint32_t length) { |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 191 | return controller_ ? controller_->FirstRectForCharacterRange(location, length) |
| 192 | : std::vector<int>(); |
| 193 | } |
| 194 | |
| 195 | void TextInputControllerBindings::SetComposition(const std::string& text) { |
| 196 | if (controller_) |
Alex Keng | d1e6590d | 2022-06-28 10:31:12 | [diff] [blame] | 197 | controller_->SetComposition(text, -1, -1); |
| 198 | } |
| 199 | void TextInputControllerBindings::SetCompositionWithReplacementRange( |
| 200 | const std::string& text, |
| 201 | int replacement_start, |
| 202 | int replacement_end) { |
| 203 | if (controller_) |
| 204 | controller_->SetComposition(text, replacement_start, replacement_end); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 205 | } |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 206 | void TextInputControllerBindings::ForceTextInputStateUpdate() { |
| 207 | if (controller_) |
| 208 | controller_->ForceTextInputStateUpdate(); |
| 209 | } |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 210 | // TextInputController --------------------------------------------------------- |
| 211 | |
Dave Tapuska | 13da016a | 2021-02-04 21:51:58 | [diff] [blame] | 212 | TextInputController::TextInputController( |
| 213 | WebFrameTestProxy* web_frame_test_proxy) |
| 214 | : web_frame_test_proxy_(web_frame_test_proxy) {} |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 215 | |
| 216 | TextInputController::~TextInputController() {} |
| 217 | |
lukasza | 8b6d5f3 | 2016-04-22 16:56:31 | [diff] [blame] | 218 | void TextInputController::Install(blink::WebLocalFrame* frame) { |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 219 | TextInputControllerBindings::Install(weak_factory_.GetWeakPtr(), frame); |
| 220 | } |
| 221 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 222 | void TextInputController::InsertText(const std::string& text) { |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 223 | if (auto* controller = GetInputMethodController()) { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 224 | controller->CommitText(blink::WebString::FromUTF8(text), |
Dave Tapuska | 7fa7521 | 2020-06-04 17:46:11 | [diff] [blame] | 225 | std::vector<ui::ImeTextSpan>(), blink::WebRange(), |
| 226 | 0); |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 227 | } |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | void TextInputController::UnmarkText() { |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 231 | if (auto* controller = GetInputMethodController()) { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 232 | controller->FinishComposingText( |
| 233 | blink::WebInputMethodController::kKeepSelection); |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 234 | } |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 235 | } |
| 236 | |
Ryan Landay | 7b5dbd5 | 2018-01-11 19:05:59 | [diff] [blame] | 237 | void TextInputController::UnmarkAndUnselectText() { |
| 238 | if (auto* controller = GetInputMethodController()) { |
| 239 | controller->FinishComposingText( |
| 240 | blink::WebInputMethodController::kDoNotKeepSelection); |
| 241 | } |
| 242 | } |
| 243 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 244 | void TextInputController::DoCommand(const std::string& text) { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 245 | if (view()->MainFrame()) { |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 246 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 247 | "called if the main frame " |
| 248 | "is not a local frame."; |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 249 | view()->MainFrame()->ToWebLocalFrame()->ExecuteCommand( |
| 250 | blink::WebString::FromUTF8(text)); |
yabinh | 8204efc | 2016-07-08 13:58:57 | [diff] [blame] | 251 | } |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 252 | } |
| 253 | |
Ryan Landay | 9c680942 | 2018-01-17 07:04:15 | [diff] [blame] | 254 | void TextInputController::ExtendSelectionAndDelete(int before, int after) { |
| 255 | if (view()->MainFrame()) { |
| 256 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 257 | "called if the main frame " |
| 258 | "is not a local frame."; |
| 259 | view()->MainFrame()->ToWebLocalFrame()->ExtendSelectionAndDelete(before, |
| 260 | after); |
| 261 | } |
| 262 | } |
| 263 | |
Ryan Landay | 6d163ae | 2018-01-17 06:58:06 | [diff] [blame] | 264 | void TextInputController::DeleteSurroundingText(int before, int after) { |
| 265 | if (view()->MainFrame()) { |
| 266 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 267 | "called if the main frame " |
| 268 | "is not a local frame."; |
| 269 | view()->MainFrame()->ToWebLocalFrame()->DeleteSurroundingText(before, |
| 270 | after); |
| 271 | } |
| 272 | } |
| 273 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 274 | void TextInputController::SetMarkedText(const std::string& text, |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 275 | uint32_t start, |
| 276 | uint32_t length) { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 277 | blink::WebString web_text(blink::WebString::FromUTF8(text)); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 278 | |
| 279 | // Split underline into up to 3 elements (before, selection, and after). |
Dave Tapuska | 7fa7521 | 2020-06-04 17:46:11 | [diff] [blame] | 280 | std::vector<ui::ImeTextSpan> ime_text_spans; |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 281 | ui::ImeTextSpan selection; |
| 282 | if (start) { |
| 283 | ui::ImeTextSpan before; |
| 284 | before.end_offset = start; |
| 285 | ime_text_spans.push_back(before); |
| 286 | |
| 287 | selection.start_offset = start; |
| 288 | selection.end_offset = base::ClampedNumeric(start) + length; |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 289 | } else { |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 290 | selection.end_offset = length; |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 291 | } |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 292 | if (selection.end_offset != selection.start_offset) { |
| 293 | selection.thickness = ui::ImeTextSpan::Thickness::kThick; |
| 294 | selection.underline_style = ui::ImeTextSpan::UnderlineStyle::kSolid; |
| 295 | ime_text_spans.push_back(selection); |
| 296 | } |
| 297 | if (selection.end_offset < web_text.length()) { |
| 298 | ui::ImeTextSpan after; |
| 299 | after.start_offset = selection.end_offset; |
| 300 | after.end_offset = web_text.length(); |
| 301 | ime_text_spans.push_back(after); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 302 | } |
| 303 | |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 304 | if (auto* controller = GetInputMethodController()) { |
Ryan Landay | 9e42fd74 | 2017-08-12 01:59:11 | [diff] [blame] | 305 | controller->SetComposition(web_text, ime_text_spans, blink::WebRange(), |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 306 | selection.start_offset, selection.end_offset); |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 307 | } |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 308 | } |
| 309 | |
Peter Kasting | e468f50 | 2023-11-06 19:22:52 | [diff] [blame] | 310 | void TextInputController::SetMarkedTextFromExistingText(uint32_t start, |
| 311 | uint32_t end) { |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 312 | if (!view()->MainFrame()) |
| 313 | return; |
| 314 | |
| 315 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 316 | "called if the main frame " |
| 317 | "is not a local frame."; |
| 318 | |
| 319 | view()->MainFrame()->ToWebLocalFrame()->SetCompositionFromExistingText( |
Dave Tapuska | 7fa7521 | 2020-06-04 17:46:11 | [diff] [blame] | 320 | start, end, std::vector<ui::ImeTextSpan>()); |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 321 | } |
| 322 | |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 323 | bool TextInputController::HasMarkedText() { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 324 | if (!view()->MainFrame()) |
yabinh | 8204efc | 2016-07-08 13:58:57 | [diff] [blame] | 325 | return false; |
| 326 | |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 327 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 328 | "called if the main frame " |
| 329 | "is not a local frame."; |
yabinh | 8204efc | 2016-07-08 13:58:57 | [diff] [blame] | 330 | |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 331 | return view()->MainFrame()->ToWebLocalFrame()->HasMarkedText(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | std::vector<int> TextInputController::MarkedRange() { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 335 | if (!view()->MainFrame()) |
[email protected] | 6f939712 | 2014-02-25 09:30:50 | [diff] [blame] | 336 | return std::vector<int>(); |
| 337 | |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 338 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 339 | "called if the main frame " |
| 340 | "is not a local frame."; |
yabinh | 8204efc | 2016-07-08 13:58:57 | [diff] [blame] | 341 | |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 342 | blink::WebRange range = view()->MainFrame()->ToWebLocalFrame()->MarkedRange(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 343 | std::vector<int> int_array(2); |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 344 | int_array[0] = range.StartOffset(); |
| 345 | int_array[1] = range.EndOffset(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 346 | |
| 347 | return int_array; |
| 348 | } |
| 349 | |
| 350 | std::vector<int> TextInputController::SelectedRange() { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 351 | if (!view()->MainFrame()) |
[email protected] | 6f939712 | 2014-02-25 09:30:50 | [diff] [blame] | 352 | return std::vector<int>(); |
| 353 | |
Ryan Landay | d203467 | 2018-01-12 22:22:32 | [diff] [blame] | 354 | CHECK(view()->MainFrame()->ToWebLocalFrame()) << "This function cannot be " |
| 355 | "called if the main frame " |
| 356 | "is not a local frame."; |
yabinh | 8204efc | 2016-07-08 13:58:57 | [diff] [blame] | 357 | |
| 358 | blink::WebRange range = |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 359 | view()->MainFrame()->ToWebLocalFrame()->SelectionRange(); |
| 360 | if (range.IsNull()) |
yosin | 702c78a3 | 2016-06-13 08:39:13 | [diff] [blame] | 361 | return std::vector<int>(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 362 | std::vector<int> int_array(2); |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 363 | int_array[0] = range.StartOffset(); |
| 364 | int_array[1] = range.EndOffset(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 365 | |
| 366 | return int_array; |
| 367 | } |
| 368 | |
| 369 | std::vector<int> TextInputController::FirstRectForCharacterRange( |
Peter Kasting | 7064abb | 2022-08-11 18:11:38 | [diff] [blame] | 370 | uint32_t location, |
| 371 | uint32_t length) { |
Dave Tapuska | e8fe9b2 | 2021-01-21 20:38:21 | [diff] [blame] | 372 | gfx::Rect rect; |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 373 | if (!view()->FocusedFrame() || |
| 374 | !view()->FocusedFrame()->FirstRectForCharacterRange(location, length, |
lukasza | 8b6d5f3 | 2016-04-22 16:56:31 | [diff] [blame] | 375 | rect)) { |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 376 | return std::vector<int>(); |
[email protected] | 6f939712 | 2014-02-25 09:30:50 | [diff] [blame] | 377 | } |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 378 | |
| 379 | std::vector<int> int_array(4); |
Dave Tapuska | e8fe9b2 | 2021-01-21 20:38:21 | [diff] [blame] | 380 | int_array[0] = rect.x(); |
| 381 | int_array[1] = rect.y(); |
| 382 | int_array[2] = rect.width(); |
| 383 | int_array[3] = rect.height(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 384 | |
| 385 | return int_array; |
| 386 | } |
| 387 | |
Alex Keng | d1e6590d | 2022-06-28 10:31:12 | [diff] [blame] | 388 | void TextInputController::SetComposition(const std::string& text, |
| 389 | int replacement_range_start, |
| 390 | int replacement_range_end) { |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 391 | // Sends a keydown event with key code = 0xE5 to emulate input method |
| 392 | // behavior. |
Dave Tapuska | 347d60a | 2020-04-21 23:55:47 | [diff] [blame] | 393 | blink::WebKeyboardEvent key_down(blink::WebInputEvent::Type::kRawKeyDown, |
Daniel Cheng | 224569ee | 2018-04-25 05:45:06 | [diff] [blame] | 394 | blink::WebInputEvent::kNoModifiers, |
| 395 | ui::EventTimeForNow()); |
dtapuska | 899ac22 | 2017-01-03 18:09:16 | [diff] [blame] | 396 | |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 397 | key_down.windows_key_code = 0xE5; // VKEY_PROCESSKEY |
danakj | 763c240 | 2018-11-09 02:46:22 | [diff] [blame] | 398 | view()->MainFrameWidget()->HandleInputEvent( |
Dave Tapuska | cc788dd | 2020-05-11 22:33:56 | [diff] [blame] | 399 | blink::WebCoalescedInputEvent(key_down, ui::LatencyInfo())); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 400 | |
yabinh | dd6d1b6 | 2016-08-23 07:09:02 | [diff] [blame] | 401 | // The value returned by std::string::length() may not correspond to the |
| 402 | // actual number of encoded characters in sequences of multi-byte or |
| 403 | // variable-length characters. |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 404 | blink::WebString newText = blink::WebString::FromUTF8(text); |
yabinh | dd6d1b6 | 2016-08-23 07:09:02 | [diff] [blame] | 405 | size_t textLength = newText.length(); |
| 406 | |
Dave Tapuska | 7fa7521 | 2020-06-04 17:46:11 | [diff] [blame] | 407 | std::vector<ui::ImeTextSpan> ime_text_spans; |
| 408 | ime_text_spans.push_back(ui::ImeTextSpan( |
| 409 | ui::ImeTextSpan::Type::kComposition, 0, textLength, |
| 410 | ui::ImeTextSpan::Thickness::kThin, |
| 411 | ui::ImeTextSpan::UnderlineStyle::kSolid, SK_ColorTRANSPARENT)); |
Alex Keng | d1e6590d | 2022-06-28 10:31:12 | [diff] [blame] | 412 | blink::WebRange replacement_range = |
| 413 | (replacement_range_start == -1 && replacement_range_end == -1) |
| 414 | ? blink::WebRange() |
| 415 | : blink::WebRange(replacement_range_start, |
| 416 | replacement_range_end - replacement_range_start); |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 417 | if (auto* controller = GetInputMethodController()) { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 418 | controller->SetComposition( |
Xianzhu Wang | aece0d4 | 2025-01-29 01:57:12 | [diff] [blame] | 419 | newText, std::vector<ui::ImeTextSpan>(std::move(ime_text_spans)), |
Alex Keng | d1e6590d | 2022-06-28 10:31:12 | [diff] [blame] | 420 | replacement_range, textLength, textLength); |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 421 | } |
lukasza | 8b6d5f3 | 2016-04-22 16:56:31 | [diff] [blame] | 422 | } |
| 423 | |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 424 | void TextInputController::ForceTextInputStateUpdate() { |
Dave Tapuska | a5c5348510 | 2020-12-01 21:31:02 | [diff] [blame] | 425 | blink::WebFrameWidget* frame_widget = |
Dave Tapuska | 13da016a | 2021-02-04 21:51:58 | [diff] [blame] | 426 | web_frame_test_proxy_->GetLocalRootWebFrameWidget(); |
Dave Tapuska | a5c5348510 | 2020-12-01 21:31:02 | [diff] [blame] | 427 | frame_widget->ShowVirtualKeyboard(); |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 428 | } |
| 429 | |
lukasza | 8b6d5f3 | 2016-04-22 16:56:31 | [diff] [blame] | 430 | blink::WebView* TextInputController::view() { |
Dave Tapuska | 13da016a | 2021-02-04 21:51:58 | [diff] [blame] | 431 | return web_frame_test_proxy_->GetWebFrame()->View(); |
[email protected] | f751653a9 | 2014-02-18 16:32:55 | [diff] [blame] | 432 | } |
| 433 | |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 434 | blink::WebInputMethodController* |
| 435 | TextInputController::GetInputMethodController() { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 436 | if (!view()->MainFrame()) |
ekaramad | c75b1b3b3 | 2016-12-02 03:57:52 | [diff] [blame] | 437 | return nullptr; |
| 438 | |
Kent Tamura | 21d1de6 | 2018-12-10 04:45:20 | [diff] [blame] | 439 | // TODO(lukasza): Finish adding OOPIF support to the web tests harness. |
Daniel Cheng | 3c82943 | 2017-06-19 03:42:31 | [diff] [blame] | 440 | CHECK(view()->MainFrame()->IsWebLocalFrame()) |
| 441 | << "WebView does not have a local main frame and" |
| 442 | " cannot handle input method controller tasks."; |
| 443 | |
danakj | e3e48d4 | 2020-05-01 23:47:33 | [diff] [blame] | 444 | return view()->MainFrameWidget()->GetActiveWebInputMethodController(); |
ekaramad | 2daaf67 | 2016-11-10 20:29:01 | [diff] [blame] | 445 | } |
| 446 | |
danakj | 741848a | 2020-04-07 22:48:06 | [diff] [blame] | 447 | } // namespace content |