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

Unified Diff: content/browser/renderer_host/java/gin_java_method_invocation_helper.cc

Issue 302173006: [Android] Java Bridge with Gin: implement Java methods invocation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed compilation of tests Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
diff --git a/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc b/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5c8abb6a9b3be79803fca6fe23aa295e51935099
--- /dev/null
+++ b/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
@@ -0,0 +1,310 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/java/gin_java_method_invocation_helper.h"
+
+#include <unistd.h>
+
+#include "base/android/event_log.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/float_util.h"
+#include "content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.h"
+#include "content/browser/renderer_host/java/java_method.h"
+#include "content/browser/renderer_host/java/jni_helper.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "content/public/browser/browser_thread.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace content {
+
+namespace {
+
+const char kObjectIsGone[] = "Java object is gone";
+const char kMethodNotFound[] = "Method not found";
+const char kAccessToObjectGetClassIsBlocked[] =
+ "Access to java.lang.Object.getClass is blocked";
+const char kJavaExceptionRaised[] =
+ "Java exception has been raised during method invocation";
+
+// See frameworks/base/core/java/android/webkit/EventLogTags.logtags
+const int kObjectGetClassInvocationAttemptLogTag = 70151;
+
+} // namespace
+
+GinJavaMethodInvocationHelper::GinJavaMethodInvocationHelper(
+ scoped_ptr<ObjectDelegate> object,
+ const std::string& method_name,
+ const base::ListValue& arguments)
+ : object_(object.Pass()),
+ method_name_(method_name),
+ arguments_(arguments.DeepCopy()) {
+}
+
+GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
+
+void GinJavaMethodInvocationHelper::Init(DispatcherDelegate* dispatcher) {
+ // Build on the UI thread a map of object_id -> WeakRef for Java objects from
+ // |arguments_|. Then we can use this map on the background thread without
+ // accessing |dispatcher|.
+ BuildObjectRefsFromListValue(dispatcher, arguments_.get());
+}
+
+// As V8ValueConverter has finite recursion depth when serializing
+// JavaScript values, we don't bother about having a recursion threshold here.
+void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue(
+ DispatcherDelegate* dispatcher,
+ const base::Value* list_value) {
+ DCHECK(list_value->IsType(base::Value::TYPE_LIST));
+ const base::ListValue* list;
+ list_value->GetAsList(&list);
+ for (base::ListValue::const_iterator iter = list->begin();
+ iter != list->end();
+ ++iter) {
+ if (AppendObjectRef(dispatcher, *iter))
+ continue;
+ if ((*iter)->IsType(base::Value::TYPE_LIST)) {
+ BuildObjectRefsFromListValue(dispatcher, *iter);
+ } else if ((*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
+ BuildObjectRefsFromDictionaryValue(dispatcher, *iter);
+ }
+ }
+}
+
+void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue(
+ DispatcherDelegate* dispatcher,
+ const base::Value* dict_value) {
+ DCHECK(dict_value->IsType(base::Value::TYPE_DICTIONARY));
+ const base::DictionaryValue* dict;
+ dict_value->GetAsDictionary(&dict);
+ for (base::DictionaryValue::Iterator iter(*dict);
+ !iter.IsAtEnd();
+ iter.Advance()) {
+ if (AppendObjectRef(dispatcher, &iter.value()))
+ continue;
+ if (iter.value().IsType(base::Value::TYPE_LIST)) {
+ BuildObjectRefsFromListValue(dispatcher, &iter.value());
+ } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
+ BuildObjectRefsFromDictionaryValue(dispatcher, &iter.value());
+ }
+ }
+}
+
+bool GinJavaMethodInvocationHelper::AppendObjectRef(
+ DispatcherDelegate* dispatcher,
+ const base::Value* raw_value) {
+ if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value))
+ return false;
+ scoped_ptr<const GinJavaBridgeValue> value(
+ GinJavaBridgeValue::FromValue(raw_value));
+ if (!value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID))
+ return false;
+ GinJavaBoundObject::ObjectID object_id;
+ if (value->GetAsObjectID(&object_id)) {
+ ObjectRefs::iterator iter = object_refs_.find(object_id);
+ if (iter == object_refs_.end()) {
+ JavaObjectWeakGlobalRef object_ref(
+ dispatcher->GetObjectWeakRef(object_id));
+ if (!object_ref.is_empty()) {
+ object_refs_.insert(std::make_pair(object_id, object_ref));
+ }
+ }
+ }
+ return true;
+}
+
+void GinJavaMethodInvocationHelper::Invoke() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj(object_->GetLocalRef(env));
+ if (obj.is_null()) {
+ SetInvocationFailure(kObjectIsGone);
+ return;
+ }
+ const JavaMethod* method =
+ object_->FindMethod(method_name_, arguments_->GetSize());
+ if (!method) {
+ SetInvocationFailure(kMethodNotFound);
+ return;
+ }
+
+ if (object_->IsObjectGetClassMethod(method)) {
+ base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag,
+ getuid());
+ SetInvocationFailure(kAccessToObjectGetClassIsBlocked);
+ return;
+ }
+
+ std::vector<jvalue> parameters(method->num_parameters());
+ for (size_t i = 0; i < method->num_parameters(); ++i) {
+ const base::Value* argument;
+ arguments_->Get(i, &argument);
+ parameters[i] = CoerceJavaScriptValueToJavaValue(
+ env, argument, method->parameter_type(i), true, object_refs_);
+ }
+ InvokeMethod(obj.obj(), method->return_type(), method->id(), &parameters[0]);
+
+ // Now that we're done with the jvalue, release any local references created
+ // by CoerceJavaScriptValueToJavaValue().
+ for (size_t i = 0; i < method->num_parameters(); ++i) {
+ ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
+ }
+}
+
+void GinJavaMethodInvocationHelper::SetInvocationFailure(
+ const char* error_message) {
+ holds_primitive_result_ = true;
+ primitive_result_.reset(new base::ListValue());
+ error_message_ = error_message;
+}
+
+void GinJavaMethodInvocationHelper::SetPrimitiveResult(
+ const base::ListValue& result_wrapper) {
+ holds_primitive_result_ = true;
+ primitive_result_.reset(result_wrapper.DeepCopy());
+}
+
+void GinJavaMethodInvocationHelper::SetObjectResult(
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz) {
+ holds_primitive_result_ = false;
+ object_result_.Reset(object);
+ safe_annotation_clazz_.Reset(safe_annotation_clazz);
+}
+
+bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
+ return holds_primitive_result_;
+}
+
+const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
+ return *primitive_result_.get();
+}
+
+const base::android::JavaRef<jobject>&
+GinJavaMethodInvocationHelper::GetObjectResult() {
+ return object_result_;
+}
+
+const base::android::JavaRef<jclass>&
+GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
+ return safe_annotation_clazz_;
+}
+
+const std::string& GinJavaMethodInvocationHelper::GetErrorMessage() {
+ return error_message_;
+}
+
+void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
+ const JavaType& return_type,
+ jmethodID id,
+ jvalue* parameters) {
+ JNIEnv* env = AttachCurrentThread();
+ base::ListValue result_wrapper;
+ switch (return_type.type) {
+ case JavaType::TypeBoolean:
+ result_wrapper.AppendBoolean(
+ env->CallBooleanMethodA(object, id, parameters));
+ break;
+ case JavaType::TypeByte:
+ result_wrapper.AppendInteger(
+ env->CallByteMethodA(object, id, parameters));
+ break;
+ case JavaType::TypeChar:
+ result_wrapper.AppendInteger(
+ env->CallCharMethodA(object, id, parameters));
+ break;
+ case JavaType::TypeShort:
+ result_wrapper.AppendInteger(
+ env->CallShortMethodA(object, id, parameters));
+ break;
+ case JavaType::TypeInt:
+ result_wrapper.AppendInteger(
+ env->CallIntMethodA(object, id, parameters));
+ break;
+ case JavaType::TypeLong:
+ result_wrapper.AppendDouble(
+ env->CallLongMethodA(object, id, parameters));
+ break;
+ case JavaType::TypeFloat: {
+ float result = env->CallFloatMethodA(object, id, parameters);
+ if (base::IsFinite(result)) {
+ result_wrapper.AppendDouble(result);
+ } else {
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateNonFiniteValue(result).release());
+ }
+ break;
+ }
+ case JavaType::TypeDouble: {
+ double result = env->CallDoubleMethodA(object, id, parameters);
+ if (base::IsFinite(result)) {
+ result_wrapper.AppendDouble(result);
+ } else {
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateNonFiniteValue(result).release());
+ }
+ break;
+ }
+ case JavaType::TypeVoid:
+ env->CallVoidMethodA(object, id, parameters);
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateUndefinedValue().release());
+ break;
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
+ // return arrays. Spec requires calling the method and converting the
+ // result to a JavaScript array.
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateUndefinedValue().release());
+ break;
+ case JavaType::TypeString: {
+ jstring java_string = static_cast<jstring>(
+ env->CallObjectMethodA(object, id, parameters));
+ // If an exception was raised, we must clear it before calling most JNI
+ // methods. ScopedJavaLocalRef is liable to make such calls, so we test
+ // first.
+ if (base::android::ClearException(env)) {
+ SetInvocationFailure(kJavaExceptionRaised);
+ return;
+ }
+ ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
+ if (!scoped_java_string.obj()) {
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
+ // Spec requires returning a null string.
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateUndefinedValue().release());
+ break;
+ }
+ result_wrapper.AppendString(
+ base::android::ConvertJavaStringToUTF8(scoped_java_string));
+ break;
+ }
+ case JavaType::TypeObject: {
+ // If an exception was raised, we must clear it before calling most JNI
+ // methods. ScopedJavaLocalRef is liable to make such calls, so we test
+ // first.
+ jobject java_object = env->CallObjectMethodA(object, id, parameters);
+ if (base::android::ClearException(env)) {
+ SetInvocationFailure(kJavaExceptionRaised);
+ return;
+ }
+ ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
+ if (!scoped_java_object.obj()) {
+ result_wrapper.Append(base::Value::CreateNullValue());
+ break;
+ }
+ SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
+ return;
+ }
+ }
+ // This is for all cases except JavaType::TypeObject.
+ if (!base::android::ClearException(env)) {
+ SetPrimitiveResult(result_wrapper);
+ } else {
+ SetInvocationFailure(kJavaExceptionRaised);
+ }
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698