Do not assume WebView is running on the main thread.

Cherry pick of Change-Id: I981136571782352889684e4c3181d8cc22a1679d

WebView will run at most one UI thread per process, but that
thread is not necessarily the main thread.

If the first WebView that an app constructs is on the main thread,
then we will bind Chromium's notion of the UI thread to the main
thread. This mitigates the risk introduced by this refactoring for the vast
majority of apps that create their Views on the main thread.

In the case that the WebView is created on a background thread, we delay
binding of Chromium's notion of the UI thread until we get a clear
signal from the apps usage of WebView that it is intended to use that
background thread as an Android UI thread.

In the case that the app does not intend to use the thread it creates
the WebView on as an Android UI thread, we defer as long as possible
before we are required to run operations that require Chromium to be
started, at which point we bind to the main thread. This covers the case
that an app creates WebView on a background thread to read some state
from it (e.g. the user agent) and then later intends to use WebView
as a View on the main thread.

This change does not support:
 - Apps that create more than one Android UI thread in the same process
 and expect to use a WebView as a View on both of them.

Bug 10932261

Conflicts:
	chromium/java/com/android/webview/chromium/WebViewChromium.java

Change-Id: Ief406e30d12b201b6cac2c10c73a0aa0631ca900
diff --git a/chromium/java/com/android/webview/chromium/ContentSettingsAdapter.java b/chromium/java/com/android/webview/chromium/ContentSettingsAdapter.java
index a152046..c09a3a1 100644
--- a/chromium/java/com/android/webview/chromium/ContentSettingsAdapter.java
+++ b/chromium/java/com/android/webview/chromium/ContentSettingsAdapter.java
@@ -35,6 +35,10 @@
         mAwSettings = awSettings;
     }
 
