OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/string_number_conversions.h" | |
10 #include "base/stringprintf.h" | |
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" | |
12 | |
13 using base::StringPrintf; | |
14 using base::android::AttachCurrentThread; | |
15 using base::android::ConvertUTF8ToJavaString; | |
16 using base::android::GetMethodIDFromClassName; | |
17 using base::android::JavaRef; | |
18 using base::android::ScopedJavaGlobalRef; | |
19 using base::android::ScopedJavaLocalRef; | |
20 using WebKit::WebBindings; | |
21 | |
22 // The conversion between JavaScript and Java types is based on the Live | |
23 // Connect 2 spec. See | |
24 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. | |
25 | |
26 // Note that in some cases, we differ from from the spec in order to maintain | |
27 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may | |
28 // revisit this decision in the future. | |
29 | |
30 namespace { | |
31 | |
32 std::string NPIdentifierToString(NPIdentifier identifier) { | |
33 const NPUTF8* string; | |
34 int32_t number; | |
35 bool is_string; | |
36 WebBindings::extractIdentifierData(identifier, string, number, is_string); | |
37 DCHECK(is_string); | |
38 return string; | |
39 } | |
40 | |
41 // Our special NPObject type. We extend an NPObject with a pointer to a | |
42 // JavaBoundObject. We also add static methods for each of the NPObject | |
43 // callbacks, which are registered by our NPClass. These methods simply | |
44 // delegate to the private implementation methods of JavaBoundObject. | |
45 struct JavaNPObject : public NPObject { | |
46 JavaBoundObject* bound_object; | |
47 | |
48 static const NPClass kNPClass; | |
49 | |
50 static NPObject* Allocate(NPP npp, NPClass* np_class); | |
51 static void Deallocate(NPObject* np_object); | |
52 static bool HasMethod(NPObject* np_object, NPIdentifier np_identifier); | |
53 static bool Invoke(NPObject* np_object, NPIdentifier np_identifier, | |
54 const NPVariant *args, uint32_t arg_count, | |
55 NPVariant *result); | |
56 static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier); | |
57 static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier, | |
58 NPVariant *result); | |
59 }; | |
60 | |
61 const NPClass JavaNPObject::kNPClass = { | |
62 NP_CLASS_STRUCT_VERSION, | |
63 JavaNPObject::Allocate, | |
64 JavaNPObject::Deallocate, | |
65 NULL, // NPInvalidate | |
66 JavaNPObject::HasMethod, | |
67 JavaNPObject::Invoke, | |
68 NULL, // NPInvokeDefault | |
69 JavaNPObject::HasProperty, | |
70 JavaNPObject::GetProperty, | |
71 NULL, // NPSetProperty, | |
72 NULL, // NPRemoveProperty | |
73 }; | |
74 | |
75 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) { | |
76 JavaNPObject* obj = new JavaNPObject; | |
77 return obj; | |
78 } | |
79 | |
80 void JavaNPObject::Deallocate(NPObject* np_object) { | |
81 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); | |
82 delete obj->bound_object; | |
83 delete obj; | |
84 } | |
85 | |
86 bool JavaNPObject::HasMethod(NPObject* np_object, NPIdentifier np_identifier) { | |
87 std::string name = NPIdentifierToString(np_identifier); | |
88 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); | |
89 return obj->bound_object->HasMethod(name); | |
90 } | |
91 | |
92 bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier, | |
93 const NPVariant* args, uint32_t arg_count, | |
94 NPVariant* result) { | |
95 std::string name = NPIdentifierToString(np_identifier); | |
96 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); | |
97 return obj->bound_object->Invoke(name, args, arg_count, result); | |
98 } | |
99 | |
100 bool JavaNPObject::HasProperty(NPObject* np_object, | |
101 NPIdentifier np_identifier) { | |
102 // LIVECONNECT_COMPLIANCE: Return false to indicate that the property is not | |
103 // present. We should support this correctly. | |
104 return false; | |
105 } | |
106 | |
107 bool JavaNPObject::GetProperty(NPObject* np_object, | |
108 NPIdentifier np_identifier, | |
109 NPVariant* result) { | |
110 // LIVECONNECT_COMPLIANCE: Return false to indicate that the property is | |
111 // undefined. We should support this correctly. | |
112 return false; | |
113 } | |
114 | |
115 // Calls a Java method through JNI and returns the result as an NPVariant. Note | |
116 // that this method does not do any type coercion. The Java return value is | |
117 // simply converted to the corresponding NPAPI type. | |
118 NPVariant CallJNIMethod(jobject object, JavaType::Type return_type, | |
119 jmethodID id, jvalue* parameters) { | |
120 JNIEnv* env = AttachCurrentThread(); | |
121 NPVariant result; | |
122 switch (return_type) { | |
123 case JavaType::TypeBoolean: | |
124 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters), | |
125 result); | |
126 break; | |
127 case JavaType::TypeByte: | |
128 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), result); | |
129 break; | |
130 case JavaType::TypeChar: | |
131 INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), result); | |
132 break; | |
133 case JavaType::TypeShort: | |
134 INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters), result); | |
135 break; | |
136 case JavaType::TypeInt: | |
137 INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), result); | |
138 break; | |
139 case JavaType::TypeLong: | |
140 DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters), result); | |
141 break; | |
142 case JavaType::TypeFloat: | |
143 DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters), | |
144 result); | |
145 break; | |
146 case JavaType::TypeDouble: | |
147 DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters), | |
148 result); | |
149 break; | |
150 case JavaType::TypeVoid: | |
151 env->CallVoidMethodA(object, id, parameters); | |
152 VOID_TO_NPVARIANT(result); | |
153 break; | |
154 case JavaType::TypeArray: | |
155 // TODO(steveblock): Handle arrays | |
156 VOID_TO_NPVARIANT(result); | |
157 break; | |
158 case JavaType::TypeString: { | |
159 ScopedJavaLocalRef<jstring> java_string(env, static_cast<jstring>( | |
160 env->CallObjectMethodA(object, id, parameters))); | |
161 if (!java_string.obj()) { | |
162 // LIVECONNECT_COMPLIANCE: Return undefined to maintain existing | |
163 // behavior. We should return a null string. | |
164 VOID_TO_NPVARIANT(result); | |
165 break; | |
166 } | |
167 std::string str = | |
168 base::android::ConvertJavaStringToUTF8(env, java_string.obj()); | |
M-A Ruel
2011/11/11 13:31:13
See line 15. Please be coherent one way or another
| |
169 // Take a copy and pass ownership to the variant. We must allocate using | |
170 // NPN_MemAlloc, to match NPN_ReleaseVariant, which uses NPN_MemFree. | |
171 size_t length = str.length(); | |
172 char* buffer = static_cast<char*>(NPN_MemAlloc(length)); | |
173 str.copy(buffer, length, 0); | |
174 STRINGN_TO_NPVARIANT(buffer, length, result); | |
175 break; | |
176 } | |
177 case JavaType::TypeObject: { | |
178 ScopedJavaLocalRef<jobject> java_object( | |
179 env, | |
180 env->CallObjectMethodA(object, id, parameters)); | |
181 if (!java_object.obj()) { | |
182 NULL_TO_NPVARIANT(result); | |
183 break; | |
184 } | |
185 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(java_object), result); | |
186 break; | |
187 } | |
188 } | |
189 return result; | |
190 } | |
191 | |
192 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, | |
193 JavaType::Type target_type) { | |
194 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. | |
195 jvalue result; | |
196 DCHECK(variant.type == NPVariantType_Int32 || | |
197 variant.type == NPVariantType_Double); | |
198 bool is_double = variant.type == NPVariantType_Double; | |
199 switch (target_type) { | |
200 case JavaType::TypeByte: | |
201 result.b = is_double ? static_cast<jbyte>(NPVARIANT_TO_DOUBLE(variant)) : | |
202 static_cast<jbyte>(NPVARIANT_TO_INT32(variant)); | |
203 break; | |
204 case JavaType::TypeChar: | |
205 // LIVECONNECT_COMPLIANCE: Convert double to 0 to maintain existing | |
206 // behavior. | |
207 result.c = is_double ? 0 : | |
208 static_cast<jchar>(NPVARIANT_TO_INT32(variant)); | |
209 break; | |
210 case JavaType::TypeShort: | |
211 result.s = is_double ? static_cast<jshort>(NPVARIANT_TO_DOUBLE(variant)) : | |
212 static_cast<jshort>(NPVARIANT_TO_INT32(variant)); | |
213 break; | |
214 case JavaType::TypeInt: | |
215 result.i = is_double ? static_cast<jint>(NPVARIANT_TO_DOUBLE(variant)) : | |
216 NPVARIANT_TO_INT32(variant); | |
217 break; | |
218 case JavaType::TypeLong: | |
219 result.j = is_double ? static_cast<jlong>(NPVARIANT_TO_DOUBLE(variant)) : | |
220 NPVARIANT_TO_INT32(variant); | |
221 break; | |
222 case JavaType::TypeFloat: | |
223 result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) : | |
224 NPVARIANT_TO_INT32(variant); | |
225 break; | |
226 case JavaType::TypeDouble: | |
227 result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) : | |
228 NPVARIANT_TO_INT32(variant); | |
229 break; | |
230 case JavaType::TypeObject: | |
231 // LIVECONNECT_COMPLIANCE: Convert to null to maintain existing behavior. | |
232 // We should handle object equivalents of primitive types. | |
233 result.l = NULL; | |
234 break; | |
235 case JavaType::TypeString: | |
236 result.l = ConvertUTF8ToJavaString( | |
237 AttachCurrentThread(), | |
238 is_double ? StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : | |
239 base::IntToString(NPVARIANT_TO_INT32(variant))); | |
240 break; | |
241 case JavaType::TypeBoolean: | |
242 // LIVECONNECT_COMPLIANCE: Convert to false to maintain existing behavior. | |
243 // We should convert to false for o or NaN, true otherwise. | |
244 result.z = JNI_FALSE; | |
245 break; | |
246 case JavaType::TypeArray: | |
247 // LIVECONNECT_COMPLIANCE: Convert to null to maintain existing behavior. | |
248 // We should raise a JavaScript exception. | |
249 result.l = NULL; | |
250 break; | |
251 case JavaType::TypeVoid: | |
252 // Conversion to void must never happen. | |
253 NOTREACHED(); | |
254 break; | |
255 } | |
256 return result; | |
257 } | |
258 | |
259 jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant, | |
260 JavaType::Type target_type) { | |
261 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES. | |
262 DCHECK_EQ(NPVariantType_Bool, variant.type); | |
263 bool boolean_value = NPVARIANT_TO_BOOLEAN(variant); | |
264 jvalue result; | |
265 switch (target_type) { | |
266 case JavaType::TypeBoolean: | |
267 result.z = boolean_value ? JNI_TRUE : JNI_FALSE; | |
268 break; | |
269 case JavaType::TypeObject: | |
270 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
271 // We should handle java.lang.Boolean and java.lang.Object. | |
272 result.l = NULL; | |
273 break; | |
274 case JavaType::TypeString: | |
275 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), | |
276 boolean_value ? "true" : "false"); | |
277 break; | |
278 case JavaType::TypeByte: | |
279 case JavaType::TypeChar: | |
280 case JavaType::TypeShort: | |
281 case JavaType::TypeInt: | |
282 case JavaType::TypeLong: | |
283 case JavaType::TypeFloat: | |
284 case JavaType::TypeDouble: { | |
285 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. We | |
286 // should convert to 0 or 1. | |
287 jvalue null_value = {0}; | |
288 result = null_value; | |
289 break; | |
290 } | |
291 case JavaType::TypeArray: | |
292 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
293 // We should raise a JavaScript exception. | |
294 result.l = NULL; | |
295 break; | |
296 case JavaType::TypeVoid: | |
297 // Conversion to void must never happen. | |
298 NOTREACHED(); | |
299 break; | |
300 } | |
301 return result; | |
302 } | |
303 | |
304 jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant, | |
305 JavaType::Type target_type) { | |
306 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES. | |
307 DCHECK_EQ(NPVariantType_String, variant.type); | |
308 jvalue result; | |
309 switch (target_type) { | |
310 case JavaType::TypeString: | |
311 result.l = ConvertUTF8ToJavaString( | |
312 AttachCurrentThread(), | |
313 base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters, | |
314 NPVARIANT_TO_STRING(variant).UTF8Length)); | |
315 break; | |
316 case JavaType::TypeObject: | |
317 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
318 // We should handle java.lang.Object. | |
319 result.l = NULL; | |
320 break; | |
321 case JavaType::TypeByte: | |
322 case JavaType::TypeShort: | |
323 case JavaType::TypeInt: | |
324 case JavaType::TypeLong: | |
325 case JavaType::TypeFloat: | |
326 case JavaType::TypeDouble: { | |
327 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. we | |
328 // should use valueOf() method of corresponding object type. | |
329 jvalue null_value = {0}; | |
330 result = null_value; | |
331 break; | |
332 } | |
333 case JavaType::TypeChar: | |
334 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. we | |
335 // should use java.lang.Short.decode(). | |
336 result.c = 0; | |
337 break; | |
338 case JavaType::TypeBoolean: | |
339 // LIVECONNECT_COMPLIANCE: Convert to false to maintain existing behavior. | |
340 // We should convert the empty string to false, otherwise true. | |
341 result.z = JNI_FALSE; | |
342 break; | |
343 case JavaType::TypeArray: | |
344 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
345 // We should raise a JavaScript exception. | |
346 result.l = NULL; | |
347 break; | |
348 case JavaType::TypeVoid: | |
349 // Conversion to void must never happen. | |
350 NOTREACHED(); | |
351 break; | |
352 } | |
353 return result; | |
354 } | |
355 | |
356 jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, | |
357 JavaType::Type target_type) { | |
358 // We only handle Java objects. See | |
359 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS. | |
360 // TODO(steveblock): Handle arrays. | |
361 // TODO(steveblock): Handle JavaScript objects. | |
362 DCHECK_EQ(NPVariantType_Object, variant.type); | |
363 | |
364 // The only type of object we should encounter is a Java object, as | |
365 // other objects should have been converted to NULL in the renderer. | |
366 // See CreateNPVariantParam(). | |
367 // TODO(steveblock): This will have to change once we support arrays and | |
368 // JavaScript objects. | |
369 NPObject* object = NPVARIANT_TO_OBJECT(variant); | |
370 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); | |
371 | |
372 jvalue result; | |
373 switch (target_type) { | |
374 case JavaType::TypeObject: | |
375 // LIVECONNECT_COMPLIANCE: Pass all Java objects to maintain existing | |
376 // behavior. We should pass only Java objects which are | |
377 // assignment-compatibile. | |
378 result.l = AttachCurrentThread()->NewLocalRef( | |
379 JavaBoundObject::GetJavaObject(object)); | |
380 break; | |
381 case JavaType::TypeString: | |
382 // LIVECONNECT_COMPLIANCE: Convert to "undefined" to maintain existing | |
383 // behavior. We should call toString() on the Java object. | |
384 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"); | |
385 break; | |
386 case JavaType::TypeByte: | |
387 case JavaType::TypeShort: | |
388 case JavaType::TypeInt: | |
389 case JavaType::TypeLong: | |
390 case JavaType::TypeFloat: | |
391 case JavaType::TypeDouble: | |
392 case JavaType::TypeChar: { | |
393 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. We | |
394 // should raise a JavaScript exception. | |
395 jvalue null_value = {0}; | |
396 result = null_value; | |
397 break; | |
398 } | |
399 case JavaType::TypeBoolean: | |
400 // LIVECONNECT_COMPLIANCE: Convert to false to maintain existing behavior. | |
401 // We should raise a JavaScript exception. | |
402 result.z = JNI_FALSE; | |
403 break; | |
404 case JavaType::TypeArray: | |
405 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
406 // We should raise a JavaScript exception. | |
407 result.l = NULL; | |
408 break; | |
409 case JavaType::TypeVoid: | |
410 // Conversion to void must never happen. | |
411 NOTREACHED(); | |
412 break; | |
413 } | |
414 return result; | |
415 } | |
416 | |
417 jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, | |
418 JavaType::Type target_type) { | |
419 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL. | |
420 DCHECK(variant.type == NPVariantType_Null || | |
421 variant.type == NPVariantType_Void); | |
422 jvalue result; | |
423 switch (target_type) { | |
424 case JavaType::TypeObject: | |
425 result.l = NULL; | |
426 break; | |
427 case JavaType::TypeString: | |
428 if (variant.type == NPVariantType_Void) { | |
429 // LIVECONNECT_COMPLIANCE: Convert undefined to "undefined" to maintain | |
430 // existing behavior. We should convert undefined to NULL. | |
431 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"); | |
432 } else { | |
433 result.l = NULL; | |
434 } | |
435 break; | |
436 case JavaType::TypeByte: | |
437 case JavaType::TypeChar: | |
438 case JavaType::TypeShort: | |
439 case JavaType::TypeInt: | |
440 case JavaType::TypeLong: | |
441 case JavaType::TypeFloat: | |
442 case JavaType::TypeDouble: { | |
443 jvalue null_value = {0}; | |
444 result = null_value; | |
445 break; | |
446 } | |
447 case JavaType::TypeBoolean: | |
448 result.z = JNI_FALSE; | |
449 break; | |
450 case JavaType::TypeArray: | |
451 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
452 // We should raise a JavaScript exception. | |
453 result.l = NULL; | |
454 break; | |
455 case JavaType::TypeVoid: | |
456 // Conversion to void must never happen. | |
457 NOTREACHED(); | |
458 break; | |
459 } | |
460 return result; | |
461 } | |
462 | |
463 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, | |
464 JavaType::Type target_type) { | |
465 // Note that in all these conversions, the relevant field of the jvalue must | |
466 // always be explicitly set, as jvalue does not initialize its fields. | |
467 | |
468 // Some of these methods create new Java Strings. Note that we don't | |
469 // explicitly release the local ref to these new objects, as there's no simple | |
470 // way to do so. | |
471 switch (variant.type) { | |
472 case NPVariantType_Int32: | |
473 case NPVariantType_Double: | |
474 return CoerceJavaScriptNumberToJavaValue(variant, target_type); | |
475 case NPVariantType_Bool: | |
476 return CoerceJavaScriptBooleanToJavaValue(variant, target_type); | |
477 case NPVariantType_String: | |
478 return CoerceJavaScriptStringToJavaValue(variant, target_type); | |
479 case NPVariantType_Object: | |
480 return CoerceJavaScriptObjectToJavaValue(variant, target_type); | |
481 case NPVariantType_Null: | |
482 case NPVariantType_Void: | |
483 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type); | |
484 } | |
485 NOTREACHED(); | |
486 return jvalue(); | |
487 } | |
488 | |
489 static jmethodID g_object_get_class_id = NULL; | |
M-A Ruel
2011/11/11 13:31:13
Keep constants at the top of the file, otherwise i
Steve Block
2011/11/11 16:05:20
Done.
| |
490 jmethodID GetObjectGetClassID() { | |
491 if (!g_object_get_class_id) { | |
492 g_object_get_class_id = GetMethodIDFromClassName(AttachCurrentThread(), | |
493 "java/lang/Object", | |
494 "getClass", | |
495 "()Ljava/lang/Class;"); | |
496 } | |
497 return g_object_get_class_id; | |
498 } | |
499 | |
500 static jmethodID g_class_get_methods_id = NULL; | |
M-A Ruel
2011/11/11 13:31:13
Same.
| |
501 jmethodID ClassGetMethodsID() { | |
502 if (!g_class_get_methods_id) { | |
503 g_class_get_methods_id = GetMethodIDFromClassName( | |
504 AttachCurrentThread(), | |
505 "java/lang/Class", | |
506 "getMethods", | |
507 "()[Ljava/lang/reflect/Method;"); | |
508 } | |
509 return g_class_get_methods_id; | |
510 } | |
511 | |
512 } // namespace | |
513 | |
514 | |
515 NPObject* JavaBoundObject::Create(const JavaRef<jobject>& object) { | |
516 // The first argument (a plugin's instance handle) is passed through to the | |
517 // allocate function directly, and we don't use it, so it's ok to be 0. | |
518 // The object is created with a ref count of one. | |
519 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( | |
520 &JavaNPObject::kNPClass)); | |
521 // The NPObject takes ownership of the JavaBoundObject. | |
522 reinterpret_cast<JavaNPObject*>(np_object)->bound_object = | |
523 new JavaBoundObject(object); | |
524 return np_object; | |
525 } | |
526 | |
527 JavaBoundObject::JavaBoundObject(const JavaRef<jobject>& object) | |
528 : java_object_(object.env()->NewGlobalRef(object.obj())), | |
529 are_methods_set_up_(false) { | |
530 // We don't do anything with our Java object when first created. We do it all | |
531 // lazily when a method is first invoked. | |
532 } | |
533 | |
534 JavaBoundObject::~JavaBoundObject() { | |
535 // Use the current thread's JNI env to release our global ref to the Java | |
536 // object. | |
537 AttachCurrentThread()->DeleteGlobalRef(java_object_); | |
538 } | |
539 | |
540 jobject JavaBoundObject::GetJavaObject(NPObject* object) { | |
541 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); | |
542 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; | |
543 return jbo->java_object_; | |
544 } | |
545 | |
546 bool JavaBoundObject::HasMethod(const std::string& name) const { | |
547 const_cast<JavaBoundObject*>(this)->EnsureMethodsAreSetUp(); | |
548 return methods_.find(name) != methods_.end(); | |
549 } | |
550 | |
551 bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args, | |
552 size_t arg_count, NPVariant* result) { | |
553 EnsureMethodsAreSetUp(); | |
554 | |
555 // Get all methods with the correct name. | |
556 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> | |
557 iters = methods_.equal_range(name); | |
558 if (iters.first == iters.second) { | |
559 return false; | |
560 } | |
561 | |
562 // Take the first method with the correct number of arguments. | |
M-A Ruel
2011/11/11 13:31:13
What about function overloading with different typ
Steve Block
2011/11/11 16:05:20
We don't attempt to find the 'closest' match based
| |
563 JavaMethod* method = NULL; | |
564 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; | |
565 ++iter) { | |
566 if (iter->second->num_parameters() == arg_count) { | |
567 method = iter->second.get(); | |
568 break; | |
569 } | |
570 } | |
571 if (!method) { | |
572 return false; | |
573 } | |
574 | |
575 // Coerce | |
576 std::vector<jvalue> parameters(arg_count); | |
577 for (size_t i = 0; i < arg_count; ++i) { | |
578 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], | |
579 method->parameter_type(i)); | |
580 } | |
581 | |
582 // Call | |
583 *result = CallJNIMethod(java_object_, method->return_type(), method->id(), | |
584 ¶meters[0]); | |
585 return true; | |
586 } | |
587 | |
588 void JavaBoundObject::EnsureMethodsAreSetUp() { | |
589 if (are_methods_set_up_) { | |
590 return; | |
591 } | |
592 are_methods_set_up_ = true; | |
593 | |
594 JNIEnv* env = AttachCurrentThread(); | |
595 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( | |
596 env->CallObjectMethod(java_object_, GetObjectGetClassID()))); | |
597 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( | |
598 env->CallObjectMethod(clazz.obj(), ClassGetMethodsID()))); | |
599 size_t num_methods = env->GetArrayLength(methods.obj()); | |
600 for (size_t i = 0; i < num_methods; ++i) { | |
601 ScopedJavaLocalRef<jobject> java_method( | |
602 env, | |
603 env->GetObjectArrayElement(methods.obj(), i)); | |
604 JavaMethod* method = new JavaMethod(java_method); | |
605 methods_.insert(std::make_pair(method->name(), method)); | |
606 } | |
607 } | |
OLD | NEW |