1 // Copyright (c) 2012 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.
5 #include "content/browser/renderer_host/ime_adapter_android.h"
7 #include <android/input.h>
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_string.h"
11 #include "base/android/scoped_java_ref.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/frame_tree.h"
15 #include "content/browser/frame_host/frame_tree_node.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/renderer_host/render_view_host_delegate.h"
18 #include "content/browser/renderer_host/render_view_host_impl.h"
19 #include "content/browser/renderer_host/render_widget_host_impl.h"
20 #include "content/browser/renderer_host/render_widget_host_view_android.h"
21 #include "content/common/frame_messages.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/native_web_keyboard_event.h"
25 #include "jni/ImeAdapter_jni.h"
26 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
27 #include "third_party/WebKit/public/web/WebInputEvent.h"
29 using base::android::AttachCurrentThread;
30 using base::android::ConvertJavaStringToUTF16;
35 // Maps a java KeyEvent into a NativeWebKeyboardEvent.
36 // |java_key_event| is used to maintain a globalref for KeyEvent.
37 // |action| will help determine the WebInputEvent type.
38 // type, |modifiers|, |time_ms|, |key_code|, |unicode_char| is used to create
39 // WebKeyboardEvent. |key_code| is also needed ad need to treat the enter key
40 // as a key press of character \r.
41 NativeWebKeyboardEvent NativeWebKeyboardEventFromKeyEvent(
43 jobject java_key_event,
50 blink::WebInputEvent::Type type = blink::WebInputEvent::Undefined;
51 if (action == AKEY_EVENT_ACTION_DOWN)
52 type = blink::WebInputEvent::RawKeyDown;
53 else if (action == AKEY_EVENT_ACTION_UP)
54 type = blink::WebInputEvent::KeyUp;
55 return NativeWebKeyboardEvent(java_key_event, type, modifiers,
56 time_ms / 1000.0, key_code, unicode_char, is_system_key);
59 } // anonymous namespace
61 bool RegisterImeAdapter(JNIEnv* env) {
62 if (!RegisterNativesImpl(env))
65 Java_ImeAdapter_initializeWebInputEvents(env,
66 blink::WebInputEvent::RawKeyDown,
67 blink::WebInputEvent::KeyUp,
68 blink::WebInputEvent::Char,
69 blink::WebInputEvent::ShiftKey,
70 blink::WebInputEvent::AltKey,
71 blink::WebInputEvent::ControlKey,
72 blink::WebInputEvent::CapsLockOn,
73 blink::WebInputEvent::NumLockOn);
74 Java_ImeAdapter_initializeTextInputTypes(
76 ui::TEXT_INPUT_TYPE_NONE,
77 ui::TEXT_INPUT_TYPE_TEXT,
78 ui::TEXT_INPUT_TYPE_TEXT_AREA,
79 ui::TEXT_INPUT_TYPE_PASSWORD,
80 ui::TEXT_INPUT_TYPE_SEARCH,
81 ui::TEXT_INPUT_TYPE_URL,
82 ui::TEXT_INPUT_TYPE_EMAIL,
83 ui::TEXT_INPUT_TYPE_TELEPHONE,
84 ui::TEXT_INPUT_TYPE_NUMBER,
85 ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE);
89 ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva)
93 ImeAdapterAndroid::~ImeAdapterAndroid() {
94 JNIEnv* env = AttachCurrentThread();
95 base::android::ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env);
97 Java_ImeAdapter_detach(env, obj.obj());
100 bool ImeAdapterAndroid::SendSyntheticKeyEvent(JNIEnv*,
106 NativeWebKeyboardEvent event(static_cast<blink::WebInputEvent::Type>(type),
107 0 /* modifiers */, time_ms / 1000.0, key_code,
108 text, false /* is_system_key */);
109 rwhva_->SendKeyEvent(event);
113 bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject,
114 jobject original_key_event,
115 int action, int modifiers,
116 long time_ms, int key_code,
117 bool is_system_key, int unicode_char) {
118 NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent(
119 env, original_key_event, action, modifiers,
120 time_ms, key_code, is_system_key, unicode_char);
121 bool key_down_text_insertion =
122 event.type == blink::WebInputEvent::RawKeyDown && event.text[0];
123 // If we are going to follow up with a synthetic Char event, then that's the
124 // one we expect to test if it's handled or unhandled, so skip handling the
125 // "real" event in the browser.
126 event.skip_in_browser = key_down_text_insertion;
127 rwhva_->SendKeyEvent(event);
128 if (key_down_text_insertion) {
129 // Send a Char event, but without an os_event since we don't want to
130 // roundtrip back to java such synthetic event.
131 NativeWebKeyboardEvent char_event(blink::WebInputEvent::Char, modifiers,
132 time_ms / 1000.0, key_code, unicode_char,
134 char_event.skip_in_browser = key_down_text_insertion;
135 rwhva_->SendKeyEvent(char_event);
140 void ImeAdapterAndroid::SetComposingText(JNIEnv* env, jobject, jstring text,
141 int new_cursor_pos) {
142 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
146 base::string16 text16 = ConvertJavaStringToUTF16(env, text);
147 std::vector<blink::WebCompositionUnderline> underlines;
148 underlines.push_back(
149 blink::WebCompositionUnderline(0, text16.length(), SK_ColorBLACK,
151 // new_cursor_position is as described in the Android API for
152 // InputConnection#setComposingText, whereas the parameters for
153 // ImeSetComposition are relative to the start of the composition.
154 if (new_cursor_pos > 0)
155 new_cursor_pos = text16.length() + new_cursor_pos - 1;
157 rwhi->ImeSetComposition(text16, underlines, new_cursor_pos, new_cursor_pos);
160 void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text) {
161 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
165 base::string16 text16 = ConvertJavaStringToUTF16(env, text);
166 rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false);
169 void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) {
170 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
174 rwhi->ImeConfirmComposition(base::string16(), gfx::Range::InvalidRange(),
178 void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) {
179 java_ime_adapter_ = JavaObjectWeakGlobalRef(env, java_object);
182 void ImeAdapterAndroid::CancelComposition() {
183 base::android::ScopedJavaLocalRef<jobject> obj =
184 java_ime_adapter_.get(AttachCurrentThread());
186 Java_ImeAdapter_cancelComposition(AttachCurrentThread(), obj.obj());
189 void ImeAdapterAndroid::FocusedNodeChanged(bool is_editable_node) {
190 base::android::ScopedJavaLocalRef<jobject> obj =
191 java_ime_adapter_.get(AttachCurrentThread());
192 if (!obj.is_null()) {
193 Java_ImeAdapter_focusedNodeChanged(AttachCurrentThread(),
199 void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject,
200 int start, int end) {
201 RenderFrameHost* rfh = GetFocusedFrame();
205 rfh->Send(new FrameMsg_SetEditableSelectionOffsets(rfh->GetRoutingID(),
209 void ImeAdapterAndroid::SetComposingRegion(JNIEnv*, jobject,
210 int start, int end) {
211 RenderFrameHost* rfh = GetFocusedFrame();
215 std::vector<blink::WebCompositionUnderline> underlines;
216 underlines.push_back(
217 blink::WebCompositionUnderline(0, end - start, SK_ColorBLACK, false));
219 rfh->Send(new FrameMsg_SetCompositionFromExistingText(
220 rfh->GetRoutingID(), start, end, underlines));
223 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*, jobject,
224 int before, int after) {
225 RenderFrameHostImpl* rfh =
226 static_cast<RenderFrameHostImpl*>(GetFocusedFrame());
228 rfh->ExtendSelectionAndDelete(before, after);
231 void ImeAdapterAndroid::Unselect(JNIEnv* env, jobject) {
232 RenderFrameHost* rfh = GetFocusedFrame();
237 void ImeAdapterAndroid::SelectAll(JNIEnv* env, jobject) {
238 RenderFrameHost* rfh = GetFocusedFrame();
243 void ImeAdapterAndroid::Cut(JNIEnv* env, jobject) {
244 RenderFrameHost* rfh = GetFocusedFrame();
249 void ImeAdapterAndroid::Copy(JNIEnv* env, jobject) {
250 RenderFrameHost* rfh = GetFocusedFrame();
255 void ImeAdapterAndroid::Paste(JNIEnv* env, jobject) {
256 RenderFrameHost* rfh = GetFocusedFrame();
261 void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) {
262 java_ime_adapter_.reset();
265 RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() {
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
268 RenderWidgetHost* rwh = rwhva_->GetRenderWidgetHost();
272 return RenderWidgetHostImpl::From(rwh);
275 RenderFrameHost* ImeAdapterAndroid::GetFocusedFrame() {
276 RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
279 if (!rwh->IsRenderView())
281 RenderViewHost* rvh = RenderViewHost::From(rwh);
282 FrameTreeNode* focused_frame =
283 rvh->GetDelegate()->GetFrameTree()->GetFocusedFrame();
287 return focused_frame->current_frame_host();
290 } // namespace content