• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
NativeWebKeyboardEventFromKeyEvent(JNIEnv * env,jobject java_key_event,int action,int modifiers,long time_ms,int key_code,bool is_system_key,int unicode_char)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 
RegisterImeAdapter(JNIEnv * env)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 
ImeAdapterAndroid(RenderWidgetHostViewAndroid * rwhva)82 ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva)
83     : rwhva_(rwhva) {
84 }
85 
~ImeAdapterAndroid()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 
SendSyntheticKeyEvent(JNIEnv *,jobject,int type,long time_ms,int key_code,int text)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 
SendKeyEvent(JNIEnv * env,jobject,jobject original_key_event,int action,int modifiers,long time_ms,int key_code,bool is_system_key,int unicode_char)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 
SetComposingText(JNIEnv * env,jobject,jstring text,int new_cursor_pos)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 
CommitText(JNIEnv * env,jobject,jstring text)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 
FinishComposingText(JNIEnv * env,jobject)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 
AttachImeAdapter(JNIEnv * env,jobject java_object)171 void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) {
172   java_ime_adapter_ = JavaObjectWeakGlobalRef(env, java_object);
173 }
174 
CancelComposition()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 
SetEditableSelectionOffsets(JNIEnv *,jobject,int start,int end)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 
SetComposingRegion(JNIEnv *,jobject,int start,int end)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 
DeleteSurroundingText(JNIEnv *,jobject,int before,int after)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 
Unselect(JNIEnv * env,jobject)216 void ImeAdapterAndroid::Unselect(JNIEnv* env, jobject) {
217   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
218   if (!rwhi)
219     return;
220 
221   rwhi->Unselect();
222 }
223 
SelectAll(JNIEnv * env,jobject)224 void ImeAdapterAndroid::SelectAll(JNIEnv* env, jobject) {
225   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
226   if (!rwhi)
227     return;
228 
229   rwhi->SelectAll();
230 }
231 
Cut(JNIEnv * env,jobject)232 void ImeAdapterAndroid::Cut(JNIEnv* env, jobject) {
233   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
234   if (!rwhi)
235     return;
236 
237   rwhi->Cut();
238 }
239 
Copy(JNIEnv * env,jobject)240 void ImeAdapterAndroid::Copy(JNIEnv* env, jobject) {
241   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
242   if (!rwhi)
243     return;
244 
245   rwhi->Copy();
246 }
247 
Paste(JNIEnv * env,jobject)248 void ImeAdapterAndroid::Paste(JNIEnv* env, jobject) {
249   RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
250   if (!rwhi)
251     return;
252 
253   rwhi->Paste();
254 }
255 
ResetImeAdapter(JNIEnv * env,jobject)256 void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) {
257   java_ime_adapter_.reset();
258 }
259 
GetRenderWidgetHostImpl()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