+    AwSettings getAwSettings() {
+        return mAwSettings;
+    }
+
     @Override
     @Deprecated
     public void setNavDump(boolean enabled) {
diff --git a/chromium/java/com/android/webview/chromium/WebViewChromium.java b/chromium/java/com/android/webview/chromium/WebViewChromium.java
index 2832278..0354c98 100644
--- a/chromium/java/com/android/webview/chromium/WebViewChromium.java
+++ b/chromium/java/com/android/webview/chromium/WebViewChromium.java
@@ -26,6 +26,7 @@
 import android.net.http.SslCertificate;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Looper;
 import android.os.Message;
 import android.print.PrintDocumentAdapter;
 import android.text.TextUtils;
@@ -57,6 +58,7 @@
 import org.chromium.android_webview.AwBrowserContext;
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwLayoutSizer;
+import org.chromium.android_webview.AwSettings;
 import org.chromium.base.ThreadUtils;
 import org.chromium.content.browser.LoadUrlParams;
 import org.chromium.net.NetworkChangeNotifier;
@@ -65,10 +67,12 @@
 import java.io.File;
 import java.lang.annotation.Annotation;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Queue;
 
 /**
  * This class is the delegate to which WebViewProxy forwards all API calls.
@@ -81,6 +85,40 @@
 class WebViewChromium implements WebViewProvider,
           WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
 
+    private class WebViewChromiumRunQueue {
+        public WebViewChromiumRunQueue() {
+            mQueue = new ConcurrentLinkedQueue<Runnable>();
+        }
+
+        public void addTask(Runnable task) {
+            mQueue.add(task);
+            if (mFactory.hasStarted()) {
+                ThreadUtils.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        drainQueue();
+                    }
+                });
+            }
+        }
+
+        public void drainQueue() {
+            if (mQueue == null || mQueue.isEmpty()) {
+                return;
+            }
+
+            Runnable task = mQueue.poll();
+            while(task != null) {
+                task.run();
+                task = mQueue.poll();
+            }
+        }
+
+        private Queue<Runnable> mQueue;
+    }
+
+    private WebViewChromiumRunQueue mRunQueue;
+
     private static final String TAG = WebViewChromium.class.getSimpleName();
 
     // The WebView that this WebViewChromium is the provider for.
@@ -91,28 +129,28 @@
     private WebViewContentsClientAdapter mContentsClientAdapter;
 
     // Variables for functionality provided by this adapter ---------------------------------------
-    // WebSettings adapter, lazily initialized in the getter
-    private WebSettings mWebSettings;
+    private ContentSettingsAdapter mWebSettings;
     // The WebView wrapper for ContentViewCore and required browser compontents.
     private AwContents mAwContents;
     // Non-null if this webview is using the GL accelerated draw path.
     private DrawGLFunctor mGLfunctor;
 
-    private AwBrowserContext mBrowserContext;
-
     private final WebView.HitTestResult mHitTestResult;
 
     private final int mAppTargetSdkVersion;
 
+    private WebViewChromiumFactoryProvider mFactory;
+
     // This does not touch any global / non-threadsafe state, but note that
     // init is ofter called right after and is NOT threadsafe.
-    public WebViewChromium(WebView webView, WebView.PrivateAccess webViewPrivate,
-            AwBrowserContext browserContext) {
+    public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView,
+            WebView.PrivateAccess webViewPrivate) {
         mWebView = webView;
         mWebViewPrivate = webViewPrivate;
         mHitTestResult = new WebView.HitTestResult();
-        mBrowserContext = browserContext;
         mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion;
+        mFactory = factory;
+        mRunQueue = new WebViewChromiumRunQueue();
     }
 
     static void completeWindowCreation(WebView parent, WebView child) {
@@ -122,16 +160,19 @@
         parentContents.supplyContentsForPopup(childContents);
     }
 
-    private static <T> T runBlockingFuture(FutureTask<T> task) {
+    private <T> T runBlockingFuture(FutureTask<T> task) {
+        if (!mFactory.hasStarted()) throw new RuntimeException("Must be started before we block!");
         if (ThreadUtils.runningOnUiThread()) {
             throw new IllegalStateException("This method should only be called off the UI thread");
         }
-        ThreadUtils.postOnUiThread(task);
+        mRunQueue.addTask(task);
         try {
             return task.get(4, TimeUnit.SECONDS);
-        } catch (Exception e) { // Timeout is one of the possible exceptions here
+        } catch (java.util.concurrent.TimeoutException e) {
             throw new RuntimeException("Probable deadlock detected due to WebView API being called "
                     + "on incorrect thread while the UI thread is blocked.", e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
@@ -143,46 +184,24 @@
         runBlockingFuture(task);
     }
 
-    private static <T> T runOnUiThreadBlocking(Callable<T> c) {
+    private <T> T runOnUiThreadBlocking(Callable<T> c) {
         return runBlockingFuture(new FutureTask<T>(c));
     }
 
     // WebViewProvider methods --------------------------------------------------------------------
 
     @Override
+    // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
+    // so is ignored. TODO: remove it from WebViewProvider.
     public void init(final Map<String, Object> javaScriptInterfaces,
             final boolean privateBrowsing) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            runVoidTaskOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    init(javaScriptInterfaces, privateBrowsing);
-                }
-            });
-            return;
-        }
-        // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
-        // so is ignored. TODO: remove it from WebViewProvider.
-        final boolean isAccessFromFileURLsGrantedByDefault =
-                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
-        final boolean areLegacyQuirksEnabled =
-                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
-        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
-        mAwContents = new AwContents(mBrowserContext, mWebView, new InternalAccessAdapter(),
-                mContentsClientAdapter, isAccessFromFileURLsGrantedByDefault,
-                new AwLayoutSizer(), areLegacyQuirksEnabled);
-        mWebSettings = new ContentSettingsAdapter(mAwContents.getSettings());
-
         if (privateBrowsing) {
+            mFactory.startYourEngines(true);
             final String msg = "Private browsing is not supported in WebView.";
             if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
                 throw new IllegalArgumentException(msg);
             } else {
                 Log.w(TAG, msg);
-                // Intentionally irreversibly disable the webview instance, so that private
-                // user data cannot leak through misuse of a non-privateBrowing WebView instance.
-                // Can't just null out mAwContents as we never null-check it before use.
-                mAwContents.destroy();
                 TextView warningLabel = new TextView(mWebView.getContext());
                 warningLabel.setText(mWebView.getContext().getString(
                         com.android.internal.R.string.webviewchromium_private_browsing_warning));
@@ -190,6 +209,46 @@
             }
         }
 
+        if (!mFactory.hasStarted()) {
+            // We will defer real initialization until we know which thread to do it on, unless we
+            // are on the main thread already.
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                mFactory.startYourEngines(true);
+            }
+        }
+
+        final boolean isAccessFromFileURLsGrantedByDefault =
+                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
+        final boolean areLegacyQuirksEnabled =
+                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
+        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
+        mWebSettings = new ContentSettingsAdapter(new AwSettings(
+                mWebView.getContext(), isAccessFromFileURLsGrantedByDefault,
+                areLegacyQuirksEnabled));
+
+        mRunQueue.addTask(new Runnable() {
+                @Override
+                public void run() {
+                    initForReal();
+                    if (privateBrowsing) {
+                        // Intentionally irreversibly disable the webview instance, so that private
+                        // user data cannot leak through misuse of a non-privateBrowing WebView
+                        // instance. Can't just null out mAwContents as we never null-check it
+                        // before use.
+                        destroy();
+                    }
+                }
+        });
+    }
+
+    private void initForReal() {
+        mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView,
+                new InternalAccessAdapter(), mContentsClientAdapter, new AwLayoutSizer(),
+                mWebSettings.getAwSettings());
+    }
+
+    void startYourEngine() {
+        mRunQueue.drainQueue();
     }
 
     private RuntimeException createThreadException() {
@@ -197,6 +256,15 @@
                 "Calling View methods on another thread than the UI thread.");
     }
 
+    private boolean checkNeedsPost() {
+        boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread();
+        if (!needsPost && mAwContents == null) {
+            throw new IllegalStateException(
+                    "AwContents must be created if we are not posting!");
+        }
+        return needsPost;
+    }
+
     //  Intentionally not static, as no need to check thread on static methods
     private void checkThread() {
         if (!ThreadUtils.runningOnUiThread()) {
@@ -213,8 +281,8 @@
 
     @Override
     public void setHorizontalScrollbarOverlay(final boolean overlay) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setHorizontalScrollbarOverlay(overlay);
@@ -227,8 +295,8 @@
 
     @Override
     public void setVerticalScrollbarOverlay(final boolean overlay) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setVerticalScrollbarOverlay(overlay);
@@ -241,7 +309,8 @@
 
     @Override
     public boolean overlayHorizontalScrollbar() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -255,7 +324,8 @@
 
     @Override
     public boolean overlayVerticalScrollbar() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -275,7 +345,8 @@
 
     @Override
     public SslCertificate getCertificate() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
                 @Override
                 public SslCertificate call() {
@@ -300,8 +371,8 @@
     @Override
     public void setHttpAuthUsernamePassword(final String host, final String realm,
             final String username, final String password) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setHttpAuthUsernamePassword(host, realm, username, password);
@@ -314,7 +385,8 @@
 
     @Override
     public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
                 @Override
                 public String[] call() {
@@ -328,8 +400,8 @@
 
     @Override
     public void destroy() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     destroy();
@@ -349,8 +421,8 @@
     public void setNetworkAvailable(final boolean networkUp) {
         // Note that this purely toggles the JS navigator.online property.
         // It does not in affect chromium or network stack state in any way.
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setNetworkAvailable(networkUp);
@@ -363,7 +435,8 @@
 
     @Override
     public WebBackForwardList saveState(final Bundle outState) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
                 @Override
                 public WebBackForwardList call() {
@@ -391,7 +464,8 @@
 
     @Override
     public WebBackForwardList restoreState(final Bundle inState) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
                 @Override
                 public WebBackForwardList call() {
@@ -406,7 +480,7 @@
     }
 
     @Override
-    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
+    public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) {
         // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
 
         // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
@@ -416,8 +490,19 @@
         final String JAVASCRIPT_SCHEME = "javascript:";
         if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
                 url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
-            mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
-                    url.substring(JAVASCRIPT_SCHEME.length()));
+            mFactory.startYourEngines(true);
+            if (checkNeedsPost()) {
+                mRunQueue.addTask(new Runnable() {
+                    @Override
+                    public void run() {
+                        mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
+                                url.substring(JAVASCRIPT_SCHEME.length()));
+                    }
+                });
+            } else {
+                mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
+                        url.substring(JAVASCRIPT_SCHEME.length()));
+            }
             return;
         }
 
@@ -513,18 +598,22 @@
     }
 
     private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
-        if (ThreadUtils.runningOnUiThread()) {
-            mAwContents.loadUrl(loadUrlParams);
-        } else {
+        // This is the last point that we can delay starting the Chromium backend up
+        // and if the app has not caused us to bind the Chromium UI thread to a background thread
+        // we now bind Chromium's notion of the UI thread to the app main thread.
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             // Disallowed in WebView API for apps targetting a new SDK
             assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
-            ThreadUtils.postOnUiThread(new Runnable() {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     mAwContents.loadUrl(loadUrlParams);
                 }
             });
+            return;
         }
+        mAwContents.loadUrl(loadUrlParams);
     }
 
     public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
@@ -540,8 +629,8 @@
     @Override
     public void saveWebArchive(final String basename, final boolean autoname,
             final ValueCallback<String> callback) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     saveWebArchive(basename, autoname, callback);
@@ -554,8 +643,8 @@
 
     @Override
     public void stopLoading() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     stopLoading();
@@ -569,8 +658,8 @@
 
     @Override
     public void reload() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     reload();
@@ -583,7 +672,8 @@
 
     @Override
     public boolean canGoBack() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -597,8 +687,8 @@
 
     @Override
     public void goBack() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     goBack();
@@ -611,7 +701,8 @@
 
     @Override
     public boolean canGoForward() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -625,8 +716,8 @@
 
     @Override
     public void goForward() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     goForward();
@@ -639,7 +730,8 @@
 
     @Override
     public boolean canGoBackOrForward(final int steps) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -653,8 +745,8 @@
 
     @Override
     public void goBackOrForward(final int steps) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     goBackOrForward(steps);
@@ -673,7 +765,8 @@
 
     @Override
     public boolean pageUp(final boolean top) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -687,7 +780,8 @@
 
     @Override
     public boolean pageDown(final boolean bottom) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -701,8 +795,8 @@
 
     @Override
     public void clearView() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     clearView();
@@ -715,7 +809,8 @@
 
     @Override
     public Picture capturePicture() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
                 @Override
                 public Picture call() {
@@ -737,27 +832,20 @@
     @Override
     public float getScale() {
         // No checkThread() as it is mostly thread safe (workaround for b/10652991).
+        mFactory.startYourEngines(true);
         return mAwContents.getScale();
     }
 
     @Override
     public void setInitialScale(final int scaleInPercent) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    setInitialScale(scaleInPercent);
-                }
-            });
-            return;
-        }
-        mAwContents.getSettings().setInitialPageScale(scaleInPercent);
+        // No checkThread() as it is thread safe
+        mWebSettings.getAwSettings().setInitialPageScale(scaleInPercent);
     }
 
     @Override
     public void invokeZoomPicker() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     invokeZoomPicker();
@@ -770,7 +858,8 @@
 
     @Override
     public WebView.HitTestResult getHitTestResult() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             WebView.HitTestResult ret = runOnUiThreadBlocking(
                     new Callable<WebView.HitTestResult>() {
                 @Override
@@ -788,8 +877,8 @@
 
     @Override
     public void requestFocusNodeHref(final Message hrefMsg) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     requestFocusNodeHref(hrefMsg);
@@ -802,8 +891,8 @@
 
     @Override
     public void requestImageRef(final Message msg) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     requestImageRef(msg);
@@ -816,7 +905,8 @@
 
     @Override
     public String getUrl() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             String ret = runOnUiThreadBlocking(new Callable<String>() {
                 @Override
                 public String call() {
@@ -832,7 +922,8 @@
 
     @Override
     public String getOriginalUrl() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             String ret = runOnUiThreadBlocking(new Callable<String>() {
                 @Override
                 public String call() {
@@ -848,7 +939,8 @@
 
     @Override
     public String getTitle() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             String ret = runOnUiThreadBlocking(new Callable<String>() {
                 @Override
                 public String call() {
@@ -862,7 +954,8 @@
 
     @Override
     public Bitmap getFavicon() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() {
                 @Override
                 public Bitmap call() {
@@ -882,26 +975,29 @@
 
     @Override
     public int getProgress() {
+        if (mAwContents == null) return 100;
         // No checkThread() because the value is cached java side (workaround for b/10533304).
         return mAwContents.getMostRecentProgress();
     }
 
     @Override
     public int getContentHeight() {
+        if (mAwContents == null) return 0;
         // No checkThread() as it is mostly thread safe (workaround for b/10594869).
         return mAwContents.getContentHeightCss();
     }
 
     @Override
     public int getContentWidth() {
+        if (mAwContents == null) return 0;
         // No checkThread() as it is mostly thread safe (workaround for b/10594869).
         return mAwContents.getContentWidthCss();
     }
 
     @Override
     public void pauseTimers() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     pauseTimers();
@@ -914,8 +1010,8 @@
 
     @Override
     public void resumeTimers() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     resumeTimers();
@@ -928,8 +1024,8 @@
 
     @Override
     public void onPause() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onPause();
@@ -942,8 +1038,8 @@
 
     @Override
     public void onResume() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onResume();
@@ -956,7 +1052,8 @@
 
     @Override
     public boolean isPaused() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -975,8 +1072,8 @@
 
     @Override
     public void clearCache(final boolean includeDiskFiles) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     clearCache(includeDiskFiles);
@@ -992,8 +1089,8 @@
      */
     @Override
     public void clearFormData() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     clearFormData();
@@ -1006,8 +1103,8 @@
 
     @Override
     public void clearHistory() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     clearHistory();
@@ -1020,8 +1117,8 @@
 
     @Override
     public void clearSslPreferences() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     clearSslPreferences();
@@ -1034,7 +1131,8 @@
 
     @Override
     public WebBackForwardList copyBackForwardList() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
                 @Override
                 public WebBackForwardList call() {
@@ -1054,8 +1152,8 @@
 
     @Override
     public void findNext(final boolean forwards) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     findNext(forwards);
@@ -1074,8 +1172,8 @@
 
     @Override
     public void findAllAsync(final String searchString) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     findAllAsync(searchString);
@@ -1088,7 +1186,8 @@
 
     @Override
     public boolean showFindDialog(final String text, final boolean showIme) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             return false;
         }
         if (mWebView.getParent() == null) {
@@ -1116,8 +1215,8 @@
 
     @Override
     public void notifyFindDialogDismissed() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     notifyFindDialogDismissed();
@@ -1130,8 +1229,8 @@
 
     @Override
     public void clearMatches() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     clearMatches();
@@ -1144,8 +1243,8 @@
 
     @Override
     public void documentHasImages(final Message response) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     documentHasImages(response);
@@ -1173,8 +1272,8 @@
 
     @Override
     public void setPictureListener(final WebView.PictureListener listener) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setPictureListener(listener);
@@ -1189,8 +1288,8 @@
 
     @Override
     public void addJavascriptInterface(final Object obj, final String interfaceName) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     addJavascriptInterface(obj, interfaceName);
@@ -1207,8 +1306,8 @@
 
     @Override
     public void removeJavascriptInterface(final String interfaceName) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     removeJavascriptInterface(interfaceName);
@@ -1231,8 +1330,8 @@
 
     @Override
     public void flingScroll(final int vx, final int vy) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     flingScroll(vx, vy);
@@ -1245,7 +1344,8 @@
 
     @Override
     public View getZoomControls() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             return null;
         }
 
@@ -1257,7 +1357,7 @@
 
     @Override
     public boolean canZoomIn() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        if (checkNeedsPost()) {
             return false;
         }
         return mAwContents.canZoomIn();
@@ -1265,7 +1365,7 @@
 
     @Override
     public boolean canZoomOut() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        if (checkNeedsPost()) {
             return false;
         }
         return mAwContents.canZoomOut();
@@ -1273,7 +1373,8 @@
 
     @Override
     public boolean zoomIn() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1287,7 +1388,8 @@
 
     @Override
     public boolean zoomOut() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1331,7 +1433,8 @@
     // ViewGroup.
     // @Override
     public boolean shouldDelayChildPressedState() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1345,7 +1448,8 @@
 
 //    @Override
     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             AccessibilityNodeProvider ret = runOnUiThreadBlocking(
                     new Callable<AccessibilityNodeProvider>() {
                 @Override
@@ -1359,8 +1463,9 @@
     }
 
     @Override
-        public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
-        if (!ThreadUtils.runningOnUiThread()) {
+    public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             runVoidTaskOnUiThreadBlocking(new Runnable() {
                 @Override
                 public void run() {
@@ -1374,7 +1479,8 @@
 
     @Override
     public void onInitializeAccessibilityEvent(final AccessibilityEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             runVoidTaskOnUiThreadBlocking(new Runnable() {
                 @Override
                 public void run() {
@@ -1388,7 +1494,8 @@
 
     @Override
     public boolean performAccessibilityAction(final int action, final Bundle arguments) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1405,8 +1512,14 @@
 
     @Override
     public void setOverScrollMode(final int mode) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        // This gets called from the android.view.View c'tor that WebView inherits from. This
+        // causes the method to be called when mAwContents == null.
+        // It's safe to ignore these calls however since AwContents will read the current value of
+        // this setting when it's created.
+        if (mAwContents == null) return;
+
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setOverScrollMode(mode);
@@ -1414,19 +1527,13 @@
             });
             return;
         }
-        // This gets called from the android.view.View c'tor that WebView inherits from. This
-        // causes the method to be called when mAwContents == null.
-        // It's safe to ignore these calls however since AwContents will read the current value of
-        // this setting when it's created.
-        if (mAwContents != null) {
-            mAwContents.setOverScrollMode(mode);
-        }
+        mAwContents.setOverScrollMode(mode);
     }
 
     @Override
     public void setScrollBarStyle(final int style) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     setScrollBarStyle(style);
@@ -1440,15 +1547,6 @@
     @Override
     public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l,
             final int t, final int r, final int b) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            runVoidTaskOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
-                }
-            });
-            return;
-        }
         // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
         // WebViewChromium doesn't support that the vanilla implementation of this method can be
         // used.
@@ -1458,8 +1556,8 @@
     @Override
     public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
             final boolean clampedY) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onOverScrolled(scrollX, scrollY, clampedX, clampedY);
