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