Cherrypick: [Android] Switch to Gin Java Bridge implementation

Original description:

> What this patch does:
>
>  -- plumbs in GinJavaBridge... instead of JavaBridge... classes on
>     browser and renderer sides;
>
>  -- fixes the issue with adding JavaScript interfaces to popup windows
>     created via WebChromeClient.onCreatePopup;
>
>  -- updates JavaBridge tests to match the behaviour of the new
>     implementation.
>
> BUG=355644
>
> Review URL: https://codereview.chromium.org/353163002
>
> git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282004 0039d316-1c4b-4281-b951-d872f2087c98

Bug: 13238305
Change-Id: I3c42dea96c28aa439b53e6efaab70d24150f1536
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index df19d57..2b3d278 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -23,6 +23,7 @@
 import android.os.Message;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -645,6 +646,13 @@
         if (wasAttached) onDetachedFromWindow();
         if (!wasPaused) onPause();
 
+        // Save injected JavaScript interfaces.
+        Map<String, Pair<Object, Class>> javascriptInterfaces =
+                new HashMap<String, Pair<Object, Class>>();
+        if (mContentViewCore != null) {
+            javascriptInterfaces.putAll(mContentViewCore.getJavascriptInterfaces());
+        }
+
         setNewAwContents(popupNativeAwContents);
 
         // Finally refresh all view state for mContentViewCore and mNativeAwContents.
@@ -658,6 +666,14 @@
         if (wasViewVisible) setViewVisibilityInternal(true);
         if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused);
         if (wasFocused) onFocusChanged(true, 0, null);
