OLD | NEW |
| (Empty) |
1 // 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 | |
5 #include "components/test_runner/text_input_controller.h" | |
6 | |
7 #include "base/macros.h" | |
8 #include "components/test_runner/web_test_delegate.h" | |
9 #include "components/test_runner/web_view_test_proxy.h" | |
10 #include "gin/arguments.h" | |
11 #include "gin/handle.h" | |
12 #include "gin/object_template_builder.h" | |
13 #include "gin/wrappable.h" | |
14 #include "third_party/WebKit/public/platform/WebCoalescedInputEvent.h" | |
15 #include "third_party/WebKit/public/platform/WebInputEventResult.h" | |
16 #include "third_party/WebKit/public/platform/WebKeyboardEvent.h" | |
17 #include "third_party/WebKit/public/web/WebCompositionUnderline.h" | |
18 #include "third_party/WebKit/public/web/WebFrameWidget.h" | |
19 #include "third_party/WebKit/public/web/WebInputMethodController.h" | |
20 #include "third_party/WebKit/public/web/WebKit.h" | |
21 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
22 #include "third_party/WebKit/public/web/WebRange.h" | |
23 #include "third_party/WebKit/public/web/WebView.h" | |
24 #include "third_party/skia/include/core/SkColor.h" | |
25 #include "ui/events/base_event_utils.h" | |
26 #include "v8/include/v8.h" | |
27 | |
28 namespace test_runner { | |
29 | |
30 class TextInputControllerBindings | |
31 : public gin::Wrappable<TextInputControllerBindings> { | |
32 public: | |
33 static gin::WrapperInfo kWrapperInfo; | |
34 | |
35 static void Install(base::WeakPtr<TextInputController> controller, | |
36 blink::WebLocalFrame* frame); | |
37 | |
38 private: | |
39 explicit TextInputControllerBindings( | |
40 base::WeakPtr<TextInputController> controller); | |
41 ~TextInputControllerBindings() override; | |
42 | |
43 // gin::Wrappable: | |
44 gin::ObjectTemplateBuilder GetObjectTemplateBuilder( | |
45 v8::Isolate* isolate) override; | |
46 | |
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); | |
57 void ForceTextInputStateUpdate(); | |
58 | |
59 base::WeakPtr<TextInputController> controller_; | |
60 | |
61 DISALLOW_COPY_AND_ASSIGN(TextInputControllerBindings); | |
62 }; | |
63 | |
64 gin::WrapperInfo TextInputControllerBindings::kWrapperInfo = { | |
65 gin::kEmbedderNativeGin}; | |
66 | |
67 // static | |
68 void TextInputControllerBindings::Install( | |
69 base::WeakPtr<TextInputController> controller, | |
70 blink::WebLocalFrame* frame) { | |
71 v8::Isolate* isolate = blink::mainThreadIsolate(); | |
72 v8::HandleScope handle_scope(isolate); | |
73 v8::Local<v8::Context> context = frame->mainWorldScriptContext(); | |
74 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)); | |
81 if (bindings.IsEmpty()) | |
82 return; | |
83 v8::Local<v8::Object> global = context->Global(); | |
84 global->Set(gin::StringToV8(isolate, "textInputController"), bindings.ToV8()); | |
85 } | |
86 | |
87 TextInputControllerBindings::TextInputControllerBindings( | |
88 base::WeakPtr<TextInputController> controller) | |
89 : controller_(controller) {} | |
90 | |
91 TextInputControllerBindings::~TextInputControllerBindings() {} | |
92 | |
93 gin::ObjectTemplateBuilder | |
94 TextInputControllerBindings::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) | |
106 .SetMethod("setComposition", &TextInputControllerBindings::SetComposition) | |
107 .SetMethod("forceTextInputStateUpdate", | |
108 &TextInputControllerBindings::ForceTextInputStateUpdate); | |
109 } | |
110 | |
111 void TextInputControllerBindings::InsertText(const std::string& text) { | |
112 if (controller_) | |
113 controller_->InsertText(text); | |
114 } | |
115 | |
116 void TextInputControllerBindings::UnmarkText() { | |
117 if (controller_) | |
118 controller_->UnmarkText(); | |
119 } | |
120 | |
121 void TextInputControllerBindings::DoCommand(const std::string& text) { | |
122 if (controller_) | |
123 controller_->DoCommand(text); | |
124 } | |
125 | |
126 void TextInputControllerBindings::SetMarkedText(const std::string& text, | |
127 int start, | |
128 int length) { | |
129 if (controller_) | |
130 controller_->SetMarkedText(text, start, length); | |
131 } | |
132 | |
133 bool TextInputControllerBindings::HasMarkedText() { | |
134 return controller_ ? controller_->HasMarkedText() : false; | |
135 } | |
136 | |
137 std::vector<int> TextInputControllerBindings::MarkedRange() { | |
138 return controller_ ? controller_->MarkedRange() : std::vector<int>(); | |
139 } | |
140 | |
141 std::vector<int> TextInputControllerBindings::SelectedRange() { | |
142 return controller_ ? controller_->SelectedRange() : std::vector<int>(); | |
143 } | |
144 | |
145 std::vector<int> TextInputControllerBindings::FirstRectForCharacterRange( | |
146 unsigned location, | |
147 unsigned length) { | |
148 return controller_ ? controller_->FirstRectForCharacterRange(location, length) | |
149 : std::vector<int>(); | |
150 } | |
151 | |
152 void TextInputControllerBindings::SetComposition(const std::string& text) { | |
153 if (controller_) | |
154 controller_->SetComposition(text); | |
155 } | |
156 void TextInputControllerBindings::ForceTextInputStateUpdate() { | |
157 if (controller_) | |
158 controller_->ForceTextInputStateUpdate(); | |
159 } | |
160 // TextInputController --------------------------------------------------------- | |
161 | |
162 TextInputController::TextInputController( | |
163 WebViewTestProxyBase* web_view_test_proxy_base) | |
164 : web_view_test_proxy_base_(web_view_test_proxy_base), | |
165 weak_factory_(this) {} | |
166 | |
167 TextInputController::~TextInputController() {} | |
168 | |
169 void TextInputController::Install(blink::WebLocalFrame* frame) { | |
170 TextInputControllerBindings::Install(weak_factory_.GetWeakPtr(), frame); | |
171 } | |
172 | |
173 void TextInputController::InsertText(const std::string& text) { | |
174 if (auto* controller = GetInputMethodController()) { | |
175 controller->commitText(blink::WebString::fromUTF8(text), | |
176 std::vector<blink::WebCompositionUnderline>(), | |
177 blink::WebRange(), 0); | |
178 } | |
179 } | |
180 | |
181 void TextInputController::UnmarkText() { | |
182 if (auto* controller = GetInputMethodController()) { | |
183 controller->finishComposingText( | |
184 blink::WebInputMethodController::KeepSelection); | |
185 } | |
186 } | |
187 | |
188 void TextInputController::DoCommand(const std::string& text) { | |
189 if (view()->mainFrame()) { | |
190 if (!view()->mainFrame()->toWebLocalFrame()) { | |
191 CHECK(false) << "This function cannot be called if the main frame is not" | |
192 "a local frame."; | |
193 } | |
194 view()->mainFrame()->toWebLocalFrame()->executeCommand( | |
195 blink::WebString::fromUTF8(text)); | |
196 } | |
197 } | |
198 | |
199 void TextInputController::SetMarkedText(const std::string& text, | |
200 int start, | |
201 int length) { | |
202 blink::WebString web_text(blink::WebString::fromUTF8(text)); | |
203 | |
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) { | |
208 underline.endOffset = length; | |
209 } else { | |
210 underline.endOffset = start; | |
211 underlines.push_back(underline); | |
212 underline.startOffset = start; | |
213 underline.endOffset = start + length; | |
214 } | |
215 underline.thick = true; | |
216 underlines.push_back(underline); | |
217 if (start + length < static_cast<int>(web_text.length())) { | |
218 underline.startOffset = underline.endOffset; | |
219 underline.endOffset = web_text.length(); | |
220 underline.thick = false; | |
221 underlines.push_back(underline); | |
222 } | |
223 | |
224 if (auto* controller = GetInputMethodController()) { | |
225 controller->setComposition(web_text, underlines, blink::WebRange(), start, | |
226 start + length); | |
227 } | |
228 } | |
229 | |
230 bool TextInputController::HasMarkedText() { | |
231 if (!view()->mainFrame()) | |
232 return false; | |
233 | |
234 if (!view()->mainFrame()->toWebLocalFrame()) { | |
235 CHECK(false) << "This function cannot be called if the main frame is not" | |
236 "a local frame."; | |
237 } | |
238 | |
239 return view()->mainFrame()->toWebLocalFrame()->hasMarkedText(); | |
240 } | |
241 | |
242 std::vector<int> TextInputController::MarkedRange() { | |
243 if (!view()->mainFrame()) | |
244 return std::vector<int>(); | |
245 | |
246 if (!view()->mainFrame()->toWebLocalFrame()) { | |
247 CHECK(false) << "This function cannot be called if the main frame is not" | |
248 "a local frame."; | |
249 } | |
250 | |
251 blink::WebRange range = view()->mainFrame()->toWebLocalFrame()->markedRange(); | |
252 std::vector<int> int_array(2); | |
253 int_array[0] = range.startOffset(); | |
254 int_array[1] = range.endOffset(); | |
255 | |
256 return int_array; | |
257 } | |
258 | |
259 std::vector<int> TextInputController::SelectedRange() { | |
260 if (!view()->mainFrame()) | |
261 return std::vector<int>(); | |
262 | |
263 if (!view()->mainFrame()->toWebLocalFrame()) { | |
264 CHECK(false) << "This function cannot be called if the main frame is not" | |
265 "a local frame."; | |
266 } | |
267 | |
268 blink::WebRange range = | |
269 view()->mainFrame()->toWebLocalFrame()->selectionRange(); | |
270 if (range.isNull()) | |
271 return std::vector<int>(); | |
272 std::vector<int> int_array(2); | |
273 int_array[0] = range.startOffset(); | |
274 int_array[1] = range.endOffset(); | |
275 | |
276 return int_array; | |
277 } | |
278 | |
279 std::vector<int> TextInputController::FirstRectForCharacterRange( | |
280 unsigned location, | |
281 unsigned length) { | |
282 blink::WebRect rect; | |
283 if (!view()->focusedFrame() || | |
284 !view()->focusedFrame()->firstRectForCharacterRange(location, length, | |
285 rect)) { | |
286 return std::vector<int>(); | |
287 } | |
288 | |
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 | |
298 void TextInputController::SetComposition(const std::string& text) { | |
299 // Sends a keydown event with key code = 0xE5 to emulate input method | |
300 // behavior. | |
301 blink::WebKeyboardEvent key_down( | |
302 blink::WebInputEvent::RawKeyDown, blink::WebInputEvent::NoModifiers, | |
303 ui::EventTimeStampToSeconds(ui::EventTimeForNow())); | |
304 | |
305 key_down.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY | |
306 view()->handleInputEvent(blink::WebCoalescedInputEvent(key_down)); | |
307 | |
308 // 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. | |
311 blink::WebString newText = blink::WebString::fromUTF8(text); | |
312 size_t textLength = newText.length(); | |
313 | |
314 std::vector<blink::WebCompositionUnderline> underlines; | |
315 underlines.push_back(blink::WebCompositionUnderline( | |
316 0, textLength, SK_ColorBLACK, false, SK_ColorTRANSPARENT)); | |
317 if (auto* controller = GetInputMethodController()) { | |
318 controller->setComposition( | |
319 newText, blink::WebVector<blink::WebCompositionUnderline>(underlines), | |
320 blink::WebRange(), textLength, textLength); | |
321 } | |
322 } | |
323 | |
324 void TextInputController::ForceTextInputStateUpdate() { | |
325 web_view_test_proxy_base_->delegate()->ForceTextInputStateUpdate( | |
326 view()->mainFrame()); | |
327 } | |
328 | |
329 blink::WebView* TextInputController::view() { | |
330 return web_view_test_proxy_base_->web_view(); | |
331 } | |
332 | |
333 blink::WebInputMethodController* | |
334 TextInputController::GetInputMethodController() { | |
335 if (!view()->mainFrame()) | |
336 return nullptr; | |
337 | |
338 blink::WebLocalFrame* mainFrame = view()->mainFrame()->toWebLocalFrame(); | |
339 if (!mainFrame) { | |
340 CHECK(false) << "WebView does not have a local main frame and" | |
341 " cannot handle input method controller tasks."; | |
342 } | |
343 return mainFrame->frameWidget()->getActiveWebInputMethodController(); | |
344 } | |
345 | |
346 } // namespace test_runner | |
OLD | NEW |