Cherrypick: [Android] Java Bridge with Gin: implement Java Bridge dispatcher

Original description:

> This patch adds implementation for GinJavaBridgeDispatcherHost class, which
> is responsible for serving JB requests from renderers, and fulfilling
> them with help of GinJavaBoundObject instances.
>
> This patch also enables passing a error message for method invocation
> errors back to renderers, so they can use it when raising JavaScript
> exceptions.
>
> BUG=355644
>
> Review URL: https://codereview.chromium.org/345753003
>
> git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280298 0039d316-1c4b-4281-b951-d872f2087c98

Also update Android makefiles for all platforms.

Bug: 13238305

Change-Id: I06f82ded53250c055147e33455dfbd20deb7a44f
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 0000000..6c6e86b
--- /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
diff --git a/content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.h b/content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.h
new file mode 100644
index 0000000..d77da3a
--- /dev/null
+++ b/content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.h
@@ -0,0 +1,123 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BRIDGE_DISPATCHER_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BRIDGE_DISPATCHER_HOST_H_
+
+#include <map>
+#include <set>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/renderer_host/java/gin_java_bound_object.h"
+#include "content/browser/renderer_host/java/gin_java_method_invocation_helper.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace IPC {
+class Message;
+}
+
+namespace content {
+
+// This class handles injecting Java objects into a single RenderView. The Java
+// object itself lives in the browser process on a background thread, while a
+// proxy object is created in the renderer. An instance of this class exists
+// for each RenderFrameHost.
+class GinJavaBridgeDispatcherHost
+    : public base::SupportsWeakPtr<GinJavaBridgeDispatcherHost>,
+      public WebContentsObserver,
+      public GinJavaMethodInvocationHelper::DispatcherDelegate {
+ public:
+
+  GinJavaBridgeDispatcherHost(WebContents* web_contents,
+                              jobject retained_object_set);
+  virtual ~GinJavaBridgeDispatcherHost();
+
+  void AddNamedObject(
+      const std::string& name,
+      const base::android::JavaRef<jobject>& object,
+      const base::android::JavaRef<jclass>& safe_annotation_clazz);
+  void RemoveNamedObject(const std::string& name);
+  void SetAllowObjectContentsInspection(bool allow);
+
+  // WebContentsObserver
+  virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
+  virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
+  virtual void DocumentAvailableInMainFrame() OVERRIDE;
+  virtual bool OnMessageReceived(const IPC::Message& message,
+                                 RenderFrameHost* render_frame_host) OVERRIDE;
+
+  // GinJavaMethodInvocationHelper::DispatcherDelegate
+  virtual JavaObjectWeakGlobalRef GetObjectWeakRef(
+      GinJavaBoundObject::ObjectID object_id) OVERRIDE;
+
+  void OnGetMethods(RenderFrameHost* render_frame_host,
+                    GinJavaBoundObject::ObjectID object_id,
+                    IPC::Message* reply_msg);
+  void OnHasMethod(RenderFrameHost* render_frame_host,
+                   GinJavaBoundObject::ObjectID object_id,
+                   const std::string& method_name,
+                   IPC::Message* reply_msg);
+  void OnInvokeMethod(RenderFrameHost* render_frame_host,
+                      GinJavaBoundObject::ObjectID object_id,
+                      const std::string& method_name,
+                      const base::ListValue& arguments,
+                      IPC::Message* reply_msg);
+
+ private:
+  typedef std::set<RenderFrameHost*> RendererSet;
+  void OnObjectWrapperDeleted(RenderFrameHost* render_frame_host,
+                              GinJavaBoundObject::ObjectID object_id);
+
+  void SendReply(RenderFrameHost* render_frame_host, IPC::Message* reply_msg);
+  void SendMethods(RenderFrameHost* render_frame_host,
+                   IPC::Message* reply_msg,
+                   const std::set<std::string>& method_names);
+  void SendHasMethodReply(RenderFrameHost* render_frame_host,
+                          IPC::Message* reply_msg,
+                          bool result);
+  void ProcessMethodInvocationResult(
+      RenderFrameHost* render_frame_host,
+      IPC::Message* reply_msg,
+      scoped_refptr<GinJavaMethodInvocationHelper> result);
+  void ProcessMethodInvocationObjectResult(
+      RenderFrameHost* render_frame_host,
+      IPC::Message* reply_msg,
+      scoped_refptr<GinJavaMethodInvocationHelper> result);
+  GinJavaBoundObject::ObjectID AddObject(
+      const base::android::JavaRef<jobject>& object,
+      const base::android::JavaRef<jclass>& safe_annotation_clazz,
+      bool is_named,
+      RenderFrameHost* holder);
+  bool FindObjectId(const base::android::JavaRef<jobject>& object,
+                    GinJavaBoundObject::ObjectID* object_id);
+  void RemoveHolder(RenderFrameHost* holder,
+                    const GinJavaBoundObject::ObjectMap::iterator& from,
+                    size_t count);
+
+  // Every time a GinJavaBoundObject backed by a real Java object is
+  // created/destroyed, we insert/remove a strong ref to that Java object into
+  // this set so that it doesn't get garbage collected while it's still
+  // potentially in use. Although the set is managed native side, it's owned
+  // and defined in Java so that pushing refs into it does not create new GC
+  // roots that would prevent ContentViewCore from being garbage collected.
+  JavaObjectWeakGlobalRef retained_object_set_;
+  bool allow_object_contents_inspection_;
+  RendererSet renderers_;
+  GinJavaBoundObject::ObjectMap objects_;
+  typedef std::map<std::string, GinJavaBoundObject::ObjectID> NamedObjectMap;
+  NamedObjectMap named_objects_;
+
+  DISALLOW_COPY_AND_ASSIGN(GinJavaBridgeDispatcherHost);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BRIDGE_DISPATCHER_HOST_H_
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
index d3fb138..5c60ecf 100644
--- a/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
+++ b/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
@@ -23,13 +23,6 @@
 
 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;
 
@@ -41,7 +34,8 @@
     const base::ListValue& arguments)
     : object_(object.Pass()),
       method_name_(method_name),
-      arguments_(arguments.DeepCopy()) {
+      arguments_(arguments.DeepCopy()),
+      invocation_error_(kGinJavaBridgeNoError) {
 }
 
 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
@@ -121,14 +115,14 @@
   const JavaMethod* method =
       object_->FindMethod(method_name_, arguments_->GetSize());
   if (!method) {
-    SetInvocationFailure(kMethodNotFound);
+    SetInvocationError(kGinJavaBridgeMethodNotFound);
     return;
   }
 
   if (object_->IsObjectGetClassMethod(method)) {
     base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag,
                                     getuid());
-    SetInvocationFailure(kAccessToObjectGetClassIsBlocked);
+    SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked);
     return;
   }
 
