Add ICU message format support
[chromium-blink-merge.git] / content / browser / renderer_host / ime_adapter_android.cc
blob88b1e126bfdefb043d1a73c0e26e499bbb9b49f0
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.
5 #include "content/browser/renderer_host/ime_adapter_android.h"
7 #include <algorithm>
8 #include <android/input.h>
9 #include <vector>
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"
34 using base::android::AttachCurrentThread;
35 using base::android::ConvertJavaStringToUTF16;
37 namespace content {
38 namespace {
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 else
61 NOTREACHED() << "Invalid Android key event action: " << action;
62 return NativeWebKeyboardEvent(java_key_event, type, modifiers,
63 time_ms / 1000.0, key_code, unicode_char, is_system_key);
66 } // anonymous namespace
68 bool RegisterImeAdapter(JNIEnv* env) {
69 return RegisterNativesImpl(env);
72 // Callback from Java to convert BackgroundColorSpan data to a
73 // blink::WebCompositionUnderline instance, and append it to |underlines_ptr|.
74 void AppendBackgroundColorSpan(JNIEnv*,
75 jclass,
76 jlong underlines_ptr,
77 jint start,
78 jint end,
79 jint background_color) {
80 DCHECK_GE(start, 0);
81 DCHECK_GE(end, 0);
82 // Do not check |background_color|.
83 std::vector<blink::WebCompositionUnderline>* underlines =
84 reinterpret_cast<std::vector<blink::WebCompositionUnderline>*>(
85 underlines_ptr);
86 underlines->push_back(
87 blink::WebCompositionUnderline(static_cast<unsigned>(start),
88 static_cast<unsigned>(end),
89 SK_ColorTRANSPARENT,
90 false,
91 static_cast<unsigned>(background_color)));
94 // Callback from Java to convert UnderlineSpan data to a
95 // blink::WebCompositionUnderline instance, and append it to |underlines_ptr|.
96 void AppendUnderlineSpan(JNIEnv*,
97 jclass,
98 jlong underlines_ptr,
99 jint start,
100 jint end) {
101 DCHECK_GE(start, 0);
102 DCHECK_GE(end, 0);
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_ColorBLACK,
110 false,
111 SK_ColorTRANSPARENT));
114 ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva)
115 : rwhva_(rwhva) {
118 ImeAdapterAndroid::~ImeAdapterAndroid() {
119 JNIEnv* env = AttachCurrentThread();
120 base::android::ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env);
121 if (!obj.is_null())
122 Java_ImeAdapter_detach(env, obj.obj());
125 bool ImeAdapterAndroid::SendSyntheticKeyEvent(JNIEnv*,
126 jobject,
127 int type,
128 long time_ms,
129 int key_code,
130 int modifiers,
131 int text) {
132 NativeWebKeyboardEvent event(static_cast<blink::WebInputEvent::Type>(type),
133 modifiers, time_ms / 1000.0, key_code,
134 text, false /* is_system_key */);
135 rwhva_->SendKeyEvent(event);
136 return true;
139 bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject,
140 jobject original_key_event,
141 int action, int modifiers,
142 long time_ms, int key_code,
143 bool is_system_key, int unicode_char) {
144 NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent(
145 env, original_key_event, action, modifiers,
146 time_ms, key_code, is_system_key, unicode_char);
147 bool key_down_text_insertion =
148 event.type == blink::WebInputEvent::RawKeyDown && event.text[0];
149 // If we are going to follow up with a synthetic Char event, then that's the
150 // one we expect to test if it's handled or unhandled, so skip handling the
151 // "real" event in the browser.
152 event.skip_in_browser = key_down_text_insertion;
153 rwhva_->SendKeyEvent(event);
154 if (key_down_text_insertion) {
155 // Send a Char event, but without an os_event since we don't want to
156 // roundtrip back to java such synthetic event.
157 NativeWebKeyboardEvent char_event(blink::WebInputEvent::Char, modifiers,
158 time_ms / 1000.0, key_code, unicode_char,
159 is_system_key);
160 char_event.skip_in_browser = key_down_text_insertion;
161 rwhva_->SendKeyEvent(char_event);
163 return true;
166 void ImeAdapterAndroid::SetComposingText(JNIEnv* env,
167 jobject obj,
168 jobject text,
169 jstring text_str,
170 int new_cursor_pos) {
171 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
172 if (!rwhi)
173 return;
175 base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
177 std::vector<blink::WebCompositionUnderline> underlines;
178 // Iterate over spans in |text|, dispatch those that we care about (e.g.,
179 // BackgroundColorSpan) to a matching callback (e.g.,
180 // AppendBackgroundColorSpan()), and populate |underlines|.
181 Java_ImeAdapter_populateUnderlinesFromSpans(
182 env, obj, text, reinterpret_cast<jlong>(&underlines));
184 // Default to plain underline if we didn't find any span that we care about.
185 if (underlines.empty()) {
186 underlines.push_back(blink::WebCompositionUnderline(
187 0, text16.length(), SK_ColorBLACK, false, SK_ColorTRANSPARENT));
189 // Sort spans by |.startOffset|.
190 std::sort(underlines.begin(), underlines.end());
192 // new_cursor_position is as described in the Android API for
193 // InputConnection#setComposingText, whereas the parameters for
194 // ImeSetComposition are relative to the start of the composition.
195 if (new_cursor_pos > 0)
196 new_cursor_pos = text16.length() + new_cursor_pos - 1;
198 rwhi->ImeSetComposition(text16, underlines, new_cursor_pos, new_cursor_pos);
201 void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text_str) {
202 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
203 if (!rwhi)
204 return;
206 base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
207 rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false);
210 void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) {
211 RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
212 if (!rwhi)
213 return;
215 rwhi->ImeConfirmComposition(base::string16(), gfx::Range::InvalidRange(),
216 true);
219 void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) {
220 java_ime_adapter_ = JavaObjectWeakGlobalRef(env, java_object);
223 void ImeAdapterAndroid::CancelComposition() {
224 base::android::ScopedJavaLocalRef<jobject> obj =
225 java_ime_adapter_.get(AttachCurrentThread());
226 if (!obj.is_null())
227 Java_ImeAdapter_cancelComposition(AttachCurrentThread(), obj.obj());
230 void ImeAdapterAndroid::FocusedNodeChanged(bool is_editable_node) {
231 base::android::ScopedJavaLocalRef<jobject> obj =
232 java_ime_adapter_.get(AttachCurrentThread());
233 if (!obj.is_null()) {
234 Java_ImeAdapter_focusedNodeChanged(AttachCurrentThread(),
235 obj.obj(),
236 is_editable_node);
240 void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject,
241 int start, int end) {
242 RenderFrameHost* rfh = GetFocusedFrame();
243 if (!rfh)
244 return;
246 rfh->Send(new FrameMsg_SetEditableSelectionOffsets(rfh->GetRoutingID(),
247 start, end));
250 void ImeAdapterAndroid::SetComposingRegion(JNIEnv*, jobject,
251 int start, int end) {
252 RenderFrameHost* rfh = GetFocusedFrame();
253 if (!rfh)
254 return;
256 std::vector<blink::WebCompositionUnderline> underlines;
257 underlines.push_back(blink::WebCompositionUnderline(
258 0, end - start, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
260 rfh->Send(new InputMsg_SetCompositionFromExistingText(
261 rfh->GetRoutingID(), start, end, underlines));
264 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*, jobject,
265 int before, int after) {
266 RenderFrameHostImpl* rfh =
267 static_cast<RenderFrameHostImpl*>(GetFocusedFrame());
268 if (rfh)
269 rfh->ExtendSelectionAndDelete(before, after);
272 void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) {
273 java_ime_adapter_.reset();
276 RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() {
277 DCHECK_CURRENTLY_ON(BrowserThread::UI);
278 DCHECK(rwhva_);
279 RenderWidgetHost* rwh = rwhva_->GetRenderWidgetHost();
280 if (!rwh)
281 return NULL;
283 return RenderWidgetHostImpl::From(rwh);
286 RenderFrameHost* ImeAdapterAndroid::GetFocusedFrame() {
287 RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
288 if (!rwh)
289 return NULL;
290 if (!rwh->IsRenderView())
291 return NULL;
292 RenderViewHost* rvh = RenderViewHost::From(rwh);
293 FrameTreeNode* focused_frame =
294 rvh->GetDelegate()->GetFrameTree()->GetFocusedFrame();
295 if (!focused_frame)
296 return NULL;
298 return focused_frame->current_frame_host();
301 WebContents* ImeAdapterAndroid::GetWebContents() {
302 RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
303 if (!rwh)
304 return NULL;
305 if (!rwh->IsRenderView())
306 return NULL;
307 return WebContents::FromRenderViewHost(RenderViewHost::From(rwh));
310 } // namespace content