blob: 9e7d7baad5e167005a87ca92067bd901a3c8bf88 [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
dcheng59826e32017-02-22 10:31:365#include "content/shell/test_runner/text_input_controller.h"
[email protected]f751653a92014-02-18 16:32:556
avi5dd91f82015-12-25 22:30:467#include "base/macros.h"
dcheng59826e32017-02-22 10:31:368#include "content/shell/test_runner/web_test_delegate.h"
9#include "content/shell/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) {
Blink Reformat1c4d759e2017-04-09 16:34:5471 v8::Isolate* isolate = blink::MainThreadIsolate();
[email protected]f751653a92014-02-18 16:32:5572 v8::HandleScope handle_scope(isolate);
Blink Reformat1c4d759e2017-04-09 16:34:5473 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()) {
Blink Reformat1c4d759e2017-04-09 16:34:54175 controller->CommitText(blink::WebString::FromUTF8(text),
ekaramadce32ef9f2017-02-09 17:33:56176 std::vector<blink::WebCompositionUnderline>(),
177 blink::WebRange(), 0);
ekaramadc75b1b3b32016-12-02 03:57:52178 }
[email protected]f751653a92014-02-18 16:32:55179}
180
181void TextInputController::UnmarkText() {
ekaramadc75b1b3b32016-12-02 03:57:52182 if (auto* controller = GetInputMethodController()) {
Blink Reformat1c4d759e2017-04-09 16:34:54183 controller->FinishComposingText(
184 blink::WebInputMethodController::kKeepSelection);
ekaramadc75b1b3b32016-12-02 03:57:52185 }
[email protected]f751653a92014-02-18 16:32:55186}
187
188void TextInputController::DoCommand(const std::string& text) {
Blink Reformat1c4d759e2017-04-09 16:34:54189 if (view()->MainFrame()) {
190 if (!view()->MainFrame()->ToWebLocalFrame()) {
yabinh8204efc2016-07-08 13:58:57191 CHECK(false) << "This function cannot be called if the main frame is not"
192 "a local frame.";
193 }
Blink Reformat1c4d759e2017-04-09 16:34:54194 view()->MainFrame()->ToWebLocalFrame()->ExecuteCommand(
195 blink::WebString::FromUTF8(text));
yabinh8204efc2016-07-08 13:58:57196 }
[email protected]f751653a92014-02-18 16:32:55197}
198
199void TextInputController::SetMarkedText(const std::string& text,
200 int start,
201 int length) {
Blink Reformat1c4d759e2017-04-09 16:34:54202 blink::WebString web_text(blink::WebString::FromUTF8(text));
[email protected]f751653a92014-02-18 16:32:55203
204 // Split underline into up to 3 elements (before, selection, and after).
205 std::vector<blink::WebCompositionUnderline> underlines;
206 blink::WebCompositionUnderline underline;
207 if (!start) {
Blink Reformat1c4d759e2017-04-09 16:34:54208 underline.end_offset = length;
[email protected]f751653a92014-02-18 16:32:55209 } else {
Blink Reformat1c4d759e2017-04-09 16:34:54210 underline.end_offset = start;
[email protected]f751653a92014-02-18 16:32:55211 underlines.push_back(underline);
Blink Reformat1c4d759e2017-04-09 16:34:54212 underline.start_offset = start;
213 underline.end_offset = start + length;
[email protected]f751653a92014-02-18 16:32:55214 }
215 underline.thick = true;
216 underlines.push_back(underline);
217 if (start + length < static_cast<int>(web_text.length())) {
Blink Reformat1c4d759e2017-04-09 16:34:54218 underline.start_offset = underline.end_offset;
219 underline.end_offset = web_text.length();
[email protected]f751653a92014-02-18 16:32:55220 underline.thick = false;
221 underlines.push_back(underline);
222 }
223
ekaramadc75b1b3b32016-12-02 03:57:52224 if (auto* controller = GetInputMethodController()) {
Blink Reformat1c4d759e2017-04-09 16:34:54225 controller->SetComposition(web_text, underlines, blink::WebRange(), start,
ekaramadce32ef9f2017-02-09 17:33:56226 start + length);
ekaramadc75b1b3b32016-12-02 03:57:52227 }
[email protected]f751653a92014-02-18 16:32:55228}
229
230bool TextInputController::HasMarkedText() {
Blink Reformat1c4d759e2017-04-09 16:34:54231 if (!view()->MainFrame())
yabinh8204efc2016-07-08 13:58:57232 return false;
233
Blink Reformat1c4d759e2017-04-09 16:34:54234 if (!view()->MainFrame()->ToWebLocalFrame()) {
yabinh8204efc2016-07-08 13:58:57235 CHECK(false) << "This function cannot be called if the main frame is not"
236 "a local frame.";
237 }
238
Blink Reformat1c4d759e2017-04-09 16:34:54239 return view()->MainFrame()->ToWebLocalFrame()->HasMarkedText();
[email protected]f751653a92014-02-18 16:32:55240}
241
242std::vector<int> TextInputController::MarkedRange() {
Blink Reformat1c4d759e2017-04-09 16:34:54243 if (!view()->MainFrame())
[email protected]6f9397122014-02-25 09:30:50244 return std::vector<int>();
245
Blink Reformat1c4d759e2017-04-09 16:34:54246 if (!view()->MainFrame()->ToWebLocalFrame()) {
yabinh8204efc2016-07-08 13:58:57247 CHECK(false) << "This function cannot be called if the main frame is not"
248 "a local frame.";
249 }
250
Blink Reformat1c4d759e2017-04-09 16:34:54251 blink::WebRange range = view()->MainFrame()->ToWebLocalFrame()->MarkedRange();
[email protected]f751653a92014-02-18 16:32:55252 std::vector<int> int_array(2);
Blink Reformat1c4d759e2017-04-09 16:34:54253 int_array[0] = range.StartOffset();
254 int_array[1] = range.EndOffset();
[email protected]f751653a92014-02-18 16:32:55255
256 return int_array;
257}
258
259std::vector<int> TextInputController::SelectedRange() {
Blink Reformat1c4d759e2017-04-09 16:34:54260 if (!view()->MainFrame())
[email protected]6f9397122014-02-25 09:30:50261 return std::vector<int>();
262
Blink Reformat1c4d759e2017-04-09 16:34:54263 if (!view()->MainFrame()->ToWebLocalFrame()) {
yabinh8204efc2016-07-08 13:58:57264 CHECK(false) << "This function cannot be called if the main frame is not"
265 "a local frame.";
266 }
267
268 blink::WebRange range =
Blink Reformat1c4d759e2017-04-09 16:34:54269 view()->MainFrame()->ToWebLocalFrame()->SelectionRange();
270 if (range.IsNull())
yosin702c78a32016-06-13 08:39:13271 return std::vector<int>();
[email protected]f751653a92014-02-18 16:32:55272 std::vector<int> int_array(2);
Blink Reformat1c4d759e2017-04-09 16:34:54273 int_array[0] = range.StartOffset();
274 int_array[1] = range.EndOffset();
[email protected]f751653a92014-02-18 16:32:55275
276 return int_array;
277}
278
279std::vector<int> TextInputController::FirstRectForCharacterRange(
280 unsigned location,
281 unsigned length) {
282 blink::WebRect rect;
Blink Reformat1c4d759e2017-04-09 16:34:54283 if (!view()->FocusedFrame() ||
284 !view()->FocusedFrame()->FirstRectForCharacterRange(location, length,
lukasza8b6d5f32016-04-22 16:56:31285 rect)) {
[email protected]f751653a92014-02-18 16:32:55286 return std::vector<int>();
[email protected]6f9397122014-02-25 09:30:50287 }
[email protected]f751653a92014-02-18 16:32:55288
289 std::vector<int> int_array(4);
290 int_array[0] = rect.x;
291 int_array[1] = rect.y;
292 int_array[2] = rect.width;
293 int_array[3] = rect.height;
294
295 return int_array;
296}
297
298void TextInputController::SetComposition(const std::string& text) {
299 // Sends a keydown event with key code = 0xE5 to emulate input method
300 // behavior.
dtapuska899ac222017-01-03 18:09:16301 blink::WebKeyboardEvent key_down(
Blink Reformat1c4d759e2017-04-09 16:34:54302 blink::WebInputEvent::kRawKeyDown, blink::WebInputEvent::kNoModifiers,
dtapuska899ac222017-01-03 18:09:16303 ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
304
Blink Reformat1c4d759e2017-04-09 16:34:54305 key_down.windows_key_code = 0xE5; // VKEY_PROCESSKEY
306 view()->HandleInputEvent(blink::WebCoalescedInputEvent(key_down));
[email protected]f751653a92014-02-18 16:32:55307
yabinhdd6d1b62016-08-23 07:09:02308 // The value returned by std::string::length() may not correspond to the
309 // actual number of encoded characters in sequences of multi-byte or
310 // variable-length characters.
Blink Reformat1c4d759e2017-04-09 16:34:54311 blink::WebString newText = blink::WebString::FromUTF8(text);
yabinhdd6d1b62016-08-23 07:09:02312 size_t textLength = newText.length();
313
changwan9bdc18f02015-11-20 02:43:52314 std::vector<blink::WebCompositionUnderline> underlines;
yabinhdd6d1b62016-08-23 07:09:02315 underlines.push_back(blink::WebCompositionUnderline(
316 0, textLength, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
ekaramadc75b1b3b32016-12-02 03:57:52317 if (auto* controller = GetInputMethodController()) {
Blink Reformat1c4d759e2017-04-09 16:34:54318 controller->SetComposition(
ekaramadc75b1b3b32016-12-02 03:57:52319 newText, blink::WebVector<blink::WebCompositionUnderline>(underlines),
ekaramadce32ef9f2017-02-09 17:33:56320 blink::WebRange(), textLength, textLength);
ekaramadc75b1b3b32016-12-02 03:57:52321 }
lukasza8b6d5f32016-04-22 16:56:31322}
323
ekaramad2daaf672016-11-10 20:29:01324void TextInputController::ForceTextInputStateUpdate() {
lukaszabedb4b22017-06-23 00:00:13325 // TODO(lukasza): Finish adding OOPIF support to the layout tests harness.
Daniel Cheng3c829432017-06-19 03:42:31326 CHECK(view()->MainFrame()->IsWebLocalFrame())
327 << "WebView does not have a local main frame and"
328 " cannot handle input method controller tasks.";
ekaramad2daaf672016-11-10 20:29:01329 web_view_test_proxy_base_->delegate()->ForceTextInputStateUpdate(
Daniel Cheng3c829432017-06-19 03:42:31330 view()->MainFrame()->ToWebLocalFrame());
ekaramad2daaf672016-11-10 20:29:01331}
332
lukasza8b6d5f32016-04-22 16:56:31333blink::WebView* TextInputController::view() {
lfg05e41372016-07-22 15:38:10334 return web_view_test_proxy_base_->web_view();
[email protected]f751653a92014-02-18 16:32:55335}
336
ekaramadc75b1b3b32016-12-02 03:57:52337blink::WebInputMethodController*
338TextInputController::GetInputMethodController() {
Blink Reformat1c4d759e2017-04-09 16:34:54339 if (!view()->MainFrame())
ekaramadc75b1b3b32016-12-02 03:57:52340 return nullptr;
341
lukaszabedb4b22017-06-23 00:00:13342 // TODO(lukasza): Finish adding OOPIF support to the layout tests harness.
Daniel Cheng3c829432017-06-19 03:42:31343 CHECK(view()->MainFrame()->IsWebLocalFrame())
344 << "WebView does not have a local main frame and"
345 " cannot handle input method controller tasks.";
346
347 return view()
348 ->MainFrame()
349 ->ToWebLocalFrame()
350 ->FrameWidget()
351 ->GetActiveWebInputMethodController();
ekaramad2daaf672016-11-10 20:29:01352}
353
jochenf5f31752015-06-03 12:06:34354} // namespace test_runner