@@ -140,7 +134,7 @@
     obj = object_->GetLocalRef(env);
   }
   if (obj.is_null() && cls.is_null()) {
-    SetInvocationFailure(kObjectIsGone);
+    SetInvocationError(kGinJavaBridgeObjectIsGone);
     return;
   }
 
@@ -166,11 +160,11 @@
   }
 }
 
-void GinJavaMethodInvocationHelper::SetInvocationFailure(
-    const char* error_message) {
+void GinJavaMethodInvocationHelper::SetInvocationError(
+    GinJavaBridgeError error) {
   holds_primitive_result_ = true;
   primitive_result_.reset(new base::ListValue());
-  error_message_ = error_message;
+  invocation_error_ = error;
 }
 
 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
@@ -205,8 +199,8 @@
   return safe_annotation_clazz_;
 }
 
-const std::string& GinJavaMethodInvocationHelper::GetErrorMessage() {
-  return error_message_;
+const GinJavaBridgeError GinJavaMethodInvocationHelper::GetInvocationError() {
+  return invocation_error_;
 }
 
 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
@@ -295,7 +289,7 @@
       // methods. ScopedJavaLocalRef is liable to make such calls, so we test
       // first.
       if (base::android::ClearException(env)) {
-        SetInvocationFailure(kJavaExceptionRaised);
+        SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
         return;
       }
       ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
@@ -318,7 +312,7 @@
           object ? env->CallObjectMethodA(object, id, parameters)
                  : env->CallStaticObjectMethodA(clazz, id, parameters);
       if (base::android::ClearException(env)) {
-        SetInvocationFailure(kJavaExceptionRaised);
+        SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
         return;
       }
       ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
@@ -334,7 +328,7 @@
   if (!base::android::ClearException(env)) {
     SetPrimitiveResult(result_wrapper);
   } else {
-    SetInvocationFailure(kJavaExceptionRaised);
+    SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
   }
 }
 
