Home | History | Annotate | Download | only in renderer_host
      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