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 <algorithm>
      8 #include <android/input.h>
      9 #include <vector>
     10 
     11 #include "base/android/jni_android.h"
     12 #include "base/android/jni_string.h"
     13 #include "base/android/scoped_java_ref.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "base/time/time.h"
     16 #include "content/browser/frame_host/frame_tree.h"
     17 #include "content/browser/frame_host/frame_tree_node.h"
     18 #include "content/browser/frame_host/render_frame_host_impl.h"
     19 #include "content/browser/renderer_host/render_view_host_delegate.h"
     20 #include "content/browser/renderer_host/render_view_host_impl.h"
     21 #include "content/browser/renderer_host/render_widget_host_impl.h"
     22 #include "content/browser/renderer_host/render_widget_host_view_android.h"
     23 #include "content/common/frame_messages.h"
     24 #include "content/common/input_messages.h"
     25 #include "content/common/view_messages.h"
     26 #include "content/public/browser/browser_thread.h"
     27 #include "content/public/browser/native_web_keyboard_event.h"
     28 #include "content/public/browser/web_contents.h"
     29 #include "jni/ImeAdapter_jni.h"
     30 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
     31 #include "third_party/WebKit/public/web/WebInputEvent.h"
     32 #include "third_party/WebKit/public/web/WebTextInputType.h"
     33 
     34 using base::android::AttachCurrentThread;
     35 using base::android::ConvertJavaStringToUTF16;
     36 
     37 namespace content {
     38 namespace {
     39 
     40 // Maps a java KeyEvent into a NativeWebKeyboardEvent.
     41 // |java_key_event| is used to maintain a globalref for KeyEvent.
     42 // |action| will help determine the WebInputEvent type.
     43 // type, |modifiers|, |time_ms|, |key_code|, |unicode_char| is used to create
     44 // WebKeyboardEvent. |key_code| is also needed ad need to treat the enter key
     45 // as a key press of character \r.
     46 NativeWebKeyboardEvent NativeWebKeyboardEventFromKeyEvent(
     47     JNIEnv* env,
     48     jobject java_key_event,
     49     int action,
     50     int modifiers,
     51     long time_ms,
     52     int key_code,
     53     bool is_system_key,
     54     int unicode_char) {
     55   blink::WebInputEvent::Type type = blink::WebInputEvent::Undefined;
     56   if (action == AKEY_EVENT_ACTION_DOWN)
     57     type = blink::WebInputEvent::RawKeyDown;
     58   else if (action == AKEY_EVENT_ACTION_UP)
     59     type = blink::WebInputEvent::KeyUp;
     60   return NativeWebKeyboardEvent(java_key_event, type, modifiers,
     61       time_ms / 1000.0, key_code, unicode_char, is_system_key);
     62 }
     63 
     64 }  // anonymous namespace
     65 
     66 bool RegisterImeAdapter(JNIEnv* env) {
     67   if (!RegisterNativesImpl(env))
     68     return false;
     69 
     70   Java_ImeAdapter_initializeWebInputEvents(env,
     71                                            blink::WebInputEvent::RawKeyDown,
     72                                            blink::WebInputEvent::KeyUp,
     73                                            blink::WebInputEvent::Char,
     74                                            blink::WebInputEvent::ShiftKey,
     75                                            blink::WebInputEvent::AltKey,
     76                                            blink::WebInputEvent::ControlKey,
     77                                            blink::WebInputEvent::CapsLockOn,
     78                                            blink::WebInputEvent::NumLockOn);
     79   Java_ImeAdapter_initializeTextInputTypes(
     80       env,
     81       ui::TEXT_INPUT_TYPE_NONE,
     82       ui::TEXT_INPUT_TYPE_TEXT,
     83       ui::TEXT_INPUT_TYPE_TEXT_AREA,
     84       ui::TEXT_INPUT_TYPE_PASSWORD,
     85       ui::TEXT_INPUT_TYPE_SEARCH,
     86       ui::TEXT_INPUT_TYPE_URL,
     87       ui::TEXT_INPUT_TYPE_EMAIL,
     88       ui::TEXT_INPUT_TYPE_TELEPHONE,
     89       ui::TEXT_INPUT_TYPE_NUMBER,
     90       ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE);
     91   Java_ImeAdapter_initializeTextInputFlags(
     92       env,
     93       blink::WebTextInputFlagAutocompleteOn,
     94       blink::WebTextInputFlagAutocompleteOff,
     95       blink::WebTextInputFlagAutocorrectOn,
     96       blink::WebTextInputFlagAutocorrectOff,
     97       blink::WebTextInputFlagSpellcheckOn,
     98       blink::WebTextInputFlagSpellcheckOff);
     99   return true;
    100 }
    101 
    102 // Callback from Java to convert BackgroundColorSpan data to a
    103 // blink::WebCompositionUnderline instance, and append it to |underlines_ptr|.
    104 void AppendBackgroundColorSpan(JNIEnv*,
    105                                jclass,
    106                                jlong underlines_ptr,
    107                                jint start,
    108                                jint end,
    109                                jint background_color) {
    110   DCHECK(start >= 0);
    111   DCHECK(end >= 0);
    112   // Do not check |background_color|.
    113   std::vector<blink::WebCompositionUnderline>* underlines =
    114       reinterpret_cast<std::vector<blink::WebCompositionUnderline>*>(
    115           underlines_ptr);
    116   underlines->push_back(
    117       blink::WebCompositionUnderline(static_cast<unsigned>(start),
    118                                      static_cast<unsigned>(end),
    119                                      SK_ColorTRANSPARENT,
    120                                      false,
    121                                      static_cast<unsigned>(background_color)));
    122 }
    123 
    124 // Callback from Java to convert UnderlineSpan data to a
    125 // blink::WebCompositionUnderline instance, and append it to |underlines_ptr|.
    126 void AppendUnderlineSpan(JNIEnv*,
    127                          jclass,
    128                          jlong underlines_ptr,
    129                          jint start,
    130                          jint end) {
    131   DCHECK(start >= 0);
    132   DCHECK(end >= 0);
    133   std::vector<blink::WebCompositionUnderline>* underlines =
    134       reinterpret_cast<std::vector<blink::WebCompositionUnderline>*>(
    135           underlines_ptr);
    136   underlines->push_back(
    137       blink::WebCompositionUnderline(static_cast<unsigned>(start),
    138                                      static_cast<unsigned>(end),
    139                                      SK_ColorBLACK,
    140                                      false,
    141                                      SK_ColorTRANSPARENT));
    142 }
    143 
    144 ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva)
    145     : rwhva_(rwhva) {
    146 }
    147 
    148 ImeAdapterAndroid::~ImeAdapterAndroid() {
    149   JNIEnv* env = AttachCurrentThread();
    150   base::android::ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env);
    151   if (!obj.is_null())
    152     Java_ImeAdapter_detach(env, obj.obj());
    153 }
    154 
    155 bool ImeAdapterAndroid::SendSyntheticKeyEvent(JNIEnv*,
    156                                               jobject,
    157                                               int type,
    158                                               long time_ms,
    159                                               int key_code,
    160                                               int modifiers,
    161                                               int text) {
    162   NativeWebKeyboardEvent event(static_cast<blink::WebInputEvent::Type>(type),
    163                                modifiers, time_ms / 1000.0, key_code,
    164                                text, false /* is_system_key */);
    165   rwhva_->SendKeyEvent(event);
    166   return true;
    167 }
    168 
    169 bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject,
    170                                      jobject original_key_event,
    171                                      int action, int modifiers,
    172                                      long time_ms, int key_code,
    173                                      bool is_system_key, int unicode_char) {
    174   NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent(
    175           env, original_key_event, action, modifiers,
    176           time_ms, key_code, is_system_key, unicode_char);
    177   bool key_down_text_insertion =
    178       event.type == blink::WebInputEvent::RawKeyDown && event.text[0];
    179   // If we are going to follow up with a synthetic Char event, then that's the
    180   // one we expect to test if it's handled or unhandled, so skip handling the
    181   // "real" event in the browser.
    182   event.skip_in_browser = key_down_text_insertion;
    183   rwhva_->SendKeyEvent(event);
    184   if (key_down_text_insertion) {
    185     // Send a Char event, but without an os_event since we don't want to
    186     // roundtrip back to java such synthetic event.
    187     NativeWebKeyboardEvent char_event(blink::WebInputEvent::Char, modifiers,
    188                                       time_ms / 1000.0, key_code, unicode_char,
    189                                       is_system_key);
    190     char_event.skip_in_browser = key_down_text_insertion;
    191     rwhva_->SendKeyEvent(char_event);
    192   }
    193   return true;
    194 }
    195 
    196 void ImeAdapterAndroid::SetComposingText(JNIEnv* env,
    197                                          jobject obj,
    198                                          jobject text,
    199                                          jstring text_str,
    200                                          int new_cursor_pos) {
    201   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
    202   if (!rwhi)
    203     return;
    204 
    205   base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
    206 
    207   std::vector<blink::WebCompositionUnderline> underlines;
    208   // Iterate over spans in |text|, dispatch those that we care about (e.g.,
    209   // BackgroundColorSpan) to a matching callback (e.g.,
    210   // AppendBackgroundColorSpan()), and populate |underlines|.
    211   Java_ImeAdapter_populateUnderlinesFromSpans(
    212       env, obj, text, reinterpret_cast<jlong>(&underlines));
    213 
    214   // Default to plain underline if we didn't find any span that we care about.
    215   if (underlines.empty()) {
    216     underlines.push_back(blink::WebCompositionUnderline(
    217         0, text16.length(), SK_ColorBLACK, false, SK_ColorTRANSPARENT));
    218   }
    219   // Sort spans by |.startOffset|.
    220   std::sort(underlines.begin(), underlines.end());
    221 
    222   // new_cursor_position is as described in the Android API for
    223   // InputConnection#setComposingText, whereas the parameters for
    224   // ImeSetComposition are relative to the start of the composition.
    225   if (new_cursor_pos > 0)
    226     new_cursor_pos = text16.length() + new_cursor_pos - 1;
    227 
    228   rwhi->ImeSetComposition(text16, underlines, new_cursor_pos, new_cursor_pos);
    229 }
    230 
    231 void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text_str) {
    232   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
    233   if (!rwhi)
    234     return;
    235 
    236   base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
    237   rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false);
    238 }
    239 
    240 void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) {
    241   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
    242   if (!rwhi)
    243     return;
    244 
    245   rwhi->ImeConfirmComposition(base::string16(), gfx::Range::InvalidRange(),
    246                               true);
    247 }
    248 
    249 void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) {
    250   java_ime_adapter_ = JavaObjectWeakGlobalRef(env, java_object);
    251 }
    252 
    253 void ImeAdapterAndroid::CancelComposition() {
    254   base::android::ScopedJavaLocalRef<jobject> obj =
    255       java_ime_adapter_.get(AttachCurrentThread());
    256   if (!obj.is_null())
    257     Java_ImeAdapter_cancelComposition(AttachCurrentThread(), obj.obj());
    258 }
    259 
    260 void ImeAdapterAndroid::FocusedNodeChanged(bool is_editable_node) {
    261   base::android::ScopedJavaLocalRef<jobject> obj =
    262       java_ime_adapter_.get(AttachCurrentThread());
    263   if (!obj.is_null()) {
    264     Java_ImeAdapter_focusedNodeChanged(AttachCurrentThread(),
    265                                        obj.obj(),
    266                                        is_editable_node);
    267   }
    268 }
    269 
    270 void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject,
    271                                                     int start, int end) {
    272   RenderFrameHost* rfh = GetFocusedFrame();
    273   if (!rfh)
    274     return;
    275 
    276   rfh->Send(new FrameMsg_SetEditableSelectionOffsets(rfh->GetRoutingID(),
    277                                                      start, end));
    278 }
    279 
    280 void ImeAdapterAndroid::SetComposingRegion(JNIEnv*, jobject,
    281                                            int start, int end) {
    282   RenderFrameHost* rfh = GetFocusedFrame();
    283   if (!rfh)
    284     return;
    285 
    286   std::vector<blink::WebCompositionUnderline> underlines;
    287   underlines.push_back(blink::WebCompositionUnderline(
    288       0, end - start, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
    289 
    290   rfh->Send(new InputMsg_SetCompositionFromExistingText(
    291       rfh->GetRoutingID(), start, end, underlines));
    292 }
    293 
    294 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*, jobject,
    295                                               int before, int after) {
    296   RenderFrameHostImpl* rfh =
    297       static_cast<RenderFrameHostImpl*>(GetFocusedFrame());
    298   if (rfh)
    299     rfh->ExtendSelectionAndDelete(before, after);
    300 }
    301 
    302 void ImeAdapterAndroid::Unselect(JNIEnv* env, jobject) {
    303   WebContents* wc = GetWebContents();
    304   if (wc)
    305     wc->Unselect();
    306 }
    307 
    308 void ImeAdapterAndroid::SelectAll(JNIEnv* env, jobject) {
    309   WebContents* wc = GetWebContents();
    310   if (wc)
    311     wc->SelectAll();
    312 }
    313 
    314 void ImeAdapterAndroid::Cut(JNIEnv* env, jobject) {
    315   WebContents* wc = GetWebContents();
    316   if (wc)
    317     wc->Cut();
    318 }
    319 
    320 void ImeAdapterAndroid::Copy(JNIEnv* env, jobject) {
    321   WebContents* wc = GetWebContents();
    322   if (wc)
    323     wc->Copy();
    324 }
    325 
    326 void ImeAdapterAndroid::Paste(JNIEnv* env, jobject) {
    327   WebContents* wc = GetWebContents();
    328   if (wc)
    329     wc->Paste();
    330 }
    331 
    332 void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) {
    333   java_ime_adapter_.reset();
    334 }
    335 
    336 RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() {
    337   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    338   DCHECK(rwhva_);
    339   RenderWidgetHost* rwh = rwhva_->GetRenderWidgetHost();
    340   if (!rwh)
    341     return NULL;
    342 
    343   return RenderWidgetHostImpl::From(rwh);
    344 }
    345 
    346 RenderFrameHost* ImeAdapterAndroid::GetFocusedFrame() {
    347   RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
    348   if (!rwh)
    349     return NULL;
    350   if (!rwh->IsRenderView())
    351     return NULL;
    352   RenderViewHost* rvh = RenderViewHost::From(rwh);
    353   FrameTreeNode* focused_frame =
    354       rvh->GetDelegate()->GetFrameTree()->GetFocusedFrame();
    355   if (!focused_frame)
    356     return NULL;
    357 
    358   return focused_frame->current_frame_host();
    359 }
    360 
    361 WebContents* ImeAdapterAndroid::GetWebContents() {
    362   RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
    363   if (!rwh)
    364     return NULL;
    365   if (!rwh->IsRenderView())
    366     return NULL;
    367   return WebContents::FromRenderViewHost(RenderViewHost::From(rwh));
    368 }
    369 
    370 }  // namespace content
    371