diff --git a/content/browser/renderer_host/java/gin_java_method_invocation_helper.h b/content/browser/renderer_host/java/gin_java_method_invocation_helper.h
index 89805ca..4cdb09b 100644
--- a/content/browser/renderer_host/java/gin_java_method_invocation_helper.h
+++ b/content/browser/renderer_host/java/gin_java_method_invocation_helper.h
@@ -13,6 +13,7 @@
 #include "base/values.h"
 #include "content/browser/renderer_host/java/gin_java_bound_object.h"
 #include "content/browser/renderer_host/java/java_type.h"
+#include "content/common/android/gin_java_bridge_errors.h"
 #include "content/common/content_export.h"
 
 namespace content {
@@ -68,7 +69,7 @@
   const base::ListValue& GetPrimitiveResult();
   const base::android::JavaRef<jobject>& GetObjectResult();
   const base::android::JavaRef<jclass>& GetSafeAnnotationClass();
-  const std::string& GetErrorMessage();
+  const GinJavaBridgeError GetInvocationError();
 
  private:
   friend class base::RefCountedThreadSafe<GinJavaMethodInvocationHelper>;
@@ -89,7 +90,7 @@
                     const JavaType& return_type,
                     jmethodID id,
                     jvalue* parameters);
-  void SetInvocationFailure(const char* error_message);
+  void SetInvocationError(GinJavaBridgeError error);
   void SetPrimitiveResult(const base::ListValue& result_wrapper);
   void SetObjectResult(
       const base::android::JavaRef<jobject>& object,
@@ -104,7 +105,7 @@
   ObjectRefs object_refs_;
   bool holds_primitive_result_;
   scoped_ptr<base::ListValue> primitive_result_;
-  std::string error_message_;
+  GinJavaBridgeError invocation_error_;
   base::android::ScopedJavaGlobalRef<jobject> object_result_;
   base::android::ScopedJavaGlobalRef<jclass> safe_annotation_clazz_;
 
diff --git a/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc b/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc
index d763441..067a5e1 100644
--- a/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc
+++ b/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/renderer_host/java/gin_java_method_invocation_helper.h"
 
 #include "base/android/jni_android.h"
+#include "content/browser/renderer_host/java/jni_helper.h"
 #include "content/common/android/gin_java_bridge_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -154,21 +155,73 @@
   counter.AssertInvocationsCount(1, 6);
 }
 
+namespace {
+
+class ObjectIsGoneObjectDelegate : public NullObjectDelegate {
+ public:
+  ObjectIsGoneObjectDelegate() :
+      get_local_ref_called_(false) {
+    // We need a Java Method object to create a valid JavaMethod instance.
+    JNIEnv* env = base::android::AttachCurrentThread();
+    jmethodID method_id =
+        GetMethodIDFromClassName(env, "java/lang/Object", "hashCode", "()I");
+    EXPECT_TRUE(method_id);
+    base::android::ScopedJavaLocalRef<jobject> method_obj(
+        env,
+        env->ToReflectedMethod(
+            base::android::GetClass(env, "java/lang/Object").obj(),
+            method_id,
+            false));
+    EXPECT_TRUE(method_obj.obj());
+    method_.reset(new JavaMethod(method_obj));
+  }
+
+  virtual ~ObjectIsGoneObjectDelegate() {}
+
+  virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
+      JNIEnv* env) OVERRIDE {
+    get_local_ref_called_ = true;
+    return NullObjectDelegate::GetLocalRef(env);
+  }
+
+  virtual const JavaMethod* FindMethod(const std::string& method_name,
+                                       size_t num_parameters) OVERRIDE {
+    return method_.get();
+  }
+
+  bool get_local_ref_called() { return get_local_ref_called_; }
+
+  const std::string& get_method_name() { return method_->name(); }
+
+ protected:
+  scoped_ptr<JavaMethod> method_;
+  bool get_local_ref_called_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ObjectIsGoneObjectDelegate);
+};
+
+}  // namespace
+
 TEST_F(GinJavaMethodInvocationHelperTest, HandleObjectIsGone) {
   base::ListValue no_objects;
+  ObjectIsGoneObjectDelegate* object_delegate =
+      new ObjectIsGoneObjectDelegate();
   scoped_refptr<GinJavaMethodInvocationHelper> helper =
       new GinJavaMethodInvocationHelper(
           scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>(
-              new NullObjectDelegate()),
-          "foo",
+              object_delegate),
+          object_delegate->get_method_name(),
           no_objects);
   NullDispatcherDelegate dispatcher;
   helper->Init(&dispatcher);
