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

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

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

Powered by Google App Engine
This is Rietveld 408576698