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

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

Issue 345753003: [Android] Java Bridge with Gin: implement Java Bridge dispatcher (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use an enum for passing error codes 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_bridge_dispatcher_host.cc
diff --git a/content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc b/content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6c6e86b75e0867c75e355655d21e553230dd1c24
--- /dev/null
+++ b/content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc
@@ -0,0 +1,497 @@
+// 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_bridge_dispatcher_host.h"
+
+#include "base/android/java_handler_thread.h"
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/lazy_instance.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
+#include "content/browser/renderer_host/java/gin_java_bound_object_delegate.h"
+#include "content/browser/renderer_host/java/jni_helper.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "content/common/android/hash_set.h"
+#include "content/common/gin_java_bridge_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "ipc/ipc_message_utils.h"
+
+#if !defined(OS_ANDROID)
+#error "JavaBridge only supports OS_ANDROID"
+#endif
+
+namespace content {
+
+namespace {
+// The JavaBridge needs to use a Java thread so the callback
+// will happen on a thread with a prepared Looper.
+class JavaBridgeThread : public base::android::JavaHandlerThread {
+ public:
+ JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
+ Start();
+ }
+ virtual ~JavaBridgeThread() {
+ Stop();
+ }
+};
+
+base::LazyInstance<JavaBridgeThread> g_background_thread =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost(
+ WebContents* web_contents,
+ jobject retained_object_set)
+ : WebContentsObserver(web_contents),
+ retained_object_set_(base::android::AttachCurrentThread(),
+ retained_object_set),
+ allow_object_contents_inspection_(true) {
+ DCHECK(retained_object_set);
+}
+
+GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
+}
+
+void GinJavaBridgeDispatcherHost::RenderFrameCreated(
+ RenderFrameHost* render_frame_host) {
+ renderers_.insert(render_frame_host);
+ for (NamedObjectMap::const_iterator iter = named_objects_.begin();
+ iter != named_objects_.end();
+ ++iter) {
+ render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject(
+ render_frame_host->GetRoutingID(), iter->first, iter->second));
+ }
+}
+
+void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
+ RenderFrameHost* render_frame_host) {
+ renderers_.erase(render_frame_host);
+ RemoveHolder(render_frame_host,
+ GinJavaBoundObject::ObjectMap::iterator(&objects_),
+ objects_.size());
+}
+
+GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject(
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ bool is_named,
+ RenderFrameHost* holder) {
+ DCHECK(is_named || holder);
+ GinJavaBoundObject::ObjectID object_id;
+ JNIEnv* env = base::android::AttachCurrentThread();
+ JavaObjectWeakGlobalRef ref(env, object.obj());
+ if (is_named) {
+ object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
+ GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz)));
+ } else {
+ object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
+ GinJavaBoundObject::CreateTransient(
+ ref, safe_annotation_clazz, holder)));
+ }
+#if DCHECK_IS_ON
+ {
+ GinJavaBoundObject::ObjectID added_object_id;
+ DCHECK(FindObjectId(object, &added_object_id));
+ DCHECK_EQ(object_id, added_object_id);
+ }
+#endif // DCHECK_IS_ON
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_add(env, retained_object_set, object);
+ }
+ return object_id;
+}
+
+bool GinJavaBridgeDispatcherHost::FindObjectId(
+ const base::android::JavaRef<jobject>& object,
+ GinJavaBoundObject::ObjectID* object_id) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
+ it.Advance()) {
+ if (env->IsSameObject(
+ object.obj(),
+ it.GetCurrentValue()->get()->GetLocalRef(env).obj())) {
+ *object_id = it.GetCurrentKey();
+ return true;
+ }
+ }
+ return false;
+}
+
+JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef(
+ GinJavaBoundObject::ObjectID object_id) {
+ scoped_refptr<GinJavaBoundObject>* result = objects_.Lookup(object_id);
+ scoped_refptr<GinJavaBoundObject> object(result ? *result : NULL);
+ if (object.get())
+ return object->GetWeakRef();
+ else
+ return JavaObjectWeakGlobalRef();
+}
+
+void GinJavaBridgeDispatcherHost::RemoveHolder(
+ RenderFrameHost* holder,
+ const GinJavaBoundObject::ObjectMap::iterator& from,
+ size_t count) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ size_t i = 0;
+ for (GinJavaBoundObject::ObjectMap::iterator it(from);
+ !it.IsAtEnd() && i < count;
+ it.Advance(), ++i) {
+ scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
+ if (object->IsNamed())
+ continue;
+ object->RemoveHolder(holder);
+ if (!object->HasHolders()) {
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_remove(
+ env, retained_object_set, object->GetLocalRef(env));
+ }
+ objects_.Remove(it.GetCurrentKey());
+ }
+ }
+}
+
+void GinJavaBridgeDispatcherHost::AddNamedObject(
+ const std::string& name,
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ GinJavaBoundObject::ObjectID object_id;
+ NamedObjectMap::iterator iter = named_objects_.find(name);
+ bool existing_object = FindObjectId(object, &object_id);
+ if (existing_object && iter != named_objects_.end() &&
+ iter->second == object_id) {
+ // Nothing to do.
+ return;
+ }
+ if (iter != named_objects_.end()) {
+ RemoveNamedObject(iter->first);
+ }
+ if (existing_object) {
+ (*objects_.Lookup(object_id))->AddName();
+ } else {
+ object_id = AddObject(object, safe_annotation_clazz, true, NULL);
+ }
+ named_objects_[name] = object_id;
+
+ for (RendererSet::iterator iter = renderers_.begin();
+ iter != renderers_.end(); ++iter) {
+ (*iter)->Send(new GinJavaBridgeMsg_AddNamedObject(
+ (*iter)->GetRoutingID(), name, object_id));
+ }
+}
+
+void GinJavaBridgeDispatcherHost::RemoveNamedObject(
+ const std::string& name) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ NamedObjectMap::iterator iter = named_objects_.find(name);
+ if (iter == named_objects_.end())
+ return;
+
+ scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second));
+ named_objects_.erase(iter);
+ object->RemoveName();
+
+ // Not erasing from the objects map, as we can still receive method
+ // invocation requests for this object, and they should work until the
+ // java object is gone.
+ if (!object->IsNamed()) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_remove(
+ env, retained_object_set, object->GetLocalRef(env));
+ }
+ }
+
+ for (RendererSet::iterator iter = renderers_.begin();
+ iter != renderers_.end(); ++iter) {
+ (*iter)->Send(new GinJavaBridgeMsg_RemoveNamedObject(
+ (*iter)->GetRoutingID(), name));
+ }
+}
+
+void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
+ allow_object_contents_inspection_ = allow;
+}
+
+void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // Called when the window object has been cleared in the main frame.
+ // That means, all sub-frames have also been cleared, so only named
+ // objects survived.
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_clear(env, retained_object_set);
+ }
+
+ // We also need to add back the named objects we have so far as they
+ // should survive navigations.
+ for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
+ it.Advance()) {
+ scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
+ if (object->IsNamed()) {
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_add(
+ env, retained_object_set, object->GetLocalRef(env));
+ }
+ } else {
+ objects_.Remove(it.GetCurrentKey());
+ }
+ }
+}
+
+namespace {
+
+// TODO(mnaganov): Implement passing of a parameter into sync message handlers.
+class MessageForwarder : public IPC::Sender {
+ public:
+ MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh,
+ RenderFrameHost* render_frame_host)
+ : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {}
+ void OnGetMethods(GinJavaBoundObject::ObjectID object_id,
+ IPC::Message* reply_msg) {
+ gjbdh_->OnGetMethods(render_frame_host_,
+ object_id,
+ reply_msg);
+ }
+ void OnHasMethod(GinJavaBoundObject::ObjectID object_id,
+ const std::string& method_name,
+ IPC::Message* reply_msg) {
+ gjbdh_->OnHasMethod(render_frame_host_,
+ object_id,
+ method_name,
+ reply_msg);
+ }
+ void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,
+ const std::string& method_name,
+ const base::ListValue& arguments,
+ IPC::Message* reply_msg) {
+ gjbdh_->OnInvokeMethod(render_frame_host_,
+ object_id,
+ method_name,
+ arguments,
+ reply_msg);
+ }
+ virtual bool Send(IPC::Message* msg) OVERRIDE {
+ NOTREACHED();
+ return false;
+ }
+ private:
+ GinJavaBridgeDispatcherHost* gjbdh_;
+ RenderFrameHost* render_frame_host_;
+};
+
+}
+
+bool GinJavaBridgeDispatcherHost::OnMessageReceived(
+ const IPC::Message& message,
+ RenderFrameHost* render_frame_host) {
+ DCHECK(render_frame_host);
+ bool handled = true;
+ MessageForwarder forwarder(this, render_frame_host);
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message,
+ render_frame_host)
+ IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods,
+ &forwarder,
+ MessageForwarder::OnGetMethods)
+ IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod,
+ &forwarder,
+ MessageForwarder::OnHasMethod)
+ IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod,
+ &forwarder,
+ MessageForwarder::OnInvokeMethod)
+ IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted,
+ OnObjectWrapperDeleted)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void GinJavaBridgeDispatcherHost::SendReply(
+ RenderFrameHost* render_frame_host,
+ IPC::Message* reply_msg) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (renderers_.find(render_frame_host) != renderers_.end()) {
+ render_frame_host->Send(reply_msg);
+ } else {
+ delete reply_msg;
+ }
+}
+
+void GinJavaBridgeDispatcherHost::OnGetMethods(
+ RenderFrameHost* render_frame_host,
+ GinJavaBoundObject::ObjectID object_id,
+ IPC::Message* reply_msg) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(render_frame_host);
+ if (!allow_object_contents_inspection_) {
+ IPC::WriteParam(reply_msg, std::set<std::string>());
+ render_frame_host->Send(reply_msg);
+ return;
+ }
+ scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
+ if (!object) {
+ LOG(ERROR) << "WebView: Unknown object: " << object_id;
+ IPC::WriteParam(reply_msg, std::set<std::string>());
+ render_frame_host->Send(reply_msg);
+ return;
+ }
+ base::PostTaskAndReplyWithResult(
+ g_background_thread.Get().message_loop()->message_loop_proxy(),
+ FROM_HERE,
+ base::Bind(&GinJavaBoundObject::GetMethodNames, object),
+ base::Bind(&GinJavaBridgeDispatcherHost::SendMethods,
+ AsWeakPtr(),
+ render_frame_host,
+ reply_msg));
+}
+
+void GinJavaBridgeDispatcherHost::SendMethods(
+ RenderFrameHost* render_frame_host,
+ IPC::Message* reply_msg,
+ const std::set<std::string>& method_names) {
+ IPC::WriteParam(reply_msg, method_names);
+ SendReply(render_frame_host, reply_msg);
+}
+
+void GinJavaBridgeDispatcherHost::OnHasMethod(
+ RenderFrameHost* render_frame_host,
+ GinJavaBoundObject::ObjectID object_id,
+ const std::string& method_name,
+ IPC::Message* reply_msg) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(render_frame_host);
+ scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
+ if (!object) {
+ LOG(ERROR) << "WebView: Unknown object: " << object_id;
+ IPC::WriteParam(reply_msg, false);
+ render_frame_host->Send(reply_msg);
+ return;
+ }
+ base::PostTaskAndReplyWithResult(
+ g_background_thread.Get().message_loop()->message_loop_proxy(),
+ FROM_HERE,
+ base::Bind(&GinJavaBoundObject::HasMethod, object, method_name),
+ base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply,
+ AsWeakPtr(),
+ render_frame_host,
+ reply_msg));
+}
+
+void GinJavaBridgeDispatcherHost::SendHasMethodReply(
+ RenderFrameHost* render_frame_host,
+ IPC::Message* reply_msg,
+ bool result) {
+ IPC::WriteParam(reply_msg, result);
+ SendReply(render_frame_host, reply_msg);
+}
+
+void GinJavaBridgeDispatcherHost::OnInvokeMethod(
+ RenderFrameHost* render_frame_host,
+ GinJavaBoundObject::ObjectID object_id,
+ const std::string& method_name,
+ const base::ListValue& arguments,
+ IPC::Message* reply_msg) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(render_frame_host);
+ scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
+ if (!object) {
+ LOG(ERROR) << "WebView: Unknown object: " << object_id;
+ base::ListValue result;
+ result.Append(base::Value::CreateNullValue());
+ IPC::WriteParam(reply_msg, result);
+ IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId);
+ render_frame_host->Send(reply_msg);
+ return;
+ }
+ scoped_refptr<GinJavaMethodInvocationHelper> result =
+ new GinJavaMethodInvocationHelper(
+ make_scoped_ptr(new GinJavaBoundObjectDelegate(object))
+ .PassAs<GinJavaMethodInvocationHelper::ObjectDelegate>(),
+ method_name,
+ arguments);
+ result->Init(this);
+ g_background_thread.Get()
+ .message_loop()
+ ->message_loop_proxy()
+ ->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&GinJavaMethodInvocationHelper::Invoke, result),
+ base::Bind(
+ &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult,
+ AsWeakPtr(),
+ render_frame_host,
+ reply_msg,
+ result));
+}
+
+void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
+ RenderFrameHost* render_frame_host,
+ IPC::Message* reply_msg,
+ scoped_refptr<GinJavaMethodInvocationHelper> result) {
+ if (result->HoldsPrimitiveResult()) {
+ IPC::WriteParam(reply_msg, result->GetPrimitiveResult());
+ IPC::WriteParam(reply_msg, result->GetInvocationError());
+ SendReply(render_frame_host, reply_msg);
+ } else {
+ ProcessMethodInvocationObjectResult(render_frame_host, reply_msg, result);
+ }
+}
+
+void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
+ RenderFrameHost* render_frame_host,
+ IPC::Message* reply_msg,
+ scoped_refptr<GinJavaMethodInvocationHelper> result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (renderers_.find(render_frame_host) == renderers_.end()) {
+ delete reply_msg;
+ return;
+ }
+ base::ListValue wrapped_result;
+ if (!result->GetObjectResult().is_null()) {
+ GinJavaBoundObject::ObjectID returned_object_id;
+ if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
+ (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host);
+ } else {
+ returned_object_id = AddObject(result->GetObjectResult(),
+ result->GetSafeAnnotationClass(),
+ false,
+ render_frame_host);
+ }
+ wrapped_result.Append(
+ GinJavaBridgeValue::CreateObjectIDValue(returned_object_id).release());
+ } else {
+ wrapped_result.Append(base::Value::CreateNullValue());
+ }
+ IPC::WriteParam(reply_msg, wrapped_result);
+ IPC::WriteParam(reply_msg, result->GetInvocationError());
+ render_frame_host->Send(reply_msg);
+}
+
+void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
+ RenderFrameHost* render_frame_host,
+ GinJavaBoundObject::ObjectID object_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(render_frame_host);
+ if (objects_.Lookup(object_id)) {
+ GinJavaBoundObject::ObjectMap::iterator iter(&objects_);
+ while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id)
+ iter.Advance();
+ DCHECK(!iter.IsAtEnd());
+ RemoveHolder(render_frame_host, iter, 1);
+ }
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698