blob: 9b5c9828cebc4e2efb9d2a4a6ffa6bdf278c4120 [file] [log] [blame]
[email protected]f751653a92014-02-18 16:32:551// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
jochen73e711c2015-06-03 10:01:465#include "components/test_runner/text_input_controller.h"
[email protected]f751653a92014-02-18 16:32:556
avi5dd91f82015-12-25 22:30:467#include "base/macros.h"
ekaramad2daaf672016-11-10 20:29:018#include "components/test_runner/web_test_delegate.h"
lfg05e41372016-07-22 15:38:109#include "components/test_runner/web_view_test_proxy.h"
[email protected]f751653a92014-02-18 16:32:5510#include "gin/arguments.h"
11#include "gin/handle.h"
12#include "gin/object_template_builder.h"
13#include "gin/wrappable.h"
nzolghadr5d8596502017-01-23 22:59:3514#include "third_party/WebKit/public/platform/WebCoalescedInputEvent.h"
ekaramadc75b1b3b32016-12-02 03:57:5215#include "third_party/WebKit/public/platform/WebInputEventResult.h"
dtapuskaa64845d2017-01-20 21:20:4516#include "third_party/WebKit/public/platform/WebKeyboardEvent.h"
[email protected]f751653a92014-02-18 16:32:5517#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
ekaramad2daaf672016-11-10 20:29:0118#include "third_party/WebKit/public/web/WebFrameWidget.h"
ekaramad2daaf672016-11-10 20:29:0119#include "third_party/WebKit/public/web/WebInputMethodController.h"
[email protected]f751653a92014-02-18 16:32:5520#include "third_party/WebKit/public/web/WebKit.h"
lukasza8b6d5f32016-04-22 16:56:3121#include "third_party/WebKit/public/web/WebLocalFrame.h"
[email protected]f751653a92014-02-18 16:32:5522#include "third_party/WebKit/public/web/WebRange.h"
23#include "third_party/WebKit/public/web/WebView.h"
changwan9bdc18f02015-11-20 02:43:5224#include "third_party/skia/include/core/SkColor.h"
dtapuska899ac222017-01-03 18:09:1625#include "ui/events/base_event_utils.h"
[email protected]f751653a92014-02-18 16:32:5526#include "v8/include/v8.h"
27
jochenf5f31752015-06-03 12:06:3428namespace test_runner {
[email protected]f751653a92014-02-18 16:32:5529
30class TextInputControllerBindings
31 : public gin::Wrappable<TextInputControllerBindings> {
32 public:
33 static gin::WrapperInfo kWrapperInfo;
34
35 static void Install(base::WeakPtr<TextInputController> controller,
lukasza8b6d5f32016-04-22 16:56:3136 blink::WebLocalFrame* frame);
[email protected]f751653a92014-02-18 16:32:5537
38 private:
39 explicit TextInputControllerBindings(
40 base::WeakPtr<TextInputController> controller);
dchenge933b3e2014-10-21 11:44:0941 ~TextInputControllerBindings() override;
[email protected]f751653a92014-02-18 16:32:5542
43 // gin::Wrappable:
dchenge933b3e2014-10-21 11:44:0944 gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
anand.ratn449f39a42014-10-06 13:45:5745 v8::Isolate* isolate) override;
[email protected]f751653a92014-02-18 16:32:5546
47 void InsertText(const std::string& text);
48 void UnmarkText();
49 void DoCommand(const std::string& text);
50 void SetMarkedText(const std::string& text, int start, int length);
51 bool HasMarkedText();
52 std::vector<int> MarkedRange();
53 std::vector<int> SelectedRange();
54 std::vector<int> FirstRectForCharacterRange(unsigned location,
55 unsigned length);
56 void SetComposition(const std::string& text);
ekaramad2daaf672016-11-10 20:29:0157 void ForceTextInputStateUpdate();
[email protected]f751653a92014-02-18 16:32:5558
59 base::WeakPtr<TextInputController> controller_;
60
61 DISALLOW_COPY_AND_ASSIGN(TextInputControllerBindings);
62};
63
64gin::WrapperInfo TextInputControllerBindings::kWrapperInfo = {
65 gin::kEmbedderNativeGin};
66
67// static
68void TextInputControllerBindings::Install(
69 base::WeakPtr<TextInputController> controller,
lukasza8b6d5f32016-04-22 16:56:3170 blink::WebLocalFrame* frame) {
[email protected]f751653a92014-02-18 16:32:5571 v8::Isolate* isolate = blink::mainThreadIsolate();
72 v8::HandleScope handle_scope(isolate);
deepak.s750d68f2015-04-30 07:32:4173 v8::Local<v8::Context> context = frame->mainWorldScriptContext();
[email protected]f751653a92014-02-18 16:32:5574 if (context.IsEmpty())
75 return;
76
77 v8::Context::Scope context_scope(context);
78
79 gin::Handle<TextInputControllerBindings> bindings =
80 gin::CreateHandle(isolate, new TextInputControllerBindings(controller));
[email protected]ad4d2032014-04-28 13:50:5981 if (bindings.IsEmpty())
82 return;
deepak.s750d68f2015-04-30 07:32:4183 v8::Local<v8::Object> global = context->Global();
[email protected]f751653a92014-02-18 16:32:5584 global->Set(gin::StringToV8(isolate, "textInputController"), bindings.ToV8());
85}
86
87TextInputControllerBindings::TextInputControllerBindings(
88 base::WeakPtr<TextInputController> controller)
89 : controller_(controller) {}
90
91TextInputControllerBindings::~TextInputControllerBindings() {}
92
93gin::ObjectTemplateBuilder
94TextInputControllerBindings::GetObjectTemplateBuilder(v8::Isolate* isolate) {
95 return gin::Wrappable<TextInputControllerBindings>::GetObjectTemplateBuilder(
96 isolate)
97 .SetMethod("insertText", &TextInputControllerBindings::InsertText)
98 .SetMethod("unmarkText", &TextInputControllerBindings::UnmarkText)
99 .SetMethod("doCommand", &TextInputControllerBindings::DoCommand)
100 .SetMethod("setMarkedText", &TextInputControllerBindings::SetMarkedText)
101 .SetMethod("hasMarkedText", &TextInputControllerBindings::HasMarkedText)
102 .SetMethod("markedRange", &TextInputControllerBindings::MarkedRange)
103 .SetMethod("selectedRange", &TextInputControllerBindings::SelectedRange)
104 .SetMethod("firstRectForCharacterRange",
105 &TextInputControllerBindings::FirstRectForCharacterRange)
ekaramad2daaf672016-11-10 20:29:01106 .SetMethod("setComposition", &TextInputControllerBindings::SetComposition)
107 .SetMethod("forceTextInputStateUpdate",
108 &TextInputControllerBindings::ForceTextInputStateUpdate);
[email protected]f751653a92014-02-18 16:32:55109}
110
111void TextInputControllerBindings::InsertText(const std::string& text) {
112 if (controller_)
113 controller_->InsertText(text);
114}
115
116void TextInputControllerBindings::UnmarkText() {
117 if (controller_)
118 controller_->UnmarkText();
119}
120
121void TextInputControllerBindings::DoCommand(const std::string& text) {
122 if (controller_)
123 controller_->DoCommand(text);
124}
125
126void TextInputControllerBindings::SetMarkedText(const std::string& text,
127 int start,
128 int length) {
129 if (controller_)
130 controller_->SetMarkedText(text, start, length);
131}
132
133bool TextInputControllerBindings::HasMarkedText() {
134 return controller_ ? controller_->HasMarkedText() : false;
135}
136
137std::vector<int> TextInputControllerBindings::MarkedRange() {
138 return controller_ ? controller_->MarkedRange() : std::vector<int>();
139}
140
141std::vector<int> TextInputControllerBindings::SelectedRange() {
142 return controller_ ? controller_->SelectedRange() : std::vector<int>();
143}
144
145std::vector<int> TextInputControllerBindings::FirstRectForCharacterRange(
146 unsigned location,
147 unsigned length) {
148 return controller_ ? controller_->FirstRectForCharacterRange(location, length)
149 : std::vector<int>();
150}
151
152void TextInputControllerBindings::SetComposition(const std::string& text) {
153 if (controller_)
154 controller_->SetComposition(text);
155}
ekaramad2daaf672016-11-10 20:29:01156void TextInputControllerBindings::ForceTextInputStateUpdate() {
157 if (controller_)
158 controller_->ForceTextInputStateUpdate();
159}
[email protected]f751653a92014-02-18 16:32:55160// TextInputController ---------------------------------------------------------
161
lfg05e41372016-07-22 15:38:10162TextInputController::TextInputController(
163 WebViewTestProxyBase* web_view_test_proxy_base)
164 : web_view_test_proxy_base_(web_view_test_proxy_base),
165 weak_factory_(this) {}
[email protected]f751653a92014-02-18 16:32:55166
167TextInputController::~TextInputController() {}
168
lukasza8b6d5f32016-04-22 16:56:31169void TextInputController::Install(blink::WebLocalFrame* frame) {
[email protected]f751653a92014-02-18 16:32:55170 TextInputControllerBindings::Install(weak_factory_.GetWeakPtr(), frame);
171}
172
[email protected]f751653a92014-02-18 16:32:55173void TextInputController::InsertText(const std::string& text) {
ekaramadc75b1b3b32016-12-02 03:57:52174 if (auto* controller = GetInputMethodController()) {
rlanday7efe2302017-01-11 00:14:28175 controller->commitText(blink::WebString::fromUTF8(text),
176 std::vector<blink::WebCompositionUnderline>(), 0);
ekaramadc75b1b3b32016-12-02 03:57:52177 }
[email protected]f751653a92014-02-18 16:32:55178}
179
180void TextInputController::UnmarkText() {
ekaramadc75b1b3b32016-12-02 03:57:52181 if (auto* controller = GetInputMethodController()) {
182 controller->finishComposingText(
183 blink::WebInputMethodController::KeepSelection);
184 }
[email protected]f751653a92014-02-18 16:32:55185}
186
187void TextInputController::DoCommand(const std::string& text) {
yabinh8204efc2016-07-08 13:58:57188 if (view()->mainFrame()) {
189 if (!view()->mainFrame()->toWebLocalFrame()) {
190 CHECK(false) << "This function cannot be called if the main frame is not"
191 "a local frame.";
192 }
193 view()->mainFrame()->toWebLocalFrame()->executeCommand(
194 blink::WebString::fromUTF8(text));
195 }
[email protected]f751653a92014-02-18 16:32:55196}
197
198void TextInputController::SetMarkedText(const std::string& text,
199 int start,
200 int length) {
201 blink::WebString web_text(blink::WebString::fromUTF8(text));
202
203 // Split underline into up to 3 elements (before, selection, and after).
204 std::vector<blink::WebCompositionUnderline> underlines;
205 blink::WebCompositionUnderline underline;
206 if (!start) {
207 underline.endOffset = length;
208 } else {
209 underline.endOffset = start;
210 underlines.push_back(underline);
211 underline.startOffset = start;
212 underline.endOffset = start + length;
213 }
214 underline.thick = true;
215 underlines.push_back(underline);
216 if (start + length < static_cast<int>(web_text.length())) {
217 underline.startOffset = underline.endOffset;
218 underline.endOffset = web_text.length();
219 underline.thick = false;
220 underlines.push_back(underline);
221 }
222
ekaramadc75b1b3b32016-12-02 03:57:52223 if (auto* controller = GetInputMethodController()) {
224 controller->setComposition(web_text, underlines, start, start + length);
225 }
[email protected]f751653a92014-02-18 16:32:55226}
227
228bool TextInputController::HasMarkedText() {
yabinh8204efc2016-07-08 13:58:57229 if (!view()->mainFrame())
230 return false;
231
232 if (!view()->mainFrame()->toWebLocalFrame()) {
233 CHECK(false) << "This function cannot be called if the main frame is not"
234 "a local frame.";
235 }
236
237 return view()->mainFrame()->toWebLocalFrame()->hasMarkedText();
[email protected]f751653a92014-02-18 16:32:55238}
239
240std::vector<int> TextInputController::MarkedRange() {
lukasza8b6d5f32016-04-22 16:56:31241 if (!view()->mainFrame())
[email protected]6f9397122014-02-25 09:30:50242 return std::vector<int>();
243
yabinh8204efc2016-07-08 13:58:57244 if (!view()->mainFrame()->toWebLocalFrame()) {
245 CHECK(false) << "This function cannot be called if the main frame is not"
246 "a local frame.";
247 }
248
249 blink::WebRange range = view()->mainFrame()->toWebLocalFrame()->markedRange();
[email protected]f751653a92014-02-18 16:32:55250 std::vector<int> int_array(2);
251 int_array[0] = range.startOffset();
252 int_array[1] = range.endOffset();
253
254 return int_array;
255}
256
257std::vector<int> TextInputController::SelectedRange() {
lukasza8b6d5f32016-04-22 16:56:31258 if (!view()->mainFrame())
[email protected]6f9397122014-02-25 09:30:50259 return std::vector<int>();
260
yabinh8204efc2016-07-08 13:58:57261 if (!view()->mainFrame()->toWebLocalFrame()) {
262 CHECK(false) << "This function cannot be called if the main frame is not"
263 "a local frame.";
264 }
265
266 blink::WebRange range =
267 view()->mainFrame()->toWebLocalFrame()->selectionRange();
yosin702c78a32016-06-13 08:39:13268 if (range.isNull())
269 return std::vector<int>();
[email protected]f751653a92014-02-18 16:32:55270 std::vector<int> int_array(2);
271 int_array[0] = range.startOffset();
272 int_array[1] = range.endOffset();
273
274 return int_array;
275}
276
277std::vector<int> TextInputController::FirstRectForCharacterRange(
278 unsigned location,
279 unsigned length) {
280 blink::WebRect rect;
lukasza8b6d5f32016-04-22 16:56:31281 if (!view()->focusedFrame() ||
282 !view()->focusedFrame()->firstRectForCharacterRange(location, length,
283 rect)) {
[email protected]f751653a92014-02-18 16:32:55284 return std::vector<int>();
[email protected]6f9397122014-02-25 09:30:50285 }
[email protected]f751653a92014-02-18 16:32:55286
287 std::vector<int> int_array(4);
288 int_array[0] = rect.x;
289 int_array[1] = rect.y;
290 int_array[2] = rect.width;
291 int_array[3] = rect.height;
292
293 return int_array;
294}
295
296void TextInputController::SetComposition(const std::string& text) {
297 // Sends a keydown event with key code = 0xE5 to emulate input method
298 // behavior.
dtapuska899ac222017-01-03 18:09:16299 blink::WebKeyboardEvent key_down(
300 blink::WebInputEvent::RawKeyDown, blink::WebInputEvent::NoModifiers,
301 ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
302
[email protected]f751653a92014-02-18 16:32:55303 key_down.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY
nzolghadr5d8596502017-01-23 22:59:35304 view()->handleInputEvent(blink::WebCoalescedInputEvent(key_down));
[email protected]f751653a92014-02-18 16:32:55305
yabinhdd6d1b62016-08-23 07:09:02306 // The value returned by std::string::length() may not correspond to the
307 // actual number of encoded characters in sequences of multi-byte or
308 // variable-length characters.
309 blink::WebString newText = blink::WebString::fromUTF8(text);
310 size_t textLength = newText.length();
311
changwan9bdc18f02015-11-20 02:43:52312 std::vector<blink::WebCompositionUnderline> underlines;
yabinhdd6d1b62016-08-23 07:09:02313 underlines.push_back(blink::WebCompositionUnderline(
314 0, textLength, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
ekaramadc75b1b3b32016-12-02 03:57:52315 if (auto* controller = GetInputMethodController()) {
316 controller->setComposition(
317 newText, blink::WebVector<blink::WebCompositionUnderline>(underlines),
318 textLength, textLength);
319 }
lukasza8b6d5f32016-04-22 16:56:31320}
321
ekaramad2daaf672016-11-10 20:29:01322void TextInputController::ForceTextInputStateUpdate() {
323 web_view_test_proxy_base_->delegate()->ForceTextInputStateUpdate(
324 view()->mainFrame());
325}
326
lukasza8b6d5f32016-04-22 16:56:31327blink::WebView* TextInputController::view() {
lfg05e41372016-07-22 15:38:10328 return web_view_test_proxy_base_->web_view();
[email protected]f751653a92014-02-18 16:32:55329}
330
ekaramadc75b1b3b32016-12-02 03:57:52331blink::WebInputMethodController*
332TextInputController::GetInputMethodController() {
333 if (!view()->mainFrame())
334 return nullptr;
335
ekaramad2daaf672016-11-10 20:29:01336 blink::WebLocalFrame* mainFrame = view()->mainFrame()->toWebLocalFrame();
337 if (!mainFrame) {
338 CHECK(false) << "WebView does not have a local main frame and"
339 " cannot handle input method controller tasks.";
340 }
341 return mainFrame->frameWidget()->getActiveWebInputMethodController();
342}
343
jochenf5f31752015-06-03 12:06:34344} // namespace test_runner