+
+        // Restore injected JavaScript interfaces.
+        for (Map.Entry<String, Pair<Object, Class>> entry : javascriptInterfaces.entrySet()) {
+            mContentViewCore.addPossiblyUnsafeJavascriptInterface(
+                    entry.getValue().first,
+                    entry.getKey(),
+                    entry.getValue().second);
+        }
     }
 
     /**
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index d6ac546..188fccf 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -1940,7 +1940,7 @@
                 createAwTestContainerViewOnMainSync(contentClient);
         final AwContents awContents = testContainer.getAwContents();
         final AwSettings awSettings = getAwSettingsOnUiThread(awContents);
-        CallbackHelper callback = new CallbackHelper();
+        final CallbackHelper callback = new CallbackHelper();
         awSettings.setJavaScriptEnabled(true);
 
         TestWebServer webServer = null;
@@ -1956,8 +1956,13 @@
                     "onerror=\"AudioEvent.onError();\" /> </body></html>";
             // Actual test. Blocking should trigger onerror handler.
             awSettings.setBlockNetworkLoads(true);
-            awContents.addPossiblyUnsafeJavascriptInterface(
-                    new AudioEvent(callback), "AudioEvent", null);
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    awContents.addPossiblyUnsafeJavascriptInterface(
+                            new AudioEvent(callback), "AudioEvent", null);
+                }
+            });
             int count = callback.getCallCount();
             loadDataSync(awContents, contentClient.getOnPageFinishedHelper(), pageHtml,
                     "text/html", false);
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 6e533fc..1992f7d 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -19,8 +19,7 @@
 #include "cc/output/begin_frame_args.h"
 #include "content/browser/android/gesture_event_type.h"
 #include "content/browser/android/interstitial_page_delegate_android.h"
-#include "content/browser/android/java/java_bound_object.h"
-#include "content/browser/android/java/java_bridge_dispatcher_host_manager.h"
+#include "content/browser/android/java/gin_java_bridge_dispatcher_host.h"
 #include "content/browser/android/load_url_params.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
@@ -52,7 +51,6 @@
 #include "content/public/common/page_transition_types.h"
 #include "content/public/common/user_agent.h"
 #include "jni/ContentViewCore_jni.h"
-#include "third_party/WebKit/public/web/WebBindings.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
 #include "ui/base/android/view_android.h"
 #include "ui/base/android/window_android.h"
@@ -244,9 +242,9 @@
       BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product);
   web_contents->SetUserAgentOverride(spoofed_ua);
 
-  java_bridge_dispatcher_host_manager_.reset(
-      new JavaBridgeDispatcherHostManager(web_contents,
-                                          java_bridge_retained_object_set));
+  java_bridge_dispatcher_host_.reset(
+      new GinJavaBridgeDispatcherHost(web_contents,
+                                      java_bridge_retained_object_set));
 
   InitWebContents();
 }
@@ -1222,7 +1220,7 @@
     JNIEnv* env,
     jobject obj,
     jboolean allow) {
-  java_bridge_dispatcher_host_manager_->SetAllowObjectContentsInspection(allow);
+  java_bridge_dispatcher_host_->SetAllowObjectContentsInspection(allow);
 }
 
 void ContentViewCoreImpl::AddJavascriptInterface(
@@ -1233,24 +1231,15 @@
     jclass safe_annotation_clazz) {
   ScopedJavaLocalRef<jobject> scoped_object(env, object);
   ScopedJavaLocalRef<jclass> scoped_clazz(env, safe_annotation_clazz);
-
-  // JavaBoundObject creates the NPObject with a ref count of 1, and
-  // JavaBridgeDispatcherHostManager takes its own ref.
-  NPObject* bound_object = JavaBoundObject::Create(
-      scoped_object,
-      scoped_clazz,
-      java_bridge_dispatcher_host_manager_->AsWeakPtr(),
-      java_bridge_dispatcher_host_manager_->GetAllowObjectContentsInspection());
-  java_bridge_dispatcher_host_manager_->AddNamedObject(
-      ConvertJavaStringToUTF16(env, name), bound_object);
-  blink::WebBindings::releaseObject(bound_object);
+  java_bridge_dispatcher_host_->AddNamedObject(
+      ConvertJavaStringToUTF8(env, name), scoped_object, scoped_clazz);
 }
 
 void ContentViewCoreImpl::RemoveJavascriptInterface(JNIEnv* env,
                                                     jobject /* obj */,
                                                     jstring name) {
-  java_bridge_dispatcher_host_manager_->RemoveNamedObject(
-      ConvertJavaStringToUTF16(env, name));
+  java_bridge_dispatcher_host_->RemoveNamedObject(
+      ConvertJavaStringToUTF8(env, name));
 }
 
 void ContentViewCoreImpl::WasResized(JNIEnv* env, jobject obj) {
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index 3bb1291..62ed68b 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -28,7 +28,7 @@
 }
 
 namespace content {
-class JavaBridgeDispatcherHostManager;
+class GinJavaBridgeDispatcherHost;
 class RenderWidgetHostViewAndroid;
 struct MenuItem;
 
@@ -380,8 +380,8 @@
   bool accessibility_enabled_;
 
   // Manages injecting Java objects.
-  scoped_ptr<JavaBridgeDispatcherHostManager>
-      java_bridge_dispatcher_host_manager_;
+  scoped_ptr<GinJavaBridgeDispatcherHost>
+      java_bridge_dispatcher_host_;
 
   DISALLOW_COPY_AND_ASSIGN(ContentViewCoreImpl);
 };
diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
index a60310d..6526381 100644
--- a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
+++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
@@ -18,6 +18,7 @@
 #include "content/common/gin_java_bridge_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 #include "ipc/ipc_message_utils.h"
 
 #if !defined(OS_ANDROID)
@@ -59,7 +60,6 @@
 
 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) {
@@ -70,7 +70,6 @@
 
 void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
     RenderFrameHost* render_frame_host) {
-  renderers_.erase(render_frame_host);
   RemoveHolder(render_frame_host,
                GinJavaBoundObject::ObjectMap::iterator(&objects_),
                objects_.size());
@@ -182,11 +181,8 @@
   }
   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));
