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. 4 5 #include "content/browser/renderer_host/ime_adapter_android.h" 6 7 #include <android/input.h> 8 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/renderer_host/render_widget_host_impl.h" 15 #include "content/browser/renderer_host/render_widget_host_view_android.h" 16 #include "content/common/view_messages.h" 17 #include "content/public/browser/native_web_keyboard_event.h" 18 #include "jni/ImeAdapter_jni.h" 19 #include "third_party/WebKit/public/web/WebCompositionUnderline.h" 20 #include "third_party/WebKit/public/web/WebInputEvent.h" 21 22 using base::android::AttachCurrentThread; 23 using base::android::ConvertJavaStringToUTF16; 24 25 namespace content { 26 namespace { 27 28 // Maps a java KeyEvent into a NativeWebKeyboardEvent. 29 // |java_key_event| is used to maintain a globalref for KeyEvent. 30 // |action| will help determine the WebInputEvent type. 31 // type, |modifiers|, |time_ms|, |key_code|, |unicode_char| is used to create 32 // WebKeyboardEvent. |key_code| is also needed ad need to treat the enter key 33 // as a key press of character \r. 34 NativeWebKeyboardEvent NativeWebKeyboardEventFromKeyEvent( 35 JNIEnv* env, 36 jobject java_key_event, 37 int action, 38 int modifiers, 39 long time_ms, 40 int key_code, 41 bool is_system_key, 42 int unicode_char) { 43 blink::WebInputEvent::Type type = blink::WebInputEvent::Undefined; 44 if (action == AKEY_EVENT_ACTION_DOWN) 45 type = blink::WebInputEvent::RawKeyDown; 46 else if (action == AKEY_EVENT_ACTION_UP) 47 type = blink::WebInputEvent::KeyUp; 48 return NativeWebKeyboardEvent(java_key_event, type, modifiers, 49 time_ms, key_code, unicode_char, is_system_key); 50 } 51 52 } // anonymous namespace 53 54 bool RegisterImeAdapter(JNIEnv* env) { 55 if (!RegisterNativesImpl(env)) 56 return false; 57 58 Java_ImeAdapter_initializeWebInputEvents(env, 59 blink::WebInputEvent::RawKeyDown, 60 blink::WebInputEvent::KeyUp, 61 blink::WebInputEvent::Char, 62 blink::WebInputEvent::ShiftKey, 63 blink::WebInputEvent::AltKey, 64 blink::WebInputEvent::ControlKey, 65 blink::WebInputEvent::CapsLockOn, 66 blink::WebInputEvent::NumLockOn); 67 Java_ImeAdapter_initializeTextInputTypes( 68 env, 69 ui::TEXT_INPUT_TYPE_NONE, 70 ui::TEXT_INPUT_TYPE_TEXT, 71 ui::TEXT_INPUT_TYPE_TEXT_AREA, 72 ui::TEXT_INPUT_TYPE_PASSWORD, 73 ui::TEXT_INPUT_TYPE_SEARCH, 74 ui::TEXT_INPUT_TYPE_URL, 75 ui::TEXT_INPUT_TYPE_EMAIL, 76 ui::TEXT_INPUT_TYPE_TELEPHONE, 77 ui::TEXT_INPUT_TYPE_NUMBER, 78 ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE); 79 return true; 80 } 81 82 ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva) 83 : rwhva_(rwhva) { 84 } 85 86 ImeAdapterAndroid::~ImeAdapterAndroid() { 87 JNIEnv* env = AttachCurrentThread(); 88 base::android::ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env); 89 if (!obj.is_null()) 90 Java_ImeAdapter_detach(env, obj.obj()); 91 } 92 93 bool ImeAdapterAndroid::SendSyntheticKeyEvent(JNIEnv*, 94 jobject, 95 int type, 96 long time_ms, 97 int key_code, 98 int text) { 99 NativeWebKeyboardEvent event(static_cast<blink::WebInputEvent::Type>(type), 100 0 /* modifiers */, time_ms / 1000.0, key_code, 101 text, false /* is_system_key */); 102 rwhva_->SendKeyEvent(event); 103 return true; 104 } 105 106 bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject, 107 jobject original_key_event, 108 int action, int modifiers, 109 long time_ms, int key_code, 110 bool is_system_key, int unicode_char) { 111 NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent( 112 env, original_key_event, action, modifiers, 113 time_ms, key_code, is_system_key, unicode_char); 114 bool key_down_text_insertion = 115 event.type == blink::WebInputEvent::RawKeyDown && event.text[0]; 116 // If we are going to follow up with a synthetic Char event, then that's the 117 // one we expect to test if it's handled or unhandled, so skip handling the 118 // "real" event in the browser. 119 event.skip_in_browser = key_down_text_insertion; 120 rwhva_->SendKeyEvent(event); 121 if (key_down_text_insertion) { 122 // Send a Char event, but without an os_event since we don't want to 123 // roundtrip back to java such synthetic event. 124 NativeWebKeyboardEvent char_event(blink::WebInputEvent::Char, modifiers, 125 time_ms, key_code, unicode_char, 126 is_system_key); 127 char_event.skip_in_browser = key_down_text_insertion; 128 rwhva_->SendKeyEvent(char_event); 129 } 130 return true; 131 } 132 133 void ImeAdapterAndroid::SetComposingText(JNIEnv* env, jobject, jstring text, 134 int new_cursor_pos) { 135 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 136 if (!rwhi) 137 return; 138 139 base::string16 text16 = ConvertJavaStringToUTF16(env, text); 140 std::vector<blink::WebCompositionUnderline> underlines; 141 underlines.push_back( 142 blink::WebCompositionUnderline(0, text16.length(), SK_ColorBLACK, 143 false)); 144 // new_cursor_position is as described in the Android API for 145 // InputConnection#setComposingText, whereas the parameters for 146 // ImeSetComposition are relative to the start of the composition. 147 if (new_cursor_pos > 0) 148 new_cursor_pos = text16.length() + new_cursor_pos - 1; 149 150 rwhi->ImeSetComposition(text16, underlines, new_cursor_pos, new_cursor_pos); 151 } 152 153 void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text) { 154 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 155 if (!rwhi) 156 return; 157 158 base::string16 text16 = ConvertJavaStringToUTF16(env, text); 159 rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false); 160 } 161 162 void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) { 163 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 164 if (!rwhi) 165 return; 166 167 rwhi->ImeConfirmComposition(base::string16(), gfx::Range::InvalidRange(), 168 true); 169 } 170 171 void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) { 172 java_ime_adapter_ = JavaObjectWeakGlobalRef(env, java_object); 173 } 174 175 void ImeAdapterAndroid::CancelComposition() { 176 base::android::ScopedJavaLocalRef<jobject> obj = 177 java_ime_adapter_.get(AttachCurrentThread()); 178 if (!obj.is_null()) 179 Java_ImeAdapter_cancelComposition(AttachCurrentThread(), obj.obj()); 180 } 181 182 void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject, 183 int start, int end) { 184 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 185 if (!rwhi) 186 return; 187 188 rwhi->Send(new ViewMsg_SetEditableSelectionOffsets(rwhi->GetRoutingID(), 189 start, end)); 190 } 191 192 void ImeAdapterAndroid::SetComposingRegion(JNIEnv*, jobject, 193 int start, int end) { 194 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 195 if (!rwhi) 196 return; 197 198 std::vector<blink::WebCompositionUnderline> underlines; 199 underlines.push_back( 200 blink::WebCompositionUnderline(0, end - start, SK_ColorBLACK, false)); 201 202 rwhi->Send(new ViewMsg_SetCompositionFromExistingText( 203 rwhi->GetRoutingID(), start, end, underlines)); 204 } 205 206 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*, jobject, 207 int before, int after) { 208 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 209 if (!rwhi) 210 return; 211 212 rwhi->Send(new ViewMsg_ExtendSelectionAndDelete(rwhi->GetRoutingID(), 213 before, after)); 214 } 215 216 void ImeAdapterAndroid::Unselect(JNIEnv* env, jobject) { 217 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 218 if (!rwhi) 219 return; 220 221 rwhi->Unselect(); 222 } 223 224 void ImeAdapterAndroid::SelectAll(JNIEnv* env, jobject) { 225 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 226 if (!rwhi) 227 return; 228 229 rwhi->SelectAll(); 230 } 231 232 void ImeAdapterAndroid::Cut(JNIEnv* env, jobject) { 233 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 234 if (!rwhi) 235 return; 236 237 rwhi->Cut(); 238 } 239 240 void ImeAdapterAndroid::Copy(JNIEnv* env, jobject) { 241 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 242 if (!rwhi) 243 return; 244 245 rwhi->Copy(); 246 } 247 248 void ImeAdapterAndroid::Paste(JNIEnv* env, jobject) { 249 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 250 if (!rwhi) 251 return; 252 253 rwhi->Paste(); 254 } 255 256 void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) { 257 java_ime_adapter_.reset(); 258 } 259 260 RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() { 261 DCHECK(rwhva_); 262 RenderWidgetHost* rwh = rwhva_->GetRenderWidgetHost(); 263 if (!rwh) 264 return NULL; 265 266 return RenderWidgetHostImpl::From(rwh); 267 } 268 269 } // namespace content 270