# WebView Java Bridge (WebView#addJavascriptInterface()) [TOC] ## Overview This page explains ideas behind the Java ↔ JavaScript bridge implementation. This is to ensure that important use cases and scenarios, which must be preserved regardless of how the bridge is implemented, are captured. The need for this description arose while migrating the NPAPI-based implementation to a [Gin](/gin/)-based implementation. Although a vast number of unit tests already existed, they still didn't cover all important aspects of the bridge behavior and we had to add some new tests to ensure we are preserving compatibility. The Gin implementation was introduced in Chromium M37 (initial Android Lollipop release), with the threading issue fixed in M39 (L MR1). ## The API An API for embedders is exposed on [android.webkit.WebView](https://developer.android.com/reference/android/webkit/WebView.html) class: - [public void **addJavascriptInterface**(Object **object**, String **name**)](https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)) -- injects a Java **object** into a WebView under the given **name**; - [public void **removeJavascriptInterface**(String **name**)](https://developer.android.com/reference/android/webkit/WebView.html#removeJavascriptInterface(java.lang.String)) -- removes an object previously injected under the **name**. Important notes as defined by the API: - adding or removing an injected object is not reflected on the JavaScript side until the next page load; - only methods annotated as [`@JavascriptInterface`](https://developer.android.com/reference/android/webkit/JavascriptInterface.html) are exposed to JavaScript code; Java object fields are never exposed; - methods of Java objects are invoked on a private, background thread of WebView; this effectively means, that the interaction originated by the page must be served entirely on the background thread, while the main application thread (browser UI thread) is blocked; Argument and return values conversions are handled after [Sun Live Connect 2 spec](https://www.oracle.com/java/technologies/javase/liveconnect-docs.html). In fact, there are lots of deviations from it (probably, to preserve compatibility with earlier WebView versions). What can pass the boundary between VMs is somewhat limited. This is what is allowed: - primitive values; - single-dimentional arrays; - "array-like" JavaScript objects (possessing "length" property, and also typed arrays from ES6); - previously injected Java objects (from JS to Java); - new Java objects (from Java to JS), those are "injected" into JavaScript as if one called **addJavascriptInterface**, but w/o providing a name; also, the lifecycle of such transient objects is different (see below). ## Objects Lifecycle The purpose of Java bridge is to establish interaction between two virtual machines (VMs): Java and JavaScript. Both VMs employ a similar approach to managing objects lifetime—VMs gather and dispose unreferenced objects during garbage collection (GC) cycles. The twist that Java bridge adds is that objects in one VM can now virtually reference (and prevent from being disposed) objects from another VM. Let us consider the following Java code: ```Java // in Java webView.addJavascriptInterface(new MyObject(), "myObject"); ``` The instantiated MyObject is now being virtually hold by its JavaScript counterpart, and is not garbage-collected by Java VM despite the fact that there are no explicit references to it on the Java side. The MyObject instance is kept referenced until the action of the **addJavascriptInterface** call is cancelled by a call to **removeJavascriptInterface**: ```Java // in Java webView.removeJavascriptInterface("myObject"); ``` A more interesting situation is with transient objects returned from methods of an injected Java object. Consider the following example: ```Java // in Java class MyObject { class Handler { } @JavascriptInterface public Object getHandler() { return new Handler(); } } ``` Again, the object returned from **`getHandler`** method is not explicitly referenced on the Java side, albeit it should not be disposed until it is in use on the JavaScript side. The "in use" period is determined by the lifetime of the JavaScript interface object that has been implicitly created as a result of a call to **getHandler** from JavaScript. That means, the instance of Handler on the Java side should be kept alive during the period while the corresponding JavaScript interface object is still referenced: ```JavaScript // in JavaScript { ... let handler = myObject.getHandler(); ... } ``` The following figure illustrates relationships between Java and JavaScript objects created in the previous examples: ![relationship between Java and JavaScript objects](images/java_bridge/relationship_java_js_objects.png) Note that Java and JavaScript VMs are absolutely independent and unaware of each other's existence. They can work in different processes and, in theory, even on different physical machines. Thus, the depicted references from JavaScript objects to Java objects are virtual—they don't exist directly. Instead, it is the Java bridge who holds the Java objects for as long as it is needed. We would like to depict that, but first we need to consider the whole picture. So far, we were thinking about Java bridge in abstract terms. But in fact, it is used in the context of a WebView-based application. The Java side of the bridge is tightly coupled to an instance of WebView class, while bridge's JavaScript side is bound to a HTML rendering engine. This is further complicated by the facts that in the Chromium architecture renderers are isolated from their controlling entities, and that Chromium is mainly implemented in C++, but needs to interact with Android framework which is implemented in Java. Thus, if we want to depict the architecture of Java bridge, we also need to include parts of the Chromium framework that are glued to Java bridge: ![Java bridge architecture](images/java_bridge/java_bridge_architecture.png) The figure is now much scarier. Let's figure out what is what here: - In Java VM (browser side): **WebView** is android.webkit.WebView class. It is exposed to the embedder and interacts with Chromium rendering machinery. WebView owns a retaining set (**`Set`**) that holds all injected objects to prevent their collection. Note that WebView class manages a C++ object called **WebContents** (in fact, the relationship is more complex, but these details are not relevant for us). As Java Bridge implementation is in C++, the retaining set is actually managed by the C++ side, but objects from the native side do not hold any strong references to it, as that would create a cyclic reference and will prevent the WebView instance from being collected. - On the C++ browser side: Here we have the aforementioned **WebContents** object which delegates Java Bridge-related requests to **JavaBridgeHost**. WebContents talks to the objects on the renderer side via Chromium's IPC mechanism. - On the C++ renderer side: **RenderFrame** corresponds to a single HTML frame and it "owns" a JavaScript global context object (aka **window**). For each JavaScript interface object, a corresponding **JavaBridgeObject** instance is maintained. In Chromium terminology, this object is called "wrapper". In the Gin-based implementation, wrappers don't hold strong references to their corresponding JavaScript interface objects, also to prevent memory leaks due to cycles of references. Wrappers receive a notification from the JavaScript VM after the corresponding JavaScript objects has been garbage-collected. The diagram above misses one more important detail. WebView can load a complex HTML document consisting of several frames (typically inserted using