Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: content/browser/renderer_host/java/gin_java_method_invocation_helper.cc

Issue 302173006: [Android] Java Bridge with Gin: implement Java methods invocation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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/java/gin_java_method_invocation_helper.h "
6
7 #include <unistd.h>
8
9 #include "base/android/event_log.h"
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_string.h"
12 #include "base/float_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "content/browser/renderer_host/java/java_method.h"
17 #include "content/browser/renderer_host/java/jni_helper.h"
18 #include "content/common/android/gin_java_bridge_value.h"
19 #include "content/public/browser/browser_thread.h"
20
21 using base::android::AttachCurrentThread;
22 using base::android::ConvertUTF8ToJavaString;
23 using base::android::ScopedJavaLocalRef;
24
25 namespace content {
26
27 namespace {
28
29 const char kJavaLangString[] = "java/lang/String";
30 const char kObjectIsGone[] = "Java object is gone";
31 const char kMethodNotFound[] = "Method not found";
32 const char kAccessToObjectGetClassIsBlocked[] =
33 "Access to java.lang.Object.getClass is blocked";
34 const char kJavaExceptionRaised[] =
35 "Java exception has been raised during method invocation";
36 const char kUndefined[] = "undefined";
37
38 } // namespace
39
40 GinJavaMethodInvocationHelper::GinJavaMethodInvocationHelper(
41 scoped_ptr<ObjectDelegate> object,
42 const std::string& method_name,
43 const base::ListValue& arguments)
44 : object_(object.Pass()),
45 method_name_(method_name),
46 arguments_(arguments.DeepCopy()) {
47 }
48
49 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
50
51 void GinJavaMethodInvocationHelper::Init(DispatcherDelegate* dispatcher) {
52 // Build on the UI thread a map of object_id -> WeakRef for Java objects from
53 // |arguments_|. Then we can use this map on the background thread without
54 // accessing |dispatcher|.
55 BuildObjectRefsFromListValue(dispatcher, arguments_.get());
56 }
57
58 // As V8ValueConverter has finite recursion depth when serializing
59 // JavaScript values, we don't bother about having a recursion threshold here.
60 void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue(
61 DispatcherDelegate* dispatcher,
62 const base::Value* list_value) {
63 DCHECK(list_value->IsType(base::Value::TYPE_LIST));
64 const base::ListValue* list;
65 list_value->GetAsList(&list);
66 for (base::ListValue::const_iterator iter = list->begin();
67 iter != list->end();
68 ++iter) {
69 if (AppendObjectRef(dispatcher, *iter))
70 continue;
71 if ((*iter)->IsType(base::Value::TYPE_LIST)) {
72 BuildObjectRefsFromListValue(dispatcher, *iter);
73 } else if ((*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
74 BuildObjectRefsFromDictionaryValue(dispatcher, *iter);
75 }
76 }
77 }
78
79 void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue(
80 DispatcherDelegate* dispatcher,
81 const base::Value* dict_value) {
82 DCHECK(dict_value->IsType(base::Value::TYPE_DICTIONARY));
83 const base::DictionaryValue* dict;
84 dict_value->GetAsDictionary(&dict);
85 for (base::DictionaryValue::Iterator iter(*dict);
86 !iter.IsAtEnd();
87 iter.Advance()) {
88 if (AppendObjectRef(dispatcher, &iter.value()))
89 continue;
90 if (iter.value().IsType(base::Value::TYPE_LIST)) {
91 BuildObjectRefsFromListValue(dispatcher, &iter.value());
92 } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
93 BuildObjectRefsFromDictionaryValue(dispatcher, &iter.value());
94 }
95 }
96 }
97
98 bool GinJavaMethodInvocationHelper::AppendObjectRef(
99 DispatcherDelegate* dispatcher,
100 const base::Value* raw_value) {
101 if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value))
102 return false;
103 scoped_ptr<const GinJavaBridgeValue> value(
104 GinJavaBridgeValue::FromValue(raw_value));
105 if (!value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID))
106 return false;
107 GinJavaBoundObject::ObjectID object_id;
108 if (value->GetAsObjectID(&object_id)) {
109 ObjectRefs::iterator iter = object_refs_.find(object_id);
110 if (iter == object_refs_.end()) {
111 JavaObjectWeakGlobalRef object_ref(
112 dispatcher->GetObjectWeakRef(object_id));
113 if (!object_ref.is_null()) {
114 object_refs_.insert(std::make_pair(object_id, object_ref));
115 }
116 }
117 }
118 return true;
119 }
120
121 void GinJavaMethodInvocationHelper::Invoke() {
122 JNIEnv* env = AttachCurrentThread();
123 base::android::ScopedJavaLocalRef<jobject> obj(object_->GetLocalRef(env));
124 if (obj.is_null()) {
125 SetInvocationFailure(kObjectIsGone);
126 return;
127 }
128 const JavaMethod* method =
129 object_->FindMethod(method_name_, arguments_->GetSize());
130 if (!method) {
131 SetInvocationFailure(kMethodNotFound);
132 return;
133 }
134
135 if (object_->IsObjectGetClassMethod(method)) {
136 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
137 base::android::eventLogWriteInt(70151, getuid());
bulach 2014/06/09 11:44:33 any chance we could have those defined here?!
mnaganov (inactive) 2014/06/11 13:50:44 I have moved the value into a named constant. Ther
138 SetInvocationFailure(kAccessToObjectGetClassIsBlocked);
139 return;
140 }
141
142 std::vector<jvalue> parameters(method->num_parameters());
143 for (size_t i = 0; i < method->num_parameters(); ++i) {
144 const base::Value* argument;
145 arguments_->Get(i, &argument);
146 parameters[i] = CoerceJavaScriptValueToJavaValue(argument,
147 method->parameter_type(i),
148 true);
149 }
150 InvokeMethod(obj.obj(),
151 method->return_type(),
152 method->id(),
153 &parameters[0]);
154
155 // Now that we're done with the jvalue, release any local references created
156 // by CoerceJavaScriptValueToJavaValue().
157 for (size_t i = 0; i < method->num_parameters(); ++i) {
158 ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
159 }
160 }
161
162 void GinJavaMethodInvocationHelper::SetInvocationFailure(
163 const char* error_message) {
164 holds_primitive_result_ = true;
165 primitive_result_.reset(new base::ListValue());
166 error_message_ = error_message;
167 }
168
169 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
170 const base::ListValue& result_wrapper) {
171 holds_primitive_result_ = true;
172 primitive_result_.reset(result_wrapper.DeepCopy());
173 }
174
175 void GinJavaMethodInvocationHelper::SetObjectResult(
176 const base::android::JavaRef<jobject>& object,
177 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
178 holds_primitive_result_ = false;
179 object_result_.Reset(object);
180 safe_annotation_clazz_.Reset(safe_annotation_clazz);
181 }
182
183 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
184 return holds_primitive_result_;
185 }
186
187 const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
188 return *primitive_result_.get();
189 }
190
191 const base::android::JavaRef<jobject>&
192 GinJavaMethodInvocationHelper::GetObjectResult() {
193 return object_result_;
194 }
195
196 const base::android::JavaRef<jclass>&
197 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
198 return safe_annotation_clazz_;
199 }
200
201 const std::string& GinJavaMethodInvocationHelper::GetErrorMessage() {
202 return error_message_;
203 }
204
205 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
206 const JavaType& return_type,
207 jmethodID id,
208 jvalue* parameters) {
209 JNIEnv* env = AttachCurrentThread();
210 base::ListValue result_wrapper;
211 switch (return_type.type) {
212 case JavaType::TypeBoolean:
213 result_wrapper.AppendBoolean(
214 env->CallBooleanMethodA(object, id, parameters));
215 break;
216 case JavaType::TypeByte:
217 result_wrapper.AppendInteger(
218 env->CallByteMethodA(object, id, parameters));
219 break;
220 case JavaType::TypeChar:
221 result_wrapper.AppendInteger(
222 env->CallCharMethodA(object, id, parameters));
223 break;
224 case JavaType::TypeShort:
225 result_wrapper.AppendInteger(
226 env->CallShortMethodA(object, id, parameters));
227 break;
228 case JavaType::TypeInt:
229 result_wrapper.AppendInteger(
230 env->CallIntMethodA(object, id, parameters));
231 break;
232 case JavaType::TypeLong:
233 result_wrapper.AppendDouble(
234 env->CallLongMethodA(object, id, parameters));
235 break;
236 case JavaType::TypeFloat: {
237 float result = env->CallFloatMethodA(object, id, parameters);
238 if (base::IsFinite(result)) {
239 result_wrapper.AppendDouble(result);
240 } else {
241 result_wrapper.Append(
242 GinJavaBridgeValue::CreateNonFiniteValue(result).release());
243 }
244 break;
245 }
246 case JavaType::TypeDouble: {
247 double result = env->CallDoubleMethodA(object, id, parameters);
248 if (base::IsFinite(result)) {
249 result_wrapper.AppendDouble(result);
250 } else {
251 result_wrapper.Append(
252 GinJavaBridgeValue::CreateNonFiniteValue(result).release());
253 }
254 break;
255 }
256 case JavaType::TypeVoid:
257 env->CallVoidMethodA(object, id, parameters);
258 result_wrapper.Append(
259 GinJavaBridgeValue::CreateUndefinedValue().release());
260 break;
261 case JavaType::TypeArray:
262 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
263 // return arrays. Spec requires calling the method and converting the
264 // result to a JavaScript array.
265 result_wrapper.Append(
266 GinJavaBridgeValue::CreateUndefinedValue().release());
267 break;
268 case JavaType::TypeString: {
269 jstring java_string = static_cast<jstring>(
270 env->CallObjectMethodA(object, id, parameters));
271 // If an exception was raised, we must clear it before calling most JNI
272 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
273 // first.
274 if (base::android::ClearException(env)) {
275 SetInvocationFailure(kJavaExceptionRaised);
276 return;
277 }
278 ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
279 if (!scoped_java_string.obj()) {
280 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
281 // Spec requires returning a null string.
282 result_wrapper.Append(
283 GinJavaBridgeValue::CreateUndefinedValue().release());
284 break;
285 }
286 result_wrapper.AppendString(
287 base::android::ConvertJavaStringToUTF8(scoped_java_string));
288 break;
289 }
290 case JavaType::TypeObject: {
291 // If an exception was raised, we must clear it before calling most JNI
292 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
293 // first.
294 jobject java_object = env->CallObjectMethodA(object, id, parameters);
295 if (base::android::ClearException(env)) {
296 SetInvocationFailure(kJavaExceptionRaised);
297 return;
298 }
299 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
300 if (!scoped_java_object.obj()) {
301 result_wrapper.Append(base::Value::CreateNullValue());
302 break;
303 }
304 SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
305 return;
306 }
307 }
308 // This is for all cases except JavaType::TypeObject.
309 if (!base::android::ClearException(env)) {
310 SetPrimitiveResult(result_wrapper);
311 } else {
312 SetInvocationFailure(kJavaExceptionRaised);
313 }
314 }
315
316 void GinJavaMethodInvocationHelper::ReleaseJavaValueIfRequired(JNIEnv* env,
317 jvalue* value,
318 const JavaType& type) {
bulach 2014/06/09 11:44:33 nit: left align, or keep all indented by +4 like t
mnaganov (inactive) 2014/06/11 13:50:44 Done.
319 if (type.type == JavaType::TypeString ||
320 type.type == JavaType::TypeObject ||
321 type.type == JavaType::TypeArray) {
322 env->DeleteLocalRef(value->l);
323 value->l = NULL;
324 }
325 }
326
327 jvalue GinJavaMethodInvocationHelper::CoerceJavaScriptValueToJavaValue(
328 const base::Value* value,
329 const JavaType& target_type,
330 bool coerce_to_string) {
331 // Note that in all these conversions, the relevant field of the jvalue must
332 // always be explicitly set, as jvalue does not initialize its fields.
333
334 switch (value->GetType()) {
335 case base::Value::TYPE_INTEGER:
336 return CoerceJavaScriptIntegerToJavaValue(value, target_type,
337 coerce_to_string);
bulach 2014/06/09 11:44:33 nit: align
mnaganov (inactive) 2014/06/11 13:50:44 Done, thanks!
338 case base::Value::TYPE_DOUBLE: {
339 double double_value;
340 value->GetAsDouble(&double_value);
341 return CoerceJavaScriptDoubleToJavaValue(double_value, target_type,
342 coerce_to_string);
343 }
344 case base::Value::TYPE_BOOLEAN:
345 return CoerceJavaScriptBooleanToJavaValue(value, target_type,
346 coerce_to_string);
347 case base::Value::TYPE_STRING:
348 return CoerceJavaScriptStringToJavaValue(value, target_type);
349 case base::Value::TYPE_DICTIONARY:
350 case base::Value::TYPE_LIST:
351 return CoerceJavaScriptObjectToJavaValue(value, target_type,
352 coerce_to_string);
353 case base::Value::TYPE_NULL:
354 return CoerceJavaScriptNullOrUndefinedToJavaValue(value, target_type,
355 coerce_to_string);
356 case base::Value::TYPE_BINARY:
357 return CoerceGinJavaBridgeValueToJavaValue(value, target_type,
358 coerce_to_string);
359 }
360 NOTREACHED();
361 return jvalue();
362 }
363
364 jvalue GinJavaMethodInvocationHelper::CoerceGinJavaBridgeValueToJavaValue(
365 const base::Value* value,
366 const JavaType& target_type,
367 bool coerce_to_string) {
368 DCHECK(GinJavaBridgeValue::ContainsGinJavaBridgeValue(value));
369 scoped_ptr<const GinJavaBridgeValue> gin_value(
370 GinJavaBridgeValue::FromValue(value));
371 switch (gin_value->GetType()) {
372 case GinJavaBridgeValue::TYPE_UNDEFINED:
373 return CoerceJavaScriptNullOrUndefinedToJavaValue(
374 value, target_type, coerce_to_string);
375 case GinJavaBridgeValue::TYPE_NONFINITE: {
376 float float_value;
377 gin_value->GetAsNonFinite(&float_value);
378 return CoerceJavaScriptDoubleToJavaValue(
379 float_value, target_type, coerce_to_string);
380 }
381 case GinJavaBridgeValue::TYPE_OBJECT_ID:
382 return CoerceJavaScriptObjectToJavaValue(
383 value, target_type, coerce_to_string);
384 default:
385 NOTREACHED();
386 }
387 return jvalue();
388 }
389
390 namespace {
391
392 double RoundDoubleTowardsZero(const double& x) {
393 if (std::isnan(x)) {
394 return 0.0;
395 }
396 return x > 0.0 ? floor(x) : ceil(x);
397 }
398
399 // Rounds to jlong using Java's type conversion rules.
400 jlong RoundDoubleToLong(const double& x) {
401 double intermediate = RoundDoubleTowardsZero(x);
402 // The int64 limits can not be converted exactly to double values, so we
403 // compare to custom constants. kint64max is 2^63 - 1, but the spacing
404 // between double values in the the range 2^62 to 2^63 is 2^10. The cast is
405 // required to silence a spurious gcc warning for integer overflow.
406 const int64 limit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10);
bulach 2014/06/09 11:44:33 nit: kLimit
mnaganov (inactive) 2014/06/11 13:50:44 Done.
407 DCHECK(limit > 0);
408 const double kLargestDoubleLessThanInt64Max = limit;
409 const double kSmallestDoubleGreaterThanInt64Min = -limit;
410 if (intermediate > kLargestDoubleLessThanInt64Max) {
411 return kint64max;
412 }
413 if (intermediate < kSmallestDoubleGreaterThanInt64Min) {
414 return kint64min;
415 }
416 return static_cast<jlong>(intermediate);
417 }
418
419 // Rounds to jint using Java's type conversion rules.
420 jint RoundDoubleToInt(const double& x) {
421 double intermediate = RoundDoubleTowardsZero(x);
422 // The int32 limits cast exactly to double values.
423 intermediate = std::min(intermediate, static_cast<double>(kint32max));
424 intermediate = std::max(intermediate, static_cast<double>(kint32min));
425 return static_cast<jint>(intermediate);
426 }
427
428 } // namespace
429
430 jvalue GinJavaMethodInvocationHelper::CoerceJavaScriptIntegerToJavaValue(
431 const base::Value* value,
432 const JavaType& target_type,
433 bool coerce_to_string) {
434 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
435
436 // For conversion to numeric types, we need to replicate Java's type
437 // conversion rules. This requires that for integer values, we simply discard
438 // all but the lowest n buts, where n is the number of bits in the target
439 // type.
440 jvalue result;
441 int int_value;
442 value->GetAsInteger(&int_value);
443 switch (target_type.type) {
444 case JavaType::TypeByte:
445 result.b = static_cast<jbyte>(int_value);
446 break;
447 case JavaType::TypeChar:
448 result.c = static_cast<jchar>(int_value);
449 break;
450 case JavaType::TypeShort:
451 result.s = static_cast<jshort>(int_value);
452 break;
453 case JavaType::TypeInt:
454 result.i = int_value;
455 break;
456 case JavaType::TypeLong:
457 result.j = int_value;
458 break;
459 case JavaType::TypeFloat:
460 result.f = int_value;
461 break;
462 case JavaType::TypeDouble:
463 result.d = int_value;
464 break;
465 case JavaType::TypeObject:
466 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
467 // requires handling object equivalents of primitive types.
468 result.l = NULL;
469 break;
470 case JavaType::TypeString:
471 result.l = coerce_to_string
472 ? ConvertUTF8ToJavaString(AttachCurrentThread(),
473 base::Int64ToString(int_value))
474 .Release()
475 : NULL;
476 break;
477 case JavaType::TypeBoolean:
478 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
479 // requires converting to false for 0 or NaN, true otherwise.
480 result.z = JNI_FALSE;
481 break;
482 case JavaType::TypeArray:
483 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
484 // requires raising a JavaScript exception.
485 result.l = NULL;
486 break;
487 case JavaType::TypeVoid:
488 // Conversion to void must never happen.
489 NOTREACHED();
490 break;
491 }
492 return result;
493 }
494
495 jvalue GinJavaMethodInvocationHelper::CoerceJavaScriptDoubleToJavaValue(
496 double double_value,
497 const JavaType& target_type,
498 bool coerce_to_string) {
499 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
500 // For conversion to numeric types, we need to replicate Java's type
501 // conversion rules.
502 jvalue result;
503 switch (target_type.type) {
504 case JavaType::TypeByte:
505 result.b = static_cast<jbyte>(RoundDoubleToInt(double_value));
506 break;
507 case JavaType::TypeChar:
508 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0.
509 // Spec requires converting doubles similarly to how we convert doubles to
510 // other numeric types.
511 result.c = 0;
512 break;
513 case JavaType::TypeShort:
514 result.s = static_cast<jshort>(RoundDoubleToInt(double_value));
515 break;
516 case JavaType::TypeInt:
517 result.i = RoundDoubleToInt(double_value);
518 break;
519 case JavaType::TypeLong:
520 result.j = RoundDoubleToLong(double_value);
521 break;
522 case JavaType::TypeFloat:
523 result.f = static_cast<jfloat>(double_value);
524 break;
525 case JavaType::TypeDouble:
526 result.d = double_value;
527 break;
528 case JavaType::TypeObject:
529 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
530 // requires handling object equivalents of primitive types.
531 result.l = NULL;
532 break;
533 case JavaType::TypeString:
534 result.l = coerce_to_string
535 ? ConvertUTF8ToJavaString(
536 AttachCurrentThread(),
537 base::StringPrintf("%.6lg", double_value)).Release()
538 : NULL;
539 break;
540 case JavaType::TypeBoolean:
541 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
542 // requires converting to false for 0 or NaN, true otherwise.
543 result.z = JNI_FALSE;
544 break;
545 case JavaType::TypeArray:
546 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
547 // requires raising a JavaScript exception.
548 result.l = NULL;
549 break;
550 case JavaType::TypeVoid:
551 // Conversion to void must never happen.
552 NOTREACHED();
553 break;
554 }
555 return result;
556 }
557
558 jvalue GinJavaMethodInvocationHelper::CoerceJavaScriptBooleanToJavaValue(
559 const base::Value* value,
560 const JavaType& target_type,
561 bool coerce_to_string) {
562 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES.
563 bool boolean_value;
564 value->GetAsBoolean(&boolean_value);
565 jvalue result;
566 switch (target_type.type) {
567 case JavaType::TypeBoolean:
568 result.z = boolean_value ? JNI_TRUE : JNI_FALSE;
569 break;
570 case JavaType::TypeObject:
571 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
572 // requires handling java.lang.Boolean and java.lang.Object.
573 result.l = NULL;
574 break;
575 case JavaType::TypeString:
576 result.l = coerce_to_string ?
577 ConvertUTF8ToJavaString(AttachCurrentThread(),
578 boolean_value ? "true" : "false").Release() :
579 NULL;
580 break;
581 case JavaType::TypeByte:
582 case JavaType::TypeChar:
583 case JavaType::TypeShort:
584 case JavaType::TypeInt:
585 case JavaType::TypeLong:
586 case JavaType::TypeFloat:
587 case JavaType::TypeDouble: {
588 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
589 // requires converting to 0 or 1.
590 jvalue null_value = {0};
591 result = null_value;
592 break;
593 }
594 case JavaType::TypeArray:
595 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
596 // requires raising a JavaScript exception.
597 result.l = NULL;
598 break;
599 case JavaType::TypeVoid:
600 // Conversion to void must never happen.
601 NOTREACHED();
602 break;
603 }
604 return result;
605 }
606
607 jvalue GinJavaMethodInvocationHelper::CoerceJavaScriptStringToJavaValue(
608 const base::Value* value,
609 const JavaType& target_type) {
610 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES.
611 jvalue result;
612 switch (target_type.type) {
613 case JavaType::TypeString: {
614 std::string string_result;
615 value->GetAsString(&string_result);
616 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), string_result)
617 .Release();
618 break;
619 }
620 case JavaType::TypeObject:
621 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
622 // requires handling java.lang.Object.
623 result.l = NULL;
624 break;
625 case JavaType::TypeByte:
626 case JavaType::TypeShort:
627 case JavaType::TypeInt:
628 case JavaType::TypeLong:
629 case JavaType::TypeFloat:
630 case JavaType::TypeDouble: {
631 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
632 // requires using valueOf() method of corresponding object type.
633 jvalue null_value = {0};
634 result = null_value;
635 break;
636 }
637 case JavaType::TypeChar:
638 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
639 // requires using java.lang.Short.decode().
640 result.c = 0;
641 break;
642 case JavaType::TypeBoolean:
643 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
644 // requires converting the empty string to false, otherwise true.
645 result.z = JNI_FALSE;
646 break;
647 case JavaType::TypeArray:
648 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
649 // requires raising a JavaScript exception.
650 result.l = NULL;
651 break;
652 case JavaType::TypeVoid:
653 // Conversion to void must never happen.
654 NOTREACHED();
655 break;
656 }
657 return result;
658 }
659
660 jvalue GinJavaMethodInvocationHelper::CoerceJavaScriptObjectToJavaValue(
661 const base::Value* value,
662 const JavaType& target_type,
663 bool coerce_to_string) {
664 // This covers both JavaScript objects (including arrays) and Java objects.
665 // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS,
666 // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and
667 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS
668 jvalue result;
669 switch (target_type.type) {
670 case JavaType::TypeObject: {
671 if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) {
672 scoped_ptr<const GinJavaBridgeValue> gin_value(
673 GinJavaBridgeValue::FromValue(value));
674 DCHECK(gin_value);
675 DCHECK(gin_value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID));
676 base::android::ScopedJavaLocalRef<jobject> obj;
677 GinJavaBoundObject::ObjectID object_id;
678 if (gin_value->GetAsObjectID(&object_id)) {
679 ObjectRefs::iterator iter = object_refs_.find(object_id);
680 if (iter != object_refs_.end()) {
681 obj.Reset(iter->second.get(AttachCurrentThread()));
682 }
683 }
684 result.l = obj.Release();
685 } else {
686 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec
687 // requires converting if the target type is
688 // netscape.javascript.JSObject, otherwise raising a JavaScript
689 // exception.
690 result.l = NULL;
691 }
692 break;
693 }
694 case JavaType::TypeString:
695 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to
696 // "undefined". Spec requires calling toString() on the Java object.
697 result.l = coerce_to_string
698 ? ConvertUTF8ToJavaString(AttachCurrentThread(),
699 kUndefined).Release()
700 : NULL;
701 break;
702 case JavaType::TypeByte:
703 case JavaType::TypeShort:
704 case JavaType::TypeInt:
705 case JavaType::TypeLong:
706 case JavaType::TypeFloat:
707 case JavaType::TypeDouble:
708 case JavaType::TypeChar: {
709 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
710 // requires raising a JavaScript exception.
711 jvalue null_value = {0};
712 result = null_value;
713 break;
714 }
715 case JavaType::TypeBoolean:
716 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
717 // requires raising a JavaScript exception.
718 result.z = JNI_FALSE;
719 break;
720 case JavaType::TypeArray:
721 if (value->IsType(base::Value::TYPE_DICTIONARY)) {
722 result.l = CoerceJavaScriptDictionaryToArray(value, target_type);
723 } else if (value->IsType(base::Value::TYPE_LIST)) {
724 result.l = CoerceJavaScriptListToArray(value, target_type);
725 } else {
726 result.l = NULL;
727 }
728 break;
729 case JavaType::TypeVoid:
730 // Conversion to void must never happen.
731 NOTREACHED();
732 break;
733 }
734 return result;
735 }
736
737 jobject GinJavaMethodInvocationHelper::CoerceJavaScriptListToArray(
738 const base::Value* value,
739 const JavaType& target_type) {
740 DCHECK_EQ(JavaType::TypeArray, target_type.type);
741 const JavaType& target_inner_type = *target_type.inner_type.get();
742 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
743 // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
744 if (target_inner_type.type == JavaType::TypeArray) {
745 return NULL;
746 }
747
748 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
749 // arrays. Spec requires handling object arrays.
750 if (target_inner_type.type == JavaType::TypeObject) {
751 return NULL;
752 }
753
754 const base::ListValue* list_value;
755 value->GetAsList(&list_value);
756 // Create the Java array.
757 jsize length = static_cast<jsize>(list_value->GetSize());
758 jobject result = CreateJavaArray(target_inner_type, length);
759 if (!result) {
760 return NULL;
761 }
762 JNIEnv* env = AttachCurrentThread();
763 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
764 for (jsize i = 0; i < length; ++i) {
765 const base::Value* value_element = null_value.get();
766 list_value->Get(i, &value_element);
767 jvalue element = CoerceJavaScriptValueToJavaValue(value_element,
768 target_inner_type,
769 false);
770 SetArrayElement(result, target_inner_type, i, element);
771 // CoerceJavaScriptValueToJavaValue() creates new local references to
772 // strings, objects and arrays. Of these, only strings can occur here.
773 // SetArrayElement() causes the array to take its own reference to the
774 // string, so we can now release the local reference.
775 DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
776 DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
777 ReleaseJavaValueIfRequired(env, &element, target_inner_type);
778 }
779
780 return result;
781 }
782
783 jobject GinJavaMethodInvocationHelper::CoerceJavaScriptDictionaryToArray(
784 const base::Value* value,
785 const JavaType& target_type) {
786 DCHECK_EQ(JavaType::TypeArray, target_type.type);
787
788 const JavaType& target_inner_type = *target_type.inner_type.get();
789 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
790 // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
791 if (target_inner_type.type == JavaType::TypeArray) {
792 return NULL;
793 }
794
795 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
796 // arrays. Spec requires handling object arrays.
797 if (target_inner_type.type == JavaType::TypeObject) {
798 return NULL;
799 }
800
801 const base::DictionaryValue* dictionary_value;
802 value->GetAsDictionary(&dictionary_value);
803 const base::Value* length_value;
804 // If the object does not have a length property, return null.
805 if (!dictionary_value->Get("length", &length_value)) {
806 return NULL;
807 }
808
809 // If the length property does not have numeric type, or is outside the valid
810 // range for a Java array length, return null.
811 jsize length = -1;
812 if (length_value->IsType(base::Value::TYPE_INTEGER)) {
813 int int_length;
814 length_value->GetAsInteger(&int_length);
815 if (int_length >= 0 && int_length <= kint32max) {
816 length = static_cast<jsize>(int_length);
817 }
818 } else if (length_value->IsType(base::Value::TYPE_DOUBLE)) {
819 double double_length;
820 length_value->GetAsDouble(&double_length);
821 if (double_length >= 0.0 && double_length <= kint32max) {
822 length = static_cast<jsize>(double_length);
823 }
824 }
825 if (length == -1) {
826 return NULL;
827 }
828
829 jobject result = CreateJavaArray(target_inner_type, length);
830 if (!result) {
831 return NULL;
832 }
833 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
834 JNIEnv* env = AttachCurrentThread();
835 for (jsize i = 0; i < length; ++i) {
836 const std::string key(base::IntToString(i));
837 const base::Value* value_element = null_value.get();
838 if (dictionary_value->HasKey(key)) {
839 dictionary_value->Get(key, &value_element);
840 }
841 jvalue element = CoerceJavaScriptValueToJavaValue(value_element,
842 target_inner_type,
843 false);
844 SetArrayElement(result, target_inner_type, i, element);
845 // CoerceJavaScriptValueToJavaValue() creates new local references to
846 // strings, objects and arrays. Of these, only strings can occur here.
847 // SetArrayElement() causes the array to take its own reference to the
848 // string, so we can now release the local reference.
849 DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
850 DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
851 ReleaseJavaValueIfRequired(env, &element, target_inner_type);
852 }
853
854 return result;
855 }
856
857 // Note that this only handles primitive types and strings.
858 jobject GinJavaMethodInvocationHelper::CreateJavaArray(const JavaType& type,
859 jsize length) {
860 JNIEnv* env = AttachCurrentThread();
861 switch (type.type) {
862 case JavaType::TypeBoolean:
863 return env->NewBooleanArray(length);
864 case JavaType::TypeByte:
865 return env->NewByteArray(length);
866 case JavaType::TypeChar:
867 return env->NewCharArray(length);
868 case JavaType::TypeShort:
869 return env->NewShortArray(length);
870 case JavaType::TypeInt:
871 return env->NewIntArray(length);
872 case JavaType::TypeLong:
873 return env->NewLongArray(length);
874 case JavaType::TypeFloat:
875 return env->NewFloatArray(length);
876 case JavaType::TypeDouble:
877 return env->NewDoubleArray(length);
878 case JavaType::TypeString: {
879 ScopedJavaLocalRef<jclass> clazz(
880 base::android::GetClass(env, kJavaLangString));
881 return env->NewObjectArray(length, clazz.obj(), NULL);
882 }
883 case JavaType::TypeVoid:
884 // Conversion to void must never happen.
885 case JavaType::TypeArray:
886 case JavaType::TypeObject:
887 // Not handled.
888 NOTREACHED();
889 }
890 return NULL;
891 }
892
893 // Sets the specified element of the supplied array to the value of the
894 // supplied jvalue. Requires that the type of the array matches that of the
895 // jvalue. Handles only primitive types and strings. Note that in the case of a
896 // string, the array takes a new reference to the string object.
897 void GinJavaMethodInvocationHelper::SetArrayElement(jobject array,
898 const JavaType& type,
bulach 2014/06/09 11:44:33 nit: align
mnaganov (inactive) 2014/06/11 13:50:44 Done.
899 jsize index,
900 const jvalue& value) {
901 JNIEnv* env = AttachCurrentThread();
902 switch (type.type) {
903 case JavaType::TypeBoolean:
904 env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1,
905 &value.z);
906 break;
907 case JavaType::TypeByte:
908 env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1,
909 &value.b);
910 break;
911 case JavaType::TypeChar:
912 env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1,
913 &value.c);
914 break;
915 case JavaType::TypeShort:
916 env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1,
917 &value.s);
918 break;
919 case JavaType::TypeInt:
920 env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1,
921 &value.i);
922 break;
923 case JavaType::TypeLong:
924 env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1,
925 &value.j);
926 break;
927 case JavaType::TypeFloat:
928 env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1,
929 &value.f);
930 break;
931 case JavaType::TypeDouble:
932 env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1,
933 &value.d);
934 break;
935 case JavaType::TypeString:
936 env->SetObjectArrayElement(static_cast<jobjectArray>(array), index,
937 value.l);
938 break;
939 case JavaType::TypeVoid:
940 // Conversion to void must never happen.
941 case JavaType::TypeArray:
942 case JavaType::TypeObject:
943 // Not handled.
944 NOTREACHED();
945 }
946 base::android::CheckException(env);
947 }
948
949 jvalue
950 GinJavaMethodInvocationHelper::CoerceJavaScriptNullOrUndefinedToJavaValue(
951 const base::Value* value,
952 const JavaType& target_type,
953 bool coerce_to_string) {
954 bool is_undefined = false;
955 scoped_ptr<const GinJavaBridgeValue> gin_value;
956 if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) {
957 gin_value = GinJavaBridgeValue::FromValue(value);
958 if (gin_value->IsType(GinJavaBridgeValue::TYPE_UNDEFINED)) {
959 is_undefined = true;
960 }
961 }
962 jvalue result;
963 switch (target_type.type) {
964 case JavaType::TypeObject:
965 result.l = NULL;
966 break;
967 case JavaType::TypeString:
968 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to
969 // "undefined". Spec requires converting undefined to NULL.
970 result.l = (coerce_to_string && is_undefined)
971 ? ConvertUTF8ToJavaString(AttachCurrentThread(),
972 kUndefined).Release()
973 : NULL;
974 break;
975 case JavaType::TypeByte:
976 case JavaType::TypeChar:
977 case JavaType::TypeShort:
978 case JavaType::TypeInt:
979 case JavaType::TypeLong:
980 case JavaType::TypeFloat:
981 case JavaType::TypeDouble: {
982 jvalue null_value = {0};
983 result = null_value;
984 break;
985 }
986 case JavaType::TypeBoolean:
987 result.z = JNI_FALSE;
988 break;
989 case JavaType::TypeArray:
990 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
991 // requires raising a JavaScript exception.
992 result.l = NULL;
993 break;
994 case JavaType::TypeVoid:
995 // Conversion to void must never happen.
996 NOTREACHED();
997 break;
998 }
999 return result;
1000 }
1001
1002 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698