-  }
+  web_contents()->SendToAllFrames(
+      new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id));
 }
 
 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
@@ -213,11 +209,8 @@
     }
   }
 
-  for (RendererSet::iterator iter = renderers_.begin();
-      iter != renderers_.end(); ++iter) {
-    (*iter)->Send(new GinJavaBridgeMsg_RemoveNamedObject(
-        (*iter)->GetRoutingID(), name));
-  }
+  web_contents()->SendToAllFrames(
+      new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, name));
 }
 
 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
@@ -319,11 +312,47 @@
   return handled;
 }
 
+namespace {
+
+class IsValidRenderFrameHostHelper
+    : public base::RefCounted<IsValidRenderFrameHostHelper> {
+ public:
+  explicit IsValidRenderFrameHostHelper(RenderFrameHost* rfh_to_match)
+      : rfh_to_match_(rfh_to_match), rfh_found_(false) {}
+
+  bool rfh_found() { return rfh_found_; }
+
+  void OnFrame(RenderFrameHost* rfh) {
+    if (rfh_to_match_ == rfh) rfh_found_ = true;
+  }
+
+ private:
+  friend class base::RefCounted<IsValidRenderFrameHostHelper>;
+
+  ~IsValidRenderFrameHostHelper() {}
+
+  RenderFrameHost* rfh_to_match_;
+  bool rfh_found_;
+
+  DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper);
+};
+
+}  // namespace
+
+bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost(
+    RenderFrameHost* render_frame_host) {
+  scoped_refptr<IsValidRenderFrameHostHelper> helper =
+      new IsValidRenderFrameHostHelper(render_frame_host);
+  web_contents()->ForEachFrame(
+      base::Bind(&IsValidRenderFrameHostHelper::OnFrame, helper));
+  return helper->rfh_found();
+}
+
 void GinJavaBridgeDispatcherHost::SendReply(
     RenderFrameHost* render_frame_host,
     IPC::Message* reply_msg) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (renderers_.find(render_frame_host) != renderers_.end()) {
+  if (IsValidRenderFrameHost(render_frame_host)) {
     render_frame_host->Send(reply_msg);
   } else {
     delete reply_msg;
@@ -455,7 +484,7 @@
     IPC::Message* reply_msg,
     scoped_refptr<GinJavaMethodInvocationHelper> result) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (renderers_.find(render_frame_host) == renderers_.end()) {
+  if (!IsValidRenderFrameHost(render_frame_host)) {
     delete reply_msg;
     return;
   }
diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.h b/content/browser/android/java/gin_java_bridge_dispatcher_host.h
index 3ccd46f..615c2b0 100644
--- a/content/browser/android/java/gin_java_bridge_dispatcher_host.h
+++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.h
@@ -72,10 +72,10 @@
                       IPC::Message* reply_msg);
 
  private:
-  typedef std::set<RenderFrameHost*> RendererSet;
   void OnObjectWrapperDeleted(RenderFrameHost* render_frame_host,
                               GinJavaBoundObject::ObjectID object_id);
 
+  bool IsValidRenderFrameHost(RenderFrameHost* render_frame_host);
   void SendReply(RenderFrameHost* render_frame_host, IPC::Message* reply_msg);
   void SendMethods(RenderFrameHost* render_frame_host,
                    IPC::Message* reply_msg,
@@ -110,7 +110,6 @@
   // 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_;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 7f6d364..0d13332 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -29,6 +29,7 @@
 import android.text.Selection;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.view.ActionMode;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
@@ -117,9 +118,12 @@
     // native side. However we still need a strong reference on the Java side to
     // prevent garbage collection if the embedder doesn't maintain their own ref to the
     // interface object - the Java side ref won't create a new GC root.
-    // This map stores those refernces. We put into the map on addJavaScriptInterface()
-    // and remove from it in removeJavaScriptInterface().
-    private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
+    // This map stores those references. We put into the map on addJavaScriptInterface()
+    // and remove from it in removeJavaScriptInterface(). The annotation class is stored for
+    // the purpose of migrating injected objects from one instance of CVC to another, which
+    // is used by Android WebView to support WebChromeClient.onCreateWindow scenario.
+    private final Map<String, Pair<Object, Class>> mJavaScriptInterfaces =
+            new HashMap<String, Pair<Object, Class>>();
 
     // Additionally, we keep track of all Java bound JS objects that are in use on the
     // current page to ensure that they are not garbage collected until the page is
@@ -2615,6 +2619,16 @@
     }
 
     /**
+     * Returns JavaScript interface objects previously injected via
+     * {@link #addJavascriptInterface(Object, String)}.
+     *
+     * @return the mapping of names to interface objects and corresponding annotation classes
+     */
+    public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
+        return mJavaScriptInterfaces;
+    }
+
+    /**
      * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
      * and automatically pass in {@link JavascriptInterface} as the required annotation.
      *
@@ -2671,7 +2685,7 @@
     public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
             Class<? extends Annotation> requiredAnnotation) {
         if (mNativeContentViewCore != 0 && object != null) {
-            mJavaScriptInterfaces.put(name, object);
+            mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
             nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
         }
     }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java
index 2525a58..bf5823b 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java
@@ -169,9 +169,8 @@
         executeJavaScript("testObject.setByteArray([42]);");
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
         executeJavaScript("testObject.setCharArray([42]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray([42]);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
@@ -643,7 +642,7 @@
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
         executeJavaScript("testObject.setCharArray(int8_array);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray(int8_array);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
@@ -685,7 +684,7 @@
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
         executeJavaScript("testObject.setCharArray(uint8_array);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray(uint8_array);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
@@ -727,7 +726,7 @@
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
         executeJavaScript("testObject.setCharArray(int16_array);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray(int16_array);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
@@ -769,7 +768,7 @@
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
         executeJavaScript("testObject.setCharArray(uint16_array);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray(uint16_array);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
@@ -811,7 +810,7 @@
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
         executeJavaScript("testObject.setCharArray(int32_array);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray(int32_array);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
@@ -853,7 +852,7 @@
         assertEquals(42, mTestObject.waitForByteArray()[0]);
 
         executeJavaScript("testObject.setCharArray(uint32_array);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+        assertEquals(42, mTestObject.waitForCharArray()[0]);
 
         executeJavaScript("testObject.setShortArray(uint32_array);");
         assertEquals(42, mTestObject.waitForShortArray()[0]);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
index 3ce9a2c..ae58a1d 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
@@ -4,11 +4,8 @@
 
 package org.chromium.content.browser;
 
-import android.os.Handler;
-import android.os.Looper;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content_shell_apk.ContentShellActivity;
@@ -462,19 +459,8 @@
         assertEquals(null, object.weakRefForInner.get());
     }
 
-    /*
-     * The current Java bridge implementation doesn't reuse JS wrappers when returning
-     * the same object from a method. That looks wrong. For example, in the case of DOM,
-     * wrappers are reused, which allows JS code to attach custom properties to interface
-     * objects and use them regardless of the way the reference has been obtained:
-     * via copying a JS reference or by calling the method one more time (assuming that
-     * the method is supposed to return a reference to the same object each time).
-     * TODO(mnaganov): Fix this in the new implementation.
-     *
-     * @SmallTest
-     * @Feature({"AndroidWebView", "Android-JavaBridge"})
-     */
-    @DisabledTest
+    @SmallTest
+    @Feature({"AndroidWebView", "Android-JavaBridge"})
     public void testSameReturnedObjectUsesSameWrapper() throws Throwable {
         class InnerObject {
         }
@@ -853,60 +839,6 @@
     public void testAccessToObjectGetClassIsBlocked() throws Throwable {
         injectObjectAndReload(new Object(), "testObject");
         assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.getClass"));
-        boolean securityExceptionThrown = false;
-        try {
-            final String result = executeJavaScriptAndWaitForExceptionSynchronously(
-                    "typeof testObject.getClass()");
-            fail("A call to java.lang.Object.getClass has been allowed, result: '" + result + "'");
-        } catch (SecurityException exception) {
-            securityExceptionThrown = true;
-        }
-        assertTrue(securityExceptionThrown);
-    }
-
-    // Unlike executeJavaScriptAndGetStringResult, this method is sitting on the UI thread
-    // until a non-null result is obtained or a Java exception has been thrown. This method is
-    // capable of catching Java RuntimeExceptions happening on the UI thread asynchronously.
-    private String executeJavaScriptAndWaitForExceptionSynchronously(final String script)
-            throws Throwable {
-        class ExitLoopException extends RuntimeException {
-        }
-        mTestController.setStringValue(null);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getContentViewCore().loadUrl(new LoadUrlParams("javascript:(function() { " +
-                                "testController.setStringValue(" + script + ") })()"));
-                do {
-                    final Boolean[] deactivateExitLoopTask = new Boolean[1];
-                    deactivateExitLoopTask[0] = false;
-                    // We can't use Loop.quit(), as this is the main looper, so we throw
-                    // an exception to bail out from the loop.
-                    new Handler(Looper.myLooper()).post(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (!deactivateExitLoopTask[0]) {
-                                throw new ExitLoopException();
-                            }
-                        }
-                    });
-                    try {
-                        Looper.loop();
-                    } catch (ExitLoopException e) {
-                        // Intentionally empty.
-                    } catch (RuntimeException e) {
-                        // Prevent the task that throws the ExitLoopException from exploding
-                        // on the main loop outside of this function.
-                        deactivateExitLoopTask[0] = true;
-                        throw e;
-                    }
-                } while (mTestController.getStringValue() == null ||
-                        // When an exception in an injected method happens, the function returns
-                        // null. We ignore this and wait until the exception on the browser side
-                        // will be thrown.
-                        mTestController.getStringValue().equals("null"));
-            }
-        });
-        return mTestController.getStringValue();
+        assertRaisesException("testObject.getClass()");
     }
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
index c344416..472b253 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
@@ -172,9 +172,8 @@
         executeJavaScript("testObject.setByteValue(" + Byte.MAX_VALUE + " + 42);");
         assertEquals(Byte.MIN_VALUE + 42 - 1, mTestObject.waitForByteValue());
 
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
         executeJavaScript("testObject.setCharValue(42);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
+        assertEquals(42, mTestObject.waitForCharValue());
 
         executeJavaScript("testObject.setShortValue(42);");
         assertEquals(42, mTestObject.waitForShortValue());
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 335bd75..1c7a96f 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -59,7 +59,6 @@
 #include "content/renderer/image_loading_helper.h"
 #include "content/renderer/ime_event_guard.h"
 #include "content/renderer/internal_document_state_data.h"
-#include "content/renderer/java/java_bridge_dispatcher.h"
 #include "content/renderer/media/audio_renderer_mixer_manager.h"
 #include "content/renderer/media/media_stream_dispatcher.h"
 #include "content/renderer/media/media_stream_impl.h"
@@ -128,6 +127,7 @@
 
 #include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/renderer/android/synchronous_compositor_factory.h"
+#include "content/renderer/java/gin_java_bridge_dispatcher.h"
 #include "content/renderer/media/android/renderer_media_player_manager.h"
 #include "content/renderer/media/android/stream_texture_factory_impl.h"
 #include "content/renderer/media/android/webmediaplayer_android.h"
@@ -417,7 +417,7 @@
   render_view_->RegisterRenderFrame(this);
 
 #if defined(OS_ANDROID)
-  new JavaBridgeDispatcher(this);
+  new GinJavaBridgeDispatcher(this);
 #endif
 
 #if defined(ENABLE_NOTIFICATIONS)