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 "content/shell/renderer/test_runner/text_input_controller.h"
6
7#include "gin/arguments.h"
8#include "gin/handle.h"
9#include "gin/object_template_builder.h"
10#include "gin/wrappable.h"
11#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
12#include "third_party/WebKit/public/web/WebFrame.h"
13#include "third_party/WebKit/public/web/WebInputEvent.h"
14#include "third_party/WebKit/public/web/WebKit.h"
15#include "third_party/WebKit/public/web/WebRange.h"
16#include "third_party/WebKit/public/web/WebView.h"
17#include "v8/include/v8.h"
18
19namespace content {
20
21class TextInputControllerBindings
22    : public gin::Wrappable<TextInputControllerBindings> {
23 public:
24  static gin::WrapperInfo kWrapperInfo;
25
26  static void Install(base::WeakPtr<TextInputController> controller,
27                      blink::WebFrame* frame);
28
29 private:
30  explicit TextInputControllerBindings(
31      base::WeakPtr<TextInputController> controller);
32  virtual ~TextInputControllerBindings();
33
34  // gin::Wrappable:
35  virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
36      v8::Isolate* isolate) OVERRIDE;
37
38  void InsertText(const std::string& text);
39  void UnmarkText();
40  void DoCommand(const std::string& text);
41  void SetMarkedText(const std::string& text, int start, int length);
42  bool HasMarkedText();
43  std::vector<int> MarkedRange();
44  std::vector<int> SelectedRange();
45  std::vector<int> FirstRectForCharacterRange(unsigned location,
46                                              unsigned length);
47  void SetComposition(const std::string& text);
48
49  base::WeakPtr<TextInputController> controller_;
50
51  DISALLOW_COPY_AND_ASSIGN(TextInputControllerBindings);
52};
53
54gin::WrapperInfo TextInputControllerBindings::kWrapperInfo = {
55    gin::kEmbedderNativeGin};
56
57// static
58void TextInputControllerBindings::Install(
59    base::WeakPtr<TextInputController> controller,
60    blink::WebFrame* frame) {
61  v8::Isolate* isolate = blink::mainThreadIsolate();
62  v8::HandleScope handle_scope(isolate);
63  v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
64  if (context.IsEmpty())
65    return;
66
67  v8::Context::Scope context_scope(context);
68
69  gin::Handle<TextInputControllerBindings> bindings =
70      gin::CreateHandle(isolate, new TextInputControllerBindings(controller));
71  if (bindings.IsEmpty())
72    return;
73  v8::Handle<v8::Object> global = context->Global();
74  global->Set(gin::StringToV8(isolate, "textInputController"), bindings.ToV8());
75}
76
77TextInputControllerBindings::TextInputControllerBindings(
78    base::WeakPtr<TextInputController> controller)
79    : controller_(controller) {}
80
81TextInputControllerBindings::~TextInputControllerBindings() {}
82
83gin::ObjectTemplateBuilder
84TextInputControllerBindings::GetObjectTemplateBuilder(v8::Isolate* isolate) {
85  return gin::Wrappable<TextInputControllerBindings>::GetObjectTemplateBuilder(
86             isolate)
87      .SetMethod("insertText", &TextInputControllerBindings::InsertText)
88      .SetMethod("unmarkText", &TextInputControllerBindings::UnmarkText)
89      .SetMethod("doCommand", &TextInputControllerBindings::DoCommand)
90      .SetMethod("setMarkedText", &TextInputControllerBindings::SetMarkedText)
91      .SetMethod("hasMarkedText", &TextInputControllerBindings::HasMarkedText)
92      .SetMethod("markedRange", &TextInputControllerBindings::MarkedRange)
93      .SetMethod("selectedRange", &TextInputControllerBindings::SelectedRange)
94      .SetMethod("firstRectForCharacterRange",
95                 &TextInputControllerBindings::FirstRectForCharacterRange)
96      .SetMethod("setComposition",
97                 &TextInputControllerBindings::SetComposition);
98}
99
100void TextInputControllerBindings::InsertText(const std::string& text) {
101  if (controller_)
102    controller_->InsertText(text);
103}
104
105void TextInputControllerBindings::UnmarkText() {
106  if (controller_)
107    controller_->UnmarkText();
108}
109
110void TextInputControllerBindings::DoCommand(const std::string& text) {
111  if (controller_)
112    controller_->DoCommand(text);
113}
114
115void TextInputControllerBindings::SetMarkedText(const std::string& text,
116                                                int start,
117                                                int length) {
118  if (controller_)
119    controller_->SetMarkedText(text, start, length);
120}
121
122bool TextInputControllerBindings::HasMarkedText() {
123  return controller_ ? controller_->HasMarkedText() : false;
124}
125
126std::vector<int> TextInputControllerBindings::MarkedRange() {
127  return controller_ ? controller_->MarkedRange() : std::vector<int>();
128}
129
130std::vector<int> TextInputControllerBindings::SelectedRange() {
131  return controller_ ? controller_->SelectedRange() : std::vector<int>();
132}
133
134std::vector<int> TextInputControllerBindings::FirstRectForCharacterRange(
135    unsigned location,
136    unsigned length) {
137  return controller_ ? controller_->FirstRectForCharacterRange(location, length)
138                     : std::vector<int>();
139}
140
141void TextInputControllerBindings::SetComposition(const std::string& text) {
142  if (controller_)
143    controller_->SetComposition(text);
144}
145
146// TextInputController ---------------------------------------------------------
147
148TextInputController::TextInputController()
149    : view_(NULL), weak_factory_(this) {}
150
151TextInputController::~TextInputController() {}
152
153void TextInputController::Install(blink::WebFrame* frame) {
154  TextInputControllerBindings::Install(weak_factory_.GetWeakPtr(), frame);
155}
156
157void TextInputController::SetWebView(blink::WebView* view) {
158  view_ = view;
159}
160
161void TextInputController::InsertText(const std::string& text) {
162  view_->confirmComposition(blink::WebString::fromUTF8(text));
163}
164
165void TextInputController::UnmarkText() {
166  view_->confirmComposition();
167}
168
169void TextInputController::DoCommand(const std::string& text) {
170  if (view_->mainFrame())
171    view_->mainFrame()->executeCommand(blink::WebString::fromUTF8(text));
172}
173
174void TextInputController::SetMarkedText(const std::string& text,
175                                        int start,
176                                        int length) {
177  blink::WebString web_text(blink::WebString::fromUTF8(text));
178
179  // Split underline into up to 3 elements (before, selection, and after).
180  std::vector<blink::WebCompositionUnderline> underlines;
181  blink::WebCompositionUnderline underline;
182  if (!start) {
183    underline.endOffset = length;
184  } else {
185    underline.endOffset = start;
186    underlines.push_back(underline);
187    underline.startOffset = start;
188    underline.endOffset = start + length;
189  }
190  underline.thick = true;
191  underlines.push_back(underline);
192  if (start + length < static_cast<int>(web_text.length())) {
193    underline.startOffset = underline.endOffset;
194    underline.endOffset = web_text.length();
195    underline.thick = false;
196    underlines.push_back(underline);
197  }
198
199  view_->setComposition(web_text, underlines, start, start + length);
200}
201
202bool TextInputController::HasMarkedText() {
203  return view_->mainFrame() && view_->mainFrame()->hasMarkedText();
204}
205
206std::vector<int> TextInputController::MarkedRange() {
207  if (!view_->mainFrame())
208    return std::vector<int>();
209
210  blink::WebRange range = view_->mainFrame()->markedRange();
211  std::vector<int> int_array(2);
212  int_array[0] = range.startOffset();
213  int_array[1] = range.endOffset();
214
215  return int_array;
216}
217
218std::vector<int> TextInputController::SelectedRange() {
219  if (!view_->mainFrame())
220    return std::vector<int>();
221
222  blink::WebRange range = view_->mainFrame()->selectionRange();
223  std::vector<int> int_array(2);
224  int_array[0] = range.startOffset();
225  int_array[1] = range.endOffset();
226
227  return int_array;
228}
229
230std::vector<int> TextInputController::FirstRectForCharacterRange(
231    unsigned location,
232    unsigned length) {
233  blink::WebRect rect;
234  if (!view_->focusedFrame() ||
235      !view_->focusedFrame()->firstRectForCharacterRange(
236          location, length, rect)) {
237    return std::vector<int>();
238  }
239
240  std::vector<int> int_array(4);
241  int_array[0] = rect.x;
242  int_array[1] = rect.y;
243  int_array[2] = rect.width;
244  int_array[3] = rect.height;
245
246  return int_array;
247}
248
249void TextInputController::SetComposition(const std::string& text) {
250  // Sends a keydown event with key code = 0xE5 to emulate input method
251  // behavior.
252  blink::WebKeyboardEvent key_down;
253  key_down.type = blink::WebInputEvent::RawKeyDown;
254  key_down.modifiers = 0;
255  key_down.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
256  key_down.setKeyIdentifierFromWindowsKeyCode();
257  view_->handleInputEvent(key_down);
258
259  blink::WebVector<blink::WebCompositionUnderline> underlines;
260  blink::WebString web_text(blink::WebString::fromUTF8(text));
261  view_->setComposition(web_text, underlines, 0, web_text.length());
262}
263
264}  // namespace content
265