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