@@ -1472,8 +1570,8 @@
 
     @Override
     public void onWindowVisibilityChanged(final int visibility) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onWindowVisibilityChanged(visibility);
@@ -1486,7 +1584,8 @@
 
     @Override
     public void onDraw(final Canvas canvas) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(true);
+        if (checkNeedsPost()) {
             runVoidTaskOnUiThreadBlocking(new Runnable() {
                 @Override
                 public void run() {
@@ -1500,37 +1599,24 @@
 
     @Override
     public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    setLayoutParams(layoutParams);
-                }
-            });
-            return;
-        }
+        // This API is our strongest signal from the View system that this
+        // WebView is going to be bound to a View hierarchy and so at this
+        // point we must bind Chromium's UI thread to the current thread.
+        mFactory.startYourEngines(false);
+        checkThread();
         mWebViewPrivate.super_setLayoutParams(layoutParams);
     }
 
     @Override
     public boolean performLongClick() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
-                @Override
-                public Boolean call() {
-                    return performLongClick();
-                }
-            });
-            return ret;
-        }
         // Return false unless the WebView is attached to a View with a parent
         return mWebView.getParent() != null ? mWebViewPrivate.super_performLongClick() : false;
     }
 
     @Override
     public void onConfigurationChanged(final Configuration newConfig) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onConfigurationChanged(newConfig);
