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 WebKit::WebInputEvent::Type type = WebKit::WebInputEvent::Undefined; 44 if (action == AKEY_EVENT_ACTION_DOWN) 45 type = WebKit::WebInputEvent::RawKeyDown; 46 else if (action == AKEY_EVENT_ACTION_UP) 47 type = WebKit::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 WebKit::WebInputEvent::RawKeyDown, 60 WebKit::WebInputEvent::KeyUp, 61 WebKit::WebInputEvent::Char, 62 WebKit::WebInputEvent::ShiftKey, 63 WebKit::WebInputEvent::AltKey, 64 WebKit::WebInputEvent::ControlKey, 65 WebKit::WebInputEvent::CapsLockOn, 66 WebKit::WebInputEvent::NumLockOn); 67 // TODO(miguelg): remove date time related enums after 68 // https://bugs.webkit.org/show_bug.cgi?id=100935. 69 Java_ImeAdapter_initializeTextInputTypes( 70 env, 71 ui::TEXT_INPUT_TYPE_NONE, 72 ui::TEXT_INPUT_TYPE_TEXT, 73 ui::TEXT_INPUT_TYPE_TEXT_AREA, 74 ui::TEXT_INPUT_TYPE_PASSWORD, 75 ui::TEXT_INPUT_TYPE_SEARCH, 76 ui::TEXT_INPUT_TYPE_URL, 77 ui::TEXT_INPUT_TYPE_EMAIL, 78 ui::TEXT_INPUT_TYPE_TELEPHONE, 79 ui::TEXT_INPUT_TYPE_NUMBER, 80 ui::TEXT_INPUT_TYPE_DATE, 81 ui::TEXT_INPUT_TYPE_DATE_TIME, 82 ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL, 83 ui::TEXT_INPUT_TYPE_MONTH, 84 ui::TEXT_INPUT_TYPE_TIME, 85 ui::TEXT_INPUT_TYPE_WEEK, 86 ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE); 87 return true; 88 } 89 90 ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva) 91 : rwhva_(rwhva) { 92 } 93 94 ImeAdapterAndroid::~ImeAdapterAndroid() { 95 JNIEnv* env = AttachCurrentThread(); 96 base::android::ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env); 97 if (!obj.is_null()) 98 Java_ImeAdapter_detach(env, obj.obj()); 99 } 100 101 bool ImeAdapterAndroid::SendSyntheticKeyEvent(JNIEnv*, 102 jobject, 103 int type, 104 long time_ms, 105 int key_code, 106 int text) { 107 NativeWebKeyboardEvent event(static_cast<WebKit::WebInputEvent::Type>(type), 108 0 /* modifiers */, time_ms / 1000.0, key_code, 109 text, false /* is_system_key */); 110 rwhva_->SendKeyEvent(event); 111 return true; 112 } 113 114 bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject, 115 jobject original_key_event, 116 int action, int modifiers, 117 long time_ms, int key_code, 118 bool is_system_key, int unicode_char) { 119 NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent( 120 env, original_key_event, action, modifiers, 121 time_ms, key_code, is_system_key, unicode_char); 122 bool key_down_text_insertion = 123 event.type == WebKit::WebInputEvent::RawKeyDown && event.text[0]; 124 // If we are going to follow up with a synthetic Char event, then that's the 125 // one we expect to test if it's handled or unhandled, so skip handling the 126 // "real" event in the browser. 127 event.skip_in_browser = key_down_text_insertion; 128 rwhva_->SendKeyEvent(event); 129 if (key_down_text_insertion) { 130 // Send a Char event, but without an os_event since we don't want to 131 // roundtrip back to java such synthetic event. 132 NativeWebKeyboardEvent char_event(WebKit::WebInputEvent::Char, modifiers, 133 time_ms, key_code, unicode_char, 134 is_system_key); 135 char_event.skip_in_browser = key_down_text_insertion; 136 rwhva_->SendKeyEvent(char_event); 137 } 138 return true; 139 } 140 141 void ImeAdapterAndroid::SetComposingText(JNIEnv* env, jobject, jstring text, 142 int new_cursor_pos) { 143 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 144 if (!rwhi) 145 return; 146 147 string16 text16 = ConvertJavaStringToUTF16(env, text); 148 std::vector<WebKit::WebCompositionUnderline> underlines; 149 underlines.push_back( 150 WebKit::WebCompositionUnderline(0, text16.length(), SK_ColorBLACK, 151 false)); 152 // new_cursor_position is as described in the Android API for 153 // InputConnection#setComposingText, whereas the parameters for 154 // ImeSetComposition are relative to the start of the composition. 155 if (new_cursor_pos > 0) 156 new_cursor_pos = text16.length() + new_cursor_pos - 1; 157 158 rwhi->ImeSetComposition(text16, underlines, new_cursor_pos, new_cursor_pos); 159 } 160 161 void ImeAdapterAndroid::ImeBatchStateChanged(JNIEnv* env, 162 jobject, 163 jboolean is_begin) { 164 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 165 if (!rwhi) 166 return; 167 168 rwhi->Send(new ViewMsg_ImeBatchStateChanged(rwhi->GetRoutingID(), is_begin)); 169 } 170 171 void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text) { 172 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 173 if (!rwhi) 174 return; 175 176 string16 text16 = ConvertJavaStringToUTF16(env, text); 177 rwhi->ImeConfirmComposition(text16, ui::Range::InvalidRange(), false); 178 } 179 180 void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) { 181 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 182 if (!rwhi) 183 return; 184 185 rwhi->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), true); 186 } 187 188 void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) { 189 java_ime_adapter_ = JavaObjectWeakGlobalRef(env, java_object); 190 } 191 192 void ImeAdapterAndroid::CancelComposition() { 193 base::android::ScopedJavaLocalRef<jobject> obj = 194 java_ime_adapter_.get(AttachCurrentThread()); 195 if (!obj.is_null()) 196 Java_ImeAdapter_cancelComposition(AttachCurrentThread(), obj.obj()); 197 } 198 199 void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject, 200 int start, int end) { 201 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 202 if (!rwhi) 203 return; 204 205 rwhi->Send(new ViewMsg_SetEditableSelectionOffsets(rwhi->GetRoutingID(), 206 start, end)); 207 } 208 209 void ImeAdapterAndroid::SetComposingRegion(JNIEnv*, jobject, 210 int start, int end) { 211 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 212 if (!rwhi) 213 return; 214 215 std::vector<WebKit::WebCompositionUnderline> underlines; 216 underlines.push_back( 217 WebKit::WebCompositionUnderline(0, end - start, SK_ColorBLACK, false)); 218 219 rwhi->Send(new ViewMsg_SetCompositionFromExistingText( 220 rwhi->GetRoutingID(), start, end, underlines)); 221 } 222 223 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*, jobject, 224 int before, int after) { 225 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 226 if (!rwhi) 227 return; 228 229 rwhi->Send(new ViewMsg_ExtendSelectionAndDelete(rwhi->GetRoutingID(), 230 before, after)); 231 } 232 233 void ImeAdapterAndroid::Unselect(JNIEnv* env, jobject) { 234 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 235 if (!rwhi) 236 return; 237 238 rwhi->Unselect(); 239 } 240 241 void ImeAdapterAndroid::SelectAll(JNIEnv* env, jobject) { 242 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 243 if (!rwhi) 244 return; 245 246 rwhi->SelectAll(); 247 } 248 249 void ImeAdapterAndroid::Cut(JNIEnv* env, jobject) { 250 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 251 if (!rwhi) 252 return; 253 254 rwhi->Cut(); 255 } 256 257 void ImeAdapterAndroid::Copy(JNIEnv* env, jobject) { 258 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 259 if (!rwhi) 260 return; 261 262 rwhi->Copy(); 263 } 264 265 void ImeAdapterAndroid::Paste(JNIEnv* env, jobject) { 266 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl(); 267 if (!rwhi) 268 return; 269 270 rwhi->Paste(); 271 } 272 273 void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) { 274 java_ime_adapter_.reset(); 275 } 276 277 RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() { 278 DCHECK(rwhva_); 279 RenderWidgetHost* rwh = rwhva_->GetRenderWidgetHost(); 280 if (!rwh) 281 return NULL; 282 283 return RenderWidgetHostImpl::From(rwh); 284 } 285 286 } // namespace content 287