-  EXPECT_TRUE(helper->GetErrorMessage().empty());
+  EXPECT_FALSE(object_delegate->get_local_ref_called());
+  EXPECT_EQ(kGinJavaBridgeNoError, helper->GetInvocationError());
   helper->Invoke();
+  EXPECT_TRUE(object_delegate->get_local_ref_called());
   EXPECT_TRUE(helper->HoldsPrimitiveResult());
   EXPECT_TRUE(helper->GetPrimitiveResult().empty());
-  EXPECT_FALSE(helper->GetErrorMessage().empty());
+  EXPECT_EQ(kGinJavaBridgeObjectIsGone, helper->GetInvocationError());
 }
 
 namespace {
@@ -215,12 +268,12 @@
   NullDispatcherDelegate dispatcher;
   helper->Init(&dispatcher);
   EXPECT_FALSE(object_delegate->find_method_called());
-  EXPECT_TRUE(helper->GetErrorMessage().empty());
+  EXPECT_EQ(kGinJavaBridgeNoError, helper->GetInvocationError());
   helper->Invoke();
   EXPECT_TRUE(object_delegate->find_method_called());
   EXPECT_TRUE(helper->HoldsPrimitiveResult());
   EXPECT_TRUE(helper->GetPrimitiveResult().empty());
-  EXPECT_FALSE(helper->GetErrorMessage().empty());
+  EXPECT_EQ(kGinJavaBridgeMethodNotFound, helper->GetInvocationError());
 }
 
 namespace {
@@ -273,13 +326,14 @@
   helper->Init(&dispatcher);
   EXPECT_FALSE(object_delegate->find_method_called());
   EXPECT_FALSE(object_delegate->get_class_called());
-  EXPECT_TRUE(helper->GetErrorMessage().empty());
+  EXPECT_EQ(kGinJavaBridgeNoError, helper->GetInvocationError());
   helper->Invoke();
   EXPECT_TRUE(object_delegate->find_method_called());
   EXPECT_TRUE(object_delegate->get_class_called());
   EXPECT_TRUE(helper->HoldsPrimitiveResult());
   EXPECT_TRUE(helper->GetPrimitiveResult().empty());
-  EXPECT_FALSE(helper->GetErrorMessage().empty());
+  EXPECT_EQ(kGinJavaBridgeAccessToObjectGetClassIsBlocked,
+            helper->GetInvocationError());
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/java/java_method.h b/content/browser/renderer_host/java/java_method.h
index 1d59bac..6477263 100644
--- a/content/browser/renderer_host/java/java_method.h
+++ b/content/browser/renderer_host/java/java_method.h
@@ -11,12 +11,13 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "content/browser/renderer_host/java/java_type.h"
+#include "content/common/content_export.h"
 
 namespace content {
 
 // Wrapper around java.lang.reflect.Method. This class must be used on a single
 // thread only.
-class JavaMethod {
+class CONTENT_EXPORT JavaMethod {
  public:
   explicit JavaMethod(const base::android::JavaRef<jobject>& method);
   ~JavaMethod();
diff --git a/content/common/android/gin_java_bridge_errors.cc b/content/common/android/gin_java_bridge_errors.cc
new file mode 100644
index 0000000..7a80801
--- /dev/null
+++ b/content/common/android/gin_java_bridge_errors.cc
@@ -0,0 +1,30 @@
+// 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/common/android/gin_java_bridge_errors.h"
+
+#include "base/logging.h"
+
+namespace content {
+
+const char* GinJavaBridgeErrorToString(GinJavaBridgeError error) {
+  switch (error) {
+    case kGinJavaBridgeNoError:
+      return "No error";
+    case kGinJavaBridgeUnknownObjectId:
+      return "Unknown Java object ID";
+    case kGinJavaBridgeObjectIsGone:
+      return "Java object is gone";
+    case kGinJavaBridgeMethodNotFound:
+      return "Method not found";
+    case kGinJavaBridgeAccessToObjectGetClassIsBlocked:
+      return "Access to java.lang.Object.getClass is blocked";
+    case kGinJavaBridgeJavaExceptionRaised:
+      return "Java exception was raised during method invocation";
+  }
+  NOTREACHED();
+  return "Unknown error";
+}
+
+}  // namespace content
diff --git a/content/common/android/gin_java_bridge_errors.h b/content/common/android/gin_java_bridge_errors.h
new file mode 100644
index 0000000..75a8970
--- /dev/null
+++ b/content/common/android/gin_java_bridge_errors.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef CONTENT_COMMON_ANDROID_GIN_JAVA_BRIDGE_ERRORS_H_
+#define CONTENT_COMMON_ANDROID_GIN_JAVA_BRIDGE_ERRORS_H_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+enum GinJavaBridgeError {
+  kGinJavaBridgeNoError = 0,
+  kGinJavaBridgeUnknownObjectId,
+  kGinJavaBridgeObjectIsGone,
+  kGinJavaBridgeMethodNotFound,
+  kGinJavaBridgeAccessToObjectGetClassIsBlocked,
+  kGinJavaBridgeJavaExceptionRaised,
+};
+
+CONTENT_EXPORT const char* GinJavaBridgeErrorToString(GinJavaBridgeError error);
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_ANDROID_GIN_JAVA_BRIDGE_ERRORS_H_
diff --git a/content/common/gin_java_bridge_messages.h b/content/common/gin_java_bridge_messages.h
index f9525b1..cbce413 100644
--- a/content/common/gin_java_bridge_messages.h
+++ b/content/common/gin_java_bridge_messages.h
@@ -7,6 +7,7 @@
 // Multiply-included message file, hence no include guard.
 
 #include "base/basictypes.h"
+#include "content/common/android/gin_java_bridge_errors.h"
 #include "content/common/content_export.h"
 #include "ipc/ipc_message_macros.h"
 
@@ -16,6 +17,8 @@
 
 // Messages for handling Java objects injected into JavaScript -----------------
 
+IPC_ENUM_TRAITS(content::GinJavaBridgeError)
+
 // Sent from browser to renderer to add a Java object with the given name.
 // Object IDs are generated on the browser side.
 IPC_MESSAGE_ROUTED2(GinJavaBridgeMsg_AddNamedObject,
@@ -45,14 +48,16 @@
 // a container to work around immutability of base::Value.
 // Empty result list indicates that an error has happened on the Java side
 // (either bridge-induced error or an unhandled Java exception) and an exception
-// must be thrown into JavaScript.
+// must be thrown into JavaScript. |error_code| indicates the cause of
+// the error.
 // Some special value types that are not supported by base::Value are encoded
 // as BinaryValues via GinJavaBridgeValue.
-IPC_SYNC_MESSAGE_ROUTED3_1(GinJavaBridgeHostMsg_InvokeMethod,
+IPC_SYNC_MESSAGE_ROUTED3_2(GinJavaBridgeHostMsg_InvokeMethod,
                            int32 /* object_id */,
                            std::string /* method_name */,
                            base::ListValue /* arguments */,
-                           base::ListValue /* result */)
+                           base::ListValue /* result */,
+                           content::GinJavaBridgeError /* error_code */)
 
 // Sent from renderer to browser in two cases:
 //
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 33f796f..cf7c3d0 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -1287,6 +1287,8 @@
       'browser/renderer_host/java/gin_java_bound_object.h',
       'browser/renderer_host/java/gin_java_bound_object_delegate.cc',
       'browser/renderer_host/java/gin_java_bound_object_delegate.h',
+      'browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc',
+      'browser/renderer_host/java/gin_java_bridge_dispatcher_host.h',
       'browser/renderer_host/java/gin_java_method_invocation_helper.cc',
       'browser/renderer_host/java/gin_java_method_invocation_helper.h',
       'browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc',
diff --git a/content/content_browser.target.darwin-arm.mk b/content/content_browser.target.darwin-arm.mk
index fd0b8b7..95780e7 100644
--- a/content/content_browser.target.darwin-arm.mk
+++ b/content/content_browser.target.darwin-arm.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.darwin-arm64.mk b/content/content_browser.target.darwin-arm64.mk
index fc10a3d..ae4bdb4 100644
--- a/content/content_browser.target.darwin-arm64.mk
+++ b/content/content_browser.target.darwin-arm64.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.darwin-mips.mk b/content/content_browser.target.darwin-mips.mk
index 73750d8..53b9496 100644
--- a/content/content_browser.target.darwin-mips.mk
+++ b/content/content_browser.target.darwin-mips.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.darwin-x86.mk b/content/content_browser.target.darwin-x86.mk
index 8653900..b895015 100644
--- a/content/content_browser.target.darwin-x86.mk
+++ b/content/content_browser.target.darwin-x86.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.darwin-x86_64.mk b/content/content_browser.target.darwin-x86_64.mk
index 0a53750..c5a2b73 100644
--- a/content/content_browser.target.darwin-x86_64.mk
+++ b/content/content_browser.target.darwin-x86_64.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.linux-arm.mk b/content/content_browser.target.linux-arm.mk
index fd0b8b7..95780e7 100644
--- a/content/content_browser.target.linux-arm.mk
+++ b/content/content_browser.target.linux-arm.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.linux-arm64.mk b/content/content_browser.target.linux-arm64.mk
index fc10a3d..ae4bdb4 100644
--- a/content/content_browser.target.linux-arm64.mk
+++ b/content/content_browser.target.linux-arm64.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.linux-mips.mk b/content/content_browser.target.linux-mips.mk
index 73750d8..53b9496 100644
--- a/content/content_browser.target.linux-mips.mk
+++ b/content/content_browser.target.linux-mips.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.linux-x86.mk b/content/content_browser.target.linux-x86.mk
index 8653900..b895015 100644
--- a/content/content_browser.target.linux-x86.mk
+++ b/content/content_browser.target.linux-x86.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_browser.target.linux-x86_64.mk b/content/content_browser.target.linux-x86_64.mk
index 0a53750..c5a2b73 100644
--- a/content/content_browser.target.linux-x86_64.mk
+++ b/content/content_browser.target.linux-x86_64.mk
@@ -512,6 +512,7 @@
 	content/browser/renderer_host/p2p/socket_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_bound_object.cc \
 	content/browser/renderer_host/java/gin_java_bound_object_delegate.cc \
+	content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc \
 	content/browser/renderer_host/java/gin_java_method_invocation_helper.cc \
 	content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc \
 	content/browser/renderer_host/java/java_bound_object.cc \
diff --git a/content/content_common.gypi b/content/content_common.gypi
index d74a4ac..341cfee 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -137,6 +137,8 @@
       'common/android/address_parser_internal.h',
       'common/android/common_jni_registrar.cc',
       'common/android/common_jni_registrar.h',
+      'common/android/gin_java_bridge_errors.cc',
+      'common/android/gin_java_bridge_errors.h',
       'common/android/gin_java_bridge_value.cc',
       'common/android/gin_java_bridge_value.h',
       'common/android/hash_set.cc',
diff --git a/content/content_common.target.darwin-arm.mk b/content/content_common.target.darwin-arm.mk
index 25026d9..fd96df0 100644
--- a/content/content_common.target.darwin-arm.mk
+++ b/content/content_common.target.darwin-arm.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.darwin-arm64.mk b/content/content_common.target.darwin-arm64.mk
index 077977a..7287c55 100644
--- a/content/content_common.target.darwin-arm64.mk
+++ b/content/content_common.target.darwin-arm64.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.darwin-mips.mk b/content/content_common.target.darwin-mips.mk
index ba18ded..4c55363 100644
--- a/content/content_common.target.darwin-mips.mk
+++ b/content/content_common.target.darwin-mips.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.darwin-x86.mk b/content/content_common.target.darwin-x86.mk
index 2d10452..556a5c2 100644
--- a/content/content_common.target.darwin-x86.mk
+++ b/content/content_common.target.darwin-x86.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.darwin-x86_64.mk b/content/content_common.target.darwin-x86_64.mk
index 53c092a..c5a1624 100644
--- a/content/content_common.target.darwin-x86_64.mk
+++ b/content/content_common.target.darwin-x86_64.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.linux-arm.mk b/content/content_common.target.linux-arm.mk
index 25026d9..fd96df0 100644
--- a/content/content_common.target.linux-arm.mk
+++ b/content/content_common.target.linux-arm.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.linux-arm64.mk b/content/content_common.target.linux-arm64.mk
index 077977a..7287c55 100644
--- a/content/content_common.target.linux-arm64.mk
+++ b/content/content_common.target.linux-arm64.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.linux-mips.mk b/content/content_common.target.linux-mips.mk
index ba18ded..4c55363 100644
--- a/content/content_common.target.linux-mips.mk
+++ b/content/content_common.target.linux-mips.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.linux-x86.mk b/content/content_common.target.linux-x86.mk
index 2d10452..556a5c2 100644
--- a/content/content_common.target.linux-x86.mk
+++ b/content/content_common.target.linux-x86.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/content_common.target.linux-x86_64.mk b/content/content_common.target.linux-x86_64.mk
index 53c092a..c5a1624 100644
--- a/content/content_common.target.linux-x86_64.mk
+++ b/content/content_common.target.linux-x86_64.mk
@@ -82,6 +82,7 @@
 	content/common/android/address_parser.cc \
 	content/common/android/address_parser_internal.cc \
 	content/common/android/common_jni_registrar.cc \
+	content/common/android/gin_java_bridge_errors.cc \
 	content/common/android/gin_java_bridge_value.cc \
 	content/common/android/hash_set.cc \
 	content/common/android/surface_texture_lookup.cc \
diff --git a/content/renderer/java/gin_java_bridge_dispatcher.cc b/content/renderer/java/gin_java_bridge_dispatcher.cc
index 88d502e..9b999f2 100644
--- a/content/renderer/java/gin_java_bridge_dispatcher.cc
+++ b/content/renderer/java/gin_java_bridge_dispatcher.cc
@@ -114,14 +114,16 @@
 scoped_ptr<base::Value> GinJavaBridgeDispatcher::InvokeJavaMethod(
     ObjectID object_id,
     const std::string& method_name,
-    const base::ListValue& arguments) {
+    const base::ListValue& arguments,
+    GinJavaBridgeError* error) {
   base::ListValue result_wrapper;
   render_frame()->Send(
       new GinJavaBridgeHostMsg_InvokeMethod(routing_id(),
                                             object_id,
                                             method_name,
                                             arguments,
-                                            &result_wrapper));
+                                            &result_wrapper,
+                                            error));
   base::Value* result;
   if (result_wrapper.Get(0, &result)) {
     return scoped_ptr<base::Value>(result->DeepCopy());
diff --git a/content/renderer/java/gin_java_bridge_dispatcher.h b/content/renderer/java/gin_java_bridge_dispatcher.h
index 13c6777..efbd19f 100644
--- a/content/renderer/java/gin_java_bridge_dispatcher.h
+++ b/content/renderer/java/gin_java_bridge_dispatcher.h
@@ -12,6 +12,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "content/common/android/gin_java_bridge_errors.h"
 #include "content/public/renderer/render_frame_observer.h"
 
 namespace blink {
@@ -50,7 +51,8 @@
   bool HasJavaMethod(ObjectID object_id, const std::string& method_name);
   scoped_ptr<base::Value> InvokeJavaMethod(ObjectID object_id,
                                            const std::string& method_name,
-                                           const base::ListValue& arguments);
+                                           const base::ListValue& arguments,
+                                           GinJavaBridgeError* error);
   GinJavaBridgeObject* GetObject(ObjectID object_id);
   void OnGinJavaBridgeObjectDeleted(ObjectID object_id);
 
diff --git a/content/renderer/java/gin_java_bridge_object.cc b/content/renderer/java/gin_java_bridge_object.cc
index a153f2a..d2a2b60 100644
--- a/content/renderer/java/gin_java_bridge_object.cc
+++ b/content/renderer/java/gin_java_bridge_object.cc
@@ -5,6 +5,7 @@
 #include "content/renderer/java/gin_java_bridge_object.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "content/common/android/gin_java_bridge_errors.h"
 #include "content/common/android/gin_java_bridge_value.h"
 #include "content/public/renderer/v8_value_converter.h"
 #include "content/renderer/java/gin_java_bridge_value_converter.h"
@@ -125,11 +126,12 @@
     }
   }
 
-  scoped_ptr<base::Value> result =
-      dispatcher_->InvokeJavaMethod(object_id_, name, arguments);
+  GinJavaBridgeError error;
+  scoped_ptr<base::Value> result = dispatcher_->InvokeJavaMethod(
+      object_id_, name, arguments, &error);
   if (!result.get()) {
     args->isolate()->ThrowException(v8::Exception::Error(gin::StringToV8(
-        args->isolate(), kMethodInvocationErrorMessage)));
+        args->isolate(), GinJavaBridgeErrorToString(error))));
     return v8::Undefined(args->isolate());
   }
   if (!result->IsType(base::Value::TYPE_BINARY)) {