@@ -1543,21 +1629,17 @@
 
     @Override
     public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            InputConnection ret = runOnUiThreadBlocking(new Callable<InputConnection>() {
-                @Override
-                public InputConnection call() {
-                    return onCreateInputConnection(outAttrs);
-                }
-            });
-            return ret;
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
+           return null;
         }
         return mAwContents.onCreateInputConnection(outAttrs);
     }
 
     @Override
     public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1572,7 +1654,8 @@
 
     @Override
     public boolean onKeyDown(final int keyCode, final KeyEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1587,7 +1670,8 @@
 
     @Override
     public boolean onKeyUp(final int keyCode, final KeyEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1601,22 +1685,18 @@
 
     @Override
     public void onAttachedToWindow() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    onAttachedToWindow();
-                }
-            });
-            return;
-        }
+        // This API is our strongest signal from the View system that this
+        // WebView is going to be bound to a View hierarchy and so at this
+        // point we must bind Chromium's UI thread to the current thread.
+        mFactory.startYourEngines(false);
+        checkThread();
         mAwContents.onAttachedToWindow();
     }
 
     @Override
     public void onDetachedFromWindow() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onDetachedFromWindow();
@@ -1643,8 +1723,12 @@
 
     @Override
     public void onVisibilityChanged(final View changedView, final int visibility) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        // The AwContents will find out the container view visibility before the first draw so we
