| 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 |