IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / renderer_host / java / java_bound_object.cc
blobe2ad87a15c3977914faabcdd84bae321c43c575c
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/java/java_bound_object.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/memory/singleton.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h"
13 #include "content/browser/renderer_host/java/java_type.h"
14 #include "content/browser/renderer_host/java/jni_helper.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "third_party/WebKit/public/web/WebBindings.h"
18 using base::StringPrintf;
19 using base::android::AttachCurrentThread;
20 using base::android::ConvertUTF8ToJavaString;
21 using base::android::GetClass;
22 using base::android::JavaRef;
23 using base::android::ScopedJavaGlobalRef;
24 using base::android::ScopedJavaLocalRef;
25 using blink::WebBindings;
27 // The conversion between JavaScript and Java types is based on the Live
28 // Connect 2 spec. See
29 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS.
31 // Note that in some cases, we differ from from the spec in order to maintain
32 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may
33 // revisit this decision in the future.
35 namespace content {
36 namespace {
38 const char kJavaLangClass[] = "java/lang/Class";
39 const char kJavaLangObject[] = "java/lang/Object";
40 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
41 const char kGetClass[] = "getClass";
42 const char kGetMethods[] = "getMethods";
43 const char kIsAnnotationPresent[] = "isAnnotationPresent";
44 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
45 const char kReturningJavaLangReflectMethodArray[] =
46 "()[Ljava/lang/reflect/Method;";
47 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
49 // Our special NPObject type. We extend an NPObject with a pointer to a
50 // JavaBoundObject. We also add static methods for each of the NPObject
51 // callbacks, which are registered by our NPClass. These methods simply
52 // delegate to the private implementation methods of JavaBoundObject.
53 struct JavaNPObject : public NPObject {
54 JavaBoundObject* bound_object;
56 static const NPClass kNPClass;
58 static NPObject* Allocate(NPP npp, NPClass* np_class);
59 static void Deallocate(NPObject* np_object);
60 static bool HasMethod(NPObject* np_object, NPIdentifier np_identifier);
61 static bool Invoke(NPObject* np_object, NPIdentifier np_identifier,
62 const NPVariant *args, uint32_t arg_count,
63 NPVariant *result);
64 static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier);
65 static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier,
66 NPVariant *result);
69 const NPClass JavaNPObject::kNPClass = {
70 NP_CLASS_STRUCT_VERSION,
71 JavaNPObject::Allocate,
72 JavaNPObject::Deallocate,
73 NULL, // NPInvalidate
74 JavaNPObject::HasMethod,
75 JavaNPObject::Invoke,
76 NULL, // NPInvokeDefault
77 JavaNPObject::HasProperty,
78 JavaNPObject::GetProperty,
79 NULL, // NPSetProperty,
80 NULL, // NPRemoveProperty
83 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) {
84 JavaNPObject* obj = new JavaNPObject();
85 return obj;
88 void JavaNPObject::Deallocate(NPObject* np_object) {
89 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
90 delete obj->bound_object;
91 delete obj;
94 bool JavaNPObject::HasMethod(NPObject* np_object, NPIdentifier np_identifier) {
95 std::string name(WebBindings::utf8FromIdentifier(np_identifier));
96 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
97 return obj->bound_object->HasMethod(name);
100 bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier,
101 const NPVariant* args, uint32_t arg_count,
102 NPVariant* result) {
103 std::string name(WebBindings::utf8FromIdentifier(np_identifier));
104 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
105 return obj->bound_object->Invoke(name, args, arg_count, result);
108 bool JavaNPObject::HasProperty(NPObject* np_object,
109 NPIdentifier np_identifier) {
110 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
111 // that the property is not present. Spec requires supporting this correctly.
112 return false;
115 bool JavaNPObject::GetProperty(NPObject* np_object,
116 NPIdentifier np_identifier,
117 NPVariant* result) {
118 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
119 // that the property is undefined. Spec requires supporting this correctly.
120 return false;
123 // Calls a Java method through JNI. If the Java method raises an uncaught
124 // exception, it is cleared and this method returns false. Otherwise, this
125 // method returns true and the Java method's return value is provided as an
126 // NPVariant. Note that this method does not do any type coercion. The Java
127 // return value is simply converted to the corresponding NPAPI type.
128 bool CallJNIMethod(
129 jobject object,
130 const JavaType& return_type,
131 jmethodID id,
132 jvalue* parameters,
133 NPVariant* result,
134 const JavaRef<jclass>& safe_annotation_clazz,
135 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) {
136 JNIEnv* env = AttachCurrentThread();
137 switch (return_type.type) {
138 case JavaType::TypeBoolean:
139 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters),
140 *result);
141 break;
142 case JavaType::TypeByte:
143 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result);
144 break;
145 case JavaType::TypeChar:
146 INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), *result);
147 break;
148 case JavaType::TypeShort:
149 INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters),
150 *result);
151 break;
152 case JavaType::TypeInt:
153 INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), *result);
154 break;
155 case JavaType::TypeLong:
156 DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters),
157 *result);
158 break;
159 case JavaType::TypeFloat:
160 DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters),
161 *result);
162 break;
163 case JavaType::TypeDouble:
164 DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters),
165 *result);
166 break;
167 case JavaType::TypeVoid:
168 env->CallVoidMethodA(object, id, parameters);
169 VOID_TO_NPVARIANT(*result);
170 break;
171 case JavaType::TypeArray:
172 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
173 // return arrays. Spec requires calling the method and converting the
174 // result to a JavaScript array.
175 VOID_TO_NPVARIANT(*result);
176 break;
177 case JavaType::TypeString: {
178 jstring java_string = static_cast<jstring>(
179 env->CallObjectMethodA(object, id, parameters));
180 // If an exception was raised, we must clear it before calling most JNI
181 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
182 // first.
183 if (base::android::ClearException(env)) {
184 return false;
186 ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
187 if (!scoped_java_string.obj()) {
188 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
189 // Spec requires returning a null string.
190 VOID_TO_NPVARIANT(*result);
191 break;
193 std::string str =
194 base::android::ConvertJavaStringToUTF8(scoped_java_string);
195 size_t length = str.length();
196 // This pointer is freed in _NPN_ReleaseVariantValue in
197 // third_party/WebKit/Source/WebCore/bindings/v8/npruntime.cpp.
198 char* buffer = static_cast<char*>(malloc(length));
199 str.copy(buffer, length, 0);
200 STRINGN_TO_NPVARIANT(buffer, length, *result);
201 break;
203 case JavaType::TypeObject: {
204 // If an exception was raised, we must clear it before calling most JNI
205 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
206 // first.
207 jobject java_object = env->CallObjectMethodA(object, id, parameters);
208 if (base::android::ClearException(env)) {
209 return false;
211 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
212 if (!scoped_java_object.obj()) {
213 NULL_TO_NPVARIANT(*result);
214 break;
216 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object,
217 safe_annotation_clazz,
218 manager),
219 *result);
220 break;
223 return !base::android::ClearException(env);
226 double RoundDoubleTowardsZero(const double& x) {
227 if (std::isnan(x)) {
228 return 0.0;
230 return x > 0.0 ? floor(x) : ceil(x);
233 // Rounds to jlong using Java's type conversion rules.
234 jlong RoundDoubleToLong(const double& x) {
235 double intermediate = RoundDoubleTowardsZero(x);
236 // The int64 limits can not be converted exactly to double values, so we
237 // compare to custom constants. kint64max is 2^63 - 1, but the spacing
238 // between double values in the the range 2^62 to 2^63 is 2^10. The cast is
239 // required to silence a spurious gcc warning for integer overflow.
240 const int64 limit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10);
241 DCHECK(limit > 0);
242 const double kLargestDoubleLessThanInt64Max = limit;
243 const double kSmallestDoubleGreaterThanInt64Min = -limit;
244 if (intermediate > kLargestDoubleLessThanInt64Max) {
245 return kint64max;
247 if (intermediate < kSmallestDoubleGreaterThanInt64Min) {
248 return kint64min;
250 return static_cast<jlong>(intermediate);
253 // Rounds to jint using Java's type conversion rules.
254 jint RoundDoubleToInt(const double& x) {
255 double intermediate = RoundDoubleTowardsZero(x);
256 // The int32 limits cast exactly to double values.
257 intermediate = std::min(intermediate, static_cast<double>(kint32max));
258 intermediate = std::max(intermediate, static_cast<double>(kint32min));
259 return static_cast<jint>(intermediate);
262 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant,
263 const JavaType& target_type,
264 bool coerce_to_string) {
265 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
267 // For conversion to numeric types, we need to replicate Java's type
268 // conversion rules. This requires that for integer values, we simply discard
269 // all but the lowest n buts, where n is the number of bits in the target
270 // type. For double values, the logic is more involved.
271 jvalue result;
272 DCHECK(variant.type == NPVariantType_Int32 ||
273 variant.type == NPVariantType_Double);
274 bool is_double = variant.type == NPVariantType_Double;
275 switch (target_type.type) {
276 case JavaType::TypeByte:
277 result.b = is_double ?
278 static_cast<jbyte>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) :
279 static_cast<jbyte>(NPVARIANT_TO_INT32(variant));
280 break;
281 case JavaType::TypeChar:
282 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0.
283 // Spec requires converting doubles similarly to how we convert doubles to
284 // other numeric types.
285 result.c = is_double ? 0 :
286 static_cast<jchar>(NPVARIANT_TO_INT32(variant));
287 break;
288 case JavaType::TypeShort:
289 result.s = is_double ?
290 static_cast<jshort>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) :
291 static_cast<jshort>(NPVARIANT_TO_INT32(variant));
292 break;
293 case JavaType::TypeInt:
294 result.i = is_double ? RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant)) :
295 NPVARIANT_TO_INT32(variant);
296 break;
297 case JavaType::TypeLong:
298 result.j = is_double ? RoundDoubleToLong(NPVARIANT_TO_DOUBLE(variant)) :
299 NPVARIANT_TO_INT32(variant);
300 break;
301 case JavaType::TypeFloat:
302 result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) :
303 NPVARIANT_TO_INT32(variant);
304 break;
305 case JavaType::TypeDouble:
306 result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) :
307 NPVARIANT_TO_INT32(variant);
308 break;
309 case JavaType::TypeObject:
310 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
311 // requires handling object equivalents of primitive types.
312 result.l = NULL;
313 break;
314 case JavaType::TypeString:
315 result.l = coerce_to_string ?
316 ConvertUTF8ToJavaString(
317 AttachCurrentThread(),
318 is_double ?
319 base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) :
320 base::Int64ToString(NPVARIANT_TO_INT32(variant))).Release() :
321 NULL;
322 break;
323 case JavaType::TypeBoolean:
324 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
325 // requires converting to false for 0 or NaN, true otherwise.
326 result.z = JNI_FALSE;
327 break;
328 case JavaType::TypeArray:
329 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
330 // requires raising a JavaScript exception.
331 result.l = NULL;
332 break;
333 case JavaType::TypeVoid:
334 // Conversion to void must never happen.
335 NOTREACHED();
336 break;
338 return result;
341 jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant,
342 const JavaType& target_type,
343 bool coerce_to_string) {
344 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES.
345 DCHECK_EQ(NPVariantType_Bool, variant.type);
346 bool boolean_value = NPVARIANT_TO_BOOLEAN(variant);
347 jvalue result;
348 switch (target_type.type) {
349 case JavaType::TypeBoolean:
350 result.z = boolean_value ? JNI_TRUE : JNI_FALSE;
351 break;
352 case JavaType::TypeObject:
353 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
354 // requires handling java.lang.Boolean and java.lang.Object.
355 result.l = NULL;
356 break;
357 case JavaType::TypeString:
358 result.l = coerce_to_string ?
359 ConvertUTF8ToJavaString(AttachCurrentThread(),
360 boolean_value ? "true" : "false").Release() :
361 NULL;
362 break;
363 case JavaType::TypeByte:
364 case JavaType::TypeChar:
365 case JavaType::TypeShort:
366 case JavaType::TypeInt:
367 case JavaType::TypeLong:
368 case JavaType::TypeFloat:
369 case JavaType::TypeDouble: {
370 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
371 // requires converting to 0 or 1.
372 jvalue null_value = {0};
373 result = null_value;
374 break;
376 case JavaType::TypeArray:
377 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
378 // requires raising a JavaScript exception.
379 result.l = NULL;
380 break;
381 case JavaType::TypeVoid:
382 // Conversion to void must never happen.
383 NOTREACHED();
384 break;
386 return result;
389 jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant,
390 const JavaType& target_type) {
391 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES.
392 DCHECK_EQ(NPVariantType_String, variant.type);
393 jvalue result;
394 switch (target_type.type) {
395 case JavaType::TypeString:
396 result.l = ConvertUTF8ToJavaString(
397 AttachCurrentThread(),
398 base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters,
399 NPVARIANT_TO_STRING(variant).UTF8Length)).Release();
400 break;
401 case JavaType::TypeObject:
402 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
403 // requires handling java.lang.Object.
404 result.l = NULL;
405 break;
406 case JavaType::TypeByte:
407 case JavaType::TypeShort:
408 case JavaType::TypeInt:
409 case JavaType::TypeLong:
410 case JavaType::TypeFloat:
411 case JavaType::TypeDouble: {
412 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
413 // requires using valueOf() method of corresponding object type.
414 jvalue null_value = {0};
415 result = null_value;
416 break;
418 case JavaType::TypeChar:
419 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
420 // requires using java.lang.Short.decode().
421 result.c = 0;
422 break;
423 case JavaType::TypeBoolean:
424 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
425 // requires converting the empty string to false, otherwise true.
426 result.z = JNI_FALSE;
427 break;
428 case JavaType::TypeArray:
429 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
430 // requires raising a JavaScript exception.
431 result.l = NULL;
432 break;
433 case JavaType::TypeVoid:
434 // Conversion to void must never happen.
435 NOTREACHED();
436 break;
438 return result;
441 // Note that this only handles primitive types and strings.
442 jobject CreateJavaArray(const JavaType& type, jsize length) {
443 JNIEnv* env = AttachCurrentThread();
444 switch (type.type) {
445 case JavaType::TypeBoolean:
446 return env->NewBooleanArray(length);
447 case JavaType::TypeByte:
448 return env->NewByteArray(length);
449 case JavaType::TypeChar:
450 return env->NewCharArray(length);
451 case JavaType::TypeShort:
452 return env->NewShortArray(length);
453 case JavaType::TypeInt:
454 return env->NewIntArray(length);
455 case JavaType::TypeLong:
456 return env->NewLongArray(length);
457 case JavaType::TypeFloat:
458 return env->NewFloatArray(length);
459 case JavaType::TypeDouble:
460 return env->NewDoubleArray(length);
461 case JavaType::TypeString: {
462 ScopedJavaLocalRef<jclass> clazz(GetClass(env, "java/lang/String"));
463 return env->NewObjectArray(length, clazz.obj(), NULL);
465 case JavaType::TypeVoid:
466 // Conversion to void must never happen.
467 case JavaType::TypeArray:
468 case JavaType::TypeObject:
469 // Not handled.
470 NOTREACHED();
472 return NULL;
475 // Sets the specified element of the supplied array to the value of the
476 // supplied jvalue. Requires that the type of the array matches that of the
477 // jvalue. Handles only primitive types and strings. Note that in the case of a
478 // string, the array takes a new reference to the string object.
479 void SetArrayElement(jobject array,
480 const JavaType& type,
481 jsize index,
482 const jvalue& value) {
483 JNIEnv* env = AttachCurrentThread();
484 switch (type.type) {
485 case JavaType::TypeBoolean:
486 env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1,
487 &value.z);
488 break;
489 case JavaType::TypeByte:
490 env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1,
491 &value.b);
492 break;
493 case JavaType::TypeChar:
494 env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1,
495 &value.c);
496 break;
497 case JavaType::TypeShort:
498 env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1,
499 &value.s);
500 break;
501 case JavaType::TypeInt:
502 env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1,
503 &value.i);
504 break;
505 case JavaType::TypeLong:
506 env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1,
507 &value.j);
508 break;
509 case JavaType::TypeFloat:
510 env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1,
511 &value.f);
512 break;
513 case JavaType::TypeDouble:
514 env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1,
515 &value.d);
516 break;
517 case JavaType::TypeString:
518 env->SetObjectArrayElement(static_cast<jobjectArray>(array), index,
519 value.l);
520 break;
521 case JavaType::TypeVoid:
522 // Conversion to void must never happen.
523 case JavaType::TypeArray:
524 case JavaType::TypeObject:
525 // Not handled.
526 NOTREACHED();
528 base::android::CheckException(env);
531 void ReleaseJavaValueIfRequired(JNIEnv* env,
532 jvalue* value,
533 const JavaType& type) {
534 if (type.type == JavaType::TypeString ||
535 type.type == JavaType::TypeObject ||
536 type.type == JavaType::TypeArray) {
537 env->DeleteLocalRef(value->l);
538 value->l = NULL;
542 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant,
543 const JavaType& target_type,
544 bool coerce_to_string);
546 // Returns a new local reference to a Java array.
547 jobject CoerceJavaScriptObjectToArray(const NPVariant& variant,
548 const JavaType& target_type) {
549 DCHECK_EQ(JavaType::TypeArray, target_type.type);
550 NPObject* object = NPVARIANT_TO_OBJECT(variant);
551 DCHECK_NE(&JavaNPObject::kNPClass, object->_class);
553 const JavaType& target_inner_type = *target_type.inner_type.get();
554 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
555 // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
556 if (target_inner_type.type == JavaType::TypeArray) {
557 return NULL;
560 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
561 // arrays. Spec requires handling object arrays.
562 if (target_inner_type.type == JavaType::TypeObject) {
563 return NULL;
566 // If the object does not have a length property, return null.
567 NPVariant length_variant;
568 if (!WebBindings::getProperty(0, object,
569 WebBindings::getStringIdentifier("length"),
570 &length_variant)) {
571 WebBindings::releaseVariantValue(&length_variant);
572 return NULL;
575 // If the length property does not have numeric type, or is outside the valid
576 // range for a Java array length, return null.
577 jsize length = -1;
578 if (NPVARIANT_IS_INT32(length_variant)
579 && NPVARIANT_TO_INT32(length_variant) >= 0) {
580 length = NPVARIANT_TO_INT32(length_variant);
581 } else if (NPVARIANT_IS_DOUBLE(length_variant)
582 && NPVARIANT_TO_DOUBLE(length_variant) >= 0.0
583 && NPVARIANT_TO_DOUBLE(length_variant) <= kint32max) {
584 length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(length_variant));
586 WebBindings::releaseVariantValue(&length_variant);
587 if (length == -1) {
588 return NULL;
591 // Create the Java array.
592 // TODO(steveblock): Handle failure to create the array.
593 jobject result = CreateJavaArray(target_inner_type, length);
594 NPVariant value_variant;
595 JNIEnv* env = AttachCurrentThread();
596 for (jsize i = 0; i < length; ++i) {
597 // It seems that getProperty() will set the variant to type void on failure,
598 // but this doesn't seem to be documented, so do it explicitly here for
599 // safety.
600 VOID_TO_NPVARIANT(value_variant);
601 // If this fails, for example due to a missing element, we simply treat the
602 // value as JavaScript undefined.
603 WebBindings::getProperty(0, object, WebBindings::getIntIdentifier(i),
604 &value_variant);
605 jvalue element = CoerceJavaScriptValueToJavaValue(value_variant,
606 target_inner_type,
607 false);
608 SetArrayElement(result, target_inner_type, i, element);
609 // CoerceJavaScriptValueToJavaValue() creates new local references to
610 // strings, objects and arrays. Of these, only strings can occur here.
611 // SetArrayElement() causes the array to take its own reference to the
612 // string, so we can now release the local reference.
613 DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
614 DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
615 ReleaseJavaValueIfRequired(env, &element, target_inner_type);
616 WebBindings::releaseVariantValue(&value_variant);
619 return result;
622 jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant,
623 const JavaType& target_type,
624 bool coerce_to_string) {
625 // This covers both JavaScript objects (including arrays) and Java objects.
626 // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS,
627 // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and
628 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS
629 DCHECK_EQ(NPVariantType_Object, variant.type);
631 NPObject* object = NPVARIANT_TO_OBJECT(variant);
632 bool is_java_object = &JavaNPObject::kNPClass == object->_class;
634 jvalue result;
635 switch (target_type.type) {
636 case JavaType::TypeObject:
637 if (is_java_object) {
638 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass all Java
639 // objects. Spec requires passing only Java objects which are
640 // assignment-compatibile.
641 result.l = AttachCurrentThread()->NewLocalRef(
642 JavaBoundObject::GetJavaObject(object).obj());
643 } else {
644 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec
645 // requires converting if the target type is
646 // netscape.javascript.JSObject, otherwise raising a JavaScript
647 // exception.
648 result.l = NULL;
650 break;
651 case JavaType::TypeString:
652 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to
653 // "undefined". Spec requires calling toString() on the Java object.
654 result.l = coerce_to_string ?
655 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined").
656 Release() :
657 NULL;
658 break;
659 case JavaType::TypeByte:
660 case JavaType::TypeShort:
661 case JavaType::TypeInt:
662 case JavaType::TypeLong:
663 case JavaType::TypeFloat:
664 case JavaType::TypeDouble:
665 case JavaType::TypeChar: {
666 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
667 // requires raising a JavaScript exception.
668 jvalue null_value = {0};
669 result = null_value;
670 break;
672 case JavaType::TypeBoolean:
673 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
674 // requires raising a JavaScript exception.
675 result.z = JNI_FALSE;
676 break;
677 case JavaType::TypeArray:
678 if (is_java_object) {
679 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
680 // requires raising a JavaScript exception.
681 result.l = NULL;
682 } else {
683 result.l = CoerceJavaScriptObjectToArray(variant, target_type);
685 break;
686 case JavaType::TypeVoid:
687 // Conversion to void must never happen.
688 NOTREACHED();
689 break;
691 return result;
694 jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant,
695 const JavaType& target_type,
696 bool coerce_to_string) {
697 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL.
698 DCHECK(variant.type == NPVariantType_Null ||
699 variant.type == NPVariantType_Void);
700 jvalue result;
701 switch (target_type.type) {
702 case JavaType::TypeObject:
703 result.l = NULL;
704 break;
705 case JavaType::TypeString:
706 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to
707 // "undefined". Spec requires converting undefined to NULL.
708 result.l = (coerce_to_string && variant.type == NPVariantType_Void) ?
709 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined").
710 Release() :
711 NULL;
712 break;
713 case JavaType::TypeByte:
714 case JavaType::TypeChar:
715 case JavaType::TypeShort:
716 case JavaType::TypeInt:
717 case JavaType::TypeLong:
718 case JavaType::TypeFloat:
719 case JavaType::TypeDouble: {
720 jvalue null_value = {0};
721 result = null_value;
722 break;
724 case JavaType::TypeBoolean:
725 result.z = JNI_FALSE;
726 break;
727 case JavaType::TypeArray:
728 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
729 // requires raising a JavaScript exception.
730 result.l = NULL;
731 break;
732 case JavaType::TypeVoid:
733 // Conversion to void must never happen.
734 NOTREACHED();
735 break;
737 return result;
740 // coerce_to_string means that we should try to coerce all JavaScript values to
741 // strings when required, rather than simply converting to NULL. This is used
742 // to maintain current behaviour, which differs slightly depending upon whether
743 // or not the coercion in question is for an array element.
745 // Note that the jvalue returned by this method may contain a new local
746 // reference to an object (string, object or array). This must be released by
747 // the caller.
748 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant,
749 const JavaType& target_type,
750 bool coerce_to_string) {
751 // Note that in all these conversions, the relevant field of the jvalue must
752 // always be explicitly set, as jvalue does not initialize its fields.
754 switch (variant.type) {
755 case NPVariantType_Int32:
756 case NPVariantType_Double:
757 return CoerceJavaScriptNumberToJavaValue(variant, target_type,
758 coerce_to_string);
759 case NPVariantType_Bool:
760 return CoerceJavaScriptBooleanToJavaValue(variant, target_type,
761 coerce_to_string);
762 case NPVariantType_String:
763 return CoerceJavaScriptStringToJavaValue(variant, target_type);
764 case NPVariantType_Object:
765 return CoerceJavaScriptObjectToJavaValue(variant, target_type,
766 coerce_to_string);
767 case NPVariantType_Null:
768 case NPVariantType_Void:
769 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type,
770 coerce_to_string);
772 NOTREACHED();
773 return jvalue();
776 } // namespace
778 NPObject* JavaBoundObject::Create(
779 const JavaRef<jobject>& object,
780 const JavaRef<jclass>& safe_annotation_clazz,
781 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) {
782 // The first argument (a plugin's instance handle) is passed through to the
783 // allocate function directly, and we don't use it, so it's ok to be 0.
784 // The object is created with a ref count of one.
785 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>(
786 &JavaNPObject::kNPClass));
787 // The NPObject takes ownership of the JavaBoundObject.
788 reinterpret_cast<JavaNPObject*>(np_object)->bound_object =
789 new JavaBoundObject(object, safe_annotation_clazz, manager);
790 return np_object;
793 JavaBoundObject::JavaBoundObject(
794 const JavaRef<jobject>& object,
795 const JavaRef<jclass>& safe_annotation_clazz,
796 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager)
797 : java_object_(AttachCurrentThread(), object.obj()),
798 manager_(manager),
799 are_methods_set_up_(false),
800 safe_annotation_clazz_(safe_annotation_clazz) {
801 BrowserThread::PostTask(
802 BrowserThread::UI, FROM_HERE,
803 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectCreated,
804 manager_,
805 base::android::ScopedJavaGlobalRef<jobject>(object)));
806 // Other than informing the JavaBridgeDispatcherHostManager that a java bound
807 // object has been created (above), we don't do anything else with our Java
808 // object when first created. We do it all lazily when a method is first
809 // invoked.
812 JavaBoundObject::~JavaBoundObject() {
813 BrowserThread::PostTask(
814 BrowserThread::UI, FROM_HERE,
815 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed,
816 manager_,
817 base::android::ScopedJavaGlobalRef<jobject>(
818 java_object_.get(AttachCurrentThread()))));
821 ScopedJavaLocalRef<jobject> JavaBoundObject::GetJavaObject(NPObject* object) {
822 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class);
823 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object;
824 return jbo->java_object_.get(AttachCurrentThread());
827 bool JavaBoundObject::HasMethod(const std::string& name) const {
828 EnsureMethodsAreSetUp();
829 return methods_.find(name) != methods_.end();
832 bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args,
833 size_t arg_count, NPVariant* result) {
834 EnsureMethodsAreSetUp();
836 // Get all methods with the correct name.
837 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator>
838 iters = methods_.equal_range(name);
839 if (iters.first == iters.second) {
840 return false;
843 // Take the first method with the correct number of arguments.
844 JavaMethod* method = NULL;
845 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second;
846 ++iter) {
847 if (iter->second->num_parameters() == arg_count) {
848 method = iter->second.get();
849 break;
852 if (!method) {
853 return false;
856 // Coerce
857 std::vector<jvalue> parameters(arg_count);
858 for (size_t i = 0; i < arg_count; ++i) {
859 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i],
860 method->parameter_type(i),
861 true);
864 ScopedJavaLocalRef<jobject> obj = java_object_.get(AttachCurrentThread());
866 bool ok = false;
867 if (!obj.is_null()) {
868 // Call
869 ok = CallJNIMethod(obj.obj(), method->return_type(),
870 method->id(), &parameters[0], result,
871 safe_annotation_clazz_,
872 manager_);
875 // Now that we're done with the jvalue, release any local references created
876 // by CoerceJavaScriptValueToJavaValue().
877 JNIEnv* env = AttachCurrentThread();
878 for (size_t i = 0; i < arg_count; ++i) {
879 ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
882 return ok;
885 void JavaBoundObject::EnsureMethodsAreSetUp() const {
886 if (are_methods_set_up_)
887 return;
888 are_methods_set_up_ = true;
890 JNIEnv* env = AttachCurrentThread();
891 ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
893 if (obj.is_null()) {
894 return;
897 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
898 env->CallObjectMethod(obj.obj(), GetMethodIDFromClassName(
899 env,
900 kJavaLangObject,
901 kGetClass,
902 kReturningJavaLangClass))));
904 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
905 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
906 env,
907 kJavaLangClass,
908 kGetMethods,
909 kReturningJavaLangReflectMethodArray))));
911 size_t num_methods = env->GetArrayLength(methods.obj());
912 // Java objects always have public methods.
913 DCHECK(num_methods);
915 for (size_t i = 0; i < num_methods; ++i) {
916 ScopedJavaLocalRef<jobject> java_method(
917 env,
918 env->GetObjectArrayElement(methods.obj(), i));
920 if (!safe_annotation_clazz_.is_null()) {
921 jboolean safe = env->CallBooleanMethod(java_method.obj(),
922 GetMethodIDFromClassName(
923 env,
924 kJavaLangReflectMethod,
925 kIsAnnotationPresent,
926 kTakesJavaLangClassReturningBoolean),
927 safe_annotation_clazz_.obj());
929 if (!safe)
930 continue;
933 JavaMethod* method = new JavaMethod(java_method);
934 methods_.insert(std::make_pair(method->name(), method));
938 } // namespace content