+        // can safely ignore onVisibilityChanged callbacks that happen before init().
+        if (mAwContents == null) return;
+
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onVisibilityChanged(changedView, visibility);
@@ -1652,17 +1736,13 @@
             });
             return;
         }
-        // The AwContents will find out the container view visibility before the first draw so we
-        // can safely ignore onVisibilityChanged callbacks that happen before init().
-        if (mAwContents != null) {
-            mAwContents.onVisibilityChanged(changedView, visibility);
-        }
+        mAwContents.onVisibilityChanged(changedView, visibility);
     }
 
     @Override
     public void onWindowFocusChanged(final boolean hasWindowFocus) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onWindowFocusChanged(hasWindowFocus);
@@ -1676,8 +1756,8 @@
     @Override
     public void onFocusChanged(final boolean focused, final int direction,
             final Rect previouslyFocusedRect) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onFocusChanged(focused, direction, previouslyFocusedRect);
@@ -1690,22 +1770,13 @@
 
     @Override
     public boolean setFrame(final int left, final int top, final int right, final int bottom) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
-                @Override
-                public Boolean call() {
-                    return setFrame(left, top, right, bottom);
-                }
-            });
-            return ret;
-        }
         return mWebViewPrivate.super_setFrame(left, top, right, bottom);
     }
 
     @Override
     public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        if (checkNeedsPost()) {
+            mRunQueue.addTask(new Runnable() {
                 @Override
                 public void run() {
                     onSizeChanged(w, h, ow, oh);
@@ -1722,7 +1793,8 @@
 
     @Override
     public boolean dispatchKeyEvent(final KeyEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1736,7 +1808,8 @@
 
     @Override
     public boolean onTouchEvent(final MotionEvent ev) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1750,7 +1823,8 @@
 
     @Override
     public boolean onHoverEvent(final MotionEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1764,7 +1838,8 @@
 
     @Override
     public boolean onGenericMotionEvent(final MotionEvent event) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1784,7 +1859,8 @@
 
     @Override
     public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1799,8 +1875,9 @@
 
     @Override
     public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
+            runVoidTaskOnUiThreadBlocking(new Runnable() {
                 @Override
                 public void run() {
                     onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -1814,7 +1891,8 @@
     @Override
     public boolean requestChildRectangleOnScreen(final View child, final Rect rect,
             final boolean immediate) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
@@ -1828,7 +1906,8 @@
 
     @Override
     public void setBackgroundColor(final int color) {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             ThreadUtils.postOnUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -1855,7 +1934,8 @@
 
     @Override
     public int computeHorizontalScrollRange() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             int ret = runOnUiThreadBlocking(new Callable<Integer>() {
                 @Override
                 public Integer call() {
@@ -1869,7 +1949,8 @@
 
     @Override
     public int computeHorizontalScrollOffset() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             int ret = runOnUiThreadBlocking(new Callable<Integer>() {
                 @Override
                 public Integer call() {
@@ -1883,7 +1964,8 @@
 
     @Override
     public int computeVerticalScrollRange() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             int ret = runOnUiThreadBlocking(new Callable<Integer>() {
                 @Override
                 public Integer call() {
@@ -1897,7 +1979,8 @@
 
     @Override
     public int computeVerticalScrollOffset() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             int ret = runOnUiThreadBlocking(new Callable<Integer>() {
                 @Override
                 public Integer call() {
@@ -1911,7 +1994,8 @@
 
     @Override
     public int computeVerticalScrollExtent() {
-        if (!ThreadUtils.runningOnUiThread()) {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
             int ret = runOnUiThreadBlocking(new Callable<Integer>() {
                 @Override
                 public Integer call() {
@@ -1925,8 +2009,9 @@
 
     @Override
     public void computeScroll() {
-        if (!ThreadUtils.runningOnUiThread()) {
-            ThreadUtils.postOnUiThread(new Runnable() {
+        mFactory.startYourEngines(false);
+        if (checkNeedsPost()) {
+            runVoidTaskOnUiThreadBlocking(new Runnable() {
                 @Override
                 public void run() {
                     computeScroll();
diff --git a/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 6245ebf..07404e3 100644
--- a/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -23,6 +23,7 @@
 import android.content.SharedPreferences;
 import android.os.Build;
 import android.os.Looper;
+import android.util.Log;
 import android.webkit.CookieManager;
 import android.webkit.GeolocationPermissions;
 import android.webkit.WebIconDatabase;
@@ -50,6 +51,9 @@
 import org.chromium.content.common.CommandLine;
 import org.chromium.content.common.ProcessInitException;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
 public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
 
     private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs";
@@ -69,6 +73,9 @@
     private WebViewDatabaseAdapter mWebViewDatabase;
     private AwDevToolsServer mDevToolsServer;
 
+    private ArrayList<WeakReference<WebViewChromium>> mWebViewsToStart =
+              new ArrayList<WeakReference<WebViewChromium>>();
+
     // Read/write protected by mLock.
     private boolean mStarted;
 
@@ -77,6 +84,7 @@
         AwBrowserProcess.loadLibrary();
         // Load glue-layer support library.
         System.loadLibrary("webviewchromium_plat_support");
+        ThreadUtils.setWillOverrideUiThread();
     }
 
     private void initPlatSupportLibrary() {
@@ -85,13 +93,18 @@
         AwContents.setAwDrawGLFunctionTable(GraphicsUtils.getDrawGLFunctionTable());
     }
 
-    private void ensureChromiumStartedLocked() {
+    private void ensureChromiumStartedLocked(boolean onMainThread) {
         assert Thread.holdsLock(mLock);
 
         if (mStarted) {  // Early-out for the common case.
             return;
         }
 
+        Looper looper = !onMainThread ? Looper.myLooper() : Looper.getMainLooper();
+        Log.v("WebViewChromium", "Binding Chromium to the " +
+                (onMainThread ? "main":"background") + " looper " + looper);
+        ThreadUtils.setUiThread(looper);
+
         if (ThreadUtils.runningOnUiThread()) {
             startChromiumLocked();
             return;
@@ -171,8 +184,16 @@
         if (Build.IS_DEBUGGABLE) {
             setWebContentsDebuggingEnabled(true);
         }
-
         mStarted = true;
+
+        for (WeakReference<WebViewChromium> wvc : mWebViewsToStart) {
+            WebViewChromium w = wvc.get();
+            if (w != null) {
+                w.startYourEngine();
+            }
+        }
+        mWebViewsToStart.clear();
+        mWebViewsToStart = null;
     }
 
     @Override
@@ -182,7 +203,7 @@
                 // TODO: Optimization potential: most these methods only need the native library
                 // loaded and initialized, not the entire browser process started.
                 // See also http://b/7009882
-                ensureChromiumStartedLocked();
+                ensureChromiumStartedLocked(true);
                 mStaticMethods = new WebViewFactoryProvider.Statics() {
                     @Override
                     public String findAddress(String addr) {
@@ -223,23 +244,33 @@
 
     @Override
     public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
-        assert Looper.myLooper() == Looper.getMainLooper();
-        AwBrowserContext browserContext;
+        WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess);
+
         synchronized (mLock) {
-            ensureChromiumStartedLocked();
-            ResourceProvider.registerResources(webView.getContext());
-            browserContext = getBrowserContextLocked();
+            if (mWebViewsToStart != null) {
+                mWebViewsToStart.add(new WeakReference<WebViewChromium>(wvc));
+            }
         }
-        // Make sure GeolocationPermissions is created before creating a webview
-        getGeolocationPermissions();
-        return new WebViewChromium(webView, privateAccess, browserContext);
+        ResourceProvider.registerResources(webView.getContext());
+        return wvc;
+    }
+
+    boolean hasStarted() {
+        return mStarted;
+    }
+
+    void startYourEngines(boolean onMainThread) {
+        synchronized (mLock) {
+            ensureChromiumStartedLocked(onMainThread);
+
+        }
     }
 
     @Override
     public GeolocationPermissions getGeolocationPermissions() {
         synchronized (mLock) {
             if (mGeolocationPermissions == null) {
-                ensureChromiumStartedLocked();
+                ensureChromiumStartedLocked(true);
                 mGeolocationPermissions = new GeolocationPermissionsAdapter(
                         getBrowserContextLocked().getGeolocationPermissions());
             }
@@ -247,6 +278,12 @@
         return mGeolocationPermissions;
     }
 
+    AwBrowserContext getBrowserContext() {
+        synchronized (mLock) {
+            return getBrowserContextLocked();
+        }
+    }
+
     private AwBrowserContext getBrowserContextLocked() {
         assert Thread.holdsLock(mLock);
         assert mStarted;
@@ -262,7 +299,7 @@
     public CookieManager getCookieManager() {
         synchronized (mLock) {
             if (mCookieManager == null) {
-                ensureChromiumStartedLocked();
+                ensureChromiumStartedLocked(true);
                 mCookieManager = new CookieManagerAdapter(new AwCookieManager());
             }
         }
@@ -273,7 +310,7 @@
     public WebIconDatabase getWebIconDatabase() {
         synchronized (mLock) {
             if (mWebIconDatabase == null) {
-                ensureChromiumStartedLocked();
+                ensureChromiumStartedLocked(true);
                 mWebIconDatabase = new WebIconDatabaseAdapter();
             }
         }
@@ -284,7 +321,7 @@
     public WebStorage getWebStorage() {
         synchronized (mLock) {
             if (mWebStorage == null) {
-                ensureChromiumStartedLocked();
+                ensureChromiumStartedLocked(true);
                 mWebStorage = new WebStorageAdapter(AwQuotaManagerBridge.getInstance());
             }
         }
@@ -295,7 +332,7 @@
     public WebViewDatabase getWebViewDatabase(Context context) {
         synchronized (mLock) {
             if (mWebViewDatabase == null) {
-                ensureChromiumStartedLocked();
+                ensureChromiumStartedLocked(true);
                 AwBrowserContext browserContext = getBrowserContextLocked();
                 mWebViewDatabase = new WebViewDatabaseAdapter(
                         browserContext.getFormDatabase(),
@@ -306,9 +343,9 @@
     }
 
     private void setWebContentsDebuggingEnabled(boolean enable) {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
+        if (Looper.myLooper() != ThreadUtils.getUiThreadLooper()) {
             throw new RuntimeException(
-                    "Toggling of Web Contents Debugging must be done on the main thread");
+                    "Toggling of Web Contents Debugging must be done on the UI thread");
         }
         if (mDevToolsServer == null) {
             if (!enable) return;
diff --git a/chromium/java/com/android/webview/chromium/WebViewContentsClientAdapter.java b/chromium/java/com/android/webview/chromium/WebViewContentsClientAdapter.java
index 2554ddf..ba5b1e4 100644
--- a/chromium/java/com/android/webview/chromium/WebViewContentsClientAdapter.java
+++ b/chromium/java/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -52,6 +52,7 @@
 import org.chromium.android_webview.InterceptedRequestData;
 import org.chromium.android_webview.JsPromptResultReceiver;
 import org.chromium.android_webview.JsResultReceiver;
+import org.chromium.base.ThreadUtils;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.ContentViewClient;
 import org.chromium.content.common.TraceEvent;
@@ -459,7 +460,7 @@
         // until onPageFinished has been invoked. This work is being done
         // upstream, and we can revert this hack when it lands.
         if (mPictureListener != null) {
-            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
+            ThreadUtils.postOnUiThreadDelayed(new Runnable() {
                 @Override
                 public void run() {
                     UnimplementedWebViewApi.invoke();