Refactor WebView to be a thin proxy class

Splits interface and implementation; all client calls are forwarded
to an abstract WebViewProvider interface, and the existing implementation
moved into the WebViewClassic implementor of this interface.

Originally taken from a snapshot from the development branch, by:
git diff HEAD 9a4c328a54cc05e5 | git apply
- but then rebased to keep up to date with master

Interdepends on webkit and Browser changes:
https://android-git.corp.google.com/g/158979
https://android-git.corp.google.com/g/167911

Change-Id: I91403f32654ff308934e95c832d17b292a7d9b2e
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index db66305..11bd815 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -79,8 +79,8 @@
     private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
         new ArrayList<AccessibilityWebContentKeyBinding>();
 
-    // handle to the WebView this injector is associated with.
-    private final WebView mWebView;
+    // handle to the WebViewClassic this injector is associated with.
+    private final WebViewClassic mWebView;
 
     // events scheduled for sending as soon as we receive the selected text
     private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>();
@@ -98,11 +98,11 @@
     private int mLastDirection;
 
     /**
-     * Creates a new injector associated with a given {@link WebView}.
+     * Creates a new injector associated with a given {@link WebViewClassic}.
      *
-     * @param webView The associated WebView.
+     * @param webView The associated WebViewClassic.
      */
-    public AccessibilityInjector(WebView webView) {
+    public AccessibilityInjector(WebViewClassic webView) {
         mWebView = webView;
         ensureWebContentKeyBindings();
     }
@@ -327,7 +327,7 @@
         AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED);
         event.setClassName(mWebView.getClass().getName());
         event.setPackageName(mWebView.getContext().getPackageName());
-        event.setEnabled(mWebView.isEnabled());
+        event.setEnabled(mWebView.getWebView().isEnabled());
         return event;
     }
 
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 8ccc59c7..f09e29d 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -70,7 +70,7 @@
     private final static int MAX_OUTSTANDING_REQUESTS = 300;
 
     private final CallbackProxy mCallbackProxy;
-    private final WebSettings mSettings;
+    private final WebSettingsClassic mSettings;
     private final Context mContext;
     private final WebViewDatabase mDatabase;
     private final WebViewCore mWebViewCore;
@@ -200,7 +200,7 @@
      * XXX: Called by WebCore thread.
      */
     public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
-            WebSettings settings, Map<String, Object> javascriptInterfaces) {
+            WebSettingsClassic settings, Map<String, Object> javascriptInterfaces) {
 
         Context appContext = context.getApplicationContext();
 
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 3a05bca..484c449 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -61,8 +61,8 @@
     private volatile WebViewClient mWebViewClient;
     // Instance of WebChromeClient for handling all chrome functions.
     private volatile WebChromeClient mWebChromeClient;
-    // Instance of WebView for handling UI requests.
-    private final WebView mWebView;
+    // Instance of WebViewClassic for handling UI requests.
+    private final WebViewClassic mWebView;
     // Client registered callback listener for download events
     private volatile DownloadListener mDownloadListener;
     // Keep track of multiple progress updates.
@@ -148,7 +148,7 @@
     /**
      * Construct a new CallbackProxy.
      */
-    public CallbackProxy(Context context, WebView w) {
+    public CallbackProxy(Context context, WebViewClassic w) {
         // Used to start a default activity.
         mContext = context;
         mWebView = w;
@@ -221,7 +221,7 @@
         }
         boolean override = false;
         if (mWebViewClient != null) {
-            override = mWebViewClient.shouldOverrideUrlLoading(mWebView,
+            override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(),
                     overrideUrl);
         } else {
             Intent intent = new Intent(Intent.ACTION_VIEW,
@@ -248,7 +248,7 @@
      */
     public boolean uiOverrideKeyEvent(KeyEvent event) {
         if (mWebViewClient != null) {
-            return mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
+            return mWebViewClient.shouldOverrideKeyEvent(mWebView.getWebView(), event);
         }
         return false;
     }
@@ -264,7 +264,8 @@
                 String startedUrl = msg.getData().getString("url");
                 mWebView.onPageStarted(startedUrl);
                 if (mWebViewClient != null) {
-                    mWebViewClient.onPageStarted(mWebView, startedUrl, (Bitmap) msg.obj);
+                    mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl,
+                            (Bitmap) msg.obj);
                 }
                 break;
 
@@ -272,26 +273,26 @@
                 String finishedUrl = (String) msg.obj;
                 mWebView.onPageFinished(finishedUrl);
                 if (mWebViewClient != null) {
-                    mWebViewClient.onPageFinished(mWebView, finishedUrl);
+                    mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl);
                 }
                 break;
 
             case RECEIVED_ICON:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onReceivedIcon(mWebView, (Bitmap) msg.obj);
+                    mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj);
                 }
                 break;
 
             case RECEIVED_TOUCH_ICON_URL:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onReceivedTouchIconUrl(mWebView,
+                    mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(),
                             (String) msg.obj, msg.arg1 == 1);
                 }
                 break;
 
             case RECEIVED_TITLE:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onReceivedTitle(mWebView,
+                    mWebChromeClient.onReceivedTitle(mWebView.getWebView(),
                             (String) msg.obj);
                 }
                 break;
@@ -301,7 +302,7 @@
                     int reasonCode = msg.arg1;
                     final String description  = msg.getData().getString("description");
                     final String failUrl  = msg.getData().getString("failingUrl");
-                    mWebViewClient.onReceivedError(mWebView, reasonCode,
+                    mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode,
                             description, failUrl);
                 }
                 break;
@@ -312,7 +313,7 @@
                 Message dontResend =
                         (Message) msg.getData().getParcelable("dontResend");
                 if (mWebViewClient != null) {
-                    mWebViewClient.onFormResubmission(mWebView, dontResend,
+                    mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend,
                             resend);
                 } else {
                     dontResend.sendToTarget();
@@ -335,7 +336,7 @@
                     HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
                     String host = msg.getData().getString("host");
                     String realm = msg.getData().getString("realm");
-                    mWebViewClient.onReceivedHttpAuthRequest(mWebView, handler,
+                    mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler,
                             host, realm);
                 }
                 break;
@@ -344,7 +345,7 @@
                 if (mWebViewClient != null) {
                     HashMap<String, Object> map =
                         (HashMap<String, Object>) msg.obj;
-                    mWebViewClient.onReceivedSslError(mWebView,
+                    mWebViewClient.onReceivedSslError(mWebView.getWebView(),
                             (SslErrorHandler) map.get("handler"),
                             (SslError) map.get("error"));
                 }
@@ -352,7 +353,7 @@
 
             case PROCEEDED_AFTER_SSL_ERROR:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onProceededAfterSslError(mWebView,
+                    mWebViewClient.onProceededAfterSslError(mWebView.getWebView(),
                             (SslError) msg.obj);
                 }
                 break;
@@ -361,7 +362,7 @@
                 if (mWebViewClient != null) {
                     HashMap<String, Object> map =
                         (HashMap<String, Object>) msg.obj;
-                    mWebViewClient.onReceivedClientCertRequest(mWebView,
+                    mWebViewClient.onReceivedClientCertRequest(mWebView.getWebView(),
                             (ClientCertRequestHandler) map.get("handler"),
                             (String) map.get("host_and_port"));
                 }
@@ -373,7 +374,7 @@
                 // changed.
                 synchronized (this) {
                     if (mWebChromeClient != null) {
-                        mWebChromeClient.onProgressChanged(mWebView,
+                        mWebChromeClient.onProgressChanged(mWebView.getWebView(),
                                 mLatestProgress);
                     }
                     mProgressUpdatePending = false;
@@ -382,14 +383,14 @@
 
             case UPDATE_VISITED:
                 if (mWebViewClient != null) {
-                    mWebViewClient.doUpdateVisitedHistory(mWebView,
+                    mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(),
                             (String) msg.obj, msg.arg1 != 0);
                 }
                 break;
 
             case LOAD_RESOURCE:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onLoadResource(mWebView, (String) msg.obj);
+                    mWebViewClient.onLoadResource(mWebView.getWebView(), (String) msg.obj);
                 }
                 break;
 
@@ -409,7 +410,7 @@
 
             case CREATE_WINDOW:
                 if (mWebChromeClient != null) {
-                    if (!mWebChromeClient.onCreateWindow(mWebView,
+                    if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(),
                                 msg.arg1 == 1, msg.arg2 == 1,
                                 (Message) msg.obj)) {
                         synchronized (this) {
@@ -422,13 +423,13 @@
 
             case REQUEST_FOCUS:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onRequestFocus(mWebView);
+                    mWebChromeClient.onRequestFocus(mWebView.getWebView());
                 }
                 break;
 
             case CLOSE_WINDOW:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onCloseWindow((WebView) msg.obj);
+                    mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView());
                 }
                 break;
 
@@ -449,7 +450,7 @@
 
             case ASYNC_KEYEVENTS:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onUnhandledKeyEvent(mWebView,
+                    mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(),
                             (KeyEvent) msg.obj);
                 }
                 break;
@@ -516,7 +517,7 @@
                     final JsResult res = (JsResult) msg.obj;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsAlert(mWebView, url, message,
+                    if (!mWebChromeClient.onJsAlert(mWebView.getWebView(), url, message,
                             res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -552,7 +553,7 @@
                     final JsResult res = (JsResult) msg.obj;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsConfirm(mWebView, url, message,
+                    if (!mWebChromeClient.onJsConfirm(mWebView.getWebView(), url, message,
                             res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -597,7 +598,7 @@
                     String message = msg.getData().getString("message");
                     String defaultVal = msg.getData().getString("default");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsPrompt(mWebView, url, message,
+                    if (!mWebChromeClient.onJsPrompt(mWebView.getWebView(), url, message,
                                 defaultVal, res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -653,7 +654,7 @@
                     final JsResult res = (JsResult) msg.obj;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsBeforeUnload(mWebView, url,
+                    if (!mWebChromeClient.onJsBeforeUnload(mWebView.getWebView(), url,
                             message, res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -710,7 +711,7 @@
 
             case SCALE_CHANGED:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onScaleChanged(mWebView, msg.getData()
+                    mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData()
                             .getFloat("old"), msg.getData().getFloat("new"));
                 }
                 break;
@@ -817,7 +818,7 @@
                     String realm = msg.getData().getString("realm");
                     String account = msg.getData().getString("account");
                     String args = msg.getData().getString("args");
-                    mWebViewClient.onReceivedLoginRequest(mWebView, realm,
+                    mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm,
                             account, args);
                 }
                 break;
@@ -1074,7 +1075,7 @@
         }
         // Note: This method does _not_ send a message.
         WebResourceResponse r =
-                mWebViewClient.shouldInterceptRequest(mWebView, url);
+                mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url);
         if (r == null) {
             sendMessage(obtainMessage(LOAD_RESOURCE, url));
         }
@@ -1219,7 +1220,8 @@
             return null;
         }
 
-        WebView.WebViewTransport transport = mWebView.new WebViewTransport();
+        WebView.WebViewTransport transport =
+            mWebView.getWebView().new WebViewTransport();
         final Message msg = obtainMessage(NOTIFY);
         msg.obj = transport;
         synchronized (this) {
@@ -1234,7 +1236,7 @@
             }
         }
 
-        WebView w = transport.getWebView();
+        WebViewClassic w = WebViewClassic.fromWebView(transport.getWebView());
         if (w != null) {
             WebViewCore core = w.getWebViewCore();
             // If WebView.destroy() has been called, core may be null.  Skip
@@ -1257,7 +1259,7 @@
         sendEmptyMessage(REQUEST_FOCUS);
     }
 
-    public void onCloseWindow(WebView window) {
+    public void onCloseWindow(WebViewClassic window) {
         // Do an unsynchronized quick check to avoid posting if no callback has
         // been set.
         if (mWebChromeClient == null) {
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index 10b0885..964cf3e 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -38,7 +38,7 @@
     private View mCustomView;
     private EditText mEditText;
     private TextView mMatches;
-    private WebView mWebView;
+    private WebViewClassic mWebView;
     private InputMethodManager mInput;
     private Resources mResources;
     private boolean mMatchesFound;
@@ -90,7 +90,7 @@
      * Set the WebView to search.  Must be non null, and set before calling
      * startActionMode.
      */
-    void setWebView(WebView webView) {
+    void setWebView(WebViewClassic webView) {
         if (null == webView) {
             throw new AssertionError("WebView supplied to "
                     + "FindActionModeCallback cannot be null");
@@ -218,7 +218,7 @@
     public void onDestroyActionMode(ActionMode mode) {
         mActionMode = null;
         mWebView.notifyFindDialogDismissed();
-        mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+        mInput.hideSoftInputFromWindow(mWebView.getWebView().getWindowToken(), 0);
     }
 
     @Override
@@ -232,7 +232,7 @@
             throw new AssertionError(
                     "No WebView for FindActionModeCallback::onActionItemClicked");
         }
-        mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+        mInput.hideSoftInputFromWindow(mWebView.getWebView().getWindowToken(), 0);
         switch(item.getItemId()) {
             case com.android.internal.R.id.find_prev:
                 findNext(false);
diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java
index 91de1d8..225053b 100755
--- a/core/java/android/webkit/GeolocationService.java
+++ b/core/java/android/webkit/GeolocationService.java
@@ -24,7 +24,6 @@
 import android.location.LocationProvider;
 import android.os.Bundle;
 import android.util.Log;
-import android.webkit.WebView;
 import android.webkit.WebViewCore;
 
 
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index aedecf0..8e1f573 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -92,7 +92,7 @@
     private class IsPrivateBrowsingEnabledGetter {
         private boolean mIsReady;
         private boolean mIsPrivateBrowsingEnabled;
-        IsPrivateBrowsingEnabledGetter(Looper uiThreadLooper, final WebView webView) {
+        IsPrivateBrowsingEnabledGetter(Looper uiThreadLooper, final WebViewClassic webView) {
             new Handler(uiThreadLooper).post(new Runnable() {
                 @Override
                 public void run() {
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index bc0557e..fac549d 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -236,7 +236,7 @@
 
     @Override
     public void enterFullScreenVideoState(int layerId,
-            HTML5VideoViewProxy proxy, WebView webView) {
+            HTML5VideoViewProxy proxy, WebViewClassic webView) {
         mFullScreenMode = FULLSCREEN_SURFACECREATING;
         mCurrentBufferPercentage = 0;
         mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 73166cb..0d3b755 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -280,7 +280,7 @@
     // screen mode. Some are specific to one type, but currently are called
     // directly from the proxy.
     public void enterFullScreenVideoState(int layerId,
-            HTML5VideoViewProxy proxy, WebView webView) {
+            HTML5VideoViewProxy proxy, WebViewClassic webView) {
     }
 
     public boolean isFullScreenMode() {
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index d306c86..1644b06 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -75,8 +75,8 @@
     int mNativePointer;
     // The handler for WebCore thread messages;
     private Handler mWebCoreHandler;
-    // The WebView instance that created this view.
-    private WebView mWebView;
+    // The WebViewClassic instance that created this view.
+    private WebViewClassic mWebView;
     // The poster image to be shown when the video is not playing.
     // This ref prevents the bitmap from being GC'ed.
     private Bitmap mPoster;
@@ -142,7 +142,7 @@
         }
 
         public static void enterFullScreenVideo(int layerId, String url,
-                HTML5VideoViewProxy proxy, WebView webView) {
+                HTML5VideoViewProxy proxy, WebViewClassic webView) {
                 // Save the inline video info and inherit it in the full screen
                 int savePosition = 0;
                 if (mHTML5VideoView != null) {
@@ -163,7 +163,7 @@
         }
 
         public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
-                WebView webView) {
+                WebViewClassic webView) {
             if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
                 WebChromeClient client = webView.getWebChromeClient();
                 if (client != null) {
@@ -551,7 +551,7 @@
      * @param webView is the WebView that hosts the video.
      * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
      */
-    private HTML5VideoViewProxy(WebView webView, int nativePtr) {
+    private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr) {
         // This handler is for the main (UI) thread.
         super(Looper.getMainLooper());
         // Save the WebView object.
@@ -721,7 +721,7 @@
         return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
     }
 
-    /* package */ WebView getWebView() {
+    /* package */ WebViewClassic getWebView() {
         return mWebView;
     }
 
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index b498435..e6eaa14 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -43,10 +43,10 @@
     private boolean mTimerPaused;
     private boolean mHasDeferredTimers;
 
-    // keep track of the main WebView attached to the current window so that we
+    // keep track of the main WebViewClassic attached to the current window so that we
     // can get the proper Context.
-    private static WeakReference<WebView> sCurrentMainWebView =
-            new WeakReference<WebView>(null);
+    private static WeakReference<WebViewClassic> sCurrentMainWebView =
+            new WeakReference<WebViewClassic>(null);
 
     /* package */
     static final int REFRESH_PLUGINS = 100;
@@ -67,15 +67,15 @@
         nativeFinalize();
     }
 
-    static synchronized void setActiveWebView(WebView webview) {
+    static synchronized void setActiveWebView(WebViewClassic webview) {
         if (sCurrentMainWebView.get() != null) {
             // it is possible if there is a sub-WebView. Do nothing.
             return;
         }
-        sCurrentMainWebView = new WeakReference<WebView>(webview);
+        sCurrentMainWebView = new WeakReference<WebViewClassic>(webview);
     }
 
-    static synchronized void removeActiveWebView(WebView webview) {
+    static synchronized void removeActiveWebView(WebViewClassic webview) {
         if (sCurrentMainWebView.get() != webview) {
             // it is possible if there is a sub-WebView. Do nothing.
             return;
@@ -259,7 +259,7 @@
 
     synchronized private String getSignedPublicKey(int index, String challenge,
             String url) {
-        WebView current = sCurrentMainWebView.get();
+        WebViewClassic current = sCurrentMainWebView.get();
         if (current != null) {
             // generateKeyPair expects organizations which we don't have. Ignore
             // url.
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
index e906f7f..d91f860 100644
--- a/core/java/android/webkit/OverScrollGlow.java
+++ b/core/java/android/webkit/OverScrollGlow.java
@@ -29,7 +29,7 @@
  * @hide
  */
 public class OverScrollGlow {
-    private WebView mHostView;
+    private WebViewClassic mHostView;
 
     private EdgeEffect mEdgeGlowTop;
     private EdgeEffect mEdgeGlowBottom;
@@ -39,7 +39,7 @@
     private int mOverScrollDeltaX;
     private int mOverScrollDeltaY;
 
-    public OverScrollGlow(WebView host) {
+    public OverScrollGlow(WebViewClassic host) {
         mHostView = host;
         Context context = host.getContext();
         mEdgeGlowTop = new EdgeEffect(context);
@@ -80,7 +80,7 @@
                 mOverScrollDeltaX = 0;
             }
 
-            if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+            if (maxY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
                 final int pulledToY = oldY + mOverScrollDeltaY;
                 if (pulledToY < 0) {
                     mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
@@ -120,7 +120,7 @@
      * @param rangeY Maximum range for vertical scrolling
      */
     public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) {
-        if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+        if (rangeY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
             if (y < 0 && oldY >= 0) {
                 mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
                 if (!mEdgeGlowBottom.isFinished()) {
diff --git a/core/java/android/webkit/PluginFullScreenHolder.java b/core/java/android/webkit/PluginFullScreenHolder.java
index 42ba7c9..665cd9d 100644
--- a/core/java/android/webkit/PluginFullScreenHolder.java
+++ b/core/java/android/webkit/PluginFullScreenHolder.java
@@ -35,7 +35,7 @@
 
 class PluginFullScreenHolder {
 
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
     private final int mNpp;
     private final int mOrientation;
 
@@ -44,7 +44,7 @@
 
     private View mContentView;
 
-    PluginFullScreenHolder(WebView webView, int orientation, int npp) {
+    PluginFullScreenHolder(WebViewClassic webView, int orientation, int npp) {
         mWebView = webView;
         mNpp = npp;
         mOrientation = orientation;
@@ -134,7 +134,7 @@
         new WebChromeClient.CustomViewCallback() {
             public void onCustomViewHidden() {
 
-                mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
+                mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
                     .sendToTarget();
 
                 mWebView.getWebViewCore().sendMessage(
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
index ab3b6d5..fe40156 100644
--- a/core/java/android/webkit/PluginManager.java
+++ b/core/java/android/webkit/PluginManager.java
@@ -34,7 +34,7 @@
 import android.util.Log;
 
 /**
- * Class for managing the relationship between the {@link WebView} and installed
+ * Class for managing the relationship between the {@link WebViewClassic} and installed
  * plugins in the system. You can find this class through
  * {@link PluginManager#getInstance}.
  * 
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 2a770f5..57628d3 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -26,11 +26,11 @@
 import android.view.MenuItem;
 
 class SelectActionModeCallback implements ActionMode.Callback {
-    private WebView mWebView;
+    private WebViewClassic mWebView;
     private ActionMode mActionMode;
     private boolean mIsTextSelected = true;
 
-    void setWebView(WebView webView) {
+    void setWebView(WebViewClassic webView) {
         mWebView = webView;
     }
 
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
index 153c1c2..34065a1 100644
--- a/core/java/android/webkit/ViewManager.java
+++ b/core/java/android/webkit/ViewManager.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.util.DisplayMetrics;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -24,7 +25,7 @@
 import java.util.ArrayList;
 
 class ViewManager {
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
     private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>();
     private boolean mHidden;
     private boolean mReadyToDraw;
@@ -74,7 +75,7 @@
         }
 
         private void attachViewOnUIThread() {
-            mWebView.addView(mView);
+            mWebView.getWebView().addView(mView);
             mChildren.add(this);
             if (!mReadyToDraw) {
                 mView.setVisibility(View.GONE);
@@ -93,16 +94,15 @@
         }
 
         private void removeViewOnUIThread() {
-            mWebView.removeView(mView);
+            mWebView.getWebView().removeView(mView);
             mChildren.remove(this);
         }
     }
 
-    ViewManager(WebView w) {
+    ViewManager(WebViewClassic w) {
         mWebView = w;
-
-        int pixelArea = w.getResources().getDisplayMetrics().widthPixels *
-                        w.getResources().getDisplayMetrics().heightPixels;
+        DisplayMetrics metrics = w.getWebView().getResources().getDisplayMetrics();
+        int pixelArea = metrics.widthPixels * metrics.heightPixels;
         /* set the threshold to be 275% larger than the screen size. The
            percentage is simply an estimation and is not based on anything but
            basic trial-and-error tests run on multiple devices.
diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java
index 5f91ed3..a22fc26 100644
--- a/core/java/android/webkit/ViewStateSerializer.java
+++ b/core/java/android/webkit/ViewStateSerializer.java
@@ -34,7 +34,7 @@
 
     static final int VERSION = 1;
 
-    static boolean serializeViewState(OutputStream stream, WebView web)
+    static boolean serializeViewState(OutputStream stream, WebViewClassic web)
             throws IOException {
         int baseLayer = web.getBaseLayer();
         if (baseLayer == 0) {
@@ -48,7 +48,7 @@
                 new byte[WORKING_STREAM_STORAGE]);
     }
 
-    static DrawData deserializeViewState(InputStream stream, WebView web)
+    static DrawData deserializeViewState(InputStream stream, WebViewClassic web)
             throws IOException {
         DataInputStream dis = new DataInputStream(stream);
         int version = dis.readInt();
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index c463b40..cddd7ab 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -16,16 +16,7 @@
 
 package android.webkit;
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Handler;
 import android.os.Message;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-
-import java.util.Locale;
 
 /**
  * Manages settings state for a WebView. When a WebView is first created, it
@@ -35,7 +26,18 @@
  * been destroyed, any method call on WebSettings will throw an
  * IllegalStateException.
  */
+// This is (effectively) an abstract base class; concrete WebViewProviders must
+// create a class derived from this, and return an instance of it in the
+// WebViewProvider.getWebSettingsProvider() method implementation.
 public class WebSettings {
+    // TODO: Remove MustOverrideException and make all methods throwing it abstract instead;
+    // needs API file update.
+    private static class MustOverrideException extends RuntimeException {
+        MustOverrideException() {
+            super("abstract function called: must be overriden!");
+        }
+    }
+
     /**
      * Enum for controlling the layout of html.
      * NORMAL means no rendering changes.
@@ -141,379 +143,12 @@
         OFF
     }
 
-    // TODO: Keep this up to date
-    private static final String PREVIOUS_VERSION = "4.0.3";
-
-    // WebView associated with this WebSettings.
-    private WebView mWebView;
-    // BrowserFrame used to access the native frame pointer.
-    private BrowserFrame mBrowserFrame;
-    // Flag to prevent multiple SYNC messages at one time.
-    private boolean mSyncPending = false;
-    // Custom handler that queues messages until the WebCore thread is active.
-    private final EventHandler mEventHandler;
-
-    // Private settings so we don't have to go into native code to
-    // retrieve the values. After setXXX, postSync() needs to be called.
-    //
-    // The default values need to match those in WebSettings.cpp
-    // If the defaults change, please also update the JavaDocs so developers
-    // know what they are.
-    private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS;
-    private Context         mContext;
-    private int             mTextSize = 100;
-    private String          mStandardFontFamily = "sans-serif";
-    private String          mFixedFontFamily = "monospace";
-    private String          mSansSerifFontFamily = "sans-serif";
-    private String          mSerifFontFamily = "serif";
-    private String          mCursiveFontFamily = "cursive";
-    private String          mFantasyFontFamily = "fantasy";
-    private String          mDefaultTextEncoding;
-    private String          mUserAgent;
-    private boolean         mUseDefaultUserAgent;
-    private String          mAcceptLanguage;
-    private int             mMinimumFontSize = 8;
-    private int             mMinimumLogicalFontSize = 8;
-    private int             mDefaultFontSize = 16;
-    private int             mDefaultFixedFontSize = 13;
-    private int             mPageCacheCapacity = 0;
-    private boolean         mLoadsImagesAutomatically = true;
-    private boolean         mBlockNetworkImage = false;
-    private boolean         mBlockNetworkLoads;
-    private boolean         mJavaScriptEnabled = false;
-    private boolean         mHardwareAccelSkia = false;
-    private boolean         mShowVisualIndicator = false;
-    private PluginState     mPluginState = PluginState.OFF;
-    private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
-    private boolean         mUseDoubleTree = false;
-    private boolean         mUseWideViewport = false;
-    private boolean         mSupportMultipleWindows = false;
-    private boolean         mShrinksStandaloneImagesToFit = false;
-    private long            mMaximumDecodedImageSize = 0; // 0 means default
-    private boolean         mPrivateBrowsingEnabled = false;
-    private boolean         mSyntheticLinksEnabled = true;
-    // HTML5 API flags
-    private boolean         mAppCacheEnabled = false;
-    private boolean         mDatabaseEnabled = false;
-    private boolean         mDomStorageEnabled = false;
-    private boolean         mWorkersEnabled = false;  // only affects V8.
-    private boolean         mGeolocationEnabled = true;
-    private boolean         mXSSAuditorEnabled = false;
-    // HTML5 configuration parameters
-    private long            mAppCacheMaxSize = Long.MAX_VALUE;
-    private String          mAppCachePath = null;
-    private String          mDatabasePath = "";
-    // The WebCore DatabaseTracker only allows the database path to be set
-    // once. Keep track of when the path has been set.
-    private boolean         mDatabasePathHasBeenSet = false;
-    private String          mGeolocationDatabasePath = "";
-    // Don't need to synchronize the get/set methods as they
-    // are basic types, also none of these values are used in
-    // native WebCore code.
-    private ZoomDensity     mDefaultZoom = ZoomDensity.MEDIUM;
-    private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
-    private int             mOverrideCacheMode = LOAD_DEFAULT;
-    private int             mDoubleTapZoom = 100;
-    private boolean         mSaveFormData = true;
-    private boolean         mAutoFillEnabled = false;
-    private boolean         mSavePassword = true;
-    private boolean         mLightTouchEnabled = false;
-    private boolean         mNeedInitialFocus = true;
-    private boolean         mNavDump = false;
-    private boolean         mSupportZoom = true;
-    private boolean         mBuiltInZoomControls = false;
-    private boolean         mDisplayZoomControls = true;
-    private boolean         mAllowFileAccess = true;
-    private boolean         mAllowContentAccess = true;
-    private boolean         mLoadWithOverviewMode = false;
-    private boolean         mEnableSmoothTransition = false;
-    private boolean         mForceUserScalable = false;
-
-    // AutoFill Profile data
     /**
-     * @hide for now, pending API council approval.
+     * Hidden constructor to prevent clients from creating a new settings
+     * instance or deriving the class.
+     * @hide
      */
-    public static class AutoFillProfile {
-        private int mUniqueId;
-        private String mFullName;
-        private String mEmailAddress;
-        private String mCompanyName;
-        private String mAddressLine1;
-        private String mAddressLine2;
-        private String mCity;
-        private String mState;
-        private String mZipCode;
-        private String mCountry;
-        private String mPhoneNumber;
-
-        public AutoFillProfile(int uniqueId, String fullName, String email,
-                String companyName, String addressLine1, String addressLine2,
-                String city, String state, String zipCode, String country,
-                String phoneNumber) {
-            mUniqueId = uniqueId;
-            mFullName = fullName;
-            mEmailAddress = email;
-            mCompanyName = companyName;
-            mAddressLine1 = addressLine1;
-            mAddressLine2 = addressLine2;
-            mCity = city;
-            mState = state;
-            mZipCode = zipCode;
-            mCountry = country;
-            mPhoneNumber = phoneNumber;
-        }
-
-        public int getUniqueId() { return mUniqueId; }
-        public String getFullName() { return mFullName; }
-        public String getEmailAddress() { return mEmailAddress; }
-        public String getCompanyName() { return mCompanyName; }
-        public String getAddressLine1() { return mAddressLine1; }
-        public String getAddressLine2() { return mAddressLine2; }
-        public String getCity() { return mCity; }
-        public String getState() { return mState; }
-        public String getZipCode() { return mZipCode; }
-        public String getCountry() { return mCountry; }
-        public String getPhoneNumber() { return mPhoneNumber; }
-    }
-
-
-    private AutoFillProfile mAutoFillProfile;
-
-    private boolean         mUseWebViewBackgroundForOverscroll = true;
-
-    // private WebSettings, not accessible by the host activity
-    static private int      mDoubleTapToastCount = 3;
-
-    private static final String PREF_FILE = "WebViewSettings";
-    private static final String DOUBLE_TAP_TOAST_COUNT = "double_tap_toast_count";
-
-    // Class to handle messages before WebCore is ready.
-    private class EventHandler {
-        // Message id for syncing
-        static final int SYNC = 0;
-        // Message id for setting priority
-        static final int PRIORITY = 1;
-        // Message id for writing double-tap toast count
-        static final int SET_DOUBLE_TAP_TOAST_COUNT = 2;
-        // Actual WebCore thread handler
-        private Handler mHandler;
-
-        private synchronized void createHandler() {
-            // as mRenderPriority can be set before thread is running, sync up
-            setRenderPriority();
-
-            // create a new handler
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    switch (msg.what) {
-                        case SYNC:
-                            synchronized (WebSettings.this) {
-                                if (mBrowserFrame.mNativeFrame != 0) {
-                                    nativeSync(mBrowserFrame.mNativeFrame);
-                                }
-                                mSyncPending = false;
-                            }
-                            break;
-
-                        case PRIORITY: {
-                            setRenderPriority();
-                            break;
-                        }
-
-                        case SET_DOUBLE_TAP_TOAST_COUNT: {
-                            SharedPreferences.Editor editor = mContext
-                                    .getSharedPreferences(PREF_FILE,
-                                            Context.MODE_PRIVATE).edit();
-                            editor.putInt(DOUBLE_TAP_TOAST_COUNT,
-                                    mDoubleTapToastCount);
-                            editor.commit();
-                            break;
-                        }
-                    }
-                }
-            };
-        }
-
-        private void setRenderPriority() {
-            synchronized (WebSettings.this) {
-                if (mRenderPriority == RenderPriority.NORMAL) {
-                    android.os.Process.setThreadPriority(
-                            android.os.Process.THREAD_PRIORITY_DEFAULT);
-                } else if (mRenderPriority == RenderPriority.HIGH) {
-                    android.os.Process.setThreadPriority(
-                            android.os.Process.THREAD_PRIORITY_FOREGROUND +
-                            android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
-                } else if (mRenderPriority == RenderPriority.LOW) {
-                    android.os.Process.setThreadPriority(
-                            android.os.Process.THREAD_PRIORITY_BACKGROUND);
-                }
-            }
-        }
-
-        /**
-         * Send a message to the private queue or handler.
-         */
-        private synchronized boolean sendMessage(Message msg) {
-            if (mHandler != null) {
-                mHandler.sendMessage(msg);
-                return true;
-            } else {
-                return false;
-            }
-        }
-    }
-
-    // User agent strings.
-    private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
-        "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
-        "Chrome/11.0.696.34 Safari/534.24";
-    private static final String IPHONE_USERAGENT =
-            "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
-            + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
-            + " Mobile/7A341 Safari/528.16";
-    private static Locale sLocale;
-    private static Object sLockForLocaleSettings;
-
-    /**
-     * Package constructor to prevent clients from creating a new settings
-     * instance.
-     */
-    WebSettings(Context context, WebView webview) {
-        mEventHandler = new EventHandler();
-        mContext = context;
-        mWebView = webview;
-        mDefaultTextEncoding = context.getString(com.android.internal.
-                                                 R.string.default_text_encoding);
-
-        if (sLockForLocaleSettings == null) {
-            sLockForLocaleSettings = new Object();
-            sLocale = Locale.getDefault();
-        }
-        mAcceptLanguage = getCurrentAcceptLanguage();
-        mUserAgent = getCurrentUserAgent();
-        mUseDefaultUserAgent = true;
-
-        mBlockNetworkLoads = mContext.checkPermission(
-                "android.permission.INTERNET", android.os.Process.myPid(),
-                android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED;
-    }
-
-    private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
-
-    /**
-     * Looks at sLocale and returns current AcceptLanguage String.
-     * @return Current AcceptLanguage String.
-     */
-    private String getCurrentAcceptLanguage() {
-        Locale locale;
-        synchronized(sLockForLocaleSettings) {
-            locale = sLocale;
-        }
-        StringBuilder buffer = new StringBuilder();
-        addLocaleToHttpAcceptLanguage(buffer, locale);
-
-        if (!Locale.US.equals(locale)) {
-            if (buffer.length() > 0) {
-                buffer.append(", ");
-            }
-            buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
-        }
-
-        return buffer.toString();
-    }
-
-    /**
-     * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
-     * to new standard.
-     */
-    private static String convertObsoleteLanguageCodeToNew(String langCode) {
-        if (langCode == null) {
-            return null;
-        }
-        if ("iw".equals(langCode)) {
-            // Hebrew
-            return "he";
-        } else if ("in".equals(langCode)) {
-            // Indonesian
-            return "id";
-        } else if ("ji".equals(langCode)) {
-            // Yiddish
-            return "yi";
-        }
-        return langCode;
-    }
-
-    private static void addLocaleToHttpAcceptLanguage(StringBuilder builder,
-                                                      Locale locale) {
-        String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
-        if (language != null) {
-            builder.append(language);
-            String country = locale.getCountry();
-            if (country != null) {
-                builder.append("-");
-                builder.append(country);
-            }
-        }
-    }
-
-    /**
-     * Looks at sLocale and mContext and returns current UserAgent String.
-     * @return Current UserAgent String.
-     */
-    private synchronized String getCurrentUserAgent() {
-        Locale locale;
-        synchronized(sLockForLocaleSettings) {
-            locale = sLocale;
-        }
-        StringBuffer buffer = new StringBuffer();
-        // Add version
-        final String version = Build.VERSION.RELEASE;
-        if (version.length() > 0) {
-            if (Character.isDigit(version.charAt(0))) {
-                // Release is a version, eg "3.1"
-                buffer.append(version);
-            } else {
-                // Release is a codename, eg "Honeycomb"
-                // In this case, use the previous release's version
-                buffer.append(PREVIOUS_VERSION);
-            }
-        } else {
-            // default to "1.0"
-            buffer.append("1.0");
-        }
-        buffer.append("; ");
-        final String language = locale.getLanguage();
-        if (language != null) {
-            buffer.append(convertObsoleteLanguageCodeToNew(language));
-            final String country = locale.getCountry();
-            if (country != null) {
-                buffer.append("-");
-                buffer.append(country.toLowerCase());
-            }
-        } else {
-            // default to "en"
-            buffer.append("en");
-        }
-        buffer.append(";");
-        // add the model for the release build
-        if ("REL".equals(Build.VERSION.CODENAME)) {
-            final String model = Build.MODEL;
-            if (model.length() > 0) {
-                buffer.append(" ");
-                buffer.append(model);
-            }
-        }
-        final String id = Build.ID;
-        if (id.length() > 0) {
-            buffer.append(" Build/");
-            buffer.append(id);
-        }
-        String mobile = mContext.getResources().getText(
-            com.android.internal.R.string.web_user_agent_target_content).toString();
-        final String base = mContext.getResources().getText(
-                com.android.internal.R.string.web_user_agent).toString();
-        return String.format(base, buffer, mobile);
+    protected WebSettings() {
     }
 
     /**
@@ -522,7 +157,7 @@
      */
     @Deprecated
     public void setNavDump(boolean enabled) {
-        mNavDump = enabled;
+        throw new MustOverrideException();
     }
 
     /**
@@ -531,37 +166,35 @@
      */
     @Deprecated
     public boolean getNavDump() {
-        return mNavDump;
+        throw new MustOverrideException();
     }
 
     /**
      * Set whether the WebView supports zoom
      */
     public void setSupportZoom(boolean support) {
-        mSupportZoom = support;
-        mWebView.updateMultiTouchSupport(mContext);
+        throw new MustOverrideException();
     }
 
     /**
      * Returns whether the WebView supports zoom
      */
     public boolean supportZoom() {
-        return mSupportZoom;
+        throw new MustOverrideException();
     }
 
     /**
      * Sets whether the zoom mechanism built into WebView is used.
      */
     public void setBuiltInZoomControls(boolean enabled) {
-        mBuiltInZoomControls = enabled;
-        mWebView.updateMultiTouchSupport(mContext);
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if the zoom mechanism built into WebView is being used.
      */
     public boolean getBuiltInZoomControls() {
-        return mBuiltInZoomControls;
+        throw new MustOverrideException();
     }
 
     /**
@@ -571,15 +204,14 @@
      * to work without the on screen controls
      */
     public void setDisplayZoomControls(boolean enabled) {
-        mDisplayZoomControls = enabled;
-        mWebView.updateMultiTouchSupport(mContext);
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if the on screen zoom buttons are being used.
      */
     public boolean getDisplayZoomControls() {
-        return mDisplayZoomControls;
+        throw new MustOverrideException();
     }
 
     /**
@@ -589,14 +221,14 @@
      * file:///android_res.
      */
     public void setAllowFileAccess(boolean allow) {
-        mAllowFileAccess = allow;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if this WebView supports file access.
      */
     public boolean getAllowFileAccess() {
-        return mAllowFileAccess;
+        throw new MustOverrideException();
     }
 
     /**
@@ -605,28 +237,28 @@
      * system.  The default is enabled.
      */
     public void setAllowContentAccess(boolean allow) {
-        mAllowContentAccess = allow;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if this WebView supports content url access.
      */
     public boolean getAllowContentAccess() {
-        return mAllowContentAccess;
+        throw new MustOverrideException();
     }
 
     /**
      * Set whether the WebView loads a page with overview mode.
      */
     public void setLoadWithOverviewMode(boolean overview) {
-        mLoadWithOverviewMode = overview;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if this WebView loads page with overview mode
      */
     public boolean getLoadWithOverviewMode() {
-        return mLoadWithOverviewMode;
+        throw new MustOverrideException();
     }
 
     /**
@@ -637,15 +269,14 @@
      * If it is false, WebView will keep its fidelity. The default value is false.
      */
     public void setEnableSmoothTransition(boolean enable) {
-        mEnableSmoothTransition = enable;
+        throw new MustOverrideException();
     }
-
     /**
      * Returns true if the WebView enables smooth transition while panning or
      * zooming.
      */
     public boolean enableSmoothTransition() {
-        return mEnableSmoothTransition;
+        throw new MustOverrideException();
     }
 
     /**
@@ -656,7 +287,7 @@
      */
     @Deprecated
     public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
-        mUseWebViewBackgroundForOverscroll = view;
+        throw new MustOverrideException();
     }
 
     /**
@@ -666,14 +297,14 @@
      */
     @Deprecated
     public boolean getUseWebViewBackgroundForOverscrollBackground() {
-        return mUseWebViewBackgroundForOverscroll;
+        throw new MustOverrideException();
     }
 
     /**
      * Store whether the WebView is saving form data.
      */
     public void setSaveFormData(boolean save) {
-        mSaveFormData = save;
+        throw new MustOverrideException();
     }
 
     /**
@@ -681,21 +312,21 @@
      *  entries/autofill++.  Always false in private browsing mode.
      */
     public boolean getSaveFormData() {
-        return mSaveFormData && !mPrivateBrowsingEnabled;
+        throw new MustOverrideException();
     }
 
     /**
      *  Store whether the WebView is saving password.
      */
     public void setSavePassword(boolean save) {
-        mSavePassword = save;
+        throw new MustOverrideException();
     }
 
     /**
      *  Return whether the WebView is saving password.
      */
     public boolean getSavePassword() {
-        return mSavePassword;
+        throw new MustOverrideException();
     }
 
     /**
@@ -703,14 +334,7 @@
      * @param textZoom A percent value for increasing or decreasing the text.
      */
     public synchronized void setTextZoom(int textZoom) {
-        if (mTextSize != textZoom) {
-            if (WebView.mLogEvent) {
-                EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE,
-                        mTextSize, textZoom);
-            }
-            mTextSize = textZoom;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -719,7 +343,7 @@
      * @see setTextSizeZoom
      */
     public synchronized int getTextZoom() {
-        return mTextSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -729,7 +353,7 @@
      * @deprecated Use {@link #setTextZoom(int)} instead
      */
     public synchronized void setTextSize(TextSize t) {
-        setTextZoom(t.value);
+        throw new MustOverrideException();
     }
 
     /**
@@ -741,40 +365,7 @@
      * @deprecated Use {@link #getTextZoom()} instead
      */
     public synchronized TextSize getTextSize() {
-        TextSize closestSize = null;
-        int smallestDelta = Integer.MAX_VALUE;
-        for (TextSize size : TextSize.values()) {
-            int delta = Math.abs(mTextSize - size.value);
-            if (delta == 0) {
-                return size;
-            }
-            if (delta < smallestDelta) {
-                smallestDelta = delta;
-                closestSize = size;
-            }
-        }
-        return closestSize != null ? closestSize : TextSize.NORMAL;
-    }
-
-    /**
-     * Set the double-tap zoom of the page in percent. Default is 100.
-     * @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom.
-     * @hide
-     */
-    public void setDoubleTapZoom(int doubleTapZoom) {
-        if (mDoubleTapZoom != doubleTapZoom) {
-            mDoubleTapZoom = doubleTapZoom;
-            mWebView.updateDoubleTapZoom(doubleTapZoom);
-        }
-    }
-
-    /**
-     * Get the double-tap zoom of the page in percent.
-     * @return A percent value describing the double-tap zoom.
-     * @hide
-     */
-    public int getDoubleTapZoom() {
-        return mDoubleTapZoom;
+        throw new MustOverrideException();
     }
 
     /**
@@ -784,10 +375,7 @@
      * @see WebSettings.ZoomDensity
      */
     public void setDefaultZoom(ZoomDensity zoom) {
-        if (mDefaultZoom != zoom) {
-            mDefaultZoom = zoom;
-            mWebView.adjustDefaultZoomDensity(zoom.value);
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -797,21 +385,21 @@
      * @see WebSettings.ZoomDensity
      */
     public ZoomDensity getDefaultZoom() {
-        return mDefaultZoom;
+        throw new MustOverrideException();
     }
 
     /**
      * Enables using light touches to make a selection and activate mouseovers.
      */
     public void setLightTouchEnabled(boolean enabled) {
-        mLightTouchEnabled = enabled;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if light touches are enabled.
      */
     public boolean getLightTouchEnabled() {
-        return mLightTouchEnabled;
+        throw new MustOverrideException();
     }
 
     /**
@@ -820,7 +408,7 @@
      */
     @Deprecated
     public synchronized void setUseDoubleTree(boolean use) {
-        return;
+        // Specified to do nothing, so no need for derived classes to override.
     }
 
     /**
@@ -829,6 +417,7 @@
      */
     @Deprecated
     public synchronized boolean getUseDoubleTree() {
+        // Returns false unconditionally, so no need for derived classes to override.
         return false;
     }
 
@@ -841,23 +430,7 @@
      */
     @Deprecated
     public synchronized void setUserAgent(int ua) {
-        String uaString = null;
-        if (ua == 1) {
-            if (DESKTOP_USERAGENT.equals(mUserAgent)) {
-                return; // do nothing
-            } else {
-                uaString = DESKTOP_USERAGENT;
-            }
-        } else if (ua == 2) {
-            if (IPHONE_USERAGENT.equals(mUserAgent)) {
-                return; // do nothing
-            } else {
-                uaString = IPHONE_USERAGENT;
-            }
-        } else if (ua != 0) {
-            return; // do nothing
-        }
-        setUserAgentString(uaString);
+        throw new MustOverrideException();
     }
 
     /**
@@ -870,31 +443,21 @@
      */
     @Deprecated
     public synchronized int getUserAgent() {
-        if (DESKTOP_USERAGENT.equals(mUserAgent)) {
-            return 1;
-        } else if (IPHONE_USERAGENT.equals(mUserAgent)) {
-            return 2;
-        } else if (mUseDefaultUserAgent) {
-            return 0;
-        }
-        return -1;
+        throw new MustOverrideException();
     }
 
     /**
      * Tell the WebView to use the wide viewport
      */
     public synchronized void setUseWideViewPort(boolean use) {
-        if (mUseWideViewport != use) {
-            mUseWideViewport = use;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
      * @return True if the WebView is using a wide viewport
      */
     public synchronized boolean getUseWideViewPort() {
-        return mUseWideViewport;
+        throw new MustOverrideException();
     }
 
     /**
@@ -903,10 +466,7 @@
      *         boolean, Message)} is implemented by the host application.
      */
     public synchronized void setSupportMultipleWindows(boolean support) {
-        if (mSupportMultipleWindows != support) {
-            mSupportMultipleWindows = support;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -915,7 +475,7 @@
      *         boolean, Message)} is implemented by the host application.
      */
     public synchronized boolean supportMultipleWindows() {
-        return mSupportMultipleWindows;
+        throw new MustOverrideException();
     }
 
     /**
@@ -925,12 +485,7 @@
      * @see WebSettings.LayoutAlgorithm
      */
     public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
-        // XXX: This will only be affective if libwebcore was built with
-        // ANDROID_LAYOUT defined.
-        if (mLayoutAlgorithm != l) {
-            mLayoutAlgorithm = l;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -940,7 +495,7 @@
      * @see WebSettings.LayoutAlgorithm
      */
     public synchronized LayoutAlgorithm getLayoutAlgorithm() {
-        return mLayoutAlgorithm;
+        throw new MustOverrideException();
     }
 
     /**
@@ -948,10 +503,7 @@
      * @param font A font family name.
      */
     public synchronized void setStandardFontFamily(String font) {
-        if (font != null && !font.equals(mStandardFontFamily)) {
-            mStandardFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -959,7 +511,7 @@
      * @return The standard font family name as a string.
      */
     public synchronized String getStandardFontFamily() {
-        return mStandardFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -967,10 +519,7 @@
      * @param font A font family name.
      */
     public synchronized void setFixedFontFamily(String font) {
-        if (font != null && !font.equals(mFixedFontFamily)) {
-            mFixedFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -978,7 +527,7 @@
      * @return The fixed font family name as a string.
      */
     public synchronized String getFixedFontFamily() {
-        return mFixedFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -986,10 +535,7 @@
      * @param font A font family name.
      */
     public synchronized void setSansSerifFontFamily(String font) {
-        if (font != null && !font.equals(mSansSerifFontFamily)) {
-            mSansSerifFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -997,7 +543,7 @@
      * @return The sans-serif font family name as a string.
      */
     public synchronized String getSansSerifFontFamily() {
-        return mSansSerifFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1005,10 +551,7 @@
      * @param font A font family name.
      */
     public synchronized void setSerifFontFamily(String font) {
-        if (font != null && !font.equals(mSerifFontFamily)) {
-            mSerifFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1016,7 +559,7 @@
      * @return The serif font family name as a string.
      */
     public synchronized String getSerifFontFamily() {
-        return mSerifFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1024,10 +567,7 @@
      * @param font A font family name.
      */
     public synchronized void setCursiveFontFamily(String font) {
-        if (font != null && !font.equals(mCursiveFontFamily)) {
-            mCursiveFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1035,7 +575,7 @@
      * @return The cursive font family name as a string.
      */
     public synchronized String getCursiveFontFamily() {
-        return mCursiveFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1043,10 +583,7 @@
      * @param font A font family name.
      */
     public synchronized void setFantasyFontFamily(String font) {
-        if (font != null && !font.equals(mFantasyFontFamily)) {
-            mFantasyFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1054,7 +591,7 @@
      * @return The fantasy font family name as a string.
      */
     public synchronized String getFantasyFontFamily() {
-        return mFantasyFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1063,11 +600,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setMinimumFontSize(int size) {
-        size = pin(size);
-        if (mMinimumFontSize != size) {
-            mMinimumFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1075,7 +608,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getMinimumFontSize() {
-        return mMinimumFontSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1084,11 +617,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setMinimumLogicalFontSize(int size) {
-        size = pin(size);
-        if (mMinimumLogicalFontSize != size) {
-            mMinimumLogicalFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1096,7 +625,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getMinimumLogicalFontSize() {
-        return mMinimumLogicalFontSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1105,11 +634,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setDefaultFontSize(int size) {
-        size = pin(size);
-        if (mDefaultFontSize != size) {
-            mDefaultFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1117,7 +642,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getDefaultFontSize() {
-        return mDefaultFontSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1126,11 +651,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setDefaultFixedFontSize(int size) {
-        size = pin(size);
-        if (mDefaultFixedFontSize != size) {
-            mDefaultFixedFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1138,21 +659,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getDefaultFixedFontSize() {
-        return mDefaultFixedFontSize;
-    }
-
-    /**
-     * Set the number of pages cached by the WebKit for the history navigation.
-     * @param size A non-negative integer between 0 (no cache) and 20 (max).
-     * @hide
-     */
-    public synchronized void setPageCacheCapacity(int size) {
-        if (size < 0) size = 0;
-        if (size > 20) size = 20;
-        if (mPageCacheCapacity != size) {
-            mPageCacheCapacity = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1165,10 +672,7 @@
      * @param flag Whether the WebView should load image resources.
      */
     public synchronized void setLoadsImagesAutomatically(boolean flag) {
-        if (mLoadsImagesAutomatically != flag) {
-            mLoadsImagesAutomatically = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1177,7 +681,7 @@
      * @return True if the WebView loads image resources.
      */
     public synchronized boolean getLoadsImagesAutomatically() {
-        return mLoadsImagesAutomatically;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1195,10 +699,7 @@
      * @see #setBlockNetworkLoads
      */
     public synchronized void setBlockNetworkImage(boolean flag) {
-        if (mBlockNetworkImage != flag) {
-            mBlockNetworkImage = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1207,7 +708,7 @@
      * @return True if the WebView does not load image resources from the network.
      */
     public synchronized boolean getBlockNetworkImage() {
-        return mBlockNetworkImage;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1226,11 +727,7 @@
      * @see android.webkit.WebView#reload
      */
     public synchronized void setBlockNetworkLoads(boolean flag) {
-        if (mBlockNetworkLoads != flag) {
-            mBlockNetworkLoads = flag;
-            verifyNetworkAccess();
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1241,20 +738,7 @@
      * @return True if the WebView does not load any resources from the network.
      */
     public synchronized boolean getBlockNetworkLoads() {
-        return mBlockNetworkLoads;
-    }
-
-
-    private void verifyNetworkAccess() {
-        if (!mBlockNetworkLoads) {
-            if (mContext.checkPermission("android.permission.INTERNET",
-                    android.os.Process.myPid(), android.os.Process.myUid()) !=
-                        PackageManager.PERMISSION_GRANTED) {
-                throw new SecurityException
-                        ("Permission denied - " +
-                                "application missing INTERNET permission");
-            }
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1262,50 +746,7 @@
      * @param flag True if the WebView should execute javascript.
      */
     public synchronized void setJavaScriptEnabled(boolean flag) {
-        if (mJavaScriptEnabled != flag) {
-            mJavaScriptEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * Tell the WebView to use Skia's hardware accelerated rendering path
-     * @param flag True if the WebView should use Skia's hw-accel path
-     * @hide
-     */
-    public synchronized void setHardwareAccelSkiaEnabled(boolean flag) {
-        if (mHardwareAccelSkia != flag) {
-            mHardwareAccelSkia = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @return True if the WebView is using hardware accelerated skia
-     * @hide
-     */
-    public synchronized boolean getHardwareAccelSkiaEnabled() {
-        return mHardwareAccelSkia;
-    }
-
-    /**
-     * Tell the WebView to show the visual indicator
-     * @param flag True if the WebView should show the visual indicator
-     * @hide
-     */
-    public synchronized void setShowVisualIndicator(boolean flag) {
-        if (mShowVisualIndicator != flag) {
-            mShowVisualIndicator = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @return True if the WebView is showing the visual indicator
-     * @hide
-     */
-    public synchronized boolean getShowVisualIndicator() {
-        return mShowVisualIndicator;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1316,7 +757,7 @@
      */
     @Deprecated
     public synchronized void setPluginsEnabled(boolean flag) {
-        setPluginState(flag ? PluginState.ON : PluginState.OFF);
+        throw new MustOverrideException();
     }
 
     /**
@@ -1327,10 +768,7 @@
      * @param state One of the PluginState values.
      */
     public synchronized void setPluginState(PluginState state) {
-        if (mPluginState != state) {
-            mPluginState = state;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1342,6 +780,7 @@
      */
     @Deprecated
     public synchronized void setPluginsPath(String pluginsPath) {
+        // Specified to do nothing, so no need for derived classes to override.
     }
 
     /**
@@ -1352,11 +791,7 @@
      *     be saved. May be the empty string but should never be null.
      */
     public synchronized void setDatabasePath(String databasePath) {
-        if (databasePath != null && !mDatabasePathHasBeenSet) {
-            mDatabasePath = databasePath;
-            mDatabasePathHasBeenSet = true;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1367,40 +802,26 @@
      *     should never be null.
      */
     public synchronized void setGeolocationDatabasePath(String databasePath) {
-        if (databasePath != null
-                && !databasePath.equals(mGeolocationDatabasePath)) {
-            mGeolocationDatabasePath = databasePath;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Enable or disable the Application Cache API.
-     * @param flag Whether to enable the Application Cache API.
+     * Tell the WebView to enable Application Caches API.
+     * @param flag True if the WebView should enable Application Caches.
      */
     public synchronized void setAppCacheEnabled(boolean flag) {
-        if (mAppCacheEnabled != flag) {
-            mAppCacheEnabled = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Set the path used by the Application Cache API to store files. This
-     * setting is applied to all WebViews in the application. In order for the
-     * Application Cache API to function, this method must be called with a
-     * path which exists and is writable by the application. This method may
-     * only be called once: repeated calls are ignored.
-     * @param path Path to the directory that should be used to store Application
-     * Cache files.
+     * Set a custom path to the Application Caches files. The client
+     * must ensure it exists before this call.
+     * @param appCachePath String path to the directory containing Application
+     * Caches files. The appCache path can be the empty string but should not
+     * be null. Passing null for this parameter will result in a no-op.
      */
-    public synchronized void setAppCachePath(String path) {
-        // We test for a valid path and for repeated setting on the native
-        // side, but we can avoid syncing in some simple cases. 
-        if (mAppCachePath == null && path != null && !path.isEmpty()) {
-            mAppCachePath = path;
-            postSync();
-        }
+    public synchronized void setAppCachePath(String appCachePath) {
+        throw new MustOverrideException();
     }
 
     /**
@@ -1408,10 +829,7 @@
      * @param appCacheMaxSize the maximum size in bytes.
      */
     public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
-        if (appCacheMaxSize != mAppCacheMaxSize) {
-            mAppCacheMaxSize = appCacheMaxSize;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1420,10 +838,7 @@
      *     API.
      */
     public synchronized void setDatabaseEnabled(boolean flag) {
-       if (mDatabaseEnabled != flag) {
-           mDatabaseEnabled = flag;
-           postSync();
-       }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1432,10 +847,7 @@
      *     API.
      */
     public synchronized void setDomStorageEnabled(boolean flag) {
-       if (mDomStorageEnabled != flag) {
-           mDomStorageEnabled = flag;
-           postSync();
-       }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1443,16 +855,15 @@
      * @return True if the DOM Storage API's are enabled.
      */
     public synchronized boolean getDomStorageEnabled() {
-       return mDomStorageEnabled;
+        throw new MustOverrideException();
     }
-
     /**
      * Return the path to where database storage API databases are saved for
      * the current WebView.
      * @return the String path to the database storage API databases.
      */
     public synchronized String getDatabasePath() {
-        return mDatabasePath;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1460,21 +871,7 @@
      * @return True if the database storage API is enabled.
      */
     public synchronized boolean getDatabaseEnabled() {
-        return mDatabaseEnabled;
-    }
-
-    /**
-     * Tell the WebView to enable WebWorkers API.
-     * @param flag True if the WebView should enable WebWorkers.
-     * Note that this flag only affects V8. JSC does not have
-     * an equivalent setting.
-     * @hide
-     */
-    public synchronized void setWorkersEnabled(boolean flag) {
-        if (mWorkersEnabled != flag) {
-            mWorkersEnabled = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1482,22 +879,7 @@
      * @param flag Whether Geolocation should be enabled.
      */
     public synchronized void setGeolocationEnabled(boolean flag) {
-        if (mGeolocationEnabled != flag) {
-            mGeolocationEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * Sets whether XSS Auditor is enabled.
-     * @param flag Whether XSS Auditor should be enabled.
-     * @hide Only used by LayoutTestController.
-     */
-    public synchronized void setXSSAuditorEnabled(boolean flag) {
-        if (mXSSAuditorEnabled != flag) {
-            mXSSAuditorEnabled = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1505,7 +887,7 @@
      * @return True if javascript is enabled.
      */
     public synchronized boolean getJavaScriptEnabled() {
-        return mJavaScriptEnabled;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1515,7 +897,7 @@
      */
     @Deprecated
     public synchronized boolean getPluginsEnabled() {
-        return mPluginState == PluginState.ON;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1523,7 +905,7 @@
      * @return A value corresponding to the enum PluginState.
      */
     public synchronized PluginState getPluginState() {
-        return mPluginState;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1535,6 +917,7 @@
      */
     @Deprecated
     public synchronized String getPluginsPath() {
+        // Unconditionally returns empty string, so no need for derived classes to override.
         return "";
     }
 
@@ -1543,12 +926,8 @@
      * javascript function window.open().
      * @param flag True if javascript can open windows automatically.
      */
-    public synchronized void setJavaScriptCanOpenWindowsAutomatically(
-            boolean flag) {
-        if (mJavaScriptCanOpenWindowsAutomatically != flag) {
-            mJavaScriptCanOpenWindowsAutomatically = flag;
-            postSync();
-        }
+    public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
+        throw new MustOverrideException();
     }
 
     /**
@@ -1558,18 +937,14 @@
      *         window.open().
      */
     public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
-        return mJavaScriptCanOpenWindowsAutomatically;
+        throw new MustOverrideException();
     }
-
     /**
      * Set the default text encoding name to use when decoding html pages.
      * @param encoding The text encoding name.
      */
     public synchronized void setDefaultTextEncodingName(String encoding) {
-        if (encoding != null && !encoding.equals(mDefaultTextEncoding)) {
-            mDefaultTextEncoding = encoding;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1577,7 +952,7 @@
      * @return The default text encoding name as a string.
      */
     public synchronized String getDefaultTextEncodingName() {
-        return mDefaultTextEncoding;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1585,66 +960,14 @@
      * it will use the system default user-agent string.
      */
     public synchronized void setUserAgentString(String ua) {
-        if (ua == null || ua.length() == 0) {
-            synchronized(sLockForLocaleSettings) {
-                Locale currentLocale = Locale.getDefault();
-                if (!sLocale.equals(currentLocale)) {
-                    sLocale = currentLocale;
-                    mAcceptLanguage = getCurrentAcceptLanguage();
-                }
-            }
-            ua = getCurrentUserAgent();
-            mUseDefaultUserAgent = true;
-        } else  {
-            mUseDefaultUserAgent = false;
-        }
-
-        if (!ua.equals(mUserAgent)) {
-            mUserAgent = ua;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
      * Return the WebView's user-agent string.
      */
     public synchronized String getUserAgentString() {
-        if (DESKTOP_USERAGENT.equals(mUserAgent) ||
-                IPHONE_USERAGENT.equals(mUserAgent) ||
-                !mUseDefaultUserAgent) {
-            return mUserAgent;
-        }
-
-        boolean doPostSync = false;
-        synchronized(sLockForLocaleSettings) {
-            Locale currentLocale = Locale.getDefault();
-            if (!sLocale.equals(currentLocale)) {
-                sLocale = currentLocale;
-                mUserAgent = getCurrentUserAgent();
-                mAcceptLanguage = getCurrentAcceptLanguage();
-                doPostSync = true;
-            }
-        }
-        if (doPostSync) {
-            postSync();
-        }
-        return mUserAgent;
-    }
-
-    /* package api to grab the Accept Language string. */
-    /*package*/ synchronized String getAcceptLanguage() {
-        synchronized(sLockForLocaleSettings) {
-            Locale currentLocale = Locale.getDefault();
-            if (!sLocale.equals(currentLocale)) {
-                sLocale = currentLocale;
-                mAcceptLanguage = getCurrentAcceptLanguage();
-            }
-        }
-        return mAcceptLanguage;
-    }
-
-    /* package */ boolean isNarrowColumnLayout() {
-        return getLayoutAlgorithm() == LayoutAlgorithm.NARROW_COLUMNS;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1654,14 +977,7 @@
      * @param flag
      */
     public void setNeedInitialFocus(boolean flag) {
-        if (mNeedInitialFocus != flag) {
-            mNeedInitialFocus = flag;
-        }
-    }
-
-    /* Package api to get the choice whether it needs to set initial focus. */
-    /* package */ boolean getNeedInitialFocus() {
-        return mNeedInitialFocus;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1671,11 +987,7 @@
      * @param priority RenderPriority, can be normal, high or low.
      */
     public synchronized void setRenderPriority(RenderPriority priority) {
-        if (mRenderPriority != priority) {
-            mRenderPriority = priority;
-            mEventHandler.sendMessage(Message.obtain(null,
-                    EventHandler.PRIORITY));
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1687,10 +999,7 @@
      * @param mode One of the LOAD_ values.
      */
     public void setCacheMode(int mode) {
-        if (mode != mOverrideCacheMode) {
-            mOverrideCacheMode = mode;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1698,204 +1007,6 @@
      * description, see the {@link #setCacheMode(int)} function.
      */
     public int getCacheMode() {
-        return mOverrideCacheMode;
+        throw new MustOverrideException();
     }
-
-    /**
-     * If set, webkit alternately shrinks and expands images viewed outside
-     * of an HTML page to fit the screen. This conflicts with attempts by
-     * the UI to zoom in and out of an image, so it is set false by default.
-     * @param shrink Set true to let webkit shrink the standalone image to fit.
-     * {@hide}
-     */
-    public void setShrinksStandaloneImagesToFit(boolean shrink) {
-        if (mShrinksStandaloneImagesToFit != shrink) {
-            mShrinksStandaloneImagesToFit = shrink;
-            postSync();
-        }
-     }
-
-    /**
-     * Specify the maximum decoded image size. The default is
-     * 2 megs for small memory devices and 8 megs for large memory devices.
-     * @param size The maximum decoded size, or zero to set to the default.
-     * @hide
-     */
-    public void setMaximumDecodedImageSize(long size) {
-        if (mMaximumDecodedImageSize != size) {
-            mMaximumDecodedImageSize = size;
-            postSync();
-        }
-    }
-
-    /**
-     * Returns whether to use fixed viewport.  Use fixed viewport
-     * whenever wide viewport is on.
-     */
-    /* package */ boolean getUseFixedViewport() {
-        return getUseWideViewPort();
-    }
-
-    /**
-     * Returns whether private browsing is enabled.
-     */
-    /* package */ boolean isPrivateBrowsingEnabled() {
-        return mPrivateBrowsingEnabled;
-    }
-
-    /**
-     * Sets whether private browsing is enabled.
-     * @param flag Whether private browsing should be enabled.
-     */
-    /* package */ synchronized void setPrivateBrowsingEnabled(boolean flag) {
-        if (mPrivateBrowsingEnabled != flag) {
-            mPrivateBrowsingEnabled = flag;
-
-            // AutoFill is dependant on private browsing being enabled so
-            // reset it to take account of the new value of mPrivateBrowsingEnabled.
-            setAutoFillEnabled(mAutoFillEnabled);
-
-            postSync();
-        }
-    }
-
-    /**
-     * Returns whether the viewport metatag can disable zooming
-     * @hide
-     */
-    public boolean forceUserScalable() {
-        return mForceUserScalable;
-    }
-
-    /**
-     * Sets whether viewport metatag can disable zooming.
-     * @param flag Whether or not to forceably enable user scalable.
-     * @hide
-     */
-    public synchronized void setForceUserScalable(boolean flag) {
-        mForceUserScalable = flag;
-    }
-
-    synchronized void setSyntheticLinksEnabled(boolean flag) {
-        if (mSyntheticLinksEnabled != flag) {
-            mSyntheticLinksEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized void setAutoFillEnabled(boolean enabled) {
-        // AutoFill is always disabled in private browsing mode.
-        boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled;
-        if (mAutoFillEnabled != autoFillEnabled) {
-            mAutoFillEnabled = autoFillEnabled;
-            postSync();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized boolean getAutoFillEnabled() {
-        return mAutoFillEnabled;
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized void setAutoFillProfile(AutoFillProfile profile) {
-        if (mAutoFillProfile != profile) {
-            mAutoFillProfile = profile;
-            postSync();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized AutoFillProfile getAutoFillProfile() {
-        return mAutoFillProfile;
-    }
-
-    int getDoubleTapToastCount() {
-        return mDoubleTapToastCount;
-    }
-
-    void setDoubleTapToastCount(int count) {
-        if (mDoubleTapToastCount != count) {
-            mDoubleTapToastCount = count;
-            // write the settings in the non-UI thread
-            mEventHandler.sendMessage(Message.obtain(null,
-                    EventHandler.SET_DOUBLE_TAP_TOAST_COUNT));
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void setProperty(String key, String value) {
-        if (mWebView.nativeSetProperty(key, value)) {
-            mWebView.contentInvalidateAll();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public String getProperty(String key) {
-        return mWebView.nativeGetProperty(key);
-    }
-
-    /**
-     * Transfer messages from the queue to the new WebCoreThread. Called from
-     * WebCore thread.
-     */
-    /*package*/
-    synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
-        mBrowserFrame = frame;
-        if (DebugFlags.WEB_SETTINGS) {
-            junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
-        }
-
-        SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE,
-                Context.MODE_PRIVATE);
-        if (mDoubleTapToastCount > 0) {
-            mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT,
-                    mDoubleTapToastCount);
-        }
-        nativeSync(frame.mNativeFrame);
-        mSyncPending = false;
-        mEventHandler.createHandler();
-    }
-
-    /**
-     * Let the Settings object know that our owner is being destroyed.
-     */
-    /*package*/
-    synchronized void onDestroyed() {
-    }
-
-    private int pin(int size) {
-        // FIXME: 72 is just an arbitrary max text size value.
-        if (size < 1) {
-            return 1;
-        } else if (size > 72) {
-            return 72;
-        }
-        return size;
-    }
-
-    /* Post a SYNC message to handle syncing the native settings. */
-    private synchronized void postSync() {
-        // Only post if a sync is not pending
-        if (!mSyncPending) {
-            mSyncPending = mEventHandler.sendMessage(
-                    Message.obtain(null, EventHandler.SYNC));
-        }
-    }
-
-    // Synchronize the native and java settings.
-    private native void nativeSync(int nativeFrame);
 }
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index c463b40..6850eea 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,124 +28,15 @@
 import java.util.Locale;
 
 /**
- * Manages settings state for a WebView. When a WebView is first created, it
- * obtains a set of default settings. These default settings will be returned
- * from any getter call. A WebSettings object obtained from
- * WebView.getSettings() is tied to the life of the WebView. If a WebView has
- * been destroyed, any method call on WebSettings will throw an
- * IllegalStateException.
+ * WebSettings implementation for the WebViewClassic implementation of WebView.
+ * @hide
  */
-public class WebSettings {
-    /**
-     * Enum for controlling the layout of html.
-     * NORMAL means no rendering changes.
-     * SINGLE_COLUMN moves all content into one column that is the width of the
-     * view.
-     * NARROW_COLUMNS makes all columns no wider than the screen if possible.
-     */
-    // XXX: These must match LayoutAlgorithm in Settings.h in WebCore.
-    public enum LayoutAlgorithm {
-        NORMAL,
-        /**
-         * @deprecated This algorithm is now obsolete.
-         */
-        @Deprecated
-        SINGLE_COLUMN,
-        NARROW_COLUMNS
-    }
-
-    /**
-     * Enum for specifying the text size.
-     * SMALLEST is 50%
-     * SMALLER is 75%
-     * NORMAL is 100%
-     * LARGER is 150%
-     * LARGEST is 200%
-     * @deprecated Use {@link WebSettings#setTextZoom(int)} and {@link WebSettings#getTextZoom()} instead.
-     */
-    public enum TextSize {
-        SMALLEST(50),
-        SMALLER(75),
-        NORMAL(100),
-        LARGER(150),
-        LARGEST(200);
-        TextSize(int size) {
-            value = size;
-        }
-        int value;
-    }
-
-    /**
-     * Enum for specifying the WebView's desired density.
-     * FAR makes 100% looking like in 240dpi
-     * MEDIUM makes 100% looking like in 160dpi
-     * CLOSE makes 100% looking like in 120dpi
-     */
-    public enum ZoomDensity {
-        FAR(150),      // 240dpi
-        MEDIUM(100),    // 160dpi
-        CLOSE(75);     // 120dpi
-        ZoomDensity(int size) {
-            value = size;
-        }
-        int value;
-    }
-
-    /**
-     * Default cache usage pattern  Use with {@link #setCacheMode}.
-     */
-    public static final int LOAD_DEFAULT = -1;
-
-    /**
-     * Normal cache usage pattern  Use with {@link #setCacheMode}.
-     */
-    public static final int LOAD_NORMAL = 0;
-
-    /**
-     * Use cache if content is there, even if expired (eg, history nav)
-     * If it is not in the cache, load from network.
-     * Use with {@link #setCacheMode}.
-     */
-    public static final int LOAD_CACHE_ELSE_NETWORK = 1;
-
-    /**
-     * Don't use the cache, load from network
-     * Use with {@link #setCacheMode}.
-     */
-    public static final int LOAD_NO_CACHE = 2;
-
-    /**
-     * Don't use the network, load from cache only.
-     * Use with {@link #setCacheMode}.
-     */
-    public static final int LOAD_CACHE_ONLY = 3;
-
-    public enum RenderPriority {
-        NORMAL,
-        HIGH,
-        LOW
-    }
-
-    /**
-     * The plugin state effects how plugins are treated on a page. ON means
-     * that any object will be loaded even if a plugin does not exist to handle
-     * the content. ON_DEMAND means that if there is a plugin installed that
-     * can handle the content, a placeholder is shown until the user clicks on
-     * the placeholder. Once clicked, the plugin will be enabled on the page.
-     * OFF means that all plugins will be turned off and any fallback content
-     * will be used.
-     */
-    public enum PluginState {
-        ON,
-        ON_DEMAND,
-        OFF
-    }
-
+public class WebSettingsClassic extends WebSettings {
     // TODO: Keep this up to date
     private static final String PREVIOUS_VERSION = "4.0.3";
 
     // WebView associated with this WebSettings.
-    private WebView mWebView;
+    private WebViewClassic mWebView;
     // BrowserFrame used to access the native frame pointer.
     private BrowserFrame mBrowserFrame;
     // Flag to prevent multiple SYNC messages at one time.
@@ -308,7 +199,7 @@
                 public void handleMessage(Message msg) {
                     switch (msg.what) {
                         case SYNC:
-                            synchronized (WebSettings.this) {
+                            synchronized (WebSettingsClassic.this) {
                                 if (mBrowserFrame.mNativeFrame != 0) {
                                     nativeSync(mBrowserFrame.mNativeFrame);
                                 }
@@ -336,7 +227,7 @@
         }
 
         private void setRenderPriority() {
-            synchronized (WebSettings.this) {
+            synchronized (WebSettingsClassic.this) {
                 if (mRenderPriority == RenderPriority.NORMAL) {
                     android.os.Process.setThreadPriority(
                             android.os.Process.THREAD_PRIORITY_DEFAULT);
@@ -379,7 +270,7 @@
      * Package constructor to prevent clients from creating a new settings
      * instance.
      */
-    WebSettings(Context context, WebView webview) {
+    WebSettingsClassic(Context context, WebViewClassic webview) {
         mEventHandler = new EventHandler();
         mContext = context;
         mWebView = webview;
@@ -517,194 +408,195 @@
     }
 
     /**
-     * Enables dumping the pages navigation cache to a text file.
-     * @deprecated This method is now obsolete.
+     * @see android.webkit.WebSettings#setNavDump(boolean)
      */
+    @Override
     @Deprecated
     public void setNavDump(boolean enabled) {
         mNavDump = enabled;
     }
 
     /**
-     * Returns true if dumping the navigation cache is enabled.
-     * @deprecated This method is now obsolete.
+     * @see android.webkit.WebSettings#getNavDump()
      */
+    @Override
     @Deprecated
     public boolean getNavDump() {
         return mNavDump;
     }
 
     /**
-     * Set whether the WebView supports zoom
+     * @see android.webkit.WebSettings#setSupportZoom(boolean)
      */
+    @Override
     public void setSupportZoom(boolean support) {
         mSupportZoom = support;
         mWebView.updateMultiTouchSupport(mContext);
     }
 
     /**
-     * Returns whether the WebView supports zoom
+     * @see android.webkit.WebSettings#supportZoom()
      */
+    @Override
     public boolean supportZoom() {
         return mSupportZoom;
     }
 
     /**
-     * Sets whether the zoom mechanism built into WebView is used.
+     * @see android.webkit.WebSettings#setBuiltInZoomControls(boolean)
      */
+    @Override
     public void setBuiltInZoomControls(boolean enabled) {
         mBuiltInZoomControls = enabled;
         mWebView.updateMultiTouchSupport(mContext);
     }
 
     /**
-     * Returns true if the zoom mechanism built into WebView is being used.
+     * @see android.webkit.WebSettings#getBuiltInZoomControls()
      */
+    @Override
     public boolean getBuiltInZoomControls() {
         return mBuiltInZoomControls;
     }
 
     /**
-     * Sets whether the on screen zoom buttons are used.
-     * A combination of built in zoom controls enabled
-     * and on screen zoom controls disabled allows for pinch to zoom
-     * to work without the on screen controls
+     * @see android.webkit.WebSettings#setDisplayZoomControls(boolean)
      */
+    @Override
     public void setDisplayZoomControls(boolean enabled) {
         mDisplayZoomControls = enabled;
         mWebView.updateMultiTouchSupport(mContext);
     }
 
     /**
-     * Returns true if the on screen zoom buttons are being used.
+     * @see android.webkit.WebSettings#getDisplayZoomControls()
      */
+    @Override
     public boolean getDisplayZoomControls() {
         return mDisplayZoomControls;
     }
 
     /**
-     * Enable or disable file access within WebView. File access is enabled by
-     * default.  Note that this enables or disables file system access only.
-     * Assets and resources are still accessible using file:///android_asset and
-     * file:///android_res.
+     * @see android.webkit.WebSettings#setAllowFileAccess(boolean)
      */
+    @Override
     public void setAllowFileAccess(boolean allow) {
         mAllowFileAccess = allow;
     }
 
     /**
-     * Returns true if this WebView supports file access.
+     * @see android.webkit.WebSettings#getAllowFileAccess()
      */
+    @Override
     public boolean getAllowFileAccess() {
         return mAllowFileAccess;
     }
 
     /**
-     * Enable or disable content url access within WebView.  Content url access
-     * allows WebView to load content from a content provider installed in the
-     * system.  The default is enabled.
+     * @see android.webkit.WebSettings#setAllowContentAccess(boolean)
      */
+    @Override
     public void setAllowContentAccess(boolean allow) {
         mAllowContentAccess = allow;
     }
 
     /**
-     * Returns true if this WebView supports content url access.
+     * @see android.webkit.WebSettings#getAllowContentAccess()
      */
+    @Override
     public boolean getAllowContentAccess() {
         return mAllowContentAccess;
     }
 
     /**
-     * Set whether the WebView loads a page with overview mode.
+     * @see android.webkit.WebSettings#setLoadWithOverviewMode(boolean)
      */
+    @Override
     public void setLoadWithOverviewMode(boolean overview) {
         mLoadWithOverviewMode = overview;
     }
 
     /**
-     * Returns true if this WebView loads page with overview mode
+     * @see android.webkit.WebSettings#getLoadWithOverviewMode()
      */
+    @Override
     public boolean getLoadWithOverviewMode() {
         return mLoadWithOverviewMode;
     }
 
     /**
-     * Set whether the WebView will enable smooth transition while panning or
-     * zooming or while the window hosting the WebView does not have focus.
-     * If it is true, WebView will choose a solution to maximize the performance.
-     * e.g. the WebView's content may not be updated during the transition.
-     * If it is false, WebView will keep its fidelity. The default value is false.
+     * @see android.webkit.WebSettings#setEnableSmoothTransition(boolean)
      */
+    @Override
     public void setEnableSmoothTransition(boolean enable) {
         mEnableSmoothTransition = enable;
     }
 
     /**
-     * Returns true if the WebView enables smooth transition while panning or
-     * zooming.
+     * @see android.webkit.WebSettings#enableSmoothTransition()
      */
+    @Override
     public boolean enableSmoothTransition() {
         return mEnableSmoothTransition;
     }
 
     /**
-     * Set whether the WebView uses its background for over scroll background.
-     * If true, it will use the WebView's background. If false, it will use an
-     * internal pattern. Default is true.
-     * @deprecated This method is now obsolete.
+     * @see android.webkit.WebSettings#setUseWebViewBackgroundForOverscrollBackground(boolean)
      */
+    @Override
     @Deprecated
     public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
         mUseWebViewBackgroundForOverscroll = view;
     }
 
     /**
-     * Returns true if this WebView uses WebView's background instead of
-     * internal pattern for over scroll background.
-     * @deprecated This method is now obsolete.
+     * @see android.webkit.WebSettings#getUseWebViewBackgroundForOverscrollBackground()
      */
+    @Override
     @Deprecated
     public boolean getUseWebViewBackgroundForOverscrollBackground() {
         return mUseWebViewBackgroundForOverscroll;
     }
 
     /**
-     * Store whether the WebView is saving form data.
+     * @see android.webkit.WebSettings#setSaveFormData(boolean)
      */
+    @Override
     public void setSaveFormData(boolean save) {
         mSaveFormData = save;
     }
 
     /**
-     *  Return whether the WebView is saving form data and displaying prior
-     *  entries/autofill++.  Always false in private browsing mode.
+     * @see android.webkit.WebSettings#getSaveFormData()
      */
+    @Override
     public boolean getSaveFormData() {
         return mSaveFormData && !mPrivateBrowsingEnabled;
     }
 
     /**
-     *  Store whether the WebView is saving password.
+     * @see android.webkit.WebSettings#setSavePassword(boolean)
      */
+    @Override
     public void setSavePassword(boolean save) {
         mSavePassword = save;
     }
 
     /**
-     *  Return whether the WebView is saving password.
+     * @see android.webkit.WebSettings#getSavePassword()
      */
+    @Override
     public boolean getSavePassword() {
         return mSavePassword;
     }
 
     /**
-     * Set the text zoom of the page in percent. Default is 100.
-     * @param textZoom A percent value for increasing or decreasing the text.
+     * @see android.webkit.WebSettings#setTextZoom(int)
      */
+    @Override
     public synchronized void setTextZoom(int textZoom) {
         if (mTextSize != textZoom) {
-            if (WebView.mLogEvent) {
+            if (WebViewClassic.mLogEvent) {
                 EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE,
                         mTextSize, textZoom);
             }
@@ -714,32 +606,25 @@
     }
 
     /**
-     * Get the text zoom of the page in percent.
-     * @return A percent value describing the text zoom.
-     * @see setTextSizeZoom
+     * @see android.webkit.WebSettings#getTextZoom()
      */
+    @Override
     public synchronized int getTextZoom() {
         return mTextSize;
     }
 
     /**
-     * Set the text size of the page.
-     * @param t A TextSize value for increasing or decreasing the text.
-     * @see WebSettings.TextSize
-     * @deprecated Use {@link #setTextZoom(int)} instead
+     * @see android.webkit.WebSettings#setTextSize(android.webkit.WebSettingsClassic.TextSize)
      */
+    @Override
     public synchronized void setTextSize(TextSize t) {
         setTextZoom(t.value);
     }
 
     /**
-     * Get the text size of the page. If the text size was previously specified
-     * in percent using {@link #setTextZoom(int)}, this will return
-     * the closest matching {@link TextSize}.
-     * @return A TextSize enum value describing the text size.
-     * @see WebSettings.TextSize
-     * @deprecated Use {@link #getTextZoom()} instead
+     * @see android.webkit.WebSettings#getTextSize()
      */
+    @Override
     public synchronized TextSize getTextSize() {
         TextSize closestSize = null;
         int smallestDelta = Integer.MAX_VALUE;
@@ -778,11 +663,9 @@
     }
 
     /**
-     * Set the default zoom density of the page. This should be called from UI
-     * thread.
-     * @param zoom A ZoomDensity value
-     * @see WebSettings.ZoomDensity
+     * @see android.webkit.WebSettings#setDefaultZoom(android.webkit.WebSettingsClassic.ZoomDensity)
      */
+    @Override
     public void setDefaultZoom(ZoomDensity zoom) {
         if (mDefaultZoom != zoom) {
             mDefaultZoom = zoom;
@@ -791,54 +674,51 @@
     }
 
     /**
-     * Get the default zoom density of the page. This should be called from UI
-     * thread.
-     * @return A ZoomDensity value
-     * @see WebSettings.ZoomDensity
+     * @see android.webkit.WebSettings#getDefaultZoom()
      */
+    @Override
     public ZoomDensity getDefaultZoom() {
         return mDefaultZoom;
     }
 
     /**
-     * Enables using light touches to make a selection and activate mouseovers.
+     * @see android.webkit.WebSettings#setLightTouchEnabled(boolean)
      */
+    @Override
     public void setLightTouchEnabled(boolean enabled) {
         mLightTouchEnabled = enabled;
     }
 
     /**
-     * Returns true if light touches are enabled.
+     * @see android.webkit.WebSettings#getLightTouchEnabled()
      */
+    @Override
     public boolean getLightTouchEnabled() {
         return mLightTouchEnabled;
     }
 
     /**
-     * @deprecated This setting controlled a rendering optimization
-     * that is no longer present. Setting it now has no effect.
+     * @see android.webkit.WebSettings#setUseDoubleTree(boolean)
      */
+    @Override
     @Deprecated
     public synchronized void setUseDoubleTree(boolean use) {
         return;
     }
 
     /**
-     * @deprecated This setting controlled a rendering optimization
-     * that is no longer present. Setting it now has no effect.
+     * @see android.webkit.WebSettings#getUseDoubleTree()
      */
+    @Override
     @Deprecated
     public synchronized boolean getUseDoubleTree() {
         return false;
     }
 
     /**
-     * Tell the WebView about user-agent string.
-     * @param ua 0 if the WebView should use an Android user-agent string,
-     *           1 if the WebView should use a desktop user-agent string.
-     *
-     * @deprecated Please use setUserAgentString instead.
+     * @see android.webkit.WebSettings#setUserAgent(int)
      */
+    @Override
     @Deprecated
     public synchronized void setUserAgent(int ua) {
         String uaString = null;
@@ -861,13 +741,9 @@
     }
 
     /**
-     * Return user-agent as int
-     * @return int  0 if the WebView is using an Android user-agent string.
-     *              1 if the WebView is using a desktop user-agent string.
-     *             -1 if the WebView is using user defined user-agent string.
-     *
-     * @deprecated Please use getUserAgentString instead.
+     * @see android.webkit.WebSettings#getUserAgent()
      */
+    @Override
     @Deprecated
     public synchronized int getUserAgent() {
         if (DESKTOP_USERAGENT.equals(mUserAgent)) {
@@ -881,8 +757,9 @@
     }
 
     /**
-     * Tell the WebView to use the wide viewport
+     * @see android.webkit.WebSettings#setUseWideViewPort(boolean)
      */
+    @Override
     public synchronized void setUseWideViewPort(boolean use) {
         if (mUseWideViewport != use) {
             mUseWideViewport = use;
@@ -891,17 +768,17 @@
     }
 
     /**
-     * @return True if the WebView is using a wide viewport
+     * @see android.webkit.WebSettings#getUseWideViewPort()
      */
+    @Override
     public synchronized boolean getUseWideViewPort() {
         return mUseWideViewport;
     }
 
     /**
-     * Tell the WebView whether it supports multiple windows. TRUE means
-     *         that {@link WebChromeClient#onCreateWindow(WebView, boolean,
-     *         boolean, Message)} is implemented by the host application.
+     * @see android.webkit.WebSettings#setSupportMultipleWindows(boolean)
      */
+    @Override
     public synchronized void setSupportMultipleWindows(boolean support) {
         if (mSupportMultipleWindows != support) {
             mSupportMultipleWindows = support;
@@ -910,20 +787,17 @@
     }
 
     /**
-     * @return True if the WebView is supporting multiple windows. This means
-     *         that {@link WebChromeClient#onCreateWindow(WebView, boolean,
-     *         boolean, Message)} is implemented by the host application.
+     * @see android.webkit.WebSettings#supportMultipleWindows()
      */
+    @Override
     public synchronized boolean supportMultipleWindows() {
         return mSupportMultipleWindows;
     }
 
     /**
-     * Set the underlying layout algorithm. This will cause a relayout of the
-     * WebView.
-     * @param l A LayoutAlgorithm enum specifying the algorithm to use.
-     * @see WebSettings.LayoutAlgorithm
+     * @see android.webkit.WebSettings#setLayoutAlgorithm(android.webkit.WebSettingsClassic.LayoutAlgorithm)
      */
+    @Override
     public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
         // XXX: This will only be affective if libwebcore was built with
         // ANDROID_LAYOUT defined.
@@ -934,19 +808,17 @@
     }
 
     /**
-     * Return the current layout algorithm. The default is NARROW_COLUMNS.
-     * @return LayoutAlgorithm enum value describing the layout algorithm
-     *         being used.
-     * @see WebSettings.LayoutAlgorithm
+     * @see android.webkit.WebSettings#getLayoutAlgorithm()
      */
+    @Override
     public synchronized LayoutAlgorithm getLayoutAlgorithm() {
         return mLayoutAlgorithm;
     }
 
     /**
-     * Set the standard font family name.
-     * @param font A font family name.
+     * @see android.webkit.WebSettings#setStandardFontFamily(java.lang.String)
      */
+    @Override
     public synchronized void setStandardFontFamily(String font) {
         if (font != null && !font.equals(mStandardFontFamily)) {
             mStandardFontFamily = font;
@@ -955,17 +827,17 @@
     }
 
     /**
-     * Get the standard font family name. The default is "sans-serif".
-     * @return The standard font family name as a string.
+     * @see android.webkit.WebSettings#getStandardFontFamily()
      */
+    @Override
     public synchronized String getStandardFontFamily() {
         return mStandardFontFamily;
     }
 
     /**
-     * Set the fixed font family name.
-     * @param font A font family name.
+     * @see android.webkit.WebSettings#setFixedFontFamily(java.lang.String)
      */
+    @Override
     public synchronized void setFixedFontFamily(String font) {
         if (font != null && !font.equals(mFixedFontFamily)) {
             mFixedFontFamily = font;
@@ -974,17 +846,17 @@
     }
 
     /**
-     * Get the fixed font family name. The default is "monospace".
-     * @return The fixed font family name as a string.
+     * @see android.webkit.WebSettings#getFixedFontFamily()
      */
+    @Override
     public synchronized String getFixedFontFamily() {
         return mFixedFontFamily;
     }
 
     /**
-     * Set the sans-serif font family name.
-     * @param font A font family name.
+     * @see android.webkit.WebSettings#setSansSerifFontFamily(java.lang.String)
      */
+    @Override
     public synchronized void setSansSerifFontFamily(String font) {
         if (font != null && !font.equals(mSansSerifFontFamily)) {
             mSansSerifFontFamily = font;
@@ -993,17 +865,17 @@
     }
 
     /**
-     * Get the sans-serif font family name.
-     * @return The sans-serif font family name as a string.
+     * @see android.webkit.WebSettings#getSansSerifFontFamily()
      */
+    @Override
     public synchronized String getSansSerifFontFamily() {
         return mSansSerifFontFamily;
     }
 
     /**
-     * Set the serif font family name. The default is "sans-serif".
-     * @param font A font family name.
+     * @see android.webkit.WebSettings#setSerifFontFamily(java.lang.String)
      */
+    @Override
     public synchronized void setSerifFontFamily(String font) {
         if (font != null && !font.equals(mSerifFontFamily)) {
             mSerifFontFamily = font;
@@ -1012,17 +884,17 @@
     }
 
     /**
-     * Get the serif font family name. The default is "serif".
-     * @return The serif font family name as a string.
+     * @see android.webkit.WebSettings#getSerifFontFamily()
      */
+    @Override
     public synchronized String getSerifFontFamily() {
         return mSerifFontFamily;
     }
 
     /**
-     * Set the cursive font family name.
-     * @param font A font family name.
+     * @see android.webkit.WebSettings#setCursiveFontFamily(java.lang.String)
      */
+    @Override
     public synchronized void setCursiveFontFamily(String font) {
         if (font != null && !font.equals(mCursiveFontFamily)) {
             mCursiveFontFamily = font;
@@ -1031,17 +903,17 @@
     }
 
     /**
-     * Get the cursive font family name. The default is "cursive".
-     * @return The cursive font family name as a string.
+     * @see android.webkit.WebSettings#getCursiveFontFamily()
      */
+    @Override
     public synchronized String getCursiveFontFamily() {
         return mCursiveFontFamily;
     }
 
     /**
-     * Set the fantasy font family name.
-     * @param font A font family name.
+     * @see android.webkit.WebSettings#setFantasyFontFamily(java.lang.String)
      */
+    @Override
     public synchronized void setFantasyFontFamily(String font) {
         if (font != null && !font.equals(mFantasyFontFamily)) {
             mFantasyFontFamily = font;
@@ -1050,18 +922,17 @@
     }
 
     /**
-     * Get the fantasy font family name. The default is "fantasy".
-     * @return The fantasy font family name as a string.
+     * @see android.webkit.WebSettings#getFantasyFontFamily()
      */
+    @Override
     public synchronized String getFantasyFontFamily() {
         return mFantasyFontFamily;
     }
 
     /**
-     * Set the minimum font size.
-     * @param size A non-negative integer between 1 and 72.
-     * Any number outside the specified range will be pinned.
+     * @see android.webkit.WebSettings#setMinimumFontSize(int)
      */
+    @Override
     public synchronized void setMinimumFontSize(int size) {
         size = pin(size);
         if (mMinimumFontSize != size) {
@@ -1071,18 +942,17 @@
     }
 
     /**
-     * Get the minimum font size. The default is 8.
-     * @return A non-negative integer between 1 and 72.
+     * @see android.webkit.WebSettings#getMinimumFontSize()
      */
+    @Override
     public synchronized int getMinimumFontSize() {
         return mMinimumFontSize;
     }
 
     /**
-     * Set the minimum logical font size.
-     * @param size A non-negative integer between 1 and 72.
-     * Any number outside the specified range will be pinned.
+     * @see android.webkit.WebSettings#setMinimumLogicalFontSize(int)
      */
+    @Override
     public synchronized void setMinimumLogicalFontSize(int size) {
         size = pin(size);
         if (mMinimumLogicalFontSize != size) {
@@ -1092,18 +962,17 @@
     }
 
     /**
-     * Get the minimum logical font size. The default is 8.
-     * @return A non-negative integer between 1 and 72.
+     * @see android.webkit.WebSettings#getMinimumLogicalFontSize()
      */
+    @Override
     public synchronized int getMinimumLogicalFontSize() {
         return mMinimumLogicalFontSize;
     }
 
     /**
-     * Set the default font size.
-     * @param size A non-negative integer between 1 and 72.
-     * Any number outside the specified range will be pinned.
+     * @see android.webkit.WebSettings#setDefaultFontSize(int)
      */
+    @Override
     public synchronized void setDefaultFontSize(int size) {
         size = pin(size);
         if (mDefaultFontSize != size) {
@@ -1113,18 +982,17 @@
     }
 
     /**
-     * Get the default font size. The default is 16.
-     * @return A non-negative integer between 1 and 72.
+     * @see android.webkit.WebSettings#getDefaultFontSize()
      */
+    @Override
     public synchronized int getDefaultFontSize() {
         return mDefaultFontSize;
     }
 
     /**
-     * Set the default fixed font size.
-     * @param size A non-negative integer between 1 and 72.
-     * Any number outside the specified range will be pinned.
+     * @see android.webkit.WebSettings#setDefaultFixedFontSize(int)
      */
+    @Override
     public synchronized void setDefaultFixedFontSize(int size) {
         size = pin(size);
         if (mDefaultFixedFontSize != size) {
@@ -1134,9 +1002,9 @@
     }
 
     /**
-     * Get the default fixed font size. The default is 16.
-     * @return A non-negative integer between 1 and 72.
+     * @see android.webkit.WebSettings#getDefaultFixedFontSize()
      */
+    @Override
     public synchronized int getDefaultFixedFontSize() {
         return mDefaultFixedFontSize;
     }
@@ -1156,14 +1024,9 @@
     }
 
     /**
-     * Sets whether the WebView should load image resources. Note that this method
-     * controls loading of all images, including those embedded using the data
-     * URI scheme. Use {@link #setBlockNetworkImage} to control loading only
-     * of images specified using network URI schemes. Note that if the value of this
-     * setting is changed from false to true, all images resources referenced
-     * by content currently displayed by the WebView are loaded automatically.
-     * @param flag Whether the WebView should load image resources.
+     * @see android.webkit.WebSettings#setLoadsImagesAutomatically(boolean)
      */
+    @Override
     public synchronized void setLoadsImagesAutomatically(boolean flag) {
         if (mLoadsImagesAutomatically != flag) {
             mLoadsImagesAutomatically = flag;
@@ -1172,28 +1035,17 @@
     }
 
     /**
-     * Returns true if the WebView loads image resources. This includes
-     * images embedded using the data URI scheme. The default is true.
-     * @return True if the WebView loads image resources.
+     * @see android.webkit.WebSettings#getLoadsImagesAutomatically()
      */
+    @Override
     public synchronized boolean getLoadsImagesAutomatically() {
         return mLoadsImagesAutomatically;
     }
 
     /**
-     * Sets whether the WebView should not load image resources from the
-     * network (resources accessed via http and https URI schemes).  Note
-     * that this method has no effect unless
-     * {@link #getLoadsImagesAutomatically} returns true. Also note that
-     * disabling all network loads using {@link #setBlockNetworkLoads}
-     * will also prevent network images from loading, even if this flag is set
-     * to false. When the value of this setting is changed from true to false,
-     * network images resources referenced by content currently displayed by
-     * the WebView are fetched automatically.
-     * @param flag Whether the WebView should not load image resources from
-     * the network.
-     * @see #setBlockNetworkLoads
+     * @see android.webkit.WebSettings#setBlockNetworkImage(boolean)
      */
+    @Override
     public synchronized void setBlockNetworkImage(boolean flag) {
         if (mBlockNetworkImage != flag) {
             mBlockNetworkImage = flag;
@@ -1202,29 +1054,17 @@
     }
 
     /**
-     * Returns true if the WebView does not load image resources from the network.
-     * The default is false.
-     * @return True if the WebView does not load image resources from the network.
+     * @see android.webkit.WebSettings#getBlockNetworkImage()
      */
+    @Override
     public synchronized boolean getBlockNetworkImage() {
         return mBlockNetworkImage;
     }
 
     /**
-     * Sets whether the WebView should not load resources from the network.
-     * Use {@link #setBlockNetworkImage} to only avoid loading
-     * image resources. Note that if the value of this setting is
-     * changed from true to false, network resources referenced by content
-     * currently displayed by the WebView are not fetched until
-     * {@link android.webkit.WebView#reload} is called.
-     * If the application does not have the
-     * {@link android.Manifest.permission#INTERNET} permission, attempts to set
-     * a value of false will cause a {@link java.lang.SecurityException}
-     * to be thrown.
-     * @param flag Whether the WebView should not load any resources
-     * from the network.
-     * @see android.webkit.WebView#reload
+     * @see android.webkit.WebSettings#setBlockNetworkLoads(boolean)
      */
+    @Override
     public synchronized void setBlockNetworkLoads(boolean flag) {
         if (mBlockNetworkLoads != flag) {
             mBlockNetworkLoads = flag;
@@ -1234,12 +1074,9 @@
     }
 
     /**
-     * Returns true if the WebView does not load any resources from the network.
-     * The default value is false if the application has the
-     * {@link android.Manifest.permission#INTERNET} permission, otherwise it is
-     * true.
-     * @return True if the WebView does not load any resources from the network.
+     * @see android.webkit.WebSettings#getBlockNetworkLoads()
      */
+    @Override
     public synchronized boolean getBlockNetworkLoads() {
         return mBlockNetworkLoads;
     }
@@ -1258,9 +1095,9 @@
     }
 
     /**
-     * Tell the WebView to enable javascript execution.
-     * @param flag True if the WebView should execute javascript.
+     * @see android.webkit.WebSettings#setJavaScriptEnabled(boolean)
      */
+    @Override
     public synchronized void setJavaScriptEnabled(boolean flag) {
         if (mJavaScriptEnabled != flag) {
             mJavaScriptEnabled = flag;
@@ -1309,23 +1146,18 @@
     }
 
     /**
-     * Tell the WebView to enable plugins.
-     * @param flag True if the WebView should load plugins.
-     * @deprecated This method has been deprecated in favor of
-     *             {@link #setPluginState}
+     * @see android.webkit.WebSettings#setPluginsEnabled(boolean)
      */
+    @Override
     @Deprecated
     public synchronized void setPluginsEnabled(boolean flag) {
         setPluginState(flag ? PluginState.ON : PluginState.OFF);
     }
 
     /**
-     * Tell the WebView to enable, disable, or have plugins on demand. On
-     * demand mode means that if a plugin exists that can handle the embedded
-     * content, a placeholder icon will be shown instead of the plugin. When
-     * the placeholder is clicked, the plugin will be enabled.
-     * @param state One of the PluginState values.
+     * @see android.webkit.WebSettings#setPluginState(android.webkit.WebSettingsClassic.PluginState)
      */
+    @Override
     public synchronized void setPluginState(PluginState state) {
         if (mPluginState != state) {
             mPluginState = state;
@@ -1334,23 +1166,17 @@
     }
 
     /**
-     * Set a custom path to plugins used by the WebView. This method is
-     * obsolete since each plugin is now loaded from its own package.
-     * @param pluginsPath String path to the directory containing plugins.
-     * @deprecated This method is no longer used as plugins are loaded from
-     * their own APK via the system's package manager.
+     * @see android.webkit.WebSettings#setPluginsPath(java.lang.String)
      */
+    @Override
     @Deprecated
     public synchronized void setPluginsPath(String pluginsPath) {
     }
 
     /**
-     * Set the path to where database storage API databases should be saved.
-     * Nota that the WebCore Database Tracker only allows the path to be set once.
-     * This will update WebCore when the Sync runs in the C++ side.
-     * @param databasePath String path to the directory where databases should
-     *     be saved. May be the empty string but should never be null.
+     * @see android.webkit.WebSettings#setDatabasePath(java.lang.String)
      */
+    @Override
     public synchronized void setDatabasePath(String databasePath) {
         if (databasePath != null && !mDatabasePathHasBeenSet) {
             mDatabasePath = databasePath;
@@ -1360,12 +1186,9 @@
     }
 
     /**
-     * Set the path where the Geolocation permissions database should be saved.
-     * This will update WebCore when the Sync runs in the C++ side.
-     * @param databasePath String path to the directory where the Geolocation
-     *     permissions database should be saved. May be the empty string but
-     *     should never be null.
+     * @see android.webkit.WebSettings#setGeolocationDatabasePath(java.lang.String)
      */
+    @Override
     public synchronized void setGeolocationDatabasePath(String databasePath) {
         if (databasePath != null
                 && !databasePath.equals(mGeolocationDatabasePath)) {
@@ -1375,9 +1198,9 @@
     }
 
     /**
-     * Enable or disable the Application Cache API.
-     * @param flag Whether to enable the Application Cache API.
+     * @see android.webkit.WebSettings#setAppCacheEnabled(boolean)
      */
+    @Override
     public synchronized void setAppCacheEnabled(boolean flag) {
         if (mAppCacheEnabled != flag) {
             mAppCacheEnabled = flag;
@@ -1386,14 +1209,9 @@
     }
 
     /**
-     * Set the path used by the Application Cache API to store files. This
-     * setting is applied to all WebViews in the application. In order for the
-     * Application Cache API to function, this method must be called with a
-     * path which exists and is writable by the application. This method may
-     * only be called once: repeated calls are ignored.
-     * @param path Path to the directory that should be used to store Application
-     * Cache files.
+     * @see android.webkit.WebSettings#setAppCachePath(java.lang.String)
      */
+    @Override
     public synchronized void setAppCachePath(String path) {
         // We test for a valid path and for repeated setting on the native
         // side, but we can avoid syncing in some simple cases. 
@@ -1404,9 +1222,9 @@
     }
 
     /**
-     * Set the maximum size for the Application Caches content.
-     * @param appCacheMaxSize the maximum size in bytes.
+     * @see android.webkit.WebSettings#setAppCacheMaxSize(long)
      */
+    @Override
     public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
         if (appCacheMaxSize != mAppCacheMaxSize) {
             mAppCacheMaxSize = appCacheMaxSize;
@@ -1415,10 +1233,9 @@
     }
 
     /**
-     * Set whether the database storage API is enabled.
-     * @param flag boolean True if the WebView should use the database storage
-     *     API.
+     * @see android.webkit.WebSettings#setDatabaseEnabled(boolean)
      */
+    @Override
     public synchronized void setDatabaseEnabled(boolean flag) {
        if (mDatabaseEnabled != flag) {
            mDatabaseEnabled = flag;
@@ -1427,10 +1244,9 @@
     }
 
     /**
-     * Set whether the DOM storage API is enabled.
-     * @param flag boolean True if the WebView should use the DOM storage
-     *     API.
+     * @see android.webkit.WebSettings#setDomStorageEnabled(boolean)
      */
+    @Override
     public synchronized void setDomStorageEnabled(boolean flag) {
        if (mDomStorageEnabled != flag) {
            mDomStorageEnabled = flag;
@@ -1439,26 +1255,25 @@
     }
 
     /**
-     * Returns true if the DOM Storage API's are enabled.
-     * @return True if the DOM Storage API's are enabled.
+     * @see android.webkit.WebSettings#getDomStorageEnabled()
      */
+    @Override
     public synchronized boolean getDomStorageEnabled() {
        return mDomStorageEnabled;
     }
 
     /**
-     * Return the path to where database storage API databases are saved for
-     * the current WebView.
-     * @return the String path to the database storage API databases.
+     * @see android.webkit.WebSettings#getDatabasePath()
      */
+    @Override
     public synchronized String getDatabasePath() {
         return mDatabasePath;
     }
 
     /**
-     * Returns true if database storage API is enabled.
-     * @return True if the database storage API is enabled.
+     * @see android.webkit.WebSettings#getDatabaseEnabled()
      */
+    @Override
     public synchronized boolean getDatabaseEnabled() {
         return mDatabaseEnabled;
     }
@@ -1478,9 +1293,9 @@
     }
 
     /**
-     * Sets whether Geolocation is enabled.
-     * @param flag Whether Geolocation should be enabled.
+     * @see android.webkit.WebSettings#setGeolocationEnabled(boolean)
      */
+    @Override
     public synchronized void setGeolocationEnabled(boolean flag) {
         if (mGeolocationEnabled != flag) {
             mGeolocationEnabled = flag;
@@ -1501,48 +1316,43 @@
     }
 
     /**
-     * Return true if javascript is enabled. <b>Note: The default is false.</b>
-     * @return True if javascript is enabled.
+     * @see android.webkit.WebSettings#getJavaScriptEnabled()
      */
+    @Override
     public synchronized boolean getJavaScriptEnabled() {
         return mJavaScriptEnabled;
     }
 
     /**
-     * Return true if plugins are enabled.
-     * @return True if plugins are enabled.
-     * @deprecated This method has been replaced by {@link #getPluginState}
+     * @see android.webkit.WebSettings#getPluginsEnabled()
      */
+    @Override
     @Deprecated
     public synchronized boolean getPluginsEnabled() {
         return mPluginState == PluginState.ON;
     }
 
     /**
-     * Return the current plugin state.
-     * @return A value corresponding to the enum PluginState.
+     * @see android.webkit.WebSettings#getPluginState()
      */
+    @Override
     public synchronized PluginState getPluginState() {
         return mPluginState;
     }
 
     /**
-     * Returns the directory that contains the plugin libraries. This method is
-     * obsolete since each plugin is now loaded from its own package.
-     * @return An empty string.
-     * @deprecated This method is no longer used as plugins are loaded from
-     * their own APK via the system's package manager.
+     * @see android.webkit.WebSettings#getPluginsPath()
      */
+    @Override
     @Deprecated
     public synchronized String getPluginsPath() {
         return "";
     }
 
     /**
-     * Tell javascript to open windows automatically. This applies to the
-     * javascript function window.open().
-     * @param flag True if javascript can open windows automatically.
+     * @see android.webkit.WebSettings#setJavaScriptCanOpenWindowsAutomatically(boolean)
      */
+    @Override
     public synchronized void setJavaScriptCanOpenWindowsAutomatically(
             boolean flag) {
         if (mJavaScriptCanOpenWindowsAutomatically != flag) {
@@ -1552,19 +1362,17 @@
     }
 
     /**
-     * Return true if javascript can open windows automatically. The default
-     * is false.
-     * @return True if javascript can open windows automatically during
-     *         window.open().
+     * @see android.webkit.WebSettings#getJavaScriptCanOpenWindowsAutomatically()
      */
+    @Override
     public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
         return mJavaScriptCanOpenWindowsAutomatically;
     }
 
     /**
-     * Set the default text encoding name to use when decoding html pages.
-     * @param encoding The text encoding name.
+     * @see android.webkit.WebSettings#setDefaultTextEncodingName(java.lang.String)
      */
+    @Override
     public synchronized void setDefaultTextEncodingName(String encoding) {
         if (encoding != null && !encoding.equals(mDefaultTextEncoding)) {
             mDefaultTextEncoding = encoding;
@@ -1573,17 +1381,17 @@
     }
 
     /**
-     * Get the default text encoding name. The default is "Latin-1".
-     * @return The default text encoding name as a string.
+     * @see android.webkit.WebSettings#getDefaultTextEncodingName()
      */
+    @Override
     public synchronized String getDefaultTextEncodingName() {
         return mDefaultTextEncoding;
     }
 
     /**
-     * Set the WebView's user-agent string. If the string "ua" is null or empty,
-     * it will use the system default user-agent string.
+     * @see android.webkit.WebSettings#setUserAgentString(java.lang.String)
      */
+    @Override
     public synchronized void setUserAgentString(String ua) {
         if (ua == null || ua.length() == 0) {
             synchronized(sLockForLocaleSettings) {
@@ -1606,8 +1414,9 @@
     }
 
     /**
-     * Return the WebView's user-agent string.
+     * @see android.webkit.WebSettings#getUserAgentString()
      */
+    @Override
     public synchronized String getUserAgentString() {
         if (DESKTOP_USERAGENT.equals(mUserAgent) ||
                 IPHONE_USERAGENT.equals(mUserAgent) ||
@@ -1648,11 +1457,9 @@
     }
 
     /**
-     * Tell the WebView whether it needs to set a node to have focus when
-     * {@link WebView#requestFocus(int, android.graphics.Rect)} is called.
-     *
-     * @param flag
+     * @see android.webkit.WebSettings#setNeedInitialFocus(boolean)
      */
+    @Override
     public void setNeedInitialFocus(boolean flag) {
         if (mNeedInitialFocus != flag) {
             mNeedInitialFocus = flag;
@@ -1665,11 +1472,9 @@
     }
 
     /**
-     * Set the priority of the Render thread. Unlike the other settings, this
-     * one only needs to be called once per process. The default is NORMAL.
-     *
-     * @param priority RenderPriority, can be normal, high or low.
+     * @see android.webkit.WebSettings#setRenderPriority(android.webkit.WebSettingsClassic.RenderPriority)
      */
+    @Override
     public synchronized void setRenderPriority(RenderPriority priority) {
         if (mRenderPriority != priority) {
             mRenderPriority = priority;
@@ -1679,13 +1484,9 @@
     }
 
     /**
-     * Override the way the cache is used. The way the cache is used is based
-     * on the navigation option. For a normal page load, the cache is checked
-     * and content is re-validated as needed. When navigating back, content is
-     * not revalidated, instead the content is just pulled from the cache.
-     * This function allows the client to override this behavior.
-     * @param mode One of the LOAD_ values.
+     * @see android.webkit.WebSettings#setCacheMode(int)
      */
+    @Override
     public void setCacheMode(int mode) {
         if (mode != mOverrideCacheMode) {
             mOverrideCacheMode = mode;
@@ -1694,9 +1495,9 @@
     }
 
     /**
-     * Return the current setting for overriding the cache mode. For a full
-     * description, see the {@link #setCacheMode(int)} function.
+     * @see android.webkit.WebSettings#getCacheMode()
      */
+    @Override
     public int getCacheMode() {
         return mOverrideCacheMode;
     }
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 510c168..a01c42d 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -78,7 +78,7 @@
 
     private int mRingInset;
 
-    private WebView         mWebView;
+    private WebViewClassic  mWebView;
     private boolean         mSingle;
     private int             mWidthSpec;
     private int             mHeightSpec;
@@ -177,7 +177,7 @@
      * @param   context The Context for this WebTextView.
      * @param   webView The WebView that created this.
      */
-    /* package */ WebTextView(Context context, WebView webView, int autoFillQueryId) {
+    /* package */ WebTextView(Context context, WebViewClassic webView, int autoFillQueryId) {
         super(context, null, com.android.internal.R.attr.webTextViewStyle);
         mWebView = webView;
         mMaxLength = -1;
@@ -833,9 +833,9 @@
         }
         mInsideRemove = true;
         boolean isFocused = hasFocus();
-        mWebView.removeView(this);
+        mWebView.getWebView().removeView(this);
         if (isFocused) {
-            mWebView.requestFocus();
+            mWebView.getWebView().requestFocus();
         }
         mInsideRemove = false;
         mHandler.removeCallbacksAndMessages(null);
@@ -997,7 +997,7 @@
         }
         if (getParent() == null) {
             // Insert the view so that it's drawn first (at index 0)
-            mWebView.addView(this, 0, lp);
+            mWebView.getWebView().addView(this, 0, lp);
         } else if (needsUpdate) {
             setLayoutParams(lp);
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a850379..a561577 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,126 +16,35 @@
 
 package android.webkit;
 
-import android.animation.ObjectAnimator;
 import android.annotation.Widget;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.database.DataSetObserver;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapShader;
 import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.DrawFilter;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Picture;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.RegionIterator;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
-import android.net.Proxy;
-import android.net.ProxyProperties;
-import android.net.Uri;
 import android.net.http.SslCertificate;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.StrictMode;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.security.KeyChain;
-import android.speech.tts.TextToSpeech;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Selection;
-import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
 import android.util.Log;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
-import android.view.HardwareCanvas;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.SoundEffectConstants;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.ViewParent;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewTreeObserver;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.webkit.HTML5VideoInline;
-import android.webkit.WebTextView.AutoCompleteAdapter;
-import android.webkit.WebViewCore.DrawData;
-import android.webkit.WebViewCore.EventHub;
-import android.webkit.WebViewCore.TextFieldInitData;
-import android.webkit.WebViewCore.TouchEventData;
-import android.webkit.WebViewCore.TouchHighlightData;
-import android.webkit.WebViewCore.WebKitHitTest;
 import android.widget.AbsoluteLayout;
-import android.widget.Adapter;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.CheckedTextView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.OverScroller;
-import android.widget.PopupWindow;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import junit.framework.Assert;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * <p>A View that displays web pages. This class is the basis upon which you
@@ -345,452 +254,25 @@
  *
  *
  */
+/*
+ * Implementation notes.
+ * The WebView is a thin API class that delegates its public API to a backend WebViewProvider
+ * class instance. WebView extends {@link AbsoluteLayout} for backward compatibility reasons.
+ * Methods are delegated to the provider implementation: all public API methods introduced in this
+ * file are fully delegated, whereas public and protected methods from the View base classes are
+ * only delegated where a specific need exists for them to do so.
+ */
 @Widget
 public class WebView extends AbsoluteLayout
         implements ViewTreeObserver.OnGlobalFocusChangeListener,
         ViewGroup.OnHierarchyChangeListener {
 
-    private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
-        @Override
-        public void onGlobalLayout() {
-            if (isShown()) {
-                setGLRectViewport();
-            }
-        }
-    }
+    // Default Provider factory class name.
+    private static final String DEFAULT_WEB_VIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
 
-    private class InnerScrollChangedListener implements ViewTreeObserver.OnScrollChangedListener {
-        @Override
-        public void onScrollChanged() {
-            if (isShown()) {
-                setGLRectViewport();
-            }
-        }
-    }
-
-    /**
-     * InputConnection used for ContentEditable. This captures changes
-     * to the text and sends them either as key strokes or text changes.
-     */
-    private class WebViewInputConnection extends BaseInputConnection {
-        // Used for mapping characters to keys typed.
-        private KeyCharacterMap mKeyCharacterMap;
-        private boolean mIsKeySentByMe;
-        private int mInputType;
-        private int mImeOptions;
-        private String mHint;
-        private int mMaxLength;
-
-        public WebViewInputConnection() {
-            super(WebView.this, true);
-        }
-
-        @Override
-        public boolean sendKeyEvent(KeyEvent event) {
-            // Some IMEs send key events directly using sendKeyEvents.
-            // WebViewInputConnection should treat these as text changes.
-            if (!mIsKeySentByMe) {
-                if (event.getAction() == KeyEvent.ACTION_UP) {
-                    if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
-                        return deleteSurroundingText(1, 0);
-                    } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
-                        return deleteSurroundingText(0, 1);
-                    } else if (event.getUnicodeChar() != 0){
-                        String newComposingText =
-                                Character.toString((char)event.getUnicodeChar());
-                        return commitText(newComposingText, 1);
-                    }
-                } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
-                        (event.getKeyCode() == KeyEvent.KEYCODE_DEL
-                        || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
-                        || event.getUnicodeChar() != 0)) {
-                    return true; // only act on action_down
-                }
-            }
-            return super.sendKeyEvent(event);
-        }
-
-        public void setTextAndKeepSelection(CharSequence text) {
-            Editable editable = getEditable();
-            int selectionStart = Selection.getSelectionStart(editable);
-            int selectionEnd = Selection.getSelectionEnd(editable);
-            text = limitReplaceTextByMaxLength(text, editable.length());
-            editable.replace(0, editable.length(), text);
-            restartInput();
-            // Keep the previous selection.
-            selectionStart = Math.min(selectionStart, editable.length());
-            selectionEnd = Math.min(selectionEnd, editable.length());
-            setSelection(selectionStart, selectionEnd);
-        }
-
-        public void replaceSelection(CharSequence text) {
-            Editable editable = getEditable();
-            int selectionStart = Selection.getSelectionStart(editable);
-            int selectionEnd = Selection.getSelectionEnd(editable);
-            text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart);
-            setNewText(selectionStart, selectionEnd, text);
-            editable.replace(selectionStart, selectionEnd, text);
-            restartInput();
-            // Move caret to the end of the new text
-            int newCaret = selectionStart + text.length();
-            setSelection(newCaret, newCaret);
-        }
-
-        @Override
-        public boolean setComposingText(CharSequence text, int newCursorPosition) {
-            Editable editable = getEditable();
-            int start = getComposingSpanStart(editable);
-            int end = getComposingSpanEnd(editable);
-            if (start < 0 || end < 0) {
-                start = Selection.getSelectionStart(editable);
-                end = Selection.getSelectionEnd(editable);
-            }
-            if (end < start) {
-                int temp = end;
-                end = start;
-                start = temp;
-            }
-            CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start);
-            setNewText(start, end, limitedText);
-            if (limitedText != text) {
-                newCursorPosition -= text.length() - limitedText.length();
-            }
-            super.setComposingText(limitedText, newCursorPosition);
-            if (limitedText != text) {
-                restartInput();
-                int lastCaret = start + limitedText.length();
-                finishComposingText();
-                setSelection(lastCaret, lastCaret);
-            }
-            return true;
-        }
-
-        @Override
-        public boolean commitText(CharSequence text, int newCursorPosition) {
-            setComposingText(text, newCursorPosition);
-            int cursorPosition = Selection.getSelectionEnd(getEditable());
-            setComposingRegion(cursorPosition, cursorPosition);
-            return true;
-        }
-
-        @Override
-        public boolean deleteSurroundingText(int leftLength, int rightLength) {
-            Editable editable = getEditable();
-            int cursorPosition = Selection.getSelectionEnd(editable);
-            int startDelete = Math.max(0, cursorPosition - leftLength);
-            int endDelete = Math.min(editable.length(),
-                    cursorPosition + rightLength);
-            setNewText(startDelete, endDelete, "");
-            return super.deleteSurroundingText(leftLength, rightLength);
-        }
-
-        @Override
-        public boolean performEditorAction(int editorAction) {
-
-            boolean handled = true;
-            switch (editorAction) {
-            case EditorInfo.IME_ACTION_NEXT:
-                WebView.this.requestFocus(FOCUS_FORWARD);
-                break;
-            case EditorInfo.IME_ACTION_PREVIOUS:
-                WebView.this.requestFocus(FOCUS_BACKWARD);
-                break;
-            case EditorInfo.IME_ACTION_DONE:
-                WebView.this.hideSoftKeyboard();
-                break;
-            case EditorInfo.IME_ACTION_GO:
-            case EditorInfo.IME_ACTION_SEARCH:
-                WebView.this.hideSoftKeyboard();
-                String text = getEditable().toString();
-                passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN,
-                        KeyEvent.KEYCODE_ENTER));
-                passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_UP,
-                        KeyEvent.KEYCODE_ENTER));
-                break;
-
-            default:
-                handled = super.performEditorAction(editorAction);
-                break;
-            }
-
-            return handled;
-        }
-
-        public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
-            int type = initData.mType;
-            int inputType = InputType.TYPE_CLASS_TEXT
-                    | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
-            int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
-                    | EditorInfo.IME_FLAG_NO_FULLSCREEN;
-            if (!initData.mIsSpellCheckEnabled) {
-                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
-            }
-            if (WebTextView.TEXT_AREA != type
-                    && initData.mIsTextFieldNext) {
-                imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
-            }
-            switch (type) {
-                case WebTextView.NORMAL_TEXT_FIELD:
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-                case WebTextView.TEXT_AREA:
-                    inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
-                            | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
-                            | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
-                    imeOptions |= EditorInfo.IME_ACTION_NONE;
-                    break;
-                case WebTextView.PASSWORD:
-                    inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-                case WebTextView.SEARCH:
-                    imeOptions |= EditorInfo.IME_ACTION_SEARCH;
-                    break;
-                case WebTextView.EMAIL:
-                    // inputType needs to be overwritten because of the different text variation.
-                    inputType = InputType.TYPE_CLASS_TEXT
-                            | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-                case WebTextView.NUMBER:
-                    // inputType needs to be overwritten because of the different class.
-                    inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
-                            | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
-                    // Number and telephone do not have both a Tab key and an
-                    // action, so set the action to NEXT
-                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
-                    break;
-                case WebTextView.TELEPHONE:
-                    // inputType needs to be overwritten because of the different class.
-                    inputType = InputType.TYPE_CLASS_PHONE;
-                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
-                    break;
-                case WebTextView.URL:
-                    // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
-                    // exclude it for now.
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    inputType |= InputType.TYPE_TEXT_VARIATION_URI;
-                    break;
-                default:
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-            }
-            mHint = initData.mLabel;
-            mInputType = inputType;
-            mImeOptions = imeOptions;
-            mMaxLength = initData.mMaxLength;
-        }
-
-        public void setupEditorInfo(EditorInfo outAttrs) {
-            outAttrs.inputType = mInputType;
-            outAttrs.imeOptions = mImeOptions;
-            outAttrs.hintText = mHint;
-            outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
-        }
-
-        /**
-         * Sends a text change to webkit indirectly. If it is a single-
-         * character add or delete, it sends it as a key stroke. If it cannot
-         * be represented as a key stroke, it sends it as a field change.
-         * @param start The start offset (inclusive) of the text being changed.
-         * @param end The end offset (exclusive) of the text being changed.
-         * @param text The new text to replace the changed text.
-         */
-        private void setNewText(int start, int end, CharSequence text) {
-            mIsKeySentByMe = true;
-            Editable editable = getEditable();
-            CharSequence original = editable.subSequence(start, end);
-            boolean isCharacterAdd = false;
-            boolean isCharacterDelete = false;
-            int textLength = text.length();
-            int originalLength = original.length();
-            if (textLength > originalLength) {
-                isCharacterAdd = (textLength == originalLength + 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                originalLength);
-            } else if (originalLength > textLength) {
-                isCharacterDelete = (textLength == originalLength - 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                textLength);
-            }
-            if (isCharacterAdd) {
-                sendCharacter(text.charAt(textLength - 1));
-            } else if (isCharacterDelete) {
-                sendKey(KeyEvent.KEYCODE_DEL);
-            } else if ((textLength != originalLength) ||
-                    !TextUtils.regionMatches(text, 0, original, 0,
-                            textLength)) {
-                // Send a message so that key strokes and text replacement
-                // do not come out of order.
-                Message replaceMessage = mPrivateHandler.obtainMessage(
-                        REPLACE_TEXT, start,  end, text.toString());
-                mPrivateHandler.sendMessage(replaceMessage);
-            }
-            mIsKeySentByMe = false;
-        }
-
-        /**
-         * Send a single character to the WebView as a key down and up event.
-         * @param c The character to be sent.
-         */
-        private void sendCharacter(char c) {
-            if (mKeyCharacterMap == null) {
-                mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-            }
-            char[] chars = new char[1];
-            chars[0] = c;
-            KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
-            if (events != null) {
-                for (KeyEvent event : events) {
-                    sendKeyEvent(event);
-                }
-            } else {
-                Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
-                mPrivateHandler.sendMessage(msg);
-            }
-        }
-
-        /**
-         * Send a key event for a specific key code, not a standard
-         * unicode character.
-         * @param keyCode The key code to send.
-         */
-        private void sendKey(int keyCode) {
-            long eventTime = SystemClock.uptimeMillis();
-            sendKeyEvent(new KeyEvent(eventTime, eventTime,
-                    KeyEvent.ACTION_DOWN, keyCode, 0, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                    KeyEvent.FLAG_SOFT_KEYBOARD));
-            sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
-                    KeyEvent.ACTION_UP, keyCode, 0, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                    KeyEvent.FLAG_SOFT_KEYBOARD));
-        }
-
-        private CharSequence limitReplaceTextByMaxLength(CharSequence text,
-                int numReplaced) {
-            if (mMaxLength > 0) {
-                Editable editable = getEditable();
-                int maxReplace = mMaxLength - editable.length() + numReplaced;
-                if (maxReplace < text.length()) {
-                    maxReplace = Math.max(maxReplace, 0);
-                    // New length is greater than the maximum. trim it down.
-                    text = text.subSequence(0, maxReplace);
-                }
-            }
-            return text;
-        }
-
-        private void restartInput() {
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                // Since the text has changed, do not allow the IME to replace the
-                // existing text as though it were a completion.
-                imm.restartInput(WebView.this);
-            }
-        }
-    }
-
-    private class PastePopupWindow extends PopupWindow implements OnClickListener {
-        private ViewGroup mContentView;
-        private TextView mPasteTextView;
-
-        public PastePopupWindow() {
-            super(WebView.this.mContext, null,
-                    com.android.internal.R.attr.textSelectHandleWindowStyle);
-            setClippingEnabled(true);
-            LinearLayout linearLayout = new LinearLayout(WebView.this.getContext());
-            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
-            mContentView = linearLayout;
-            mContentView.setBackgroundResource(
-                    com.android.internal.R.drawable.text_edit_paste_window);
-
-            LayoutInflater inflater = (LayoutInflater)WebView.this.mContext.
-                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-            ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams(
-                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-            mPasteTextView = (TextView) inflater.inflate(
-                    com.android.internal.R.layout.text_edit_action_popup_text, null);
-            mPasteTextView.setLayoutParams(wrapContent);
-            mContentView.addView(mPasteTextView);
-            mPasteTextView.setText(com.android.internal.R.string.paste);
-            mPasteTextView.setOnClickListener(this);
-            this.setContentView(mContentView);
-        }
-
-        public void show(Rect cursorRect, int windowLeft, int windowTop) {
-            measureContent();
-
-            int width = mContentView.getMeasuredWidth();
-            int height = mContentView.getMeasuredHeight();
-            int y = cursorRect.top - height;
-            if (y < windowTop) {
-                // There's not enough room vertically, move it below the
-                // handle.
-                // The selection handle is vertically offset by 1/4 of the
-                // line height.
-                y = cursorRect.bottom - (cursorRect.height() / 4) +
-                        mSelectHandleCenter.getIntrinsicHeight();
-            }
-            int x = cursorRect.centerX() - (width / 2);
-            if (x < windowLeft) {
-                x = windowLeft;
-            }
-            if (!isShowing()) {
-                showAtLocation(WebView.this, Gravity.NO_GRAVITY, x, y);
-            }
-            update(x, y, width, height);
-        }
-
-        public void hide() {
-            dismiss();
-        }
-
-        @Override
-        public void onClick(View view) {
-            pasteFromClipboard();
-            selectionDone();
-        }
-
-        protected void measureContent() {
-            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
-            mContentView.measure(
-                    View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
-                            View.MeasureSpec.AT_MOST),
-                    View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
-                            View.MeasureSpec.AT_MOST));
-        }
-    }
-
-    // The listener to capture global layout change event.
-    private InnerGlobalLayoutListener mGlobalLayoutListener = null;
-
-    // The listener to capture scroll event.
-    private InnerScrollChangedListener mScrollChangedListener = null;
-
-    // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
-    // the screen all-the-time. Good for profiling our drawing code
-    static private final boolean AUTO_REDRAW_HACK = false;
-    // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
-    private boolean mAutoRedraw;
-
-    // Reference to the AlertDialog displayed by InvokeListBox.
-    // It's used to dismiss the dialog in destroy if not done before.
-    private AlertDialog mListBoxDialog = null;
-
-    static final String LOGTAG = "webview";
-
-    private ZoomManager mZoomManager;
-
-    private final Rect mGLRectViewport = new Rect();
-    private final Rect mViewRectViewport = new Rect();
-    private final RectF mVisibleContentRect = new RectF();
-    private boolean mGLViewportEmpty = false;
-    WebViewInputConnection mInputConnection = null;
-    private int mFieldPointer;
-    private PastePopupWindow mPasteWindow;
+    private static final String LOGTAG = "webview_proxy";
+    // TODO: flip DEBUG to always be disabled.
+    private static final boolean DEBUG = true;
 
     /**
      *  Transportation object for returning WebView across thread boundaries.
@@ -815,531 +297,6 @@
         }
     }
 
-    private static class OnTrimMemoryListener implements ComponentCallbacks2 {
-        private static OnTrimMemoryListener sInstance = null;
-
-        static void init(Context c) {
-            if (sInstance == null) {
-                sInstance = new OnTrimMemoryListener(c.getApplicationContext());
-            }
-        }
-
-        private OnTrimMemoryListener(Context c) {
-            c.registerComponentCallbacks(this);
-        }
-
-        @Override
-        public void onConfigurationChanged(Configuration newConfig) {
-            // Ignore
-        }
-
-        @Override
-        public void onLowMemory() {
-            // Ignore
-        }
-
-        @Override
-        public void onTrimMemory(int level) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.d("WebView", "onTrimMemory: " + level);
-            }
-            // When framework reset EGL context during high memory pressure, all
-            // the existing GL resources for the html5 video will be destroyed
-            // at native side.
-            // Here we just need to clean up the Surface Texture which is static.
-            HTML5VideoInline.cleanupSurfaceTexture();
-            WebView.nativeOnTrimMemory(level);
-        }
-
-    }
-
-    // A final CallbackProxy shared by WebViewCore and BrowserFrame.
-    private final CallbackProxy mCallbackProxy;
-
-    private final WebViewDatabase mDatabase;
-
-    // SSL certificate for the main top-level page (if secure)
-    private SslCertificate mCertificate;
-
-    // Native WebView pointer that is 0 until the native object has been
-    // created.
-    private int mNativeClass;
-    // This would be final but it needs to be set to null when the WebView is
-    // destroyed.
-    private WebViewCore mWebViewCore;
-    // Handler for dispatching UI messages.
-    /* package */ final Handler mPrivateHandler = new PrivateHandler();
-    private WebTextView mWebTextView;
-    // Used to ignore changes to webkit text that arrives to the UI side after
-    // more key events.
-    private int mTextGeneration;
-
-    /* package */ void incrementTextGeneration() { mTextGeneration++; }
-
-    // Used by WebViewCore to create child views.
-    /* package */ final ViewManager mViewManager;
-
-    // Used to display in full screen mode
-    PluginFullScreenHolder mFullScreenHolder;
-
-    /**
-     * Position of the last touch event in pixels.
-     * Use integer to prevent loss of dragging delta calculation accuracy;
-     * which was done in float and converted to integer, and resulted in gradual
-     * and compounding touch position and view dragging mismatch.
-     */
-    private int mLastTouchX;
-    private int mLastTouchY;
-    private int mStartTouchX;
-    private int mStartTouchY;
-    private float mAverageAngle;
-
-    /**
-     * Time of the last touch event.
-     */
-    private long mLastTouchTime;
-
-    /**
-     * Time of the last time sending touch event to WebViewCore
-     */
-    private long mLastSentTouchTime;
-
-    /**
-     * The minimum elapsed time before sending another ACTION_MOVE event to
-     * WebViewCore. This really should be tuned for each type of the devices.
-     * For example in Google Map api test case, it takes Dream device at least
-     * 150ms to do a full cycle in the WebViewCore by processing a touch event,
-     * triggering the layout and drawing the picture. While the same process
-     * takes 60+ms on the current high speed device. If we make
-     * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
-     * to WebViewCore queue and the real layout and draw events will be pushed
-     * to further, which slows down the refresh rate. Choose 50 to favor the
-     * current high speed devices. For Dream like devices, 100 is a better
-     * choice. Maybe make this in the buildspec later.
-     * (Update 12/14/2010: changed to 0 since current device should be able to
-     * handle the raw events and Map team voted to have the raw events too.
-     */
-    private static final int TOUCH_SENT_INTERVAL = 0;
-    private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
-
-    /**
-     * Helper class to get velocity for fling
-     */
-    VelocityTracker mVelocityTracker;
-    private int mMaximumFling;
-    private float mLastVelocity;
-    private float mLastVelX;
-    private float mLastVelY;
-
-    // The id of the native layer being scrolled.
-    private int mCurrentScrollingLayerId;
-    private Rect mScrollingLayerRect = new Rect();
-
-    // only trigger accelerated fling if the new velocity is at least
-    // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
-    private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
-
-    /**
-     * Touch mode
-     */
-    private int mTouchMode = TOUCH_DONE_MODE;
-    private static final int TOUCH_INIT_MODE = 1;
-    private static final int TOUCH_DRAG_START_MODE = 2;
-    private static final int TOUCH_DRAG_MODE = 3;
-    private static final int TOUCH_SHORTPRESS_START_MODE = 4;
-    private static final int TOUCH_SHORTPRESS_MODE = 5;
-    private static final int TOUCH_DOUBLE_TAP_MODE = 6;
-    private static final int TOUCH_DONE_MODE = 7;
-    private static final int TOUCH_PINCH_DRAG = 8;
-    private static final int TOUCH_DRAG_LAYER_MODE = 9;
-
-    // Whether to forward the touch events to WebCore
-    // Can only be set by WebKit via JNI.
-    private boolean mForwardTouchEvents = false;
-
-    // Whether to prevent default during touch. The initial value depends on
-    // mForwardTouchEvents. If WebCore wants all the touch events, it says yes
-    // for touch down. Otherwise UI will wait for the answer of the first
-    // confirmed move before taking over the control.
-    private static final int PREVENT_DEFAULT_NO = 0;
-    private static final int PREVENT_DEFAULT_MAYBE_YES = 1;
-    private static final int PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN = 2;
-    private static final int PREVENT_DEFAULT_YES = 3;
-    private static final int PREVENT_DEFAULT_IGNORE = 4;
-    private int mPreventDefault = PREVENT_DEFAULT_IGNORE;
-
-    // true when the touch movement exceeds the slop
-    private boolean mConfirmMove;
-
-    // if true, touch events will be first processed by WebCore, if prevent
-    // default is not set, the UI will continue handle them.
-    private boolean mDeferTouchProcess;
-
-    // to avoid interfering with the current touch events, track them
-    // separately. Currently no snapping or fling in the deferred process mode
-    private int mDeferTouchMode = TOUCH_DONE_MODE;
-    private float mLastDeferTouchX;
-    private float mLastDeferTouchY;
-
-    // To keep track of whether the current drag was initiated by a WebTextView,
-    // so that we know not to hide the cursor
-    boolean mDragFromTextInput;
-
-    // Whether or not to draw the cursor ring.
-    private boolean mDrawCursorRing = true;
-
-    // true if onPause has been called (and not onResume)
-    private boolean mIsPaused;
-
-    private HitTestResult mInitialHitTestResult;
-    private WebKitHitTest mFocusedNode;
-
-    /**
-     * Customizable constant
-     */
-    // pre-computed square of ViewConfiguration.getScaledTouchSlop()
-    private int mTouchSlopSquare;
-    // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
-    private int mDoubleTapSlopSquare;
-    // pre-computed density adjusted navigation slop
-    private int mNavSlop;
-    // This should be ViewConfiguration.getTapTimeout()
-    // But system time out is 100ms, which is too short for the browser.
-    // In the browser, if it switches out of tap too soon, jump tap won't work.
-    // In addition, a double tap on a trackpad will always have a duration of
-    // 300ms, so this value must be at least that (otherwise we will timeout the
-    // first tap and convert it to a long press).
-    private static final int TAP_TIMEOUT = 300;
-    // This should be ViewConfiguration.getLongPressTimeout()
-    // But system time out is 500ms, which is too short for the browser.
-    // With a short timeout, it's difficult to treat trigger a short press.
-    private static final int LONG_PRESS_TIMEOUT = 1000;
-    // needed to avoid flinging after a pause of no movement
-    private static final int MIN_FLING_TIME = 250;
-    // draw unfiltered after drag is held without movement
-    private static final int MOTIONLESS_TIME = 100;
-    // The amount of content to overlap between two screens when going through
-    // pages with the space bar, in pixels.
-    private static final int PAGE_SCROLL_OVERLAP = 24;
-
-    /**
-     * These prevent calling requestLayout if either dimension is fixed. This
-     * depends on the layout parameters and the measure specs.
-     */
-    boolean mWidthCanMeasure;
-    boolean mHeightCanMeasure;
-
-    // Remember the last dimensions we sent to the native side so we can avoid
-    // sending the same dimensions more than once.
-    int mLastWidthSent;
-    int mLastHeightSent;
-    // Since view height sent to webkit could be fixed to avoid relayout, this
-    // value records the last sent actual view height.
-    int mLastActualHeightSent;
-
-    private int mContentWidth;   // cache of value from WebViewCore
-    private int mContentHeight;  // cache of value from WebViewCore
-
-    // Need to have the separate control for horizontal and vertical scrollbar
-    // style than the View's single scrollbar style
-    private boolean mOverlayHorizontalScrollbar = true;
-    private boolean mOverlayVerticalScrollbar = false;
-
-    // our standard speed. this way small distances will be traversed in less
-    // time than large distances, but we cap the duration, so that very large
-    // distances won't take too long to get there.
-    private static final int STD_SPEED = 480;  // pixels per second
-    // time for the longest scroll animation
-    private static final int MAX_DURATION = 750;   // milliseconds
-    private static final int SLIDE_TITLE_DURATION = 500;   // milliseconds
-
-    // Used by OverScrollGlow
-    OverScroller mScroller;
-
-    private boolean mInOverScrollMode = false;
-    private static Paint mOverScrollBackground;
-    private static Paint mOverScrollBorder;
-
-    private boolean mWrapContent;
-    private static final int MOTIONLESS_FALSE           = 0;
-    private static final int MOTIONLESS_PENDING         = 1;
-    private static final int MOTIONLESS_TRUE            = 2;
-    private static final int MOTIONLESS_IGNORE          = 3;
-    private int mHeldMotionless;
-
-    // An instance for injecting accessibility in WebViews with disabled
-    // JavaScript or ones for which no accessibility script exists
-    private AccessibilityInjector mAccessibilityInjector;
-
-    // flag indicating if accessibility script is injected so we
-    // know to handle Shift and arrows natively first
-    private boolean mAccessibilityScriptInjected;
-
-
-    /**
-     * How long the caret handle will last without being touched.
-     */
-    private static final long CARET_HANDLE_STAMINA_MS = 3000;
-
-    private Drawable mSelectHandleLeft;
-    private Drawable mSelectHandleRight;
-    private Drawable mSelectHandleCenter;
-    private Rect mSelectCursorBase = new Rect();
-    private int mSelectCursorBaseLayerId;
-    private Rect mSelectCursorExtent = new Rect();
-    private int mSelectCursorExtentLayerId;
-    private Rect mSelectDraggingCursor;
-    private Point mSelectDraggingOffset = new Point();
-    private boolean mIsCaretSelection;
-    static final int HANDLE_ID_START = 0;
-    static final int HANDLE_ID_END = 1;
-    static final int HANDLE_ID_BASE = 2;
-    static final int HANDLE_ID_EXTENT = 3;
-
-    static boolean sDisableNavcache = false;
-    static boolean sEnableWebTextView = false;
-    // the color used to highlight the touch rectangles
-    static final int HIGHLIGHT_COLOR = 0x6633b5e5;
-    // the region indicating where the user touched on the screen
-    private Region mTouchHighlightRegion = new Region();
-    // the paint for the touch highlight
-    private Paint mTouchHightlightPaint = new Paint();
-    // debug only
-    private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
-    private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
-    private Paint mTouchCrossHairColor;
-    private int mTouchHighlightX;
-    private int mTouchHighlightY;
-    private long mTouchHighlightRequested;
-
-    // Basically this proxy is used to tell the Video to update layer tree at
-    // SetBaseLayer time and to pause when WebView paused.
-    private HTML5VideoViewProxy mHTML5VideoViewProxy;
-
-    // If we are using a set picture, don't send view updates to webkit
-    private boolean mBlockWebkitViewMessages = false;
-
-    // cached value used to determine if we need to switch drawing models
-    private boolean mHardwareAccelSkia = false;
-
-    /*
-     * Private message ids
-     */
-    private static final int REMEMBER_PASSWORD          = 1;
-    private static final int NEVER_REMEMBER_PASSWORD    = 2;
-    private static final int SWITCH_TO_SHORTPRESS       = 3;
-    private static final int SWITCH_TO_LONGPRESS        = 4;
-    private static final int RELEASE_SINGLE_TAP         = 5;
-    private static final int REQUEST_FORM_DATA          = 6;
-    private static final int DRAG_HELD_MOTIONLESS       = 8;
-    private static final int AWAKEN_SCROLL_BARS         = 9;
-    private static final int PREVENT_DEFAULT_TIMEOUT    = 10;
-    private static final int SCROLL_SELECT_TEXT         = 11;
-
-
-    private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
-    private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
-
-    /*
-     * Package message ids
-     */
-    static final int SCROLL_TO_MSG_ID                   = 101;
-    static final int NEW_PICTURE_MSG_ID                 = 105;
-    static final int UPDATE_TEXT_ENTRY_MSG_ID           = 106;
-    static final int WEBCORE_INITIALIZED_MSG_ID         = 107;
-    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
-    static final int UPDATE_ZOOM_RANGE                  = 109;
-    static final int UNHANDLED_NAV_KEY                  = 110;
-    static final int CLEAR_TEXT_ENTRY                   = 111;
-    static final int UPDATE_TEXT_SELECTION_MSG_ID       = 112;
-    static final int SHOW_RECT_MSG_ID                   = 113;
-    static final int LONG_PRESS_CENTER                  = 114;
-    static final int PREVENT_TOUCH_ID                   = 115;
-    static final int WEBCORE_NEED_TOUCH_EVENTS          = 116;
-    // obj=Rect in doc coordinates
-    static final int INVAL_RECT_MSG_ID                  = 117;
-    static final int REQUEST_KEYBOARD                   = 118;
-    static final int DO_MOTION_UP                       = 119;
-    static final int SHOW_FULLSCREEN                    = 120;
-    static final int HIDE_FULLSCREEN                    = 121;
-    static final int DOM_FOCUS_CHANGED                  = 122;
-    static final int REPLACE_BASE_CONTENT               = 123;
-    static final int FORM_DID_BLUR                      = 124;
-    static final int RETURN_LABEL                       = 125;
-    static final int UPDATE_MATCH_COUNT                 = 126;
-    static final int CENTER_FIT_RECT                    = 127;
-    static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
-    static final int SET_SCROLLBAR_MODES                = 129;
-    static final int SELECTION_STRING_CHANGED           = 130;
-    static final int HIT_TEST_RESULT                    = 131;
-    static final int SAVE_WEBARCHIVE_FINISHED           = 132;
-
-    static final int SET_AUTOFILLABLE                   = 133;
-    static final int AUTOFILL_COMPLETE                  = 134;
-
-    static final int SELECT_AT                          = 135;
-    static final int SCREEN_ON                          = 136;
-    static final int ENTER_FULLSCREEN_VIDEO             = 137;
-    static final int UPDATE_SELECTION                   = 138;
-    static final int UPDATE_ZOOM_DENSITY                = 139;
-    static final int EXIT_FULLSCREEN_VIDEO              = 140;
-
-    static final int COPY_TO_CLIPBOARD                  = 141;
-    static final int INIT_EDIT_FIELD                    = 142;
-    static final int REPLACE_TEXT                       = 143;
-    static final int CLEAR_CARET_HANDLE                 = 144;
-    static final int KEY_PRESS                          = 145;
-
-    private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
-    private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
-
-    static final String[] HandlerPrivateDebugString = {
-        "REMEMBER_PASSWORD", //              = 1;
-        "NEVER_REMEMBER_PASSWORD", //        = 2;
-        "SWITCH_TO_SHORTPRESS", //           = 3;
-        "SWITCH_TO_LONGPRESS", //            = 4;
-        "RELEASE_SINGLE_TAP", //             = 5;
-        "REQUEST_FORM_DATA", //              = 6;
-        "RESUME_WEBCORE_PRIORITY", //        = 7;
-        "DRAG_HELD_MOTIONLESS", //           = 8;
-        "AWAKEN_SCROLL_BARS", //             = 9;
-        "PREVENT_DEFAULT_TIMEOUT", //        = 10;
-        "SCROLL_SELECT_TEXT" //              = 11;
-    };
-
-    static final String[] HandlerPackageDebugString = {
-        "SCROLL_TO_MSG_ID", //               = 101;
-        "102", //                            = 102;
-        "103", //                            = 103;
-        "104", //                            = 104;
-        "NEW_PICTURE_MSG_ID", //             = 105;
-        "UPDATE_TEXT_ENTRY_MSG_ID", //       = 106;
-        "WEBCORE_INITIALIZED_MSG_ID", //     = 107;
-        "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 108;
-        "UPDATE_ZOOM_RANGE", //              = 109;
-        "UNHANDLED_NAV_KEY", //              = 110;
-        "CLEAR_TEXT_ENTRY", //               = 111;
-        "UPDATE_TEXT_SELECTION_MSG_ID", //   = 112;
-        "SHOW_RECT_MSG_ID", //               = 113;
-        "LONG_PRESS_CENTER", //              = 114;
-        "PREVENT_TOUCH_ID", //               = 115;
-        "WEBCORE_NEED_TOUCH_EVENTS", //      = 116;
-        "INVAL_RECT_MSG_ID", //              = 117;
-        "REQUEST_KEYBOARD", //               = 118;
-        "DO_MOTION_UP", //                   = 119;
-        "SHOW_FULLSCREEN", //                = 120;
-        "HIDE_FULLSCREEN", //                = 121;
-        "DOM_FOCUS_CHANGED", //              = 122;
-        "REPLACE_BASE_CONTENT", //           = 123;
-        "FORM_DID_BLUR", //                  = 124;
-        "RETURN_LABEL", //                   = 125;
-        "UPDATE_MATCH_COUNT", //             = 126;
-        "CENTER_FIT_RECT", //                = 127;
-        "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
-        "SET_SCROLLBAR_MODES", //            = 129;
-        "SELECTION_STRING_CHANGED", //       = 130;
-        "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
-        "SAVE_WEBARCHIVE_FINISHED", //       = 132;
-        "SET_AUTOFILLABLE", //               = 133;
-        "AUTOFILL_COMPLETE", //              = 134;
-        "SELECT_AT", //                      = 135;
-        "SCREEN_ON", //                      = 136;
-        "ENTER_FULLSCREEN_VIDEO", //         = 137;
-        "UPDATE_SELECTION", //               = 138;
-        "UPDATE_ZOOM_DENSITY" //             = 139;
-    };
-
-    // If the site doesn't use the viewport meta tag to specify the viewport,
-    // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
-    static final int DEFAULT_VIEWPORT_WIDTH = 980;
-
-    // normally we try to fit the content to the minimum preferred width
-    // calculated by the Webkit. To avoid the bad behavior when some site's
-    // minimum preferred width keeps growing when changing the viewport width or
-    // the minimum preferred width is huge, an upper limit is needed.
-    static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
-
-    // initial scale in percent. 0 means using default.
-    private int mInitialScaleInPercent = 0;
-
-    // Whether or not a scroll event should be sent to webkit.  This is only set
-    // to false when restoring the scroll position.
-    private boolean mSendScrollEvent = true;
-
-    private int mSnapScrollMode = SNAP_NONE;
-    private static final int SNAP_NONE = 0;
-    private static final int SNAP_LOCK = 1; // not a separate state
-    private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
-    private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
-    private boolean mSnapPositive;
-
-    // keep these in sync with their counterparts in WebView.cpp
-    private static final int DRAW_EXTRAS_NONE = 0;
-    private static final int DRAW_EXTRAS_SELECTION = 1;
-    private static final int DRAW_EXTRAS_CURSOR_RING = 2;
-
-    // keep this in sync with WebCore:ScrollbarMode in WebKit
-    private static final int SCROLLBAR_AUTO = 0;
-    private static final int SCROLLBAR_ALWAYSOFF = 1;
-    // as we auto fade scrollbar, this is ignored.
-    private static final int SCROLLBAR_ALWAYSON = 2;
-    private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
-    private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
-
-    // constants for determining script injection strategy
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
-
-    // the alias via which accessibility JavaScript interface is exposed
-    private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
-
-    // Template for JavaScript that injects a screen-reader.
-    private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
-        "javascript:(function() {" +
-        "    var chooser = document.createElement('script');" +
-        "    chooser.type = 'text/javascript';" +
-        "    chooser.src = '%1s';" +
-        "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
-        "  })();";
-
-    // Regular expression that matches the "axs" URL parameter.
-    // The value of 0 means the accessibility script is opted out
-    // The value of 1 means the accessibility script is already injected
-    private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))";
-
-    // TextToSpeech instance exposed to JavaScript to the injected screenreader.
-    private TextToSpeech mTextToSpeech;
-
-    // variable to cache the above pattern in case accessibility is enabled.
-    private Pattern mMatchAxsUrlParameterPattern;
-
-    /**
-     * Max distance to overscroll by in pixels.
-     * This how far content can be pulled beyond its normal bounds by the user.
-     */
-    private int mOverscrollDistance;
-
-    /**
-     * Max distance to overfling by in pixels.
-     * This is how far flinged content can move beyond the end of its normal bounds.
-     */
-    private int mOverflingDistance;
-
-    private OverScrollGlow mOverScrollGlow;
-
-    // Used to match key downs and key ups
-    private Vector<Integer> mKeysPressed;
-
-    /* package */ static boolean mLogEvent = true;
-
-    // for event log
-    private long mLastTouchUpTime = 0;
-
-    private WebViewCore.AutoFillData mAutoFillData;
-
-    private static boolean sNotificationsEnabled = true;
-
     /**
      * URI scheme for telephone number
      */
@@ -1353,26 +310,6 @@
      */
     public static final String SCHEME_GEO = "geo:0,0?q=";
 
-    private int mBackgroundColor = Color.WHITE;
-
-    private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
-    private int mAutoScrollX = 0;
-    private int mAutoScrollY = 0;
-    private int mMinAutoScrollX = 0;
-    private int mMaxAutoScrollX = 0;
-    private int mMinAutoScrollY = 0;
-    private int mMaxAutoScrollY = 0;
-    private Rect mScrollingLayerBounds = new Rect();
-    private boolean mSentAutoScrollMessage = false;
-
-    // used for serializing asynchronously handled touch events.
-    private final TouchEventQueue mTouchEventQueue = new TouchEventQueue();
-
-    // Used to track whether picture updating was paused due to a window focus change.
-    private boolean mPictureUpdatePausedForFocusChange = false;
-
-    // Used to notify listeners of a new picture.
-    private PictureListener mPictureListener;
     /**
      * Interface to listen for new pictures as they change.
      * @deprecated This interface is now obsolete.
@@ -1440,15 +377,24 @@
         private int mType;
         private String mExtra;
 
-        HitTestResult() {
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        public HitTestResult() {
             mType = UNKNOWN_TYPE;
         }
 
-        private void setType(int type) {
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        public void setType(int type) {
             mType = type;
         }
 
-        private void setExtra(String extra) {
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        public void setExtra(String extra) {
             mExtra = extra;
         }
 
@@ -1471,15 +417,6 @@
     }
 
     /**
-     * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
-     */
-    static class FocusNodeHref {
-        static final String TITLE = "title";
-        static final String URL = "url";
-        static final String SRC = "src";
-    }
-
-    /**
      * Construct a new WebView with a Context object.
      * @param context A Context object used to access application assets.
      */
@@ -1529,418 +466,20 @@
      * @param javaScriptInterfaces is a Map of interface names, as keys, and
      * object implementing those interfaces, as values.
      * @param privateBrowsing If true the web view will be initialized in private mode.
-     * @hide This is an implementation detail.
+     * @hide This is used internally by dumprendertree, as it requires the javaScript interfaces to
+     * be added synchronously, before a subsequent loadUrl call takes effect.
      */
+    @SuppressWarnings("deprecation")  // for super() call into deprecated base class constructor.
     protected WebView(Context context, AttributeSet attrs, int defStyle,
             Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
         super(context, attrs, defStyle);
-        checkThread();
-
         if (context == null) {
             throw new IllegalArgumentException("Invalid context argument");
         }
+        checkThread();
 
-        // Used by the chrome stack to find application paths
-        JniUtil.setContext(context);
-
-        mCallbackProxy = new CallbackProxy(context, this);
-        mViewManager = new ViewManager(this);
-        L10nUtils.setApplicationContext(context.getApplicationContext());
-        mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
-        mDatabase = WebViewDatabase.getInstance(context);
-        mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
-        mZoomManager = new ZoomManager(this, mCallbackProxy);
-
-        /* The init method must follow the creation of certain member variables,
-         * such as the mZoomManager.
-         */
-        init();
-        setupPackageListener(context);
-        setupProxyListener(context);
-        setupTrustStorageListener(context);
-        updateMultiTouchSupport(context);
-
-        if (privateBrowsing) {
-            startPrivateBrowsing();
-        }
-
-        mAutoFillData = new WebViewCore.AutoFillData();
-    }
-
-    private static class TrustStorageListener extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
-                handleCertTrustChanged();
-            }
-        }
-    }
-    private static TrustStorageListener sTrustStorageListener;
-
-    /**
-     * Handles update to the trust storage.
-     */
-    private static void handleCertTrustChanged() {
-        // send a message for indicating trust storage change
-        WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
-    }
-
-    /*
-     * @param context This method expects this to be a valid context.
-     */
-    private static void setupTrustStorageListener(Context context) {
-        if (sTrustStorageListener != null ) {
-            return;
-        }
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
-        sTrustStorageListener = new TrustStorageListener();
-        Intent current =
-            context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
-        if (current != null) {
-            handleCertTrustChanged();
-        }
-    }
-
-    private static class ProxyReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
-                handleProxyBroadcast(intent);
-            }
-        }
-    }
-
-    /*
-     * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
-     */
-    private static ProxyReceiver sProxyReceiver;
-
-    /*
-     * @param context This method expects this to be a valid context
-     */
-    private static synchronized void setupProxyListener(Context context) {
-        if (sProxyReceiver != null || sNotificationsEnabled == false) {
-            return;
-        }
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Proxy.PROXY_CHANGE_ACTION);
-        sProxyReceiver = new ProxyReceiver();
-        Intent currentProxy = context.getApplicationContext().registerReceiver(
-                sProxyReceiver, filter);
-        if (currentProxy != null) {
-            handleProxyBroadcast(currentProxy);
-        }
-    }
-
-    /*
-     * @param context This method expects this to be a valid context
-     */
-    private static synchronized void disableProxyListener(Context context) {
-        if (sProxyReceiver == null)
-            return;
-
-        context.getApplicationContext().unregisterReceiver(sProxyReceiver);
-        sProxyReceiver = null;
-    }
-
-    private static void handleProxyBroadcast(Intent intent) {
-        ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
-        if (proxyProperties == null || proxyProperties.getHost() == null) {
-            WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null);
-            return;
-        }
-        WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties);
-    }
-
-    /*
-     * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
-     * or ACTION_PACKAGE_REMOVED.
-     */
-    private static boolean sPackageInstallationReceiverAdded = false;
-
-    /*
-     * A set of Google packages we monitor for the
-     * navigator.isApplicationInstalled() API. Add additional packages as
-     * needed.
-     */
-    private static Set<String> sGoogleApps;
-    static {
-        sGoogleApps = new HashSet<String>();
-        sGoogleApps.add("com.google.android.youtube");
-    }
-
-    private static class PackageListener extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            final String packageName = intent.getData().getSchemeSpecificPart();
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-            if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
-                // if it is replacing, refreshPlugins() when adding
-                return;
-            }
-
-            if (sGoogleApps.contains(packageName)) {
-                if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
-                } else {
-                    WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
-                }
-            }
-
-            PluginManager pm = PluginManager.getInstance(context);
-            if (pm.containsPluginPermissionAndSignatures(packageName)) {
-                pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
-            }
-        }
-    }
-
-    private void setupPackageListener(Context context) {
-
-        /*
-         * we must synchronize the instance check and the creation of the
-         * receiver to ensure that only ONE receiver exists for all WebView
-         * instances.
-         */
-        synchronized (WebView.class) {
-
-            // if the receiver already exists then we do not need to register it
-            // again
-            if (sPackageInstallationReceiverAdded) {
-                return;
-            }
-
-            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addDataScheme("package");
-            BroadcastReceiver packageListener = new PackageListener();
-            context.getApplicationContext().registerReceiver(packageListener, filter);
-            sPackageInstallationReceiverAdded = true;
-        }
-
-        // check if any of the monitored apps are already installed
-        AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
-
-            @Override
-            protected Set<String> doInBackground(Void... unused) {
-                Set<String> installedPackages = new HashSet<String>();
-                PackageManager pm = mContext.getPackageManager();
-                for (String name : sGoogleApps) {
-                    try {
-                        pm.getPackageInfo(name,
-                                PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
-                        installedPackages.add(name);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        // package not found
-                    }
-                }
-                return installedPackages;
-            }
-
-            // Executes on the UI thread
-            @Override
-            protected void onPostExecute(Set<String> installedPackages) {
-                if (mWebViewCore != null) {
-                    mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
-                }
-            }
-        };
-        task.execute();
-    }
-
-    void updateMultiTouchSupport(Context context) {
-        mZoomManager.updateMultiTouchSupport(context);
-    }
-
-    private void init() {
-        OnTrimMemoryListener.init(getContext());
-        sDisableNavcache = nativeDisableNavcache();
-        setWillNotDraw(false);
-        setFocusable(true);
-        setFocusableInTouchMode(true);
-        setClickable(true);
-        setLongClickable(true);
-
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        int slop = configuration.getScaledTouchSlop();
-        mTouchSlopSquare = slop * slop;
-        slop = configuration.getScaledDoubleTapSlop();
-        mDoubleTapSlopSquare = slop * slop;
-        final float density = getContext().getResources().getDisplayMetrics().density;
-        // use one line height, 16 based on our current default font, for how
-        // far we allow a touch be away from the edge of a link
-        mNavSlop = (int) (16 * density);
-        mZoomManager.init(density);
-        mMaximumFling = configuration.getScaledMaximumFlingVelocity();
-
-        // Compute the inverse of the density squared.
-        DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
-
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
-
-        setScrollBarStyle(super.getScrollBarStyle());
-        // Initially use a size of two, since the user is likely to only hold
-        // down two keys at a time (shift + another key)
-        mKeysPressed = new Vector<Integer>(2);
-        mHTML5VideoViewProxy = null ;
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return true;
-    }
-
-    /**
-     * Adds accessibility APIs to JavaScript.
-     *
-     * Note: This method is responsible to performing the necessary
-     *       check if the accessibility APIs should be exposed.
-     */
-    private void addAccessibilityApisToJavaScript() {
-        if (AccessibilityManager.getInstance(mContext).isEnabled()
-                && getSettings().getJavaScriptEnabled()) {
-            // exposing the TTS for now ...
-            final Context ctx = getContext();
-            if (ctx != null) {
-                final String packageName = ctx.getPackageName();
-                if (packageName != null) {
-                    mTextToSpeech = new TextToSpeech(getContext(), null, null,
-                            packageName + ".**webview**", true);
-                    addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
-                }
-            }
-        }
-    }
-
-    /**
-     * Removes accessibility APIs from JavaScript.
-     */
-    private void removeAccessibilityApisFromJavaScript() {
-        // exposing the TTS for now ...
-        if (mTextToSpeech != null) {
-            removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
-            mTextToSpeech.shutdown();
-            mTextToSpeech = null;
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setScrollable(isScrollableForAccessibility());
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setScrollable(isScrollableForAccessibility());
-        event.setScrollX(mScrollX);
-        event.setScrollY(mScrollY);
-        final int convertedContentWidth = contentToViewX(getContentWidth());
-        final int adjustedViewWidth = getWidth() - mPaddingLeft - mPaddingRight;
-        event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
-        final int convertedContentHeight = contentToViewY(getContentHeight());
-        final int adjustedViewHeight = getHeight() - mPaddingTop - mPaddingBottom;
-        event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
-    }
-
-    private boolean isScrollableForAccessibility() {
-        return (contentToViewX(getContentWidth()) > getWidth() - mPaddingLeft - mPaddingRight
-                || contentToViewY(getContentHeight()) > getHeight() - mPaddingTop - mPaddingBottom);
-    }
-
-    @Override
-    public void setOverScrollMode(int mode) {
-        super.setOverScrollMode(mode);
-        if (mode != OVER_SCROLL_NEVER) {
-            if (mOverScrollGlow == null) {
-                mOverScrollGlow = new OverScrollGlow(this);
-            }
-        } else {
-            mOverScrollGlow = null;
-        }
-    }
-
-    /* package */ void adjustDefaultZoomDensity(int zoomDensity) {
-        final float density = mContext.getResources().getDisplayMetrics().density
-                * 100 / zoomDensity;
-        updateDefaultZoomDensity(density);
-    }
-
-    /* package */ void updateDefaultZoomDensity(float density) {
-        mNavSlop = (int) (16 * density);
-        mZoomManager.updateDefaultZoomDensity(density);
-    }
-
-    /* package */ boolean onSavePassword(String schemePlusHost, String username,
-            String password, final Message resumeMsg) {
-       boolean rVal = false;
-       if (resumeMsg == null) {
-           // null resumeMsg implies saving password silently
-           mDatabase.setUsernamePassword(schemePlusHost, username, password);
-       } else {
-            final Message remember = mPrivateHandler.obtainMessage(
-                    REMEMBER_PASSWORD);
-            remember.getData().putString("host", schemePlusHost);
-            remember.getData().putString("username", username);
-            remember.getData().putString("password", password);
-            remember.obj = resumeMsg;
-
-            final Message neverRemember = mPrivateHandler.obtainMessage(
-                    NEVER_REMEMBER_PASSWORD);
-            neverRemember.getData().putString("host", schemePlusHost);
-            neverRemember.getData().putString("username", username);
-            neverRemember.getData().putString("password", password);
-            neverRemember.obj = resumeMsg;
-
-            new AlertDialog.Builder(getContext())
-                    .setTitle(com.android.internal.R.string.save_password_label)
-                    .setMessage(com.android.internal.R.string.save_password_message)
-                    .setPositiveButton(com.android.internal.R.string.save_password_notnow,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            resumeMsg.sendToTarget();
-                        }
-                    })
-                    .setNeutralButton(com.android.internal.R.string.save_password_remember,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            remember.sendToTarget();
-                        }
-                    })
-                    .setNegativeButton(com.android.internal.R.string.save_password_never,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            neverRemember.sendToTarget();
-                        }
-                    })
-                    .setOnCancelListener(new OnCancelListener() {
-                        @Override
-                        public void onCancel(DialogInterface dialog) {
-                            resumeMsg.sendToTarget();
-                        }
-                    }).show();
-            // Return true so that WebViewCore will pause while the dialog is
-            // up.
-            rVal = true;
-        }
-       return rVal;
-    }
-
-    @Override
-    public void setScrollBarStyle(int style) {
-        if (style == View.SCROLLBARS_INSIDE_INSET
-                || style == View.SCROLLBARS_OUTSIDE_INSET) {
-            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
-        } else {
-            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
-        }
-        super.setScrollBarStyle(style);
+        ensureProviderCreated();
+        mProvider.init(javaScriptInterfaces, privateBrowsing);
     }
 
     /**
@@ -1949,7 +488,7 @@
      */
     public void setHorizontalScrollbarOverlay(boolean overlay) {
         checkThread();
-        mOverlayHorizontalScrollbar = overlay;
+        mProvider.setHorizontalScrollbarOverlay(overlay);
     }
 
     /**
@@ -1958,7 +497,7 @@
      */
     public void setVerticalScrollbarOverlay(boolean overlay) {
         checkThread();
-        mOverlayVerticalScrollbar = overlay;
+        mProvider.setVerticalScrollbarOverlay(overlay);
     }
 
     /**
@@ -1967,7 +506,7 @@
      */
     public boolean overlayHorizontalScrollbar() {
         checkThread();
-        return mOverlayHorizontalScrollbar;
+        return mProvider.overlayHorizontalScrollbar();
     }
 
     /**
@@ -1976,80 +515,17 @@
      */
     public boolean overlayVerticalScrollbar() {
         checkThread();
-        return mOverlayVerticalScrollbar;
-    }
-
-    /*
-     * Return the width of the view where the content of WebView should render
-     * to.
-     * Note: this can be called from WebCoreThread.
-     */
-    /* package */ int getViewWidth() {
-        if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
-            return getWidth();
-        } else {
-            return Math.max(0, getWidth() - getVerticalScrollbarWidth());
-        }
-    }
-
-    /**
-     * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
-     * scrolling
-     * @hide
-     */
-    protected int getTitleHeight() {
-        return mTitleBar != null ? mTitleBar.getHeight() : 0;
+        return mProvider.overlayVerticalScrollbar();
     }
 
     /**
      * Return the visible height (in pixels) of the embedded title bar (if any).
      *
-     * @return This method is obsolete and always returns 0.
      * @deprecated This method is now obsolete.
      */
-    @Deprecated
     public int getVisibleTitleHeight() {
-        // Actually, this method returns the height of the embedded title bar if one is set via the
-        // hidden setEmbeddedTitleBar method.
         checkThread();
-        return getVisibleTitleHeightImpl();
-    }
-
-    private int getVisibleTitleHeightImpl() {
-        // need to restrict mScrollY due to over scroll
-        return Math.max(getTitleHeight() - Math.max(0, mScrollY),
-                getOverlappingActionModeHeight());
-    }
-
-    private int mCachedOverlappingActionModeHeight = -1;
-
-    private int getOverlappingActionModeHeight() {
-        if (mFindCallback == null) {
-            return 0;
-        }
-        if (mCachedOverlappingActionModeHeight < 0) {
-            getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
-            mCachedOverlappingActionModeHeight = Math.max(0,
-                    mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
-        }
-        return mCachedOverlappingActionModeHeight;
-    }
-
-    /*
-     * Return the height of the view where the content of WebView should render
-     * to.  Note that this excludes mTitleBar, if there is one.
-     * Note: this can be called from WebCoreThread.
-     */
-    /* package */ int getViewHeight() {
-        return getViewHeightWithTitle() - getVisibleTitleHeightImpl();
-    }
-
-    int getViewHeightWithTitle() {
-        int height = getHeight();
-        if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
-            height -= getHorizontalScrollbarHeight();
-        }
-        return height;
+        return mProvider.getVisibleTitleHeight();
     }
 
     /**
@@ -2058,7 +534,7 @@
      */
     public SslCertificate getCertificate() {
         checkThread();
-        return mCertificate;
+        return mProvider.getCertificate();
     }
 
     /**
@@ -2066,11 +542,7 @@
      */
     public void setCertificate(SslCertificate certificate) {
         checkThread();
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "setCertificate=" + certificate);
-        }
-        // here, the certificate can be null (if the site is not secure)
-        mCertificate = certificate;
+        mProvider.setCertificate(certificate);
     }
 
     //-------------------------------------------------------------------------
@@ -2086,7 +558,7 @@
      */
     public void savePassword(String host, String username, String password) {
         checkThread();
-        mDatabase.setUsernamePassword(host, username, password);
+        mProvider.savePassword(host, username, password);
     }
 
     /**
@@ -2101,7 +573,7 @@
     public void setHttpAuthUsernamePassword(String host, String realm,
             String username, String password) {
         checkThread();
-        mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
+        mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
     }
 
     /**
@@ -2115,38 +587,7 @@
      */
     public String[] getHttpAuthUsernamePassword(String host, String realm) {
         checkThread();
-        return mDatabase.getHttpAuthUsernamePassword(host, realm);
-    }
-
-    /**
-     * Remove Find or Select ActionModes, if active.
-     */
-    private void clearActionModes() {
-        if (mSelectCallback != null) {
-            mSelectCallback.finish();
-        }
-        if (mFindCallback != null) {
-            mFindCallback.finish();
-        }
-    }
-
-    /**
-     * Called to clear state when moving from one page to another, or changing
-     * in some other way that makes elements associated with the current page
-     * (such as WebTextView or ActionModes) no longer relevant.
-     */
-    private void clearHelpers() {
-        clearTextEntry();
-        clearActionModes();
-        dismissFullScreenMode();
-        cancelSelectDialog();
-    }
-
-    private void cancelSelectDialog() {
-        if (mListBoxDialog != null) {
-            mListBoxDialog.cancel();
-            mListBoxDialog = null;
-        }
+        return mProvider.getHttpAuthUsernamePassword(host, realm);
     }
 
     /**
@@ -2156,45 +597,7 @@
      */
     public void destroy() {
         checkThread();
-        destroyImpl();
-    }
-
-    private void destroyImpl() {
-        clearHelpers();
-        if (mListBoxDialog != null) {
-            mListBoxDialog.dismiss();
-            mListBoxDialog = null;
-        }
-        // remove so that it doesn't cause events
-        if (mWebTextView != null) {
-            mWebTextView.remove();
-            mWebTextView = null;
-        }
-        if (mNativeClass != 0) nativeStopGL();
-        if (mWebViewCore != null) {
-            // Set the handlers to null before destroying WebViewCore so no
-            // more messages will be posted.
-            mCallbackProxy.setWebViewClient(null);
-            mCallbackProxy.setWebChromeClient(null);
-            // Tell WebViewCore to destroy itself
-            synchronized (this) {
-                WebViewCore webViewCore = mWebViewCore;
-                mWebViewCore = null; // prevent using partial webViewCore
-                webViewCore.destroy();
-            }
-            // Remove any pending messages that might not be serviced yet.
-            mPrivateHandler.removeCallbacksAndMessages(null);
-            mCallbackProxy.removeCallbacksAndMessages(null);
-            // Wake up the WebCore thread just in case it is waiting for a
-            // JavaScript dialog.
-            synchronized (mCallbackProxy) {
-                mCallbackProxy.notify();
-            }
-        }
-        if (mNativeClass != 0) {
-            nativeDestroy();
-            mNativeClass = 0;
-        }
+        mProvider.destroy();
     }
 
     /**
@@ -2206,12 +609,7 @@
     @Deprecated
     public static void enablePlatformNotifications() {
         checkThread();
-        synchronized (WebView.class) {
-            sNotificationsEnabled = true;
-            Context context = JniUtil.getContext();
-            if (context != null)
-                setupProxyListener(context);
-        }
+        getFactory().getStatics().setPlatformNotificationsEnabled(true);
     }
 
     /**
@@ -2223,24 +621,7 @@
     @Deprecated
     public static void disablePlatformNotifications() {
         checkThread();
-        synchronized (WebView.class) {
-            sNotificationsEnabled = false;
-            Context context = JniUtil.getContext();
-            if (context != null)
-                disableProxyListener(context);
-        }
-    }
-
-    /**
-     * Sets JavaScript engine flags.
-     *
-     * @param flags JS engine flags in a String
-     *
-     * @hide This is an implementation detail.
-     */
-    public void setJsFlags(String flags) {
-        checkThread();
-        mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
+        getFactory().getStatics().setPlatformNotificationsEnabled(false);
     }
 
     /**
@@ -2251,22 +632,10 @@
      */
     public void setNetworkAvailable(boolean networkUp) {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
-                networkUp ? 1 : 0, 0);
+        mProvider.setNetworkAvailable(networkUp);
     }
 
     /**
-     * Inform WebView about the current network type.
-     * {@hide}
-     */
-    public void setNetworkType(String type, String subtype) {
-        checkThread();
-        Map<String, String> map = new HashMap<String, String>();
-        map.put("type", type);
-        map.put("subtype", subtype);
-        mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
-    }
-    /**
      * Save the state of this WebView used in
      * {@link android.app.Activity#onSaveInstanceState}. Please note that this
      * method no longer stores the display data for this WebView. The previous
@@ -2281,49 +650,7 @@
      */
     public WebBackForwardList saveState(Bundle outState) {
         checkThread();
-        if (outState == null) {
-            return null;
-        }
-        // We grab a copy of the back/forward list because a client of WebView
-        // may have invalidated the history list by calling clearHistory.
-        WebBackForwardList list = copyBackForwardList();
-        final int currentIndex = list.getCurrentIndex();
-        final int size = list.getSize();
-        // We should fail saving the state if the list is empty or the index is
-        // not in a valid range.
-        if (currentIndex < 0 || currentIndex >= size || size == 0) {
-            return null;
-        }
-        outState.putInt("index", currentIndex);
-        // FIXME: This should just be a byte[][] instead of ArrayList but
-        // Parcel.java does not have the code to handle multi-dimensional
-        // arrays.
-        ArrayList<byte[]> history = new ArrayList<byte[]>(size);
-        for (int i = 0; i < size; i++) {
-            WebHistoryItem item = list.getItemAtIndex(i);
-            if (null == item) {
-                // FIXME: this shouldn't happen
-                // need to determine how item got set to null
-                Log.w(LOGTAG, "saveState: Unexpected null history item.");
-                return null;
-            }
-            byte[] data = item.getFlattenedData();
-            if (data == null) {
-                // It would be very odd to not have any data for a given history
-                // item. And we will fail to rebuild the history list without
-                // flattened data.
-                return null;
-            }
-            history.add(data);
-        }
-        outState.putSerializable("history", history);
-        if (mCertificate != null) {
-            outState.putBundle("certificate",
-                               SslCertificate.saveState(mCertificate));
-        }
-        outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
-        mZoomManager.saveZoomState(outState);
-        return list;
+        return mProvider.saveState(outState);
     }
 
     /**
@@ -2338,59 +665,7 @@
     @Deprecated
     public boolean savePicture(Bundle b, final File dest) {
         checkThread();
-        if (dest == null || b == null) {
-            return false;
-        }
-        final Picture p = capturePicture();
-        // Use a temporary file while writing to ensure the destination file
-        // contains valid data.
-        final File temp = new File(dest.getPath() + ".writing");
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                FileOutputStream out = null;
-                try {
-                    out = new FileOutputStream(temp);
-                    p.writeToStream(out);
-                    // Writing the picture succeeded, rename the temporary file
-                    // to the destination.
-                    temp.renameTo(dest);
-                } catch (Exception e) {
-                    // too late to do anything about it.
-                } finally {
-                    if (out != null) {
-                        try {
-                            out.close();
-                        } catch (Exception e) {
-                            // Can't do anything about that
-                        }
-                    }
-                    temp.delete();
-                }
-            }
-        }).start();
-        // now update the bundle
-        b.putInt("scrollX", mScrollX);
-        b.putInt("scrollY", mScrollY);
-        mZoomManager.saveZoomState(b);
-        return true;
-    }
-
-    private void restoreHistoryPictureFields(Picture p, Bundle b) {
-        int sx = b.getInt("scrollX", 0);
-        int sy = b.getInt("scrollY", 0);
-
-        mDrawHistory = true;
-        mHistoryPicture = p;
-
-        mScrollX = sx;
-        mScrollY = sy;
-        mZoomManager.restoreZoomState(b);
-        final float scale = mZoomManager.getScale();
-        mHistoryWidth = Math.round(p.getWidth() * scale);
-        mHistoryHeight = Math.round(p.getHeight() * scale);
-
-        invalidate();
+        return mProvider.savePicture(b, dest);
     }
 
     /**
@@ -2406,91 +681,7 @@
     @Deprecated
     public boolean restorePicture(Bundle b, File src) {
         checkThread();
-        if (src == null || b == null) {
-            return false;
-        }
-        if (!src.exists()) {
-            return false;
-        }
-        try {
-            final FileInputStream in = new FileInputStream(src);
-            final Bundle copy = new Bundle(b);
-            new Thread(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        final Picture p = Picture.createFromStream(in);
-                        if (p != null) {
-                            // Post a runnable on the main thread to update the
-                            // history picture fields.
-                            mPrivateHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    restoreHistoryPictureFields(p, copy);
-                                }
-                            });
-                        }
-                    } finally {
-                        try {
-                            in.close();
-                        } catch (Exception e) {
-                            // Nothing we can do now.
-                        }
-                    }
-                }
-            }).start();
-        } catch (FileNotFoundException e){
-            e.printStackTrace();
-        }
-        return true;
-    }
-
-    /**
-     * Saves the view data to the output stream. The output is highly
-     * version specific, and may not be able to be loaded by newer versions
-     * of WebView.
-     * @param stream The {@link OutputStream} to save to
-     * @return True if saved successfully
-     * @hide
-     */
-    public boolean saveViewState(OutputStream stream) {
-        try {
-            return ViewStateSerializer.serializeViewState(stream, this);
-        } catch (IOException e) {
-            Log.w(LOGTAG, "Failed to saveViewState", e);
-        }
-        return false;
-    }
-
-    /**
-     * Loads the view data from the input stream. See
-     * {@link #saveViewState(OutputStream)} for more information.
-     * @param stream The {@link InputStream} to load from
-     * @return True if loaded successfully
-     * @hide
-     */
-    public boolean loadViewState(InputStream stream) {
-        try {
-            mLoadedPicture = ViewStateSerializer.deserializeViewState(stream, this);
-            mBlockWebkitViewMessages = true;
-            setNewPicture(mLoadedPicture, true);
-            mLoadedPicture.mViewState = null;
-            return true;
-        } catch (IOException e) {
-            Log.w(LOGTAG, "Failed to loadViewState", e);
-        }
-        return false;
-    }
-
-    /**
-     * Clears the view state set with {@link #loadViewState(InputStream)}.
-     * This WebView will then switch to showing the content from webkit
-     * @hide
-     */
-    public void clearViewState() {
-        mBlockWebkitViewMessages = false;
-        mLoadedPicture = null;
-        invalidate();
+        return mProvider.restorePicture(b, src);
     }
 
     /**
@@ -2509,55 +700,7 @@
      */
     public WebBackForwardList restoreState(Bundle inState) {
         checkThread();
-        WebBackForwardList returnList = null;
-        if (inState == null) {
-            return returnList;
-        }
-        if (inState.containsKey("index") && inState.containsKey("history")) {
-            mCertificate = SslCertificate.restoreState(
-                inState.getBundle("certificate"));
-
-            final WebBackForwardList list = mCallbackProxy.getBackForwardList();
-            final int index = inState.getInt("index");
-            // We can't use a clone of the list because we need to modify the
-            // shared copy, so synchronize instead to prevent concurrent
-            // modifications.
-            synchronized (list) {
-                final List<byte[]> history =
-                        (List<byte[]>) inState.getSerializable("history");
-                final int size = history.size();
-                // Check the index bounds so we don't crash in native code while
-                // restoring the history index.
-                if (index < 0 || index >= size) {
-                    return null;
-                }
-                for (int i = 0; i < size; i++) {
-                    byte[] data = history.remove(0);
-                    if (data == null) {
-                        // If we somehow have null data, we cannot reconstruct
-                        // the item and thus our history list cannot be rebuilt.
-                        return null;
-                    }
-                    WebHistoryItem item = new WebHistoryItem(data);
-                    list.addHistoryItem(item);
-                }
-                // Grab the most recent copy to return to the caller.
-                returnList = copyBackForwardList();
-                // Update the copy to have the correct index.
-                returnList.setCurrentIndex(index);
-            }
-            // Restore private browsing setting.
-            if (inState.getBoolean("privateBrowsingEnabled")) {
-                getSettings().setPrivateBrowsingEnabled(true);
-            }
-            mZoomManager.restoreZoomState(inState);
-            // Remove all pending messages because we are restoring previous
-            // state.
-            mWebViewCore.removeMessages();
-            // Send a restore state message.
-            mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
-        }
-        return returnList;
+        return mProvider.restoreState(inState);
     }
 
     /**
@@ -2572,16 +715,7 @@
      */
     public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
         checkThread();
-        loadUrlImpl(url, additionalHttpHeaders);
-    }
-
-    private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
-        switchOutDrawHistory();
-        WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
-        arg.mUrl = url;
-        arg.mExtraHeaders = extraHeaders;
-        mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
-        clearHelpers();
+        mProvider.loadUrl(url, additionalHttpHeaders);
     }
 
     /**
@@ -2590,14 +724,7 @@
      */
     public void loadUrl(String url) {
         checkThread();
-        loadUrlImpl(url);
-    }
-
-    private void loadUrlImpl(String url) {
-        if (url == null) {
-            return;
-        }
-        loadUrlImpl(url, null);
+        mProvider.loadUrl(url);
     }
 
     /**
@@ -2610,16 +737,7 @@
      */
     public void postUrl(String url, byte[] postData) {
         checkThread();
-        if (URLUtil.isNetworkUrl(url)) {
-            switchOutDrawHistory();
-            WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
-            arg.mUrl = url;
-            arg.mPostData = postData;
-            mWebViewCore.sendMessage(EventHub.POST_URL, arg);
-            clearHelpers();
-        } else {
-            loadUrlImpl(url);
-        }
+        mProvider.postUrl(url, postData);
     }
 
     /**
@@ -2650,18 +768,7 @@
      */
     public void loadData(String data, String mimeType, String encoding) {
         checkThread();
-        loadDataImpl(data, mimeType, encoding);
-    }
-
-    private void loadDataImpl(String data, String mimeType, String encoding) {
-        StringBuilder dataUrl = new StringBuilder("data:");
-        dataUrl.append(mimeType);
-        if ("base64".equals(encoding)) {
-            dataUrl.append(";base64");
-        }
-        dataUrl.append(",");
-        dataUrl.append(data);
-        loadUrlImpl(dataUrl.toString());
+        mProvider.loadData(data, mimeType, encoding);
     }
 
     /**
@@ -2689,20 +796,7 @@
     public void loadDataWithBaseURL(String baseUrl, String data,
             String mimeType, String encoding, String historyUrl) {
         checkThread();
-
-        if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
-            loadDataImpl(data, mimeType, encoding);
-            return;
-        }
-        switchOutDrawHistory();
-        WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
-        arg.mBaseUrl = baseUrl;
-        arg.mData = data;
-        arg.mMimeType = mimeType;
-        arg.mEncoding = encoding;
-        arg.mHistoryUrl = historyUrl;
-        mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
-        clearHelpers();
+        mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
     }
 
     /**
@@ -2712,20 +806,7 @@
      */
     public void saveWebArchive(String filename) {
         checkThread();
-        saveWebArchiveImpl(filename, false, null);
-    }
-
-    /* package */ static class SaveWebArchiveMessage {
-        SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
-            mBasename = basename;
-            mAutoname = autoname;
-            mCallback = callback;
-        }
-
-        /* package */ final String mBasename;
-        /* package */ final boolean mAutoname;
-        /* package */ final ValueCallback<String> mCallback;
-        /* package */ String mResultFile;
+        mProvider.saveWebArchive(filename);
     }
 
     /**
@@ -2742,13 +823,7 @@
      */
     public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
         checkThread();
-        saveWebArchiveImpl(basename, autoname, callback);
-    }
-
-    private void saveWebArchiveImpl(String basename, boolean autoname,
-            ValueCallback<String> callback) {
-        mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
-            new SaveWebArchiveMessage(basename, autoname, callback));
+        mProvider.saveWebArchive(basename, autoname, callback);
     }
 
     /**
@@ -2756,10 +831,7 @@
      */
     public void stopLoading() {
         checkThread();
-        // TODO: should we clear all the messages in the queue before sending
-        // STOP_LOADING?
-        switchOutDrawHistory();
-        mWebViewCore.sendMessage(EventHub.STOP_LOADING);
+        mProvider.stopLoading();
     }
 
     /**
@@ -2767,9 +839,7 @@
      */
     public void reload() {
         checkThread();
-        clearHelpers();
-        switchOutDrawHistory();
-        mWebViewCore.sendMessage(EventHub.RELOAD);
+        mProvider.reload();
     }
 
     /**
@@ -2778,14 +848,7 @@
      */
     public boolean canGoBack() {
         checkThread();
-        WebBackForwardList l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                return l.getCurrentIndex() > 0;
-            }
-        }
+        return mProvider.canGoBack();
     }
 
     /**
@@ -2793,7 +856,7 @@
      */
     public void goBack() {
         checkThread();
-        goBackOrForwardImpl(-1);
+        mProvider.goBack();
     }
 
     /**
@@ -2802,14 +865,7 @@
      */
     public boolean canGoForward() {
         checkThread();
-        WebBackForwardList l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                return l.getCurrentIndex() < l.getSize() - 1;
-            }
-        }
+        return mProvider.canGoForward();
     }
 
     /**
@@ -2817,7 +873,7 @@
      */
     public void goForward() {
         checkThread();
-        goBackOrForwardImpl(1);
+        mProvider.goForward();
     }
 
     /**
@@ -2828,15 +884,7 @@
      */
     public boolean canGoBackOrForward(int steps) {
         checkThread();
-        WebBackForwardList l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                int newIndex = l.getCurrentIndex() + steps;
-                return newIndex >= 0 && newIndex < l.getSize();
-            }
-        }
+        return mProvider.canGoBackOrForward(steps);
     }
 
     /**
@@ -2848,19 +896,7 @@
      */
     public void goBackOrForward(int steps) {
         checkThread();
-        goBackOrForwardImpl(steps);
-    }
-
-    private void goBackOrForwardImpl(int steps) {
-        goBackOrForward(steps, false);
-    }
-
-    private void goBackOrForward(int steps, boolean ignoreSnapshot) {
-        if (steps != 0) {
-            clearHelpers();
-            mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
-                    ignoreSnapshot ? 1 : 0);
-        }
+        mProvider.goBackOrForward(steps);
     }
 
     /**
@@ -2868,20 +904,7 @@
      */
     public boolean isPrivateBrowsingEnabled() {
         checkThread();
-        return getSettings().isPrivateBrowsingEnabled();
-    }
-
-    private void startPrivateBrowsing() {
-        getSettings().setPrivateBrowsingEnabled(true);
-    }
-
-    private boolean extendScroll(int y) {
-        int finalY = mScroller.getFinalY();
-        int newY = pinLocY(finalY + y);
-        if (newY == finalY) return false;
-        mScroller.setFinalY(newY);
-        mScroller.extendDuration(computeDuration(0, y));
-        return true;
+        return mProvider.isPrivateBrowsingEnabled();
     }
 
     /**
@@ -2891,24 +914,7 @@
      */
     public boolean pageUp(boolean top) {
         checkThread();
-        if (mNativeClass == 0) {
-            return false;
-        }
-        nativeClearCursor(); // start next trackball movement from page edge
-        if (top) {
-            // go to the top of the document
-            return pinScrollTo(mScrollX, 0, true, 0);
-        }
-        // Page up
-        int h = getHeight();
-        int y;
-        if (h > 2 * PAGE_SCROLL_OVERLAP) {
-            y = -h + PAGE_SCROLL_OVERLAP;
-        } else {
-            y = -h / 2;
-        }
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
-                : extendScroll(y);
+        return mProvider.pageUp(top);
     }
 
     /**
@@ -2918,23 +924,7 @@
      */
     public boolean pageDown(boolean bottom) {
         checkThread();
-        if (mNativeClass == 0) {
-            return false;
-        }
-        nativeClearCursor(); // start next trackball movement from page edge
-        if (bottom) {
-            return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
-        }
-        // Page down.
-        int h = getHeight();
-        int y;
-        if (h > 2 * PAGE_SCROLL_OVERLAP) {
-            y = h - PAGE_SCROLL_OVERLAP;
-        } else {
-            y = h / 2;
-        }
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
-                : extendScroll(y);
+        return mProvider.pageDown(bottom);
     }
 
     /**
@@ -2943,10 +933,7 @@
      */
     public void clearView() {
         checkThread();
-        mContentWidth = 0;
-        mContentHeight = 0;
-        setBaseLayer(0, null, false, false);
-        mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
+        mProvider.clearView();
     }
 
     /**
@@ -2960,29 +947,7 @@
      */
     public Picture capturePicture() {
         checkThread();
-        if (mNativeClass == 0) return null;
-        Picture result = new Picture();
-        nativeCopyBaseContentToPicture(result);
-        return result;
-    }
-
-    /**
-     *  Return true if the browser is displaying a TextView for text input.
-     */
-    private boolean inEditingMode() {
-        return mWebTextView != null && mWebTextView.getParent() != null;
-    }
-
-    /**
-     * Remove the WebTextView.
-     */
-    private void clearTextEntry() {
-        if (inEditingMode()) {
-            mWebTextView.remove();
-        } else {
-            // The keyboard may be open with the WebView as the served view
-            hideSoftKeyboard();
-        }
+        return mProvider.capturePicture();
     }
 
     /**
@@ -2991,16 +956,7 @@
      */
     public float getScale() {
         checkThread();
-        return mZoomManager.getScale();
-    }
-
-    /**
-     * Compute the reading level scale of the WebView
-     * @param scale The current scale.
-     * @return The reading level scale.
-     */
-    /*package*/ float computeReadingLevelScale(float scale) {
-        return mZoomManager.computeReadingLevelScale(scale);
+        return mProvider.getScale();
     }
 
     /**
@@ -3015,7 +971,7 @@
      */
     public void setInitialScale(int scaleInPercent) {
         checkThread();
-        mZoomManager.setInitialScaleInPercent(scaleInPercent);
+        mProvider.setInitialScale(scaleInPercent);
     }
 
     /**
@@ -3025,12 +981,7 @@
      */
     public void invokeZoomPicker() {
         checkThread();
-        if (!getSettings().supportZoom()) {
-            Log.w(LOGTAG, "This WebView doesn't support zoom.");
-            return;
-        }
-        clearHelpers();
-        mZoomManager.invokeZoomPicker();
+        mProvider.invokeZoomPicker();
     }
 
     /**
@@ -3053,101 +1004,9 @@
      */
     public HitTestResult getHitTestResult() {
         checkThread();
-        return hitTestResult(mInitialHitTestResult);
+        return mProvider.getHitTestResult();
     }
 
-    private HitTestResult hitTestResult(HitTestResult fallback) {
-        if (mNativeClass == 0 || sDisableNavcache) {
-            return fallback;
-        }
-
-        HitTestResult result = new HitTestResult();
-        if (nativeHasCursorNode()) {
-            if (nativeCursorIsTextInput()) {
-                result.setType(HitTestResult.EDIT_TEXT_TYPE);
-            } else {
-                String text = nativeCursorText();
-                if (text != null) {
-                    if (text.startsWith(SCHEME_TEL)) {
-                        result.setType(HitTestResult.PHONE_TYPE);
-                        result.setExtra(URLDecoder.decode(text
-                                .substring(SCHEME_TEL.length())));
-                    } else if (text.startsWith(SCHEME_MAILTO)) {
-                        result.setType(HitTestResult.EMAIL_TYPE);
-                        result.setExtra(text.substring(SCHEME_MAILTO.length()));
-                    } else if (text.startsWith(SCHEME_GEO)) {
-                        result.setType(HitTestResult.GEO_TYPE);
-                        result.setExtra(URLDecoder.decode(text
-                                .substring(SCHEME_GEO.length())));
-                    } else if (nativeCursorIsAnchor()) {
-                        result.setType(HitTestResult.SRC_ANCHOR_TYPE);
-                        result.setExtra(text);
-                    }
-                }
-            }
-        } else if (fallback != null) {
-            /* If webkit causes a rebuild while the long press is in progress,
-             * the cursor node may be reset, even if it is still around. This
-             * uses the cursor node saved when the touch began. Since the
-             * nativeImageURI below only changes the result if it is successful,
-             * this uses the data beneath the touch if available or the original
-             * tap data otherwise.
-             */
-            Log.v(LOGTAG, "hitTestResult use fallback");
-            result = fallback;
-        }
-        int type = result.getType();
-        if (type == HitTestResult.UNKNOWN_TYPE
-                || type == HitTestResult.SRC_ANCHOR_TYPE) {
-            // Now check to see if it is an image.
-            int contentX = viewToContentX(mLastTouchX + mScrollX);
-            int contentY = viewToContentY(mLastTouchY + mScrollY);
-            String text = nativeImageURI(contentX, contentY);
-            if (text != null) {
-                result.setType(type == HitTestResult.UNKNOWN_TYPE ?
-                        HitTestResult.IMAGE_TYPE :
-                        HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
-                result.setExtra(text);
-            }
-        }
-        return result;
-    }
-
-    int getBlockLeftEdge(int x, int y, float readingScale) {
-        if (!sDisableNavcache) {
-            return nativeGetBlockLeftEdge(x, y, readingScale);
-        }
-
-        float invReadingScale = 1.0f / readingScale;
-        int readingWidth = (int) (getViewWidth() * invReadingScale);
-        int left = NO_LEFTEDGE;
-        if (mFocusedNode != null) {
-            final int length = mFocusedNode.mEnclosingParentRects.length;
-            for (int i = 0; i < length; i++) {
-                Rect rect = mFocusedNode.mEnclosingParentRects[i];
-                if (rect.width() < mFocusedNode.mHitTestSlop) {
-                    // ignore bounding boxes that are too small
-                    continue;
-                } else if (left != NO_LEFTEDGE && rect.width() > readingWidth) {
-                    // stop when bounding box doesn't fit the screen width
-                    // at reading scale
-                    break;
-                }
-
-                left = rect.left;
-            }
-        }
-
-        return left;
-    }
-
-    // Called by JNI when the DOM has changed the focus.  Clear the focus so
-    // that new keys will go to the newly focused field
-    private void domChangedFocus() {
-        if (inEditingMode()) {
-            mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
-        }
-    }
     /**
      * Request the anchor or image element URL at the last tapped point.
      * If hrefMsg is null, this method returns immediately and does not
@@ -3164,32 +1023,7 @@
      */
     public void requestFocusNodeHref(Message hrefMsg) {
         checkThread();
-        if (hrefMsg == null) {
-            return;
-        }
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
-                && mFocusedNode.mHitTestY == contentY) {
-            hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
-            hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
-            hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
-            hrefMsg.sendToTarget();
-            return;
-        }
-        if (nativeHasCursorNode()) {
-            Rect cursorBounds = cursorRingBounds();
-            if (!cursorBounds.contains(contentX, contentY)) {
-                int slop = viewToContentDimension(mNavSlop);
-                cursorBounds.inset(-slop, -slop);
-                if (cursorBounds.contains(contentX, contentY)) {
-                    contentX = cursorBounds.centerX();
-                    contentY = cursorBounds.centerY();
-                }
-            }
-        }
-        mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
-                contentX, contentY, hrefMsg);
+        mProvider.requestFocusNodeHref(hrefMsg);
     }
 
     /**
@@ -3201,503 +1035,7 @@
      */
     public void requestImageRef(Message msg) {
         checkThread();
-        if (0 == mNativeClass) return; // client isn't initialized
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        String ref = nativeImageURI(contentX, contentY);
-        Bundle data = msg.getData();
-        data.putString("url", ref);
-        msg.setData(data);
-        msg.sendToTarget();
-    }
-
-    static int pinLoc(int x, int viewMax, int docMax) {
-//        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
-        if (docMax < viewMax) {   // the doc has room on the sides for "blank"
-            // pin the short document to the top/left of the screen
-            x = 0;
-//            Log.d(LOGTAG, "--- center " + x);
-        } else if (x < 0) {
-            x = 0;
-//            Log.d(LOGTAG, "--- zero");
-        } else if (x + viewMax > docMax) {
-            x = docMax - viewMax;
-//            Log.d(LOGTAG, "--- pin " + x);
-        }
-        return x;
-    }
-
-    // Expects x in view coordinates
-    int pinLocX(int x) {
-        if (mInOverScrollMode) return x;
-        return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
-    }
-
-    // Expects y in view coordinates
-    int pinLocY(int y) {
-        if (mInOverScrollMode) return y;
-        return pinLoc(y, getViewHeightWithTitle(),
-                      computeRealVerticalScrollRange() + getTitleHeight());
-    }
-
-    /**
-     * A title bar which is embedded in this WebView, and scrolls along with it
-     * vertically, but not horizontally.
-     */
-    private View mTitleBar;
-
-    /**
-     * the title bar rendering gravity
-     */
-    private int mTitleGravity;
-
-    /**
-     * Add or remove a title bar to be embedded into the WebView, and scroll
-     * along with it vertically, while remaining in view horizontally. Pass
-     * null to remove the title bar from the WebView, and return to drawing
-     * the WebView normally without translating to account for the title bar.
-     * @hide
-     */
-    public void setEmbeddedTitleBar(View v) {
-        if (mTitleBar == v) return;
-        if (mTitleBar != null) {
-            removeView(mTitleBar);
-        }
-        if (null != v) {
-            addView(v, new AbsoluteLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
-        }
-        mTitleBar = v;
-    }
-
-    /**
-     * Set where to render the embedded title bar
-     * NO_GRAVITY at the top of the page
-     * TOP        at the top of the screen
-     * @hide
-     */
-    public void setTitleBarGravity(int gravity) {
-        mTitleGravity = gravity;
-        // force refresh
-        invalidate();
-    }
-
-    /**
-     * Given a distance in view space, convert it to content space. Note: this
-     * does not reflect translation, just scaling, so this should not be called
-     * with coordinates, but should be called for dimensions like width or
-     * height.
-     */
-    private int viewToContentDimension(int d) {
-        return Math.round(d * mZoomManager.getInvScale());
-    }
-
-    /**
-     * Given an x coordinate in view space, convert it to content space.  Also
-     * may be used for absolute heights (such as for the WebTextView's
-     * textSize, which is unaffected by the height of the title bar).
-     */
-    /*package*/ int viewToContentX(int x) {
-        return viewToContentDimension(x);
-    }
-
-    /**
-     * Given a y coordinate in view space, convert it to content space.
-     * Takes into account the height of the title bar if there is one
-     * embedded into the WebView.
-     */
-    /*package*/ int viewToContentY(int y) {
-        return viewToContentDimension(y - getTitleHeight());
-    }
-
-    /**
-     * Given a x coordinate in view space, convert it to content space.
-     * Returns the result as a float.
-     */
-    private float viewToContentXf(int x) {
-        return x * mZoomManager.getInvScale();
-    }
-
-    /**
-     * Given a y coordinate in view space, convert it to content space.
-     * Takes into account the height of the title bar if there is one
-     * embedded into the WebView. Returns the result as a float.
-     */
-    private float viewToContentYf(int y) {
-        return (y - getTitleHeight()) * mZoomManager.getInvScale();
-    }
-
-    /**
-     * Given a distance in content space, convert it to view space. Note: this
-     * does not reflect translation, just scaling, so this should not be called
-     * with coordinates, but should be called for dimensions like width or
-     * height.
-     */
-    /*package*/ int contentToViewDimension(int d) {
-        return Math.round(d * mZoomManager.getScale());
-    }
-
-    /**
-     * Given an x coordinate in content space, convert it to view
-     * space.
-     */
-    /*package*/ int contentToViewX(int x) {
-        return contentToViewDimension(x);
-    }
-
-    /**
-     * Given a y coordinate in content space, convert it to view
-     * space.  Takes into account the height of the title bar.
-     */
-    /*package*/ int contentToViewY(int y) {
-        return contentToViewDimension(y) + getTitleHeight();
-    }
-
-    private Rect contentToViewRect(Rect x) {
-        return new Rect(contentToViewX(x.left), contentToViewY(x.top),
-                        contentToViewX(x.right), contentToViewY(x.bottom));
-    }
-
-    /*  To invalidate a rectangle in content coordinates, we need to transform
-        the rect into view coordinates, so we can then call invalidate(...).
-
-        Normally, we would just call contentToView[XY](...), which eventually
-        calls Math.round(coordinate * mActualScale). However, for invalidates,
-        we need to account for the slop that occurs with antialiasing. To
-        address that, we are a little more liberal in the size of the rect that
-        we invalidate.
-
-        This liberal calculation calls floor() for the top/left, and ceil() for
-        the bottom/right coordinates. This catches the possible extra pixels of
-        antialiasing that we might have missed with just round().
-     */
-
-    // Called by JNI to invalidate the View, given rectangle coordinates in
-    // content space
-    private void viewInvalidate(int l, int t, int r, int b) {
-        final float scale = mZoomManager.getScale();
-        final int dy = getTitleHeight();
-        invalidate((int)Math.floor(l * scale),
-                   (int)Math.floor(t * scale) + dy,
-                   (int)Math.ceil(r * scale),
-                   (int)Math.ceil(b * scale) + dy);
-    }
-
-    // Called by JNI to invalidate the View after a delay, given rectangle
-    // coordinates in content space
-    private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
-        final float scale = mZoomManager.getScale();
-        final int dy = getTitleHeight();
-        postInvalidateDelayed(delay,
-                              (int)Math.floor(l * scale),
-                              (int)Math.floor(t * scale) + dy,
-                              (int)Math.ceil(r * scale),
-                              (int)Math.ceil(b * scale) + dy);
-    }
-
-    private void invalidateContentRect(Rect r) {
-        viewInvalidate(r.left, r.top, r.right, r.bottom);
-    }
-
-    // stop the scroll animation, and don't let a subsequent fling add
-    // to the existing velocity
-    private void abortAnimation() {
-        mScroller.abortAnimation();
-        mLastVelocity = 0;
-    }
-
-    /* call from webcoreview.draw(), so we're still executing in the UI thread
-    */
-    private void recordNewContentSize(int w, int h, boolean updateLayout) {
-
-        // premature data from webkit, ignore
-        if ((w | h) == 0) {
-            return;
-        }
-
-        // don't abort a scroll animation if we didn't change anything
-        if (mContentWidth != w || mContentHeight != h) {
-            // record new dimensions
-            mContentWidth = w;
-            mContentHeight = h;
-            // If history Picture is drawn, don't update scroll. They will be
-            // updated when we get out of that mode.
-            if (!mDrawHistory) {
-                // repin our scroll, taking into account the new content size
-                updateScrollCoordinates(pinLocX(mScrollX), pinLocY(mScrollY));
-                if (!mScroller.isFinished()) {
-                    // We are in the middle of a scroll.  Repin the final scroll
-                    // position.
-                    mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
-                    mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
-                }
-            }
-        }
-        contentSizeChanged(updateLayout);
-    }
-
-    // Used to avoid sending many visible rect messages.
-    private Rect mLastVisibleRectSent = new Rect();
-    private Rect mLastGlobalRect = new Rect();
-    private Rect mVisibleRect = new Rect();
-    private Rect mGlobalVisibleRect = new Rect();
-    private Point mScrollOffset = new Point();
-
-    Rect sendOurVisibleRect() {
-        if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
-        calcOurContentVisibleRect(mVisibleRect);
-        // Rect.equals() checks for null input.
-        if (!mVisibleRect.equals(mLastVisibleRectSent)) {
-            if (!mBlockWebkitViewMessages) {
-                mScrollOffset.set(mVisibleRect.left, mVisibleRect.top);
-                mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
-                mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
-                        nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, mScrollOffset);
-            }
-            mLastVisibleRectSent.set(mVisibleRect);
-            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-        }
-        if (getGlobalVisibleRect(mGlobalVisibleRect)
-                && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
-                        + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b="
-                        + mGlobalVisibleRect.bottom);
-            }
-            // TODO: the global offset is only used by windowRect()
-            // in ChromeClientAndroid ; other clients such as touch
-            // and mouse events could return view + screen relative points.
-            if (!mBlockWebkitViewMessages) {
-                mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
-            }
-            mLastGlobalRect.set(mGlobalVisibleRect);
-        }
-        return mVisibleRect;
-    }
-
-    private Point mGlobalVisibleOffset = new Point();
-    // Sets r to be the visible rectangle of our webview in view coordinates
-    private void calcOurVisibleRect(Rect r) {
-        getGlobalVisibleRect(r, mGlobalVisibleOffset);
-        r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
-    }
-
-    // Sets r to be our visible rectangle in content coordinates
-    private void calcOurContentVisibleRect(Rect r) {
-        calcOurVisibleRect(r);
-        r.left = viewToContentX(r.left);
-        // viewToContentY will remove the total height of the title bar.  Add
-        // the visible height back in to account for the fact that if the title
-        // bar is partially visible, the part of the visible rect which is
-        // displaying our content is displaced by that amount.
-        r.top = viewToContentY(r.top + getVisibleTitleHeightImpl());
-        r.right = viewToContentX(r.right);
-        r.bottom = viewToContentY(r.bottom);
-    }
-
-    private Rect mContentVisibleRect = new Rect();
-    // Sets r to be our visible rectangle in content coordinates. We use this
-    // method on the native side to compute the position of the fixed layers.
-    // Uses floating coordinates (necessary to correctly place elements when
-    // the scale factor is not 1)
-    private void calcOurContentVisibleRectF(RectF r) {
-        calcOurVisibleRect(mContentVisibleRect);
-        r.left = viewToContentXf(mContentVisibleRect.left);
-        // viewToContentY will remove the total height of the title bar.  Add
-        // the visible height back in to account for the fact that if the title
-        // bar is partially visible, the part of the visible rect which is
-        // displaying our content is displaced by that amount.
-        r.top = viewToContentYf(mContentVisibleRect.top + getVisibleTitleHeightImpl());
-        r.right = viewToContentXf(mContentVisibleRect.right);
-        r.bottom = viewToContentYf(mContentVisibleRect.bottom);
-    }
-
-    static class ViewSizeData {
-        int mWidth;
-        int mHeight;
-        float mHeightWidthRatio;
-        int mActualViewHeight;
-        int mTextWrapWidth;
-        int mAnchorX;
-        int mAnchorY;
-        float mScale;
-        boolean mIgnoreHeight;
-    }
-
-    /**
-     * Compute unzoomed width and height, and if they differ from the last
-     * values we sent, send them to webkit (to be used as new viewport)
-     *
-     * @param force ensures that the message is sent to webkit even if the width
-     * or height has not changed since the last message
-     *
-     * @return true if new values were sent
-     */
-    boolean sendViewSizeZoom(boolean force) {
-        if (mBlockWebkitViewMessages) return false;
-        if (mZoomManager.isPreventingWebkitUpdates()) return false;
-
-        int viewWidth = getViewWidth();
-        int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
-        // This height could be fixed and be different from actual visible height.
-        int viewHeight = getViewHeightWithTitle() - getTitleHeight();
-        int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
-        // Make the ratio more accurate than (newHeight / newWidth), since the
-        // latter both are calculated and rounded.
-        float heightWidthRatio = (float) viewHeight / viewWidth;
-        /*
-         * Because the native side may have already done a layout before the
-         * View system was able to measure us, we have to send a height of 0 to
-         * remove excess whitespace when we grow our width. This will trigger a
-         * layout and a change in content size. This content size change will
-         * mean that contentSizeChanged will either call this method directly or
-         * indirectly from onSizeChanged.
-         */
-        if (newWidth > mLastWidthSent && mWrapContent) {
-            newHeight = 0;
-            heightWidthRatio = 0;
-        }
-        // Actual visible content height.
-        int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
-        // Avoid sending another message if the dimensions have not changed.
-        if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
-                actualViewHeight != mLastActualHeightSent) {
-            ViewSizeData data = new ViewSizeData();
-            data.mWidth = newWidth;
-            data.mHeight = newHeight;
-            data.mHeightWidthRatio = heightWidthRatio;
-            data.mActualViewHeight = actualViewHeight;
-            data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
-            data.mScale = mZoomManager.getScale();
-            data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
-                    && !mHeightCanMeasure;
-            data.mAnchorX = mZoomManager.getDocumentAnchorX();
-            data.mAnchorY = mZoomManager.getDocumentAnchorY();
-            mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
-            mLastWidthSent = newWidth;
-            mLastHeightSent = newHeight;
-            mLastActualHeightSent = actualViewHeight;
-            mZoomManager.clearDocumentAnchor();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Update the double-tap zoom.
-     */
-    /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
-        mZoomManager.updateDoubleTapZoom(doubleTapZoom);
-    }
-
-    private int computeRealHorizontalScrollRange() {
-        if (mDrawHistory) {
-            return mHistoryWidth;
-        } else {
-            // to avoid rounding error caused unnecessary scrollbar, use floor
-            return (int) Math.floor(mContentWidth * mZoomManager.getScale());
-        }
-    }
-
-    @Override
-    protected int computeHorizontalScrollRange() {
-        int range = computeRealHorizontalScrollRange();
-
-        // Adjust reported range if overscrolled to compress the scroll bars
-        final int scrollX = mScrollX;
-        final int overscrollRight = computeMaxScrollX();
-        if (scrollX < 0) {
-            range -= scrollX;
-        } else if (scrollX > overscrollRight) {
-            range += scrollX - overscrollRight;
-        }
-
-        return range;
-    }
-
-    @Override
-    protected int computeHorizontalScrollOffset() {
-        return Math.max(mScrollX, 0);
-    }
-
-    private int computeRealVerticalScrollRange() {
-        if (mDrawHistory) {
-            return mHistoryHeight;
-        } else {
-            // to avoid rounding error caused unnecessary scrollbar, use floor
-            return (int) Math.floor(mContentHeight * mZoomManager.getScale());
-        }
-    }
-
-    @Override
-    protected int computeVerticalScrollRange() {
-        int range = computeRealVerticalScrollRange();
-
-        // Adjust reported range if overscrolled to compress the scroll bars
-        final int scrollY = mScrollY;
-        final int overscrollBottom = computeMaxScrollY();
-        if (scrollY < 0) {
-            range -= scrollY;
-        } else if (scrollY > overscrollBottom) {
-            range += scrollY - overscrollBottom;
-        }
-
-        return range;
-    }
-
-    @Override
-    protected int computeVerticalScrollOffset() {
-        return Math.max(mScrollY - getTitleHeight(), 0);
-    }
-
-    @Override
-    protected int computeVerticalScrollExtent() {
-        return getViewHeight();
-    }
-
-    /** @hide */
-    @Override
-    protected void onDrawVerticalScrollBar(Canvas canvas,
-                                           Drawable scrollBar,
-                                           int l, int t, int r, int b) {
-        if (mScrollY < 0) {
-            t -= mScrollY;
-        }
-        scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b);
-        scrollBar.draw(canvas);
-    }
-
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
-            boolean clampedY) {
-        // Special-case layer scrolling so that we do not trigger normal scroll
-        // updating.
-        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-            scrollLayerTo(scrollX, scrollY);
-            return;
-        }
-        mInOverScrollMode = false;
-        int maxX = computeMaxScrollX();
-        int maxY = computeMaxScrollY();
-        if (maxX == 0) {
-            // do not over scroll x if the page just fits the screen
-            scrollX = pinLocX(scrollX);
-        } else if (scrollX < 0 || scrollX > maxX) {
-            mInOverScrollMode = true;
-        }
-        if (scrollY < 0 || scrollY > maxY) {
-            mInOverScrollMode = true;
-        }
-
-        int oldX = mScrollX;
-        int oldY = mScrollY;
-
-        super.scrollTo(scrollX, scrollY);
-
-        if (mOverScrollGlow != null) {
-            mOverScrollGlow.pullGlow(mScrollX, mScrollY, oldX, oldY, maxX, maxY);
-        }
+        mProvider.requestImageRef(msg);
     }
 
     /**
@@ -3708,8 +1046,7 @@
      */
     public String getUrl() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getUrl() : null;
+        return mProvider.getUrl();
     }
 
     /**
@@ -3722,8 +1059,7 @@
      */
     public String getOriginalUrl() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getOriginalUrl() : null;
+        return mProvider.getOriginalUrl();
     }
 
     /**
@@ -3733,8 +1069,7 @@
      */
     public String getTitle() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getTitle() : null;
+        return mProvider.getTitle();
     }
 
     /**
@@ -3744,8 +1079,7 @@
      */
     public Bitmap getFavicon() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getFavicon() : null;
+        return mProvider.getFavicon();
     }
 
     /**
@@ -3755,8 +1089,7 @@
      * @hide
      */
     public String getTouchIconUrl() {
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getTouchIconUrl() : null;
+        return mProvider.getTouchIconUrl();
     }
 
     /**
@@ -3765,7 +1098,7 @@
      */
     public int getProgress() {
         checkThread();
-        return mCallbackProxy.getProgress();
+        return mProvider.getProgress();
     }
 
     /**
@@ -3773,7 +1106,7 @@
      */
     public int getContentHeight() {
         checkThread();
-        return mContentHeight;
+        return mProvider.getContentHeight();
     }
 
     /**
@@ -3781,14 +1114,7 @@
      * @hide
      */
     public int getContentWidth() {
-        return mContentWidth;
-    }
-
-    /**
-     * @hide
-     */
-    public int getPageBackgroundColor() {
-        return nativeGetBackgroundColor();
+        return mProvider.getContentWidth();
     }
 
     /**
@@ -3798,7 +1124,7 @@
      */
     public void pauseTimers() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
+        mProvider.pauseTimers();
     }
 
     /**
@@ -3807,7 +1133,7 @@
      */
     public void resumeTimers() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
+        mProvider.resumeTimers();
     }
 
     /**
@@ -3820,38 +1146,7 @@
      */
     public void onPause() {
         checkThread();
-        if (!mIsPaused) {
-            mIsPaused = true;
-            mWebViewCore.sendMessage(EventHub.ON_PAUSE);
-            // We want to pause the current playing video when switching out
-            // from the current WebView/tab.
-            if (mHTML5VideoViewProxy != null) {
-                mHTML5VideoViewProxy.pauseAndDispatch();
-            }
-            if (mNativeClass != 0) {
-                nativeSetPauseDrawing(mNativeClass, true);
-            }
-
-            cancelSelectDialog();
-            WebCoreThreadWatchdog.pause();
-        }
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        updateDrawingState();
-    }
-
-    void updateDrawingState() {
-        if (mNativeClass == 0 || mIsPaused) return;
-        if (getWindowVisibility() != VISIBLE) {
-            nativeSetPauseDrawing(mNativeClass, true);
-        } else if (getVisibility() != VISIBLE) {
-            nativeSetPauseDrawing(mNativeClass, true);
-        } else {
-            nativeSetPauseDrawing(mNativeClass, false);
-        }
+        mProvider.onPause();
     }
 
     /**
@@ -3859,22 +1154,7 @@
      */
     public void onResume() {
         checkThread();
-        if (mIsPaused) {
-            mIsPaused = false;
-            mWebViewCore.sendMessage(EventHub.ON_RESUME);
-            if (mNativeClass != 0) {
-                nativeSetPauseDrawing(mNativeClass, false);
-            }
-        }
-        // Ensure that the watchdog has a currently valid Context to be able to display
-        // a prompt dialog. For example, if the Activity was finished whilst the WebCore
-        // thread was blocked and the Activity is started again, we may reuse the blocked
-        // thread, but we'll have a new Activity.
-        WebCoreThreadWatchdog.updateContext(mContext);
-        // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
-        // to ensure that the Watchdog thread is running for the new WebView, so call
-        // it outside the if block above.
-        WebCoreThreadWatchdog.resume();
+        mProvider.onResume();
     }
 
     /**
@@ -3883,7 +1163,7 @@
      * @hide
      */
     public boolean isPaused() {
-        return mIsPaused;
+        return mProvider.isPaused();
     }
 
     /**
@@ -3892,7 +1172,7 @@
      */
     public void freeMemory() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
+        mProvider.freeMemory();
     }
 
     /**
@@ -3903,11 +1183,7 @@
      */
     public void clearCache(boolean includeDiskFiles) {
         checkThread();
-        // Note: this really needs to be a static method as it clears cache for all
-        // WebView. But we need mWebViewCore to send message to WebCore thread, so
-        // we can't make this static.
-        mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
-                includeDiskFiles ? 1 : 0, 0);
+        mProvider.clearCache(includeDiskFiles);
     }
 
     /**
@@ -3916,9 +1192,7 @@
      */
     public void clearFormData() {
         checkThread();
-        if (inEditingMode()) {
-            mWebTextView.setAdapterCustom(null);
-        }
+        mProvider.clearFormData();
     }
 
     /**
@@ -3926,8 +1200,7 @@
      */
     public void clearHistory() {
         checkThread();
-        mCallbackProxy.getBackForwardList().setClearPending();
-        mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
+        mProvider.clearHistory();
     }
 
     /**
@@ -3936,7 +1209,7 @@
      */
     public void clearSslPreferences() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
+        mProvider.clearSslPreferences();
     }
 
     /**
@@ -3949,7 +1222,8 @@
      */
     public WebBackForwardList copyBackForwardList() {
         checkThread();
-        return mCallbackProxy.getBackForwardList().clone();
+        return mProvider.copyBackForwardList();
+
     }
 
     /*
@@ -3961,8 +1235,7 @@
      */
     public void findNext(boolean forward) {
         checkThread();
-        if (0 == mNativeClass) return; // client isn't initialized
-        mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0);
+        mProvider.findNext(forward);
     }
 
     /*
@@ -3972,40 +1245,8 @@
      *              that were found.
      */
     public int findAll(String find) {
-        return findAllBody(find, false);
-    }
-
-    /**
-     * @hide
-     */
-    public void findAllAsync(String find) {
-        findAllBody(find, true);
-    }
-
-    private int findAllBody(String find, boolean isAsync) {
         checkThread();
-        if (0 == mNativeClass) return 0; // client isn't initialized
-        mLastFind = find;
-        mWebViewCore.removeMessages(EventHub.FIND_ALL);
-        WebViewCore.FindAllRequest request = new
-            WebViewCore.FindAllRequest(find);
-        if (isAsync) {
-            mWebViewCore.sendMessage(EventHub.FIND_ALL, request);
-            return 0; // no need to wait for response
-        }
-        synchronized(request) {
-            try {
-                mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL,
-                    request);
-                while (request.mMatchCount == -1) {
-                    request.wait();
-                }
-            }
-            catch (InterruptedException e) {
-                return 0;
-            }
-        }
-        return request.mMatchCount;
+        return mProvider.findAll(find);
     }
 
     /**
@@ -4020,56 +1261,10 @@
      */
     public boolean showFindDialog(String text, boolean showIme) {
         checkThread();
-        FindActionModeCallback callback = new FindActionModeCallback(mContext);
-        if (getParent() == null || startActionMode(callback) == null) {
-            // Could not start the action mode, so end Find on page
-            return false;
-        }
-        mCachedOverlappingActionModeHeight = -1;
-        mFindCallback = callback;
-        setFindIsUp(true);
-        mFindCallback.setWebView(this);
-        if (showIme) {
-            mFindCallback.showSoftInput();
-        } else if (text != null) {
-            mFindCallback.setText(text);
-            mFindCallback.findAll();
-            return true;
-        }
-        if (text == null) {
-            text = mLastFind;
-        }
-        if (text != null) {
-            mFindCallback.setText(text);
-            mFindCallback.findAll();
-        }
-        return true;
+        return mProvider.showFindDialog(text, showIme);
     }
 
     /**
-     * Keep track of the find callback so that we can remove its titlebar if
-     * necessary.
-     */
-    private FindActionModeCallback mFindCallback;
-
-    /**
-     * Toggle whether the find dialog is showing, for both native and Java.
-     */
-    private void setFindIsUp(boolean isUp) {
-        mFindIsUp = isUp;
-        if (0 == mNativeClass) return; // client isn't initialized
-        nativeSetFindIsUp(isUp);
-    }
-
-    // Used to know whether the find dialog is open.  Affects whether
-    // or not we draw the highlights for matches.
-    private boolean mFindIsUp;
-
-    // Keep track of the last string sent, so we can search again when find is
-    // reopened.
-    private String mLastFind;
-
-    /**
      * Return the first substring consisting of the address of a physical
      * location. Currently, only addresses in the United States are detected,
      * and consist of:
@@ -4091,33 +1286,7 @@
      */
     public static String findAddress(String addr) {
         checkThread();
-        return findAddress(addr, false);
-    }
-
-    /**
-     * @hide
-     * Return the first substring consisting of the address of a physical
-     * location. Currently, only addresses in the United States are detected,
-     * and consist of:
-     * - a house number
-     * - a street name
-     * - a street type (Road, Circle, etc), either spelled out or abbreviated
-     * - a city name
-     * - a state or territory, either spelled out or two-letter abbr.
-     * - an optional 5 digit or 9 digit zip code.
-     *
-     * Names are optionally capitalized, and the zip code, if present,
-     * must be valid for the state. The street type must be a standard USPS
-     * spelling or abbreviation. The state or territory must also be spelled
-     * or abbreviated using USPS standards. The house number may not exceed
-     * five digits.
-     * @param addr The string to search for addresses.
-     * @param caseInsensitive addr Set to true to make search ignore case.
-     *
-     * @return the address, or if no address is found, return null.
-     */
-    public static String findAddress(String addr, boolean caseInsensitive) {
-        return WebViewCore.nativeFindAddress(addr, caseInsensitive);
+        return getFactory().getStatics().findAddress(addr);
     }
 
     /*
@@ -4125,28 +1294,7 @@
      */
     public void clearMatches() {
         checkThread();
-        if (mNativeClass == 0)
-            return;
-        mWebViewCore.removeMessages(EventHub.FIND_ALL);
-        mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
-    }
-
-
-    /**
-     * Called when the find ActionMode ends.
-     */
-    void notifyFindDialogDismissed() {
-        mFindCallback = null;
-        mCachedOverlappingActionModeHeight = -1;
-        if (mWebViewCore == null) {
-            return;
-        }
-        clearMatches();
-        setFindIsUp(false);
-        // Now that the dialog has been removed, ensure that we scroll to a
-        // location that is not beyond the end of the page.
-        pinScrollTo(mScrollX, mScrollY, false, 0);
-        invalidate();
+        mProvider.clearMatches();
     }
 
     /**
@@ -4157,427 +1305,7 @@
      */
     public void documentHasImages(Message response) {
         checkThread();
-        if (response == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
-    }
-
-    /**
-     * Request the scroller to abort any ongoing animation
-     *
-     * @hide
-     */
-    public void stopScroll() {
-        mScroller.forceFinished(true);
-        mLastVelocity = 0;
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mScroller.computeScrollOffset()) {
-            int oldX = mScrollX;
-            int oldY = mScrollY;
-            int x = mScroller.getCurrX();
-            int y = mScroller.getCurrY();
-            invalidate();  // So we draw again
-
-            if (!mScroller.isFinished()) {
-                int rangeX = computeMaxScrollX();
-                int rangeY = computeMaxScrollY();
-                int overflingDistance = mOverflingDistance;
-
-                // Use the layer's scroll data if needed.
-                if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-                    oldX = mScrollingLayerRect.left;
-                    oldY = mScrollingLayerRect.top;
-                    rangeX = mScrollingLayerRect.right;
-                    rangeY = mScrollingLayerRect.bottom;
-                    // No overscrolling for layers.
-                    overflingDistance = 0;
-                }
-
-                overScrollBy(x - oldX, y - oldY, oldX, oldY,
-                        rangeX, rangeY,
-                        overflingDistance, overflingDistance, false);
-
-                if (mOverScrollGlow != null) {
-                    mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
-                }
-            } else {
-                if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-                    mScrollX = x;
-                    mScrollY = y;
-                } else {
-                    // Update the layer position instead of WebView.
-                    scrollLayerTo(x, y);
-                }
-                abortAnimation();
-                nativeSetIsScrolling(false);
-                if (!mBlockWebkitViewMessages) {
-                    WebViewCore.resumePriority();
-                    if (!mSelectingText) {
-                        WebViewCore.resumeUpdatePicture(mWebViewCore);
-                    }
-                }
-                if (oldX != mScrollX || oldY != mScrollY) {
-                    sendOurVisibleRect();
-                }
-            }
-        } else {
-            super.computeScroll();
-        }
-    }
-
-    private void scrollLayerTo(int x, int y) {
-        if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) {
-            return;
-        }
-        if (mSelectingText) {
-            int dx = mScrollingLayerRect.left - x;
-            int dy = mScrollingLayerRect.top - y;
-            if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
-                mSelectCursorBase.offset(dx, dy);
-            }
-            if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
-                mSelectCursorExtent.offset(dx, dy);
-            }
-        }
-        nativeScrollLayer(mCurrentScrollingLayerId, x, y);
-        mScrollingLayerRect.left = x;
-        mScrollingLayerRect.top = y;
-        mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
-                mScrollingLayerRect);
-        onScrollChanged(mScrollX, mScrollY, mScrollX, mScrollY);
-        invalidate();
-    }
-
-    private static int computeDuration(int dx, int dy) {
-        int distance = Math.max(Math.abs(dx), Math.abs(dy));
-        int duration = distance * 1000 / STD_SPEED;
-        return Math.min(duration, MAX_DURATION);
-    }
-
-    // helper to pin the scrollBy parameters (already in view coordinates)
-    // returns true if the scroll was changed
-    private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
-        return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
-    }
-    // helper to pin the scrollTo parameters (already in view coordinates)
-    // returns true if the scroll was changed
-    private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
-        x = pinLocX(x);
-        y = pinLocY(y);
-        int dx = x - mScrollX;
-        int dy = y - mScrollY;
-
-        if ((dx | dy) == 0) {
-            return false;
-        }
-        abortAnimation();
-        if (animate) {
-            //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
-            mScroller.startScroll(mScrollX, mScrollY, dx, dy,
-                    animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
-            awakenScrollBars(mScroller.getDuration());
-            invalidate();
-        } else {
-            scrollTo(x, y);
-        }
-        return true;
-    }
-
-    // Scale from content to view coordinates, and pin.
-    // Also called by jni webview.cpp
-    private boolean setContentScrollBy(int cx, int cy, boolean animate) {
-        if (mDrawHistory) {
-            // disallow WebView to change the scroll position as History Picture
-            // is used in the view system.
-            // TODO: as we switchOutDrawHistory when trackball or navigation
-            // keys are hit, this should be safe. Right?
-            return false;
-        }
-        cx = contentToViewDimension(cx);
-        cy = contentToViewDimension(cy);
-        if (mHeightCanMeasure) {
-            // move our visible rect according to scroll request
-            if (cy != 0) {
-                Rect tempRect = new Rect();
-                calcOurVisibleRect(tempRect);
-                tempRect.offset(cx, cy);
-                requestRectangleOnScreen(tempRect);
-            }
-            // FIXME: We scroll horizontally no matter what because currently
-            // ScrollView and ListView will not scroll horizontally.
-            // FIXME: Why do we only scroll horizontally if there is no
-            // vertical scroll?
-//                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
-            return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
-        } else {
-            return pinScrollBy(cx, cy, animate, 0);
-        }
-    }
-
-    /**
-     * Called by CallbackProxy when the page starts loading.
-     * @param url The URL of the page which has started loading.
-     */
-    /* package */ void onPageStarted(String url) {
-        // every time we start a new page, we want to reset the
-        // WebView certificate:  if the new site is secure, we
-        // will reload it and get a new certificate set;
-        // if the new site is not secure, the certificate must be
-        // null, and that will be the case
-        setCertificate(null);
-
-        // reset the flag since we set to true in if need after
-        // loading is see onPageFinished(Url)
-        mAccessibilityScriptInjected = false;
-    }
-
-    /**
-     * Called by CallbackProxy when the page finishes loading.
-     * @param url The URL of the page which has finished loading.
-     */
-    /* package */ void onPageFinished(String url) {
-        if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
-            // If the user is now on a different page, or has scrolled the page
-            // past the point where the title bar is offscreen, ignore the
-            // scroll request.
-            if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
-                    && mScrollX == 0 && mScrollY == 0) {
-                pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
-                        SLIDE_TITLE_DURATION);
-            }
-            mPageThatNeedsToSlideTitleBarOffScreen = null;
-        }
-        mZoomManager.onPageFinished(url);
-        injectAccessibilityForUrl(url);
-    }
-
-    /**
-     * This method injects accessibility in the loaded document if accessibility
-     * is enabled. If JavaScript is enabled we try to inject a URL specific script.
-     * If no URL specific script is found or JavaScript is disabled we fallback to
-     * the default {@link AccessibilityInjector} implementation.
-     * </p>
-     * If the URL has the "axs" paramter set to 1 it has already done the
-     * script injection so we do nothing. If the parameter is set to 0
-     * the URL opts out accessibility script injection so we fall back to
-     * the default {@link AccessibilityInjector}.
-     * </p>
-     * Note: If the user has not opted-in the accessibility script injection no scripts
-     * are injected rather the default {@link AccessibilityInjector} implementation
-     * is used.
-     *
-     * @param url The URL loaded by this {@link WebView}.
-     */
-    private void injectAccessibilityForUrl(String url) {
-        if (mWebViewCore == null) {
-            return;
-        }
-        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-
-        if (!accessibilityManager.isEnabled()) {
-            // it is possible that accessibility was turned off between reloads
-            ensureAccessibilityScriptInjectorInstance(false);
-            return;
-        }
-
-        if (!getSettings().getJavaScriptEnabled()) {
-            // no JS so we fallback to the basic buil-in support
-            ensureAccessibilityScriptInjectorInstance(true);
-            return;
-        }
-
-        // check the URL "axs" parameter to choose appropriate action
-        int axsParameterValue = getAxsUrlParameterValue(url);
-        if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
-            boolean onDeviceScriptInjectionEnabled = (Settings.Secure.getInt(mContext
-                    .getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
-            if (onDeviceScriptInjectionEnabled) {
-                ensureAccessibilityScriptInjectorInstance(false);
-                // neither script injected nor script injection opted out => we inject
-                loadUrl(getScreenReaderInjectingJs());
-                // TODO: Set this flag after successfull script injection. Maybe upon injection
-                // the chooser should update the meta tag and we check it to declare success
-                mAccessibilityScriptInjected = true;
-            } else {
-                // injection disabled so we fallback to the basic built-in support
-                ensureAccessibilityScriptInjectorInstance(true);
-            }
-        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
-            // injection opted out so we fallback to the basic buil-in support
-            ensureAccessibilityScriptInjectorInstance(true);
-        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
-            ensureAccessibilityScriptInjectorInstance(false);
-            // the URL provides accessibility but we still need to add our generic script
-            loadUrl(getScreenReaderInjectingJs());
-        } else {
-            Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
-        }
-    }
-
-    /**
-     * Ensures the instance of the {@link AccessibilityInjector} to be present ot not.
-     *
-     * @param present True to ensure an insance, false to ensure no instance.
-     */
-    private void ensureAccessibilityScriptInjectorInstance(boolean present) {
-        if (present) {
-            if (mAccessibilityInjector == null) {
-                mAccessibilityInjector = new AccessibilityInjector(this);
-            }
-        } else {
-            mAccessibilityInjector = null;
-        }
-    }
-
-    /**
-     * Gets JavaScript that injects a screen-reader.
-     *
-     * @return The JavaScript snippet.
-     */
-    private String getScreenReaderInjectingJs() {
-        String screenReaderUrl = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
-        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
-    }
-
-    /**
-     * Gets the "axs" URL parameter value.
-     *
-     * @param url A url to fetch the paramter from.
-     * @return The parameter value if such, -1 otherwise.
-     */
-    private int getAxsUrlParameterValue(String url) {
-        if (mMatchAxsUrlParameterPattern == null) {
-            mMatchAxsUrlParameterPattern = Pattern.compile(PATTERN_MATCH_AXS_URL_PARAMETER);
-        }
-        Matcher matcher = mMatchAxsUrlParameterPattern.matcher(url);
-        if (matcher.find()) {
-            String keyValuePair = url.substring(matcher.start(), matcher.end());
-            return Integer.parseInt(keyValuePair.split("=")[1]);
-        }
-        return -1;
-    }
-
-    /**
-     * The URL of a page that sent a message to scroll the title bar off screen.
-     *
-     * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
-     * title bar off the screen.  Sometimes, the scroll position is set before
-     * the page finishes loading.  Rather than scrolling while the page is still
-     * loading, keep track of the URL and new scroll position so we can perform
-     * the scroll once the page finishes loading.
-     */
-    private String mPageThatNeedsToSlideTitleBarOffScreen;
-
-    /**
-     * The destination Y scroll position to be used when the page finishes
-     * loading.  See mPageThatNeedsToSlideTitleBarOffScreen.
-     */
-    private int mYDistanceToSlideTitleOffScreen;
-
-    // scale from content to view coordinates, and pin
-    // return true if pin caused the final x/y different than the request cx/cy,
-    // and a future scroll may reach the request cx/cy after our size has
-    // changed
-    // return false if the view scroll to the exact position as it is requested,
-    // where negative numbers are taken to mean 0
-    private boolean setContentScrollTo(int cx, int cy) {
-        if (mDrawHistory) {
-            // disallow WebView to change the scroll position as History Picture
-            // is used in the view system.
-            // One known case where this is called is that WebCore tries to
-            // restore the scroll position. As history Picture already uses the
-            // saved scroll position, it is ok to skip this.
-            return false;
-        }
-        int vx;
-        int vy;
-        if ((cx | cy) == 0) {
-            // If the page is being scrolled to (0,0), do not add in the title
-            // bar's height, and simply scroll to (0,0). (The only other work
-            // in contentToView_ is to multiply, so this would not change 0.)
-            vx = 0;
-            vy = 0;
-        } else {
-            vx = contentToViewX(cx);
-            vy = contentToViewY(cy);
-        }
-//        Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
-//                      vx + " " + vy + "]");
-        // Some mobile sites attempt to scroll the title bar off the page by
-        // scrolling to (0,1).  If we are at the top left corner of the
-        // page, assume this is an attempt to scroll off the title bar, and
-        // animate the title bar off screen slowly enough that the user can see
-        // it.
-        if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
-                && mTitleBar != null) {
-            // FIXME: 100 should be defined somewhere as our max progress.
-            if (getProgress() < 100) {
-                // Wait to scroll the title bar off screen until the page has
-                // finished loading.  Keep track of the URL and the destination
-                // Y position
-                mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
-                mYDistanceToSlideTitleOffScreen = vy;
-            } else {
-                pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
-            }
-            // Since we are animating, we have not yet reached the desired
-            // scroll position.  Do not return true to request another attempt
-            return false;
-        }
-        pinScrollTo(vx, vy, false, 0);
-        // If the request was to scroll to a negative coordinate, treat it as if
-        // it was a request to scroll to 0
-        if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    // scale from content to view coordinates, and pin
-    private void spawnContentScrollTo(int cx, int cy) {
-        if (mDrawHistory) {
-            // disallow WebView to change the scroll position as History Picture
-            // is used in the view system.
-            return;
-        }
-        int vx = contentToViewX(cx);
-        int vy = contentToViewY(cy);
-        pinScrollTo(vx, vy, true, 0);
-    }
-
-    /**
-     * These are from webkit, and are in content coordinate system (unzoomed)
-     */
-    private void contentSizeChanged(boolean updateLayout) {
-        // suppress 0,0 since we usually see real dimensions soon after
-        // this avoids drawing the prev content in a funny place. If we find a
-        // way to consolidate these notifications, this check may become
-        // obsolete
-        if ((mContentWidth | mContentHeight) == 0) {
-            return;
-        }
-
-        if (mHeightCanMeasure) {
-            if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
-                    || updateLayout) {
-                requestLayout();
-            }
-        } else if (mWidthCanMeasure) {
-            if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
-                    || updateLayout) {
-                requestLayout();
-            }
-        } else {
-            // If we don't request a layout, try to send our view size to the
-            // native side to ensure that WebCore has the correct dimensions.
-            sendViewSizeZoom(false);
-        }
+        mProvider.documentHasImages(response);
     }
 
     /**
@@ -4587,17 +1315,7 @@
      */
     public void setWebViewClient(WebViewClient client) {
         checkThread();
-        mCallbackProxy.setWebViewClient(client);
-    }
-
-    /**
-     * Gets the WebViewClient
-     * @return the current WebViewClient instance.
-     *
-     * @hide This is an implementation detail.
-     */
-    public WebViewClient getWebViewClient() {
-        return mCallbackProxy.getWebViewClient();
+        mProvider.setWebViewClient(client);
     }
 
     /**
@@ -4608,7 +1326,7 @@
      */
     public void setDownloadListener(DownloadListener listener) {
         checkThread();
-        mCallbackProxy.setDownloadListener(listener);
+        mProvider.setDownloadListener(listener);
     }
 
     /**
@@ -4619,36 +1337,7 @@
      */
     public void setWebChromeClient(WebChromeClient client) {
         checkThread();
-        mCallbackProxy.setWebChromeClient(client);
-    }
-
-    /**
-     * Gets the chrome handler.
-     * @return the current WebChromeClient instance.
-     *
-     * @hide This is an implementation detail.
-     */
-    public WebChromeClient getWebChromeClient() {
-        return mCallbackProxy.getWebChromeClient();
-    }
-
-    /**
-     * Set the back/forward list client. This is an implementation of
-     * WebBackForwardListClient for handling new items and changes in the
-     * history index.
-     * @param client An implementation of WebBackForwardListClient.
-     * {@hide}
-     */
-    public void setWebBackForwardListClient(WebBackForwardListClient client) {
-        mCallbackProxy.setWebBackForwardListClient(client);
-    }
-
-    /**
-     * Gets the WebBackForwardListClient.
-     * {@hide}
-     */
-    public WebBackForwardListClient getWebBackForwardListClient() {
-        return mCallbackProxy.getWebBackForwardListClient();
+        mProvider.setWebChromeClient(client);
     }
 
     /**
@@ -4660,23 +1349,7 @@
     @Deprecated
     public void setPictureListener(PictureListener listener) {
         checkThread();
-        mPictureListener = listener;
-    }
-
-    /**
-     * {@hide}
-     */
-    /* FIXME: Debug only! Remove for SDK! */
-    public void externalRepresentation(Message callback) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
-    }
-
-    /**
-     * {@hide}
-     */
-    /* FIXME: Debug only! Remove for SDK! */
-    public void documentAsText(Message callback) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
+        mProvider.setPictureListener(listener);
     }
 
     /**
@@ -4707,13 +1380,7 @@
      */
     public void addJavascriptInterface(Object object, String name) {
         checkThread();
-        if (object == null) {
-            return;
-        }
-        WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-        arg.mObject = object;
-        arg.mInterfaceName = name;
-        mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
+        mProvider.addJavascriptInterface(object, name);
     }
 
     /**
@@ -4722,11 +1389,7 @@
      */
     public void removeJavascriptInterface(String interfaceName) {
         checkThread();
-        if (mWebViewCore != null) {
-            WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-            arg.mInterfaceName = interfaceName;
-            mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
-        }
+        mProvider.removeJavascriptInterface(interfaceName);
     }
 
     /**
@@ -4737,1410 +1400,31 @@
      */
     public WebSettings getSettings() {
         checkThread();
-        return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
+        return mProvider.getSettings();
     }
 
-   /**
-    * Return the list of currently loaded plugins.
-    * @return The list of currently loaded plugins.
-    *
-    * @hide
-    * @deprecated This was used for Gears, which has been deprecated.
-    */
+    /**
+     * Return the list of currently loaded plugins.
+     * @return The list of currently loaded plugins.
+     *
+     * @hide
+     * @deprecated This was used for Gears, which has been deprecated.
+     */
     @Deprecated
     public static synchronized PluginList getPluginList() {
         checkThread();
         return new PluginList();
     }
 
-   /**
-    * @hide
-    * @deprecated This was used for Gears, which has been deprecated.
-    */
+    /**
+     * @hide
+     * @deprecated This was used for Gears, which has been deprecated.
+     */
     @Deprecated
     public void refreshPlugins(boolean reloadOpenPages) {
         checkThread();
     }
 
-    //-------------------------------------------------------------------------
-    // Override View methods
-    //-------------------------------------------------------------------------
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mNativeClass != 0) {
-                mPrivateHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        destroy();
-                    }
-                });
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (child == mTitleBar) {
-            // When drawing the title bar, move it horizontally to always show
-            // at the top of the WebView.
-            mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
-            int newTop = 0;
-            if (mTitleGravity == Gravity.NO_GRAVITY) {
-                newTop = Math.min(0, mScrollY);
-            } else if (mTitleGravity == Gravity.TOP) {
-                newTop = mScrollY;
-            }
-            mTitleBar.setBottom(newTop + mTitleBar.getHeight());
-            mTitleBar.setTop(newTop);
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    private void drawContent(Canvas canvas, boolean drawRings) {
-        drawCoreAndCursorRing(canvas, mBackgroundColor,
-                mDrawCursorRing && drawRings);
-    }
-
-    /**
-     * Draw the background when beyond bounds
-     * @param canvas Canvas to draw into
-     */
-    private void drawOverScrollBackground(Canvas canvas) {
-        if (mOverScrollBackground == null) {
-            mOverScrollBackground = new Paint();
-            Bitmap bm = BitmapFactory.decodeResource(
-                    mContext.getResources(),
-                    com.android.internal.R.drawable.status_bar_background);
-            mOverScrollBackground.setShader(new BitmapShader(bm,
-                    Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
-            mOverScrollBorder = new Paint();
-            mOverScrollBorder.setStyle(Paint.Style.STROKE);
-            mOverScrollBorder.setStrokeWidth(0);
-            mOverScrollBorder.setColor(0xffbbbbbb);
-        }
-
-        int top = 0;
-        int right = computeRealHorizontalScrollRange();
-        int bottom = top + computeRealVerticalScrollRange();
-        // first draw the background and anchor to the top of the view
-        canvas.save();
-        canvas.translate(mScrollX, mScrollY);
-        canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
-                - mScrollY, Region.Op.DIFFERENCE);
-        canvas.drawPaint(mOverScrollBackground);
-        canvas.restore();
-        // then draw the border
-        canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
-        // next clip the region for the content
-        canvas.clipRect(0, top, right, bottom);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (inFullScreenMode()) {
-            return; // no need to draw anything if we aren't visible.
-        }
-        // if mNativeClass is 0, the WebView is either destroyed or not
-        // initialized. In either case, just draw the background color and return
-        if (mNativeClass == 0) {
-            canvas.drawColor(mBackgroundColor);
-            return;
-        }
-
-        // if both mContentWidth and mContentHeight are 0, it means there is no
-        // valid Picture passed to WebView yet. This can happen when WebView
-        // just starts. Draw the background and return.
-        if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
-            canvas.drawColor(mBackgroundColor);
-            return;
-        }
-
-        if (canvas.isHardwareAccelerated()) {
-            mZoomManager.setHardwareAccelerated();
-        } else {
-            mWebViewCore.resumeWebKitDraw();
-        }
-
-        int saveCount = canvas.save();
-        if (mInOverScrollMode && !getSettings()
-                .getUseWebViewBackgroundForOverscrollBackground()) {
-            drawOverScrollBackground(canvas);
-        }
-        if (mTitleBar != null) {
-            canvas.translate(0, getTitleHeight());
-        }
-        boolean drawNativeRings = !sDisableNavcache;
-        drawContent(canvas, drawNativeRings);
-        canvas.restoreToCount(saveCount);
-
-        if (AUTO_REDRAW_HACK && mAutoRedraw) {
-            invalidate();
-        }
-        mWebViewCore.signalRepaintDone();
-
-        if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
-            invalidate();
-        }
-
-        if (mFocusTransition != null) {
-            mFocusTransition.draw(canvas);
-        } else if (shouldDrawHighlightRect()) {
-            RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
-            Rect r = new Rect();
-            while (iter.next(r)) {
-                canvas.drawRect(r, mTouchHightlightPaint);
-            }
-        }
-        if (DEBUG_TOUCH_HIGHLIGHT) {
-            if (getSettings().getNavDump()) {
-                if ((mTouchHighlightX | mTouchHighlightY) != 0) {
-                    if (mTouchCrossHairColor == null) {
-                        mTouchCrossHairColor = new Paint();
-                        mTouchCrossHairColor.setColor(Color.RED);
-                    }
-                    canvas.drawLine(mTouchHighlightX - mNavSlop,
-                            mTouchHighlightY - mNavSlop, mTouchHighlightX
-                                    + mNavSlop + 1, mTouchHighlightY + mNavSlop
-                                    + 1, mTouchCrossHairColor);
-                    canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
-                            mTouchHighlightY - mNavSlop, mTouchHighlightX
-                                    - mNavSlop,
-                            mTouchHighlightY + mNavSlop + 1,
-                            mTouchCrossHairColor);
-                }
-            }
-        }
-    }
-
-    private void removeTouchHighlight() {
-        mWebViewCore.removeMessages(EventHub.HIT_TEST);
-        mPrivateHandler.removeMessages(HIT_TEST_RESULT);
-        setTouchHighlightRects(null);
-    }
-
-    @Override
-    public void setLayoutParams(ViewGroup.LayoutParams params) {
-        if (params.height == LayoutParams.WRAP_CONTENT) {
-            mWrapContent = true;
-        }
-        super.setLayoutParams(params);
-    }
-
-    @Override
-    public boolean performLongClick() {
-        // performLongClick() is the result of a delayed message. If we switch
-        // to windows overview, the WebView will be temporarily removed from the
-        // view system. In that case, do nothing.
-        if (getParent() == null) return false;
-
-        // A multi-finger gesture can look like a long press; make sure we don't take
-        // long press actions if we're scaling.
-        final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
-        if (detector != null && detector.isInProgress()) {
-            return false;
-        }
-
-        if (mNativeClass != 0 && nativeCursorIsTextInput()) {
-            // Send the click so that the textfield is in focus
-            centerKeyPressOnTextField();
-            rebuildWebTextView();
-        } else {
-            clearTextEntry();
-        }
-        if (inEditingMode()) {
-            // Since we just called rebuildWebTextView, the layout is not set
-            // properly.  Update it so it can correctly find the word to select.
-            mWebTextView.ensureLayout();
-            // Provide a touch down event to WebTextView, which will allow it
-            // to store the location to use in performLongClick.
-            AbsoluteLayout.LayoutParams params
-                    = (AbsoluteLayout.LayoutParams) mWebTextView.getLayoutParams();
-            MotionEvent fake = MotionEvent.obtain(mLastTouchTime,
-                    mLastTouchTime, MotionEvent.ACTION_DOWN,
-                    mLastTouchX - params.x + mScrollX,
-                    mLastTouchY - params.y + mScrollY, 0);
-            mWebTextView.dispatchTouchEvent(fake);
-            return mWebTextView.performLongClick();
-        }
-        if (mSelectingText) return false; // long click does nothing on selection
-        /* if long click brings up a context menu, the super function
-         * returns true and we're done. Otherwise, nothing happened when
-         * the user clicked. */
-        if (super.performLongClick()) {
-            return true;
-        }
-        /* In the case where the application hasn't already handled the long
-         * click action, look for a word under the  click. If one is found,
-         * animate the text selection into view.
-         * FIXME: no animation code yet */
-        final boolean isSelecting = selectText();
-        if (isSelecting) {
-            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        } else if (focusCandidateIsEditableText()) {
-            mSelectCallback = new SelectActionModeCallback();
-            mSelectCallback.setWebView(this);
-            mSelectCallback.setTextSelected(false);
-            startActionMode(mSelectCallback);
-        }
-        return isSelecting;
-    }
-
-    /**
-     * Select the word at the last click point.
-     *
-     * @hide This is an implementation detail.
-     */
-    public boolean selectText() {
-        int x = viewToContentX(mLastTouchX + mScrollX);
-        int y = viewToContentY(mLastTouchY + mScrollY);
-        return selectText(x, y);
-    }
-
-    /**
-     * Select the word at the indicated content coordinates.
-     */
-    boolean selectText(int x, int y) {
-        mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
-        return true;
-    }
-
-    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        mCachedOverlappingActionModeHeight = -1;
-        if (mSelectingText && mOrientation != newConfig.orientation) {
-            selectionDone();
-        }
-        mOrientation = newConfig.orientation;
-        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
-            mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
-        }
-    }
-
-    /**
-     * Keep track of the Callback so we can end its ActionMode or remove its
-     * titlebar.
-     */
-    private SelectActionModeCallback mSelectCallback;
-
-    // These values are possible options for didUpdateWebTextViewDimensions.
-    private static final int FULLY_ON_SCREEN = 0;
-    private static final int INTERSECTS_SCREEN = 1;
-    private static final int ANYWHERE = 2;
-
-    /**
-     * Check to see if the focused textfield/textarea is still on screen.  If it
-     * is, update the the dimensions and location of WebTextView.  Otherwise,
-     * remove the WebTextView.  Should be called when the zoom level changes.
-     * @param intersection How to determine whether the textfield/textarea is
-     *        still on screen.
-     * @return boolean True if the textfield/textarea is still on screen and the
-     *         dimensions/location of WebTextView have been updated.
-     */
-    private boolean didUpdateWebTextViewDimensions(int intersection) {
-        Rect contentBounds = nativeFocusCandidateNodeBounds();
-        Rect vBox = contentToViewRect(contentBounds);
-        Rect visibleRect = new Rect();
-        calcOurVisibleRect(visibleRect);
-        offsetByLayerScrollPosition(vBox);
-        // If the textfield is on screen, place the WebTextView in
-        // its new place, accounting for our new scroll/zoom values,
-        // and adjust its textsize.
-        boolean onScreen;
-        switch (intersection) {
-            case FULLY_ON_SCREEN:
-                onScreen = visibleRect.contains(vBox);
-                break;
-            case INTERSECTS_SCREEN:
-                onScreen = Rect.intersects(visibleRect, vBox);
-                break;
-            case ANYWHERE:
-                onScreen = true;
-                break;
-            default:
-                throw new AssertionError(
-                        "invalid parameter passed to didUpdateWebTextViewDimensions");
-        }
-        if (onScreen) {
-            mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
-                    vBox.height());
-            mWebTextView.updateTextSize();
-            updateWebTextViewPadding();
-            return true;
-        } else {
-            // The textfield is now off screen.  The user probably
-            // was not zooming to see the textfield better.  Remove
-            // the WebTextView.  If the user types a key, and the
-            // textfield is still in focus, we will reconstruct
-            // the WebTextView and scroll it back on screen.
-            mWebTextView.remove();
-            return false;
-        }
-    }
-
-    private void offsetByLayerScrollPosition(Rect box) {
-        if ((mCurrentScrollingLayerId != 0)
-                && (mCurrentScrollingLayerId == nativeFocusCandidateLayerId())) {
-            box.offsetTo(box.left - mScrollingLayerRect.left,
-                    box.top - mScrollingLayerRect.top);
-        }
-    }
-
-    void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator,
-            boolean isPictureAfterFirstLayout) {
-        if (mNativeClass == 0)
-            return;
-        boolean queueFull;
-        queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
-                                       showVisualIndicator, isPictureAfterFirstLayout);
-
-        if (layer == 0 || isPictureAfterFirstLayout) {
-            mWebViewCore.resumeWebKitDraw();
-        } else if (queueFull) {
-            // temporarily disable webkit draw throttling
-            // TODO: re-enable
-            // mWebViewCore.pauseWebKitDraw();
-        }
-
-        if (mHTML5VideoViewProxy != null) {
-            mHTML5VideoViewProxy.setBaseLayer(layer);
-        }
-    }
-
-    int getBaseLayer() {
-        if (mNativeClass == 0) {
-            return 0;
-        }
-        return nativeGetBaseLayer();
-    }
-
-    private void onZoomAnimationStart() {
-        // If it is in password mode, turn it off so it does not draw misplaced.
-        if (inEditingMode()) {
-            mWebTextView.setVisibility(INVISIBLE);
-        }
-    }
-
-    private void onZoomAnimationEnd() {
-        // adjust the edit text view if needed
-        if (inEditingMode()
-                && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)) {
-            // If it is a password field, start drawing the WebTextView once
-            // again.
-            mWebTextView.setVisibility(VISIBLE);
-        }
-    }
-
-    void onFixedLengthZoomAnimationStart() {
-        WebViewCore.pauseUpdatePicture(getWebViewCore());
-        onZoomAnimationStart();
-    }
-
-    void onFixedLengthZoomAnimationEnd() {
-        if (!mBlockWebkitViewMessages && !mSelectingText) {
-            WebViewCore.resumeUpdatePicture(mWebViewCore);
-        }
-        onZoomAnimationEnd();
-    }
-
-    private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
-                                         Paint.DITHER_FLAG |
-                                         Paint.SUBPIXEL_TEXT_FLAG;
-    private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
-                                           Paint.DITHER_FLAG;
-
-    private final DrawFilter mZoomFilter =
-            new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
-    // If we need to trade better quality for speed, set mScrollFilter to null
-    private final DrawFilter mScrollFilter =
-            new PaintFlagsDrawFilter(SCROLL_BITS, 0);
-
-    private void drawCoreAndCursorRing(Canvas canvas, int color,
-        boolean drawCursorRing) {
-        if (mDrawHistory) {
-            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
-            canvas.drawPicture(mHistoryPicture);
-            return;
-        }
-        if (mNativeClass == 0) return;
-
-        boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
-        boolean animateScroll = ((!mScroller.isFinished()
-                || mVelocityTracker != null)
-                && (mTouchMode != TOUCH_DRAG_MODE ||
-                mHeldMotionless != MOTIONLESS_TRUE))
-                || mDeferTouchMode == TOUCH_DRAG_MODE;
-        if (mTouchMode == TOUCH_DRAG_MODE) {
-            if (mHeldMotionless == MOTIONLESS_PENDING) {
-                mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                mHeldMotionless = MOTIONLESS_FALSE;
-            }
-            if (mHeldMotionless == MOTIONLESS_FALSE) {
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(AWAKEN_SCROLL_BARS),
-                            ViewConfiguration.getScrollDefaultDelay());
-                mHeldMotionless = MOTIONLESS_PENDING;
-            }
-        }
-        int saveCount = canvas.save();
-        if (animateZoom) {
-            mZoomManager.animateZoom(canvas);
-        } else if (!canvas.isHardwareAccelerated()) {
-            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
-        }
-
-        boolean UIAnimationsRunning = false;
-        // Currently for each draw we compute the animation values;
-        // We may in the future decide to do that independently.
-        if (mNativeClass != 0 && !canvas.isHardwareAccelerated()
-                && nativeEvaluateLayersAnimations(mNativeClass)) {
-            UIAnimationsRunning = true;
-            // If we have unfinished (or unstarted) animations,
-            // we ask for a repaint. We only need to do this in software
-            // rendering (with hardware rendering we already have a different
-            // method of requesting a repaint)
-            mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
-            invalidate();
-        }
-
-        // decide which adornments to draw
-        int extras = DRAW_EXTRAS_NONE;
-        if (!mFindIsUp) {
-            if (mSelectingText) {
-                extras = DRAW_EXTRAS_SELECTION;
-            } else if (drawCursorRing) {
-                extras = DRAW_EXTRAS_CURSOR_RING;
-            }
-        }
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
-                    + " mSelectingText=" + mSelectingText
-                    + " nativePageShouldHandleShiftAndArrows()="
-                    + nativePageShouldHandleShiftAndArrows()
-                    + " animateZoom=" + animateZoom
-                    + " extras=" + extras);
-        }
-
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        if (canvas.isHardwareAccelerated()) {
-            Rect glRectViewport = mGLViewportEmpty ? null : mGLRectViewport;
-            Rect viewRectViewport = mGLViewportEmpty ? null : mViewRectViewport;
-
-            int functor = nativeGetDrawGLFunction(mNativeClass, glRectViewport,
-                    viewRectViewport, mVisibleContentRect, getScale(), extras);
-            ((HardwareCanvas) canvas).callDrawGLFunction(functor);
-            if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
-                mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
-                nativeUseHardwareAccelSkia(mHardwareAccelSkia);
-            }
-
-        } else {
-            DrawFilter df = null;
-            if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
-                df = mZoomFilter;
-            } else if (animateScroll) {
-                df = mScrollFilter;
-            }
-            canvas.setDrawFilter(df);
-            // XXX: Revisit splitting content.  Right now it causes a
-            // synchronization problem with layers.
-            int content = nativeDraw(canvas, mVisibleContentRect, color,
-                    extras, false);
-            canvas.setDrawFilter(null);
-            if (!mBlockWebkitViewMessages && content != 0) {
-                mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
-            }
-        }
-
-        canvas.restoreToCount(saveCount);
-        if (mSelectingText) {
-            drawTextSelectionHandles(canvas);
-        }
-
-        if (extras == DRAW_EXTRAS_CURSOR_RING) {
-            if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
-                mTouchMode = TOUCH_SHORTPRESS_MODE;
-            }
-        }
-        if (mFocusSizeChanged) {
-            mFocusSizeChanged = false;
-            // If we are zooming, this will get handled above, when the zoom
-            // finishes.  We also do not need to do this unless the WebTextView
-            // is showing. With hardware acceleration, the pageSwapCallback()
-            // updates the WebTextView position in sync with page swapping
-            if (!canvas.isHardwareAccelerated() && !animateZoom && inEditingMode()) {
-                didUpdateWebTextViewDimensions(ANYWHERE);
-            }
-        }
-    }
-
-    private void drawTextSelectionHandles(Canvas canvas) {
-        int[] handles = new int[4];
-        getSelectionHandles(handles);
-        int start_x = contentToViewDimension(handles[0]);
-        int start_y = contentToViewDimension(handles[1]);
-        int end_x = contentToViewDimension(handles[2]);
-        int end_y = contentToViewDimension(handles[3]);
-
-        if (mIsCaretSelection) {
-            if (mSelectHandleCenter == null) {
-                mSelectHandleCenter = mContext.getResources().getDrawable(
-                        com.android.internal.R.drawable.text_select_handle_middle);
-            }
-            // Caret handle is centered
-            start_x -= (mSelectHandleCenter.getIntrinsicWidth() / 2);
-            mSelectHandleCenter.setBounds(start_x, start_y,
-                    start_x + mSelectHandleCenter.getIntrinsicWidth(),
-                    start_y + mSelectHandleCenter.getIntrinsicHeight());
-            mSelectHandleCenter.draw(canvas);
-        } else {
-            if (mSelectHandleLeft == null) {
-                mSelectHandleLeft = mContext.getResources().getDrawable(
-                        com.android.internal.R.drawable.text_select_handle_left);
-            }
-            // Magic formula copied from TextView
-            start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
-            mSelectHandleLeft.setBounds(start_x, start_y,
-                    start_x + mSelectHandleLeft.getIntrinsicWidth(),
-                    start_y + mSelectHandleLeft.getIntrinsicHeight());
-            if (mSelectHandleRight == null) {
-                mSelectHandleRight = mContext.getResources().getDrawable(
-                        com.android.internal.R.drawable.text_select_handle_right);
-            }
-            end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
-            mSelectHandleRight.setBounds(end_x, end_y,
-                    end_x + mSelectHandleRight.getIntrinsicWidth(),
-                    end_y + mSelectHandleRight.getIntrinsicHeight());
-            mSelectHandleLeft.draw(canvas);
-            mSelectHandleRight.draw(canvas);
-        }
-    }
-
-    /**
-     * Takes an int[4] array as an output param with the values being
-     * startX, startY, endX, endY
-     */
-    private void getSelectionHandles(int[] handles) {
-        handles[0] = mSelectCursorBase.right;
-        handles[1] = mSelectCursorBase.bottom -
-                (mSelectCursorBase.height() / 4);
-        handles[2] = mSelectCursorExtent.left;
-        handles[3] = mSelectCursorExtent.bottom
-                - (mSelectCursorExtent.height() / 4);
-        if (!nativeIsBaseFirst(mNativeClass)) {
-            int swap = handles[0];
-            handles[0] = handles[2];
-            handles[2] = swap;
-            swap = handles[1];
-            handles[1] = handles[3];
-            handles[3] = swap;
-        }
-    }
-
-    // draw history
-    private boolean mDrawHistory = false;
-    private Picture mHistoryPicture = null;
-    private int mHistoryWidth = 0;
-    private int mHistoryHeight = 0;
-
-    // Only check the flag, can be called from WebCore thread
-    boolean drawHistory() {
-        return mDrawHistory;
-    }
-
-    int getHistoryPictureWidth() {
-        return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
-    }
-
-    // Should only be called in UI thread
-    void switchOutDrawHistory() {
-        if (null == mWebViewCore) return; // CallbackProxy may trigger this
-        if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
-            mDrawHistory = false;
-            mHistoryPicture = null;
-            invalidate();
-            int oldScrollX = mScrollX;
-            int oldScrollY = mScrollY;
-            mScrollX = pinLocX(mScrollX);
-            mScrollY = pinLocY(mScrollY);
-            if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
-                onScrollChanged(mScrollX, mScrollY, oldScrollX, oldScrollY);
-            } else {
-                sendOurVisibleRect();
-            }
-        }
-    }
-
-    // TODO: Remove this
-    WebViewCore.CursorData cursorData() {
-        if (sDisableNavcache) {
-            return new WebViewCore.CursorData(0, 0, 0, 0);
-        }
-        WebViewCore.CursorData result = cursorDataNoPosition();
-        Point position = nativeCursorPosition();
-        result.mX = position.x;
-        result.mY = position.y;
-        return result;
-    }
-
-    WebViewCore.CursorData cursorDataNoPosition() {
-        WebViewCore.CursorData result = new WebViewCore.CursorData();
-        result.mMoveGeneration = nativeMoveGeneration();
-        result.mFrame = nativeCursorFramePointer();
-        return result;
-    }
-
-    /**
-     *  Delete text from start to end in the focused textfield. If there is no
-     *  focus, or if start == end, silently fail.  If start and end are out of
-     *  order, swap them.
-     *  @param  start   Beginning of selection to delete.
-     *  @param  end     End of selection to delete.
-     */
-    /* package */ void deleteSelection(int start, int end) {
-        mTextGeneration++;
-        WebViewCore.TextSelectionData data
-                = new WebViewCore.TextSelectionData(start, end, 0);
-        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
-                data);
-    }
-
-    /**
-     *  Set the selection to (start, end) in the focused textfield. If start and
-     *  end are out of order, swap them.
-     *  @param  start   Beginning of selection.
-     *  @param  end     End of selection.
-     */
-    /* package */ void setSelection(int start, int end) {
-        if (mWebViewCore != null) {
-            mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
-        }
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (mInputConnection == null) {
-            mInputConnection = new WebViewInputConnection();
-        }
-        mInputConnection.setupEditorInfo(outAttrs);
-        return mInputConnection;
-    }
-
-    /**
-     * Called in response to a message from webkit telling us that the soft
-     * keyboard should be launched.
-     */
-    private void displaySoftKeyboard(boolean isTextView) {
-        InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-        // bring it back to the default level scale so that user can enter text
-        boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
-        if (zoom) {
-            mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
-            mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
-        }
-        if (isTextView) {
-            rebuildWebTextView();
-            if (inEditingMode()) {
-                imm.showSoftInput(mWebTextView, 0, mWebTextView.getResultReceiver());
-                if (zoom) {
-                    didUpdateWebTextViewDimensions(INTERSECTS_SCREEN);
-                }
-                return;
-            }
-        }
-        // Used by plugins and contentEditable.
-        // Also used if the navigation cache is out of date, and
-        // does not recognize that a textfield is in focus.  In that
-        // case, use WebView as the targeted view.
-        // see http://b/issue?id=2457459
-        imm.showSoftInput(this, 0);
-    }
-
-    // Called by WebKit to instruct the UI to hide the keyboard
-    private void hideSoftKeyboard() {
-        InputMethodManager imm = InputMethodManager.peekInstance();
-        if (imm != null && (imm.isActive(this)
-                || (inEditingMode() && imm.isActive(mWebTextView)))) {
-            imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
-        }
-    }
-
-    /*
-     * This method checks the current focus and cursor and potentially rebuilds
-     * mWebTextView to have the appropriate properties, such as password,
-     * multiline, and what text it contains.  It also removes it if necessary.
-     */
-    /* package */ void rebuildWebTextView() {
-        if (!sEnableWebTextView) {
-            return; // always use WebKit's text entry
-        }
-        // If the WebView does not have focus, do nothing until it gains focus.
-        if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
-            return;
-        }
-        boolean alreadyThere = inEditingMode();
-        // inEditingMode can only return true if mWebTextView is non-null,
-        // so we can safely call remove() if (alreadyThere)
-        if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
-            if (alreadyThere) {
-                mWebTextView.remove();
-            }
-            return;
-        }
-        // At this point, we know we have found an input field, so go ahead
-        // and create the WebTextView if necessary.
-        if (mWebTextView == null) {
-            mWebTextView = new WebTextView(mContext, WebView.this, mAutoFillData.getQueryId());
-            // Initialize our generation number.
-            mTextGeneration = 0;
-        }
-        mWebTextView.updateTextSize();
-        updateWebTextViewPosition();
-        String text = nativeFocusCandidateText();
-        int nodePointer = nativeFocusCandidatePointer();
-        // This needs to be called before setType, which may call
-        // requestFormData, and it needs to have the correct nodePointer.
-        mWebTextView.setNodePointer(nodePointer);
-        mWebTextView.setType(nativeFocusCandidateType());
-        // Gravity needs to be set after setType
-        mWebTextView.setGravityForRtl(nativeFocusCandidateIsRtlText());
-        if (null == text) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "rebuildWebTextView null == text");
-            }
-            text = "";
-        }
-        mWebTextView.setTextAndKeepSelection(text);
-        InputMethodManager imm = InputMethodManager.peekInstance();
-        if (imm != null && imm.isActive(mWebTextView)) {
-            imm.restartInput(mWebTextView);
-            mWebTextView.clearComposingText();
-        }
-        if (isFocused()) {
-            mWebTextView.requestFocus();
-        }
-    }
-
-    private void updateWebTextViewPosition() {
-        Rect visibleRect = new Rect();
-        calcOurContentVisibleRect(visibleRect);
-        // Note that sendOurVisibleRect calls viewToContent, so the coordinates
-        // should be in content coordinates.
-        Rect bounds = nativeFocusCandidateNodeBounds();
-        Rect vBox = contentToViewRect(bounds);
-        offsetByLayerScrollPosition(vBox);
-        mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
-        if (!Rect.intersects(bounds, visibleRect)) {
-            revealSelection();
-        }
-        updateWebTextViewPadding();
-    }
-
-    /**
-     * Update the padding of mWebTextView based on the native textfield/textarea
-     */
-    void updateWebTextViewPadding() {
-        Rect paddingRect = nativeFocusCandidatePaddingRect();
-        if (paddingRect != null) {
-            // Use contentToViewDimension since these are the dimensions of
-            // the padding.
-            mWebTextView.setPadding(
-                    contentToViewDimension(paddingRect.left),
-                    contentToViewDimension(paddingRect.top),
-                    contentToViewDimension(paddingRect.right),
-                    contentToViewDimension(paddingRect.bottom));
-        }
-    }
-
-    /**
-     * Tell webkit to put the cursor on screen.
-     */
-    /* package */ void revealSelection() {
-        if (mWebViewCore != null) {
-            mWebViewCore.sendMessage(EventHub.REVEAL_SELECTION);
-        }
-    }
-
-    /**
-     * Called by WebTextView to find saved form data associated with the
-     * textfield
-     * @param name Name of the textfield.
-     * @param nodePointer Pointer to the node of the textfield, so it can be
-     *          compared to the currently focused textfield when the data is
-     *          retrieved.
-     * @param autoFillable true if WebKit has determined this field is part of
-     *          a form that can be auto filled.
-     * @param autoComplete true if the attribute "autocomplete" is set to true
-     *          on the textfield.
-     */
-    /* package */ void requestFormData(String name, int nodePointer,
-            boolean autoFillable, boolean autoComplete) {
-        if (mWebViewCore.getSettings().getSaveFormData()) {
-            Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
-            update.arg1 = nodePointer;
-            RequestFormData updater = new RequestFormData(name, getUrl(),
-                    update, autoFillable, autoComplete);
-            Thread t = new Thread(updater);
-            t.start();
-        }
-    }
-
-    /**
-     * Pass a message to find out the <label> associated with the <input>
-     * identified by nodePointer
-     * @param framePointer Pointer to the frame containing the <input> node
-     * @param nodePointer Pointer to the node for which a <label> is desired.
-     */
-    /* package */ void requestLabel(int framePointer, int nodePointer) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
-                nodePointer);
-    }
-
-    /*
-     * This class requests an Adapter for the WebTextView which shows past
-     * entries stored in the database.  It is a Runnable so that it can be done
-     * in its own thread, without slowing down the UI.
-     */
-    private class RequestFormData implements Runnable {
-        private String mName;
-        private String mUrl;
-        private Message mUpdateMessage;
-        private boolean mAutoFillable;
-        private boolean mAutoComplete;
-        private WebSettings mWebSettings;
-
-        public RequestFormData(String name, String url, Message msg,
-                boolean autoFillable, boolean autoComplete) {
-            mName = name;
-            mUrl = WebTextView.urlForAutoCompleteData(url);
-            mUpdateMessage = msg;
-            mAutoFillable = autoFillable;
-            mAutoComplete = autoComplete;
-            mWebSettings = getSettings();
-        }
-
-        @Override
-        public void run() {
-            ArrayList<String> pastEntries = new ArrayList<String>();
-
-            if (mAutoFillable) {
-                // Note that code inside the adapter click handler in WebTextView depends
-                // on the AutoFill item being at the top of the drop down list. If you change
-                // the order, make sure to do it there too!
-                if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
-                    pastEntries.add(getResources().getText(
-                            com.android.internal.R.string.autofill_this_form).toString() +
-                            " " +
-                            mAutoFillData.getPreviewString());
-                    mWebTextView.setAutoFillProfileIsSet(true);
-                } else {
-                    // There is no autofill profile set up yet, so add an option that
-                    // will invite the user to set their profile up.
-                    pastEntries.add(getResources().getText(
-                            com.android.internal.R.string.setup_autofill).toString());
-                    mWebTextView.setAutoFillProfileIsSet(false);
-                }
-            }
-
-            if (mAutoComplete) {
-                pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
-            }
-
-            if (pastEntries.size() > 0) {
-                AutoCompleteAdapter adapter = new
-                        AutoCompleteAdapter(mContext, pastEntries);
-                mUpdateMessage.obj = adapter;
-                mUpdateMessage.sendToTarget();
-            }
-        }
-    }
-
-    /**
-     * Dump the display tree to "/sdcard/displayTree.txt"
-     *
-     * @hide debug only
-     */
-    public void dumpDisplayTree() {
-        nativeDumpDisplayTree(getUrl());
-    }
-
-    /**
-     * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
-     * "/sdcard/domTree.txt"
-     *
-     * @hide debug only
-     */
-    public void dumpDomTree(boolean toFile) {
-        mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
-    }
-
-    /**
-     * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
-     * to "/sdcard/renderTree.txt"
-     *
-     * @hide debug only
-     */
-    public void dumpRenderTree(boolean toFile) {
-        mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
-    }
-
-    /**
-     * Called by DRT on UI thread, need to proxy to WebCore thread.
-     *
-     * @hide debug only
-     */
-    public void useMockDeviceOrientation() {
-        mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
-    }
-
-    /**
-     * Called by DRT on WebCore thread.
-     *
-     * @hide debug only
-     */
-    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
-            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
-        mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
-                canProvideGamma, gamma);
-    }
-
-    // This is used to determine long press with the center key.  Does not
-    // affect long press with the trackball/touch.
-    private boolean mGotCenterDown = false;
-
-    @Override
-    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
-        if (mBlockWebkitViewMessages) {
-            return false;
-        }
-        // send complex characters to webkit for use by JS and plugins
-        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
-            // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
-            // return true as DOM handles the key
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isEnterActionKey(int keyCode) {
-        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER
-                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
-                    + "keyCode=" + keyCode
-                    + ", " + event + ", unicode=" + event.getUnicodeChar());
-        }
-        if (mIsCaretSelection) {
-            selectionDone();
-        }
-        if (mBlockWebkitViewMessages) {
-            return false;
-        }
-
-        // don't implement accelerator keys here; defer to host application
-        if (event.isCtrlPressed()) {
-            return false;
-        }
-
-        if (mNativeClass == 0) {
-            return false;
-        }
-
-        // do this hack up front, so it always works, regardless of touch-mode
-        if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
-            mAutoRedraw = !mAutoRedraw;
-            if (mAutoRedraw) {
-                invalidate();
-            }
-            return true;
-        }
-
-        // Bubble up the key event if
-        // 1. it is a system key; or
-        // 2. the host application wants to handle it;
-        if (event.isSystem()
-                || mCallbackProxy.uiOverrideKeyEvent(event)) {
-            return false;
-        }
-
-        // accessibility support
-        if (accessibilityScriptInjected()) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                // if an accessibility script is injected we delegate to it the key handling.
-                // this script is a screen reader which is a fully fledged solution for blind
-                // users to navigate in and interact with web pages.
-                mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-                return true;
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityScriptInjected = false;
-            }
-        } else if (mAccessibilityInjector != null) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                if (mAccessibilityInjector.onKeyEvent(event)) {
-                    // if an accessibility injector is present (no JavaScript enabled or the site
-                    // opts out injecting our JavaScript screen reader) we let it decide whether
-                    // to act on and consume the event.
-                    return true;
-                }
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityInjector = null;
-            }
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
-            if (event.hasNoModifiers()) {
-                pageUp(false);
-                return true;
-            } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                pageUp(true);
-                return true;
-            }
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
-            if (event.hasNoModifiers()) {
-                pageDown(false);
-                return true;
-            } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                pageDown(true);
-                return true;
-            }
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
-            pageUp(true);
-            return true;
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
-            pageDown(true);
-            return true;
-        }
-
-        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
-                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
-            switchOutDrawHistory();
-            if (nativePageShouldHandleShiftAndArrows()) {
-                letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
-                return true;
-            }
-            if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                switch (keyCode) {
-                    case KeyEvent.KEYCODE_DPAD_UP:
-                        pageUp(true);
-                        return true;
-                    case KeyEvent.KEYCODE_DPAD_DOWN:
-                        pageDown(true);
-                        return true;
-                    case KeyEvent.KEYCODE_DPAD_LEFT:
-                        nativeClearCursor(); // start next trackball movement from page edge
-                        return pinScrollTo(0, mScrollY, true, 0);
-                    case KeyEvent.KEYCODE_DPAD_RIGHT:
-                        nativeClearCursor(); // start next trackball movement from page edge
-                        return pinScrollTo(mContentWidth, mScrollY, true, 0);
-                }
-            }
-            if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
-                playSoundEffect(keyCodeToSoundsEffect(keyCode));
-                return true;
-            }
-            // Bubble up the key event as WebView doesn't handle it
-            return false;
-        }
-
-        if (isEnterActionKey(keyCode)) {
-            switchOutDrawHistory();
-            boolean wantsKeyEvents = nativeCursorNodePointer() == 0
-                || nativeCursorWantsKeyEvents();
-            if (event.getRepeatCount() == 0) {
-                if (mSelectingText) {
-                    return true; // discard press if copy in progress
-                }
-                mGotCenterDown = true;
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
-                if (!wantsKeyEvents) return true;
-            }
-            // Bubble up the key event as WebView doesn't handle it
-            if (!wantsKeyEvents) return false;
-        }
-
-        if (getSettings().getNavDump()) {
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_4:
-                    dumpDisplayTree();
-                    break;
-                case KeyEvent.KEYCODE_5:
-                case KeyEvent.KEYCODE_6:
-                    dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
-                    break;
-                case KeyEvent.KEYCODE_7:
-                case KeyEvent.KEYCODE_8:
-                    dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
-                    break;
-            }
-        }
-
-        if (nativeCursorIsTextInput()) {
-            // This message will put the node in focus, for the DOM's notion
-            // of focus.
-            mWebViewCore.sendMessage(EventHub.FAKE_CLICK, nativeCursorFramePointer(),
-                    nativeCursorNodePointer());
-            // This will bring up the WebTextView and put it in focus, for
-            // our view system's notion of focus
-            rebuildWebTextView();
-            // Now we need to pass the event to it
-            if (inEditingMode()) {
-                mWebTextView.setDefaultSelection();
-                return mWebTextView.dispatchKeyEvent(event);
-            }
-        } else if (nativeHasFocusNode()) {
-            // In this case, the cursor is not on a text input, but the focus
-            // might be.  Check it, and if so, hand over to the WebTextView.
-            rebuildWebTextView();
-            if (inEditingMode()) {
-                mWebTextView.setDefaultSelection();
-                return mWebTextView.dispatchKeyEvent(event);
-            }
-        }
-
-        // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeCursorWantsKeyEvents() || true) {
-            // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-            // return true as DOM handles the key
-            return true;
-        }
-
-        // Bubble up the key event as WebView doesn't handle it
-        return false;
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
-                    + ", " + event + ", unicode=" + event.getUnicodeChar());
-        }
-        if (mBlockWebkitViewMessages) {
-            return false;
-        }
-
-        if (mNativeClass == 0) {
-            return false;
-        }
-
-        // special CALL handling when cursor node's href is "tel:XXX"
-        if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
-            String text = nativeCursorText();
-            if (!nativeCursorIsTextInput() && text != null
-                    && text.startsWith(SCHEME_TEL)) {
-                Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
-                getContext().startActivity(intent);
-                return true;
-            }
-        }
-
-        // Bubble up the key event if
-        // 1. it is a system key; or
-        // 2. the host application wants to handle it;
-        if (event.isSystem()
-                || mCallbackProxy.uiOverrideKeyEvent(event)) {
-            return false;
-        }
-
-        // accessibility support
-        if (accessibilityScriptInjected()) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                // if an accessibility script is injected we delegate to it the key handling.
-                // this script is a screen reader which is a fully fledged solution for blind
-                // users to navigate in and interact with web pages.
-                mWebViewCore.sendMessage(EventHub.KEY_UP, event);
-                return true;
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityScriptInjected = false;
-            }
-        } else if (mAccessibilityInjector != null) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                if (mAccessibilityInjector.onKeyEvent(event)) {
-                    // if an accessibility injector is present (no JavaScript enabled or the site
-                    // opts out injecting our JavaScript screen reader) we let it decide whether to
-                    // act on and consume the event.
-                    return true;
-                }
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityInjector = null;
-            }
-        }
-
-        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
-                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
-            if (nativePageShouldHandleShiftAndArrows()) {
-                letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
-                return true;
-            }
-            // always handle the navigation keys in the UI thread
-            // Bubble up the key event as WebView doesn't handle it
-            return false;
-        }
-
-        if (isEnterActionKey(keyCode)) {
-            // remove the long press message first
-            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
-            mGotCenterDown = false;
-
-            if (mSelectingText) {
-                copySelection();
-                selectionDone();
-                return true; // discard press if copy in progress
-            }
-
-            if (!sDisableNavcache) {
-                // perform the single click
-                Rect visibleRect = sendOurVisibleRect();
-                // Note that sendOurVisibleRect calls viewToContent, so the
-                // coordinates should be in content coordinates.
-                if (!nativeCursorIntersects(visibleRect)) {
-                    return false;
-                }
-                WebViewCore.CursorData data = cursorData();
-                mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
-                playSoundEffect(SoundEffectConstants.CLICK);
-                if (nativeCursorIsTextInput()) {
-                    rebuildWebTextView();
-                    centerKeyPressOnTextField();
-                    if (inEditingMode()) {
-                        mWebTextView.setDefaultSelection();
-                    }
-                    return true;
-                }
-                clearTextEntry();
-                nativeShowCursorTimed();
-                if (mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
-                    return true;
-                }
-                if (nativeCursorNodePointer() != 0 && !nativeCursorWantsKeyEvents()) {
-                    mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
-                            nativeCursorNodePointer());
-                    return true;
-                }
-            }
-        }
-
-        // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeCursorWantsKeyEvents() || true) {
-            // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
-            // return true as DOM handles the key
-            return true;
-        }
-
-        // Bubble up the key event as WebView doesn't handle it
-        return false;
-    }
-
-    private boolean startSelectActionMode() {
-        mSelectCallback = new SelectActionModeCallback();
-        mSelectCallback.setTextSelected(!mIsCaretSelection);
-        mSelectCallback.setWebView(this);
-        if (startActionMode(mSelectCallback) == null) {
-            // There is no ActionMode, so do not allow the user to modify a
-            // selection.
-            selectionDone();
-            return false;
-        }
-        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        return true;
-    }
-
-    private void showPasteWindow() {
-        ClipboardManager cm = (ClipboardManager)(mContext
-                .getSystemService(Context.CLIPBOARD_SERVICE));
-        if (cm.hasPrimaryClip()) {
-            Rect cursorRect = contentToViewRect(mSelectCursorBase);
-            int[] location = new int[2];
-            getLocationInWindow(location);
-            cursorRect.offset(location[0] - mScrollX, location[1] - mScrollY);
-            if (mPasteWindow == null) {
-                mPasteWindow = new PastePopupWindow();
-            }
-            mPasteWindow.show(cursorRect, location[0], location[1]);
-        }
-    }
-
-    private void hidePasteButton() {
-        if (mPasteWindow != null) {
-            mPasteWindow.hide();
-        }
-    }
-
-    private void syncSelectionCursors() {
-        mSelectCursorBaseLayerId =
-                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase);
-        mSelectCursorExtentLayerId =
-                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent);
-    }
-
-    private boolean setupWebkitSelect() {
-        syncSelectionCursors();
-        if (mIsCaretSelection) {
-            showPasteWindow();
-        } else if (!startSelectActionMode()) {
-            selectionDone();
-            return false;
-        }
-        mSelectingText = true;
-        mTouchMode = TOUCH_DRAG_MODE;
-        return true;
-    }
-
-    private void updateWebkitSelection() {
-        int[] handles = null;
-        if (mIsCaretSelection) {
-            mSelectCursorExtent.set(mSelectCursorBase);
-        }
-        if (mSelectingText) {
-            handles = new int[4];
-            handles[0] = mSelectCursorBase.centerX();
-            handles[1] = mSelectCursorBase.centerY();
-            handles[2] = mSelectCursorExtent.centerX();
-            handles[3] = mSelectCursorExtent.centerY();
-        } else {
-            nativeSetTextSelection(mNativeClass, 0);
-        }
-        mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
-        mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles);
-    }
-
-    private void resetCaretTimer() {
-        mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
-        if (!mSelectionStarted) {
-            mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
-                    CARET_HANDLE_STAMINA_MS);
-        }
-    }
-
     /**
      * Use this method to put the WebView into text selection mode.
      * Do not rely on this functionality; it will be deprecated in the future.
@@ -6149,165 +1433,7 @@
     @Deprecated
     public void emulateShiftHeld() {
         checkThread();
-    }
-
-    /**
-     * Select all of the text in this WebView.
-     *
-     * @hide This is an implementation detail.
-     */
-    public void selectAll() {
-        mWebViewCore.sendMessage(EventHub.SELECT_ALL);
-    }
-
-    /**
-     * Called when the selection has been removed.
-     */
-    void selectionDone() {
-        if (mSelectingText) {
-            hidePasteButton();
-            mSelectingText = false;
-            // finish is idempotent, so this is fine even if selectionDone was
-            // called by mSelectCallback.onDestroyActionMode
-            if (mSelectCallback != null) {
-                mSelectCallback.finish();
-                mSelectCallback = null;
-            }
-            if (!mIsCaretSelection) {
-                updateWebkitSelection();
-            }
-            mIsCaretSelection = false;
-            invalidate(); // redraw without selection
-            mAutoScrollX = 0;
-            mAutoScrollY = 0;
-            mSentAutoScrollMessage = false;
-        }
-    }
-
-    /**
-     * Copy the selection to the clipboard
-     *
-     * @hide This is an implementation detail.
-     */
-    public boolean copySelection() {
-        boolean copiedSomething = false;
-        String selection = getSelection();
-        if (selection != null && selection != "") {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "copySelection \"" + selection + "\"");
-            }
-            Toast.makeText(mContext
-                    , com.android.internal.R.string.text_copied
-                    , Toast.LENGTH_SHORT).show();
-            copiedSomething = true;
-            ClipboardManager cm = (ClipboardManager)getContext()
-                    .getSystemService(Context.CLIPBOARD_SERVICE);
-            cm.setText(selection);
-            int[] handles = new int[4];
-            getSelectionHandles(handles);
-            mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
-        }
-        invalidate(); // remove selection region and pointer
-        return copiedSomething;
-    }
-
-    /**
-     * Cut the selected text into the clipboard
-     *
-     * @hide This is an implementation detail
-     */
-    public void cutSelection() {
-        copySelection();
-        int[] handles = new int[4];
-        getSelectionHandles(handles);
-        mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
-    }
-
-    /**
-     * Paste text from the clipboard to the cursor position.
-     *
-     * @hide This is an implementation detail
-     */
-    public void pasteFromClipboard() {
-        ClipboardManager cm = (ClipboardManager)getContext()
-                .getSystemService(Context.CLIPBOARD_SERVICE);
-        ClipData clipData = cm.getPrimaryClip();
-        if (clipData != null) {
-            ClipData.Item clipItem = clipData.getItemAt(0);
-            CharSequence pasteText = clipItem.getText();
-            if (mInputConnection != null) {
-                mInputConnection.replaceSelection(pasteText);
-            }
-        }
-    }
-
-    /**
-     * @hide This is an implementation detail.
-     */
-    public SearchBox getSearchBox() {
-        if ((mWebViewCore == null) || (mWebViewCore.getBrowserFrame() == null)) {
-            return null;
-        }
-        return mWebViewCore.getBrowserFrame().getSearchBox();
-    }
-
-    /**
-     * Returns the currently highlighted text as a string.
-     */
-    String getSelection() {
-        if (mNativeClass == 0) return "";
-        return nativeGetSelection();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (hasWindowFocus()) setActive(true);
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
-        if (mGlobalLayoutListener == null) {
-            mGlobalLayoutListener = new InnerGlobalLayoutListener();
-            treeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
-        }
-        if (mScrollChangedListener == null) {
-            mScrollChangedListener = new InnerScrollChangedListener();
-            treeObserver.addOnScrollChangedListener(mScrollChangedListener);
-        }
-
-        addAccessibilityApisToJavaScript();
-
-        mTouchEventQueue.reset();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        clearHelpers();
-        mZoomManager.dismissZoomPicker();
-        if (hasWindowFocus()) setActive(false);
-
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
-        if (mGlobalLayoutListener != null) {
-            treeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
-            mGlobalLayoutListener = null;
-        }
-        if (mScrollChangedListener != null) {
-            treeObserver.removeOnScrollChangedListener(mScrollChangedListener);
-            mScrollChangedListener = null;
-        }
-
-        removeAccessibilityApisFromJavaScript();
-
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        // The zoomManager may be null if the webview is created from XML that
-        // specifies the view's visibility param as not visible (see http://b/2794841)
-        if (visibility != View.VISIBLE && mZoomManager != null) {
-            mZoomManager.dismissZoomPicker();
-        }
-        updateDrawingState();
+        mProvider.emulateShiftHeld();
     }
 
     /**
@@ -6338,1571 +1464,15 @@
     public void onGlobalFocusChanged(View oldFocus, View newFocus) {
     }
 
-    void setActive(boolean active) {
-        if (active) {
-            if (hasFocus()) {
-                // If our window regained focus, and we have focus, then begin
-                // drawing the cursor ring
-                mDrawCursorRing = !inEditingMode();
-                setFocusControllerActive(true);
-            } else {
-                mDrawCursorRing = false;
-                if (!inEditingMode()) {
-                    // If our window gained focus, but we do not have it, do not
-                    // draw the cursor ring.
-                    setFocusControllerActive(false);
-                }
-                // We do not call recordButtons here because we assume
-                // that when we lost focus, or window focus, it got called with
-                // false for the first parameter
-            }
-        } else {
-            if (!mZoomManager.isZoomPickerVisible()) {
-                /*
-                 * The external zoom controls come in their own window, so our
-                 * window loses focus. Our policy is to not draw the cursor ring
-                 * if our window is not focused, but this is an exception since
-                 * the user can still navigate the web page with the zoom
-                 * controls showing.
-                 */
-                mDrawCursorRing = false;
-            }
-            mKeysPressed.clear();
-            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-            mTouchMode = TOUCH_DONE_MODE;
-            setFocusControllerActive(false);
-        }
-        invalidate();
-    }
-
-    // To avoid drawing the cursor ring, and remove the TextView when our window
-    // loses focus.
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        setActive(hasWindowFocus);
-        if (hasWindowFocus) {
-            JWebCoreJavaBridge.setActiveWebView(this);
-            if (mPictureUpdatePausedForFocusChange) {
-                WebViewCore.resumeUpdatePicture(mWebViewCore);
-                mPictureUpdatePausedForFocusChange = false;
-            }
-        } else {
-            JWebCoreJavaBridge.removeActiveWebView(this);
-            final WebSettings settings = getSettings();
-            if (settings != null && settings.enableSmoothTransition() &&
-                    mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) {
-                WebViewCore.pauseUpdatePicture(mWebViewCore);
-                mPictureUpdatePausedForFocusChange = true;
-            }
-        }
-        super.onWindowFocusChanged(hasWindowFocus);
-    }
-
-    /*
-     * Pass a message to WebCore Thread, telling the WebCore::Page's
-     * FocusController to be  "inactive" so that it will
-     * not draw the blinking cursor.  It gets set to "active" to draw the cursor
-     * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
-     */
-    /* package */ void setFocusControllerActive(boolean active) {
-        if (mWebViewCore == null) return;
-        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
-        // Need to send this message after the document regains focus.
-        if (active && mListBoxMessage != null) {
-            mWebViewCore.sendMessage(mListBoxMessage);
-            mListBoxMessage = null;
-        }
-    }
-
-    @Override
-    protected void onFocusChanged(boolean focused, int direction,
-            Rect previouslyFocusedRect) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
-        }
-        if (focused) {
-            // When we regain focus, if we have window focus, resume drawing
-            // the cursor ring
-            if (hasWindowFocus()) {
-                mDrawCursorRing = !inEditingMode();
-                setFocusControllerActive(true);
-            //} else {
-                // The WebView has gained focus while we do not have
-                // windowfocus.  When our window lost focus, we should have
-                // called recordButtons(false...)
-            }
-        } else {
-            // When we lost focus, unless focus went to the TextView (which is
-            // true if we are in editing mode), stop drawing the cursor ring.
-            mDrawCursorRing = false;
-            if (!inEditingMode()) {
-                setFocusControllerActive(false);
-            }
-            mKeysPressed.clear();
-        }
-
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
-    }
-
-    void setGLRectViewport() {
-        // Use the getGlobalVisibleRect() to get the intersection among the parents
-        // visible == false means we're clipped - send a null rect down to indicate that
-        // we should not draw
-        boolean visible = getGlobalVisibleRect(mGLRectViewport);
-        if (visible) {
-            // Then need to invert the Y axis, just for GL
-            View rootView = getRootView();
-            int rootViewHeight = rootView.getHeight();
-            mViewRectViewport.set(mGLRectViewport);
-            int savedWebViewBottom = mGLRectViewport.bottom;
-            mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeightImpl();
-            mGLRectViewport.top = rootViewHeight - savedWebViewBottom;
-            mGLViewportEmpty = false;
-        } else {
-            mGLViewportEmpty = true;
-        }
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
-                mGLViewportEmpty ? null : mViewRectViewport,
-                mVisibleContentRect, getScale());
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        boolean changed = super.setFrame(left, top, right, bottom);
-        if (!changed && mHeightCanMeasure) {
-            // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
-            // in WebViewCore after we get the first layout. We do call
-            // requestLayout() when we get contentSizeChanged(). But the View
-            // system won't call onSizeChanged if the dimension is not changed.
-            // In this case, we need to call sendViewSizeZoom() explicitly to
-            // notify the WebKit about the new dimensions.
-            sendViewSizeZoom(false);
-        }
-        setGLRectViewport();
-        return changed;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int ow, int oh) {
-        super.onSizeChanged(w, h, ow, oh);
-
-        // adjust the max viewport width depending on the view dimensions. This
-        // is to ensure the scaling is not going insane. So do not shrink it if
-        // the view size is temporarily smaller, e.g. when soft keyboard is up.
-        int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
-        if (newMaxViewportWidth > sMaxViewportWidth) {
-            sMaxViewportWidth = newMaxViewportWidth;
-        }
-
-        mZoomManager.onSizeChanged(w, h, ow, oh);
-
-        if (mLoadedPicture != null && mDelaySetPicture == null) {
-            // Size changes normally result in a new picture
-            // Re-set the loaded picture to simulate that
-            // However, do not update the base layer as that hasn't changed
-            setNewPicture(mLoadedPicture, false);
-        }
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        if (!mInOverScrollMode) {
-            sendOurVisibleRect();
-            // update WebKit if visible title bar height changed. The logic is same
-            // as getVisibleTitleHeightImpl.
-            int titleHeight = getTitleHeight();
-            if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
-                sendViewSizeZoom(false);
-            }
-        }
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        switch (event.getAction()) {
-            case KeyEvent.ACTION_DOWN:
-                mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
-                break;
-            case KeyEvent.ACTION_MULTIPLE:
-                // Always accept the action.
-                break;
-            case KeyEvent.ACTION_UP:
-                int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
-                if (location == -1) {
-                    // We did not receive the key down for this key, so do not
-                    // handle the key up.
-                    return false;
-                } else {
-                    // We did receive the key down.  Handle the key up, and
-                    // remove it from our pressed keys.
-                    mKeysPressed.remove(location);
-                }
-                break;
-            default:
-                // Accept the action.  This should not happen, unless a new
-                // action is added to KeyEvent.
-                break;
-        }
-        if (inEditingMode() && mWebTextView.isFocused()) {
-            // Ensure that the WebTextView gets the event, even if it does
-            // not currently have a bounds.
-            return mWebTextView.dispatchKeyEvent(event);
-        } else {
-            return super.dispatchKeyEvent(event);
-        }
-    }
-
-    /*
-     * Here is the snap align logic:
-     * 1. If it starts nearly horizontally or vertically, snap align;
-     * 2. If there is a dramitic direction change, let it go;
-     *
-     * Adjustable parameters. Angle is the radians on a unit circle, limited
-     * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
-     */
-    private static final float HSLOPE_TO_START_SNAP = .25f;
-    private static final float HSLOPE_TO_BREAK_SNAP = .4f;
-    private static final float VSLOPE_TO_START_SNAP = 1.25f;
-    private static final float VSLOPE_TO_BREAK_SNAP = .95f;
-    /*
-     *  These values are used to influence the average angle when entering
-     *  snap mode. If is is the first movement entering snap, we set the average
-     *  to the appropriate ideal. If the user is entering into snap after the
-     *  first movement, then we average the average angle with these values.
-     */
-    private static final float ANGLE_VERT = 2f;
-    private static final float ANGLE_HORIZ = 0f;
-    /*
-     *  The modified moving average weight.
-     *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
-     */
-    private static final float MMA_WEIGHT_N = 5;
-
-    private boolean hitFocusedPlugin(int contentX, int contentY) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
-            Rect r = nativeFocusNodeBounds();
-            Log.v(LOGTAG, "nativeFocusNodeBounds()=(" + r.left + ", " + r.top
-                    + ", " + r.right + ", " + r.bottom + ")");
-        }
-        return nativeFocusIsPlugin()
-                && nativeFocusNodeBounds().contains(contentX, contentY);
-    }
-
-    private boolean shouldForwardTouchEvent() {
-        if (mFullScreenHolder != null) return true;
-        if (mBlockWebkitViewMessages) return false;
-        return mForwardTouchEvents
-                && !mSelectingText
-                && mPreventDefault != PREVENT_DEFAULT_IGNORE
-                && mPreventDefault != PREVENT_DEFAULT_NO;
-    }
-
-    private boolean inFullScreenMode() {
-        return mFullScreenHolder != null;
-    }
-
-    private void dismissFullScreenMode() {
-        if (inFullScreenMode()) {
-            mFullScreenHolder.hide();
-            mFullScreenHolder = null;
-            invalidate();
-        }
-    }
-
-    void onPinchToZoomAnimationStart() {
-        // cancel the single touch handling
-        cancelTouch();
-        onZoomAnimationStart();
-    }
-
-    void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
-        onZoomAnimationEnd();
-        // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
-        // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
-        // as it may trigger the unwanted fling.
-        mTouchMode = TOUCH_PINCH_DRAG;
-        mConfirmMove = true;
-        startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
-    }
-
-    // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
-    // layer is found.
-    private void startScrollingLayer(float x, float y) {
-        int contentX = viewToContentX((int) x + mScrollX);
-        int contentY = viewToContentY((int) y + mScrollY);
-        mCurrentScrollingLayerId = nativeScrollableLayer(contentX, contentY,
-                mScrollingLayerRect, mScrollingLayerBounds);
-        if (mCurrentScrollingLayerId != 0) {
-            mTouchMode = TOUCH_DRAG_LAYER_MODE;
-        }
-    }
-
-    // 1/(density * density) used to compute the distance between points.
-    // Computed in init().
-    private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
-
-    // The distance between two points reported in onTouchEvent scaled by the
-    // density of the screen.
-    private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        WebViewCore.CursorData data = cursorDataNoPosition();
-        data.mX = viewToContentX((int) event.getX() + mScrollX);
-        data.mY = viewToContentY((int) event.getY() + mScrollY);
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
-        return true;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
-            return false;
-        }
-
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, ev + " at " + ev.getEventTime()
-                + " mTouchMode=" + mTouchMode
-                + " numPointers=" + ev.getPointerCount());
-        }
-
-        // If WebKit wasn't interested in this multitouch gesture, enqueue
-        // the event for handling directly rather than making the round trip
-        // to WebKit and back.
-        if (ev.getPointerCount() > 1 && mPreventDefault != PREVENT_DEFAULT_NO) {
-            passMultiTouchToWebKit(ev, mTouchEventQueue.nextTouchSequence());
-        } else {
-            mTouchEventQueue.enqueueTouchEvent(ev);
-        }
-
-        // Since all events are handled asynchronously, we always want the gesture stream.
-        return true;
-    }
-
-    private float calculateDragAngle(int dx, int dy) {
-        dx = Math.abs(dx);
-        dy = Math.abs(dy);
-        return (float) Math.atan2(dy, dx);
-    }
-
-    /*
-     * Common code for single touch and multi-touch.
-     * (x, y) denotes current focus point, which is the touch point for single touch
-     * and the middle point for multi-touch.
-     */
-    private boolean handleTouchEventCommon(MotionEvent ev, int action, int x, int y) {
-        long eventTime = ev.getEventTime();
-
-        // Due to the touch screen edge effect, a touch closer to the edge
-        // always snapped to the edge. As getViewWidth() can be different from
-        // getWidth() due to the scrollbar, adjusting the point to match
-        // getViewWidth(). Same applied to the height.
-        x = Math.min(x, getViewWidth() - 1);
-        y = Math.min(y, getViewHeightWithTitle() - 1);
-
-        int deltaX = mLastTouchX - x;
-        int deltaY = mLastTouchY - y;
-        int contentX = viewToContentX(x + mScrollX);
-        int contentY = viewToContentY(y + mScrollY);
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                mPreventDefault = PREVENT_DEFAULT_NO;
-                mConfirmMove = false;
-                mInitialHitTestResult = null;
-                if (!mScroller.isFinished()) {
-                    // stop the current scroll animation, but if this is
-                    // the start of a fling, allow it to add to the current
-                    // fling's velocity
-                    mScroller.abortAnimation();
-                    mTouchMode = TOUCH_DRAG_START_MODE;
-                    mConfirmMove = true;
-                    nativeSetIsScrolling(false);
-                } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
-                    mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
-                    if (sDisableNavcache) {
-                        removeTouchHighlight();
-                    }
-                    if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
-                        mTouchMode = TOUCH_DOUBLE_TAP_MODE;
-                    } else {
-                        // commit the short press action for the previous tap
-                        doShortPress();
-                        mTouchMode = TOUCH_INIT_MODE;
-                        mDeferTouchProcess = !mBlockWebkitViewMessages
-                                && (!inFullScreenMode() && mForwardTouchEvents)
-                                ? hitFocusedPlugin(contentX, contentY)
-                                : false;
-                    }
-                } else { // the normal case
-                    mTouchMode = TOUCH_INIT_MODE;
-                    mDeferTouchProcess = !mBlockWebkitViewMessages
-                            && (!inFullScreenMode() && mForwardTouchEvents)
-                            ? hitFocusedPlugin(contentX, contentY)
-                            : false;
-                    if (!mBlockWebkitViewMessages) {
-                        mWebViewCore.sendMessage(
-                                EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
-                    }
-                    if (sDisableNavcache) {
-                        TouchHighlightData data = new TouchHighlightData();
-                        data.mX = contentX;
-                        data.mY = contentY;
-                        data.mNativeLayerRect = new Rect();
-                        data.mNativeLayer = nativeScrollableLayer(
-                                contentX, contentY, data.mNativeLayerRect, null);
-                        data.mSlop = viewToContentDimension(mNavSlop);
-                        mTouchHighlightRegion.setEmpty();
-                        if (!mBlockWebkitViewMessages) {
-                            mTouchHighlightRequested = System.currentTimeMillis();
-                            mWebViewCore.sendMessageAtFrontOfQueue(
-                                    EventHub.HIT_TEST, data);
-                        }
-                        if (DEBUG_TOUCH_HIGHLIGHT) {
-                            if (getSettings().getNavDump()) {
-                                mTouchHighlightX = x + mScrollX;
-                                mTouchHighlightY = y + mScrollY;
-                                mPrivateHandler.postDelayed(new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        mTouchHighlightX = mTouchHighlightY = 0;
-                                        invalidate();
-                                    }
-                                }, TOUCH_HIGHLIGHT_ELAPSE_TIME);
-                            }
-                        }
-                    }
-                    if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
-                        EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
-                                (eventTime - mLastTouchUpTime), eventTime);
-                    }
-                    mSelectionStarted = false;
-                    if (mSelectingText) {
-                        int shiftedY = y - getTitleHeight() + mScrollY;
-                        int shiftedX = x + mScrollX;
-                        if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds()
-                                .contains(shiftedX, shiftedY)) {
-                            mSelectionStarted = true;
-                            mSelectDraggingCursor = mSelectCursorBase;
-                            mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
-                            hidePasteButton();
-                        } else if (mSelectHandleLeft != null
-                                && mSelectHandleLeft.getBounds()
-                                    .contains(shiftedX, shiftedY)) {
-                                mSelectionStarted = true;
-                                mSelectDraggingCursor = mSelectCursorBase;
-                        } else if (mSelectHandleRight != null
-                                && mSelectHandleRight.getBounds()
-                                .contains(shiftedX, shiftedY)) {
-                            mSelectionStarted = true;
-                            mSelectDraggingCursor = mSelectCursorExtent;
-                        } else if (mIsCaretSelection) {
-                            selectionDone();
-                        }
-                        if (mSelectDraggingCursor != null) {
-                            mSelectDraggingOffset.set(
-                                    mSelectDraggingCursor.left - contentX,
-                                    mSelectDraggingCursor.top - contentY);
-                        }
-                        if (DebugFlags.WEB_VIEW) {
-                            Log.v(LOGTAG, "select=" + contentX + "," + contentY);
-                        }
-                    }
-                }
-                // Trigger the link
-                if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
-                        || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
-                    mPrivateHandler.sendEmptyMessageDelayed(
-                            SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
-                    mPrivateHandler.sendEmptyMessageDelayed(
-                            SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
-                    if (inFullScreenMode() || mDeferTouchProcess) {
-                        mPreventDefault = PREVENT_DEFAULT_YES;
-                    } else if (!mBlockWebkitViewMessages && mForwardTouchEvents) {
-                        mPreventDefault = PREVENT_DEFAULT_MAYBE_YES;
-                    } else {
-                        mPreventDefault = PREVENT_DEFAULT_NO;
-                    }
-                    // pass the touch events from UI thread to WebCore thread
-                    if (shouldForwardTouchEvent()) {
-                        TouchEventData ted = new TouchEventData();
-                        ted.mAction = action;
-                        ted.mIds = new int[1];
-                        ted.mIds[0] = ev.getPointerId(0);
-                        ted.mPoints = new Point[1];
-                        ted.mPoints[0] = new Point(contentX, contentY);
-                        ted.mPointsInView = new Point[1];
-                        ted.mPointsInView[0] = new Point(x, y);
-                        ted.mMetaState = ev.getMetaState();
-                        ted.mReprocess = mDeferTouchProcess;
-                        ted.mNativeLayer = nativeScrollableLayer(
-                                contentX, contentY, ted.mNativeLayerRect, null);
-                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                        mTouchEventQueue.preQueueTouchEventData(ted);
-                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                        if (mDeferTouchProcess) {
-                            // still needs to set them for compute deltaX/Y
-                            mLastTouchX = x;
-                            mLastTouchY = y;
-                            break;
-                        }
-                        if (!inFullScreenMode()) {
-                            mPrivateHandler.removeMessages(PREVENT_DEFAULT_TIMEOUT);
-                            mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                    .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
-                                            action, 0), TAP_TIMEOUT);
-                        }
-                    }
-                }
-                startTouch(x, y, eventTime);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                boolean firstMove = false;
-                if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
-                        >= mTouchSlopSquare) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                    mConfirmMove = true;
-                    firstMove = true;
-                    if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
-                        mTouchMode = TOUCH_INIT_MODE;
-                    }
-                    if (sDisableNavcache) {
-                        removeTouchHighlight();
-                    }
-                }
-                if (mSelectingText && mSelectionStarted) {
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
-                    }
-                    ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                    if (deltaX != 0 || deltaY != 0) {
-                        mSelectDraggingCursor.offsetTo(
-                                contentX + mSelectDraggingOffset.x,
-                                contentY + mSelectDraggingOffset.y);
-                        updateWebkitSelection();
-                        mLastTouchX = x;
-                        mLastTouchY = y;
-                        invalidate();
-                    }
-                    break;
-                }
-
-                // pass the touch events from UI thread to WebCore thread
-                if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
-                        || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
-                    TouchEventData ted = new TouchEventData();
-                    ted.mAction = action;
-                    ted.mIds = new int[1];
-                    ted.mIds[0] = ev.getPointerId(0);
-                    ted.mPoints = new Point[1];
-                    ted.mPoints[0] = new Point(contentX, contentY);
-                    ted.mPointsInView = new Point[1];
-                    ted.mPointsInView[0] = new Point(x, y);
-                    ted.mMetaState = ev.getMetaState();
-                    ted.mReprocess = mDeferTouchProcess;
-                    ted.mNativeLayer = mCurrentScrollingLayerId;
-                    ted.mNativeLayerRect.set(mScrollingLayerRect);
-                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                    mTouchEventQueue.preQueueTouchEventData(ted);
-                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                    mLastSentTouchTime = eventTime;
-                    if (mDeferTouchProcess) {
-                        break;
-                    }
-                    if (firstMove && !inFullScreenMode()) {
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
-                                        action, 0), TAP_TIMEOUT);
-                    }
-                }
-                if (mTouchMode == TOUCH_DONE_MODE
-                        || mPreventDefault == PREVENT_DEFAULT_YES) {
-                    // no dragging during scroll zoom animation, or when prevent
-                    // default is yes
-                    break;
-                }
-                if (mVelocityTracker == null) {
-                    Log.e(LOGTAG, "Got null mVelocityTracker when "
-                            + "mPreventDefault = " + mPreventDefault
-                            + " mDeferTouchProcess = " + mDeferTouchProcess
-                            + " mTouchMode = " + mTouchMode);
-                } else {
-                    mVelocityTracker.addMovement(ev);
-                }
-
-                if (mTouchMode != TOUCH_DRAG_MODE &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-
-                    if (!mConfirmMove) {
-                        break;
-                    }
-
-                    if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES
-                            || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
-                        // track mLastTouchTime as we may need to do fling at
-                        // ACTION_UP
-                        mLastTouchTime = eventTime;
-                        break;
-                    }
-
-                    // Only lock dragging to one axis if we don't have a scale in progress.
-                    // Scaling implies free-roaming movement. Note this is only ever a question
-                    // if mZoomManager.supportsPanDuringZoom() is true.
-                    final ScaleGestureDetector detector =
-                      mZoomManager.getMultiTouchGestureDetector();
-                    mAverageAngle = calculateDragAngle(deltaX, deltaY);
-                    if (detector == null || !detector.isInProgress()) {
-                        // if it starts nearly horizontal or vertical, enforce it
-                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_X;
-                            mSnapPositive = deltaX > 0;
-                            mAverageAngle = ANGLE_HORIZ;
-                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_Y;
-                            mSnapPositive = deltaY > 0;
-                            mAverageAngle = ANGLE_VERT;
-                        }
-                    }
-
-                    mTouchMode = TOUCH_DRAG_MODE;
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-                    deltaX = 0;
-                    deltaY = 0;
-
-                    startScrollingLayer(x, y);
-                    startDrag();
-                }
-
-                // do pan
-                boolean done = false;
-                boolean keepScrollBarsVisible = false;
-                if (deltaX == 0 && deltaY == 0) {
-                    keepScrollBarsVisible = done = true;
-                } else {
-                    mAverageAngle +=
-                        (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
-                        / MMA_WEIGHT_N;
-                    if (mSnapScrollMode != SNAP_NONE) {
-                        if (mSnapScrollMode == SNAP_Y) {
-                            // radical change means getting out of snap mode
-                            if (mAverageAngle < VSLOPE_TO_BREAK_SNAP) {
-                                mSnapScrollMode = SNAP_NONE;
-                            }
-                        }
-                        if (mSnapScrollMode == SNAP_X) {
-                            // radical change means getting out of snap mode
-                            if (mAverageAngle > HSLOPE_TO_BREAK_SNAP) {
-                                mSnapScrollMode = SNAP_NONE;
-                            }
-                        }
-                    } else {
-                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_X;
-                            mSnapPositive = deltaX > 0;
-                            mAverageAngle = (mAverageAngle + ANGLE_HORIZ) / 2;
-                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_Y;
-                            mSnapPositive = deltaY > 0;
-                            mAverageAngle = (mAverageAngle + ANGLE_VERT) / 2;
-                        }
-                    }
-                    if (mSnapScrollMode != SNAP_NONE) {
-                        if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
-                            deltaY = 0;
-                        } else {
-                            deltaX = 0;
-                        }
-                    }
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-
-                    if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
-                        mHeldMotionless = MOTIONLESS_FALSE;
-                        nativeSetIsScrolling(true);
-                    } else {
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        nativeSetIsScrolling(false);
-                        keepScrollBarsVisible = true;
-                    }
-
-                    mLastTouchTime = eventTime;
-                }
-
-                doDrag(deltaX, deltaY);
-
-                // Turn off scrollbars when dragging a layer.
-                if (keepScrollBarsVisible &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-                    if (mHeldMotionless != MOTIONLESS_TRUE) {
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        invalidate();
-                    }
-                    // keep the scrollbar on the screen even there is no scroll
-                    awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
-                            false);
-                    // Post a message so that we'll keep them alive while we're not scrolling.
-                    mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                            .obtainMessage(AWAKEN_SCROLL_BARS),
-                            ViewConfiguration.getScrollDefaultDelay());
-                    // return false to indicate that we can't pan out of the
-                    // view space
-                    return !done;
-                } else {
-                    mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                }
-                break;
-            }
-            case MotionEvent.ACTION_UP: {
-                if (!isFocused()) requestFocus();
-                // pass the touch events from UI thread to WebCore thread
-                if (shouldForwardTouchEvent()) {
-                    TouchEventData ted = new TouchEventData();
-                    ted.mIds = new int[1];
-                    ted.mIds[0] = ev.getPointerId(0);
-                    ted.mAction = action;
-                    ted.mPoints = new Point[1];
-                    ted.mPoints[0] = new Point(contentX, contentY);
-                    ted.mPointsInView = new Point[1];
-                    ted.mPointsInView[0] = new Point(x, y);
-                    ted.mMetaState = ev.getMetaState();
-                    ted.mReprocess = mDeferTouchProcess;
-                    ted.mNativeLayer = mCurrentScrollingLayerId;
-                    ted.mNativeLayerRect.set(mScrollingLayerRect);
-                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                    mTouchEventQueue.preQueueTouchEventData(ted);
-                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                }
-                mLastTouchUpTime = eventTime;
-                if (mSentAutoScrollMessage) {
-                    mAutoScrollX = mAutoScrollY = 0;
-                }
-                switch (mTouchMode) {
-                    case TOUCH_DOUBLE_TAP_MODE: // double tap
-                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                        if (inFullScreenMode() || mDeferTouchProcess) {
-                            TouchEventData ted = new TouchEventData();
-                            ted.mIds = new int[1];
-                            ted.mIds[0] = ev.getPointerId(0);
-                            ted.mAction = WebViewCore.ACTION_DOUBLETAP;
-                            ted.mPoints = new Point[1];
-                            ted.mPoints[0] = new Point(contentX, contentY);
-                            ted.mPointsInView = new Point[1];
-                            ted.mPointsInView[0] = new Point(x, y);
-                            ted.mMetaState = ev.getMetaState();
-                            ted.mReprocess = mDeferTouchProcess;
-                            ted.mNativeLayer = nativeScrollableLayer(
-                                    contentX, contentY,
-                                    ted.mNativeLayerRect, null);
-                            ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                            mTouchEventQueue.preQueueTouchEventData(ted);
-                            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                        } else if (mPreventDefault != PREVENT_DEFAULT_YES){
-                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
-                            mTouchMode = TOUCH_DONE_MODE;
-                        }
-                        break;
-                    case TOUCH_INIT_MODE: // tap
-                    case TOUCH_SHORTPRESS_START_MODE:
-                    case TOUCH_SHORTPRESS_MODE:
-                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                        if (mConfirmMove) {
-                            Log.w(LOGTAG, "Miss a drag as we are waiting for" +
-                                    " WebCore's response for touch down.");
-                            if (mPreventDefault != PREVENT_DEFAULT_YES
-                                    && (computeMaxScrollX() > 0
-                                            || computeMaxScrollY() > 0)) {
-                                // If the user has performed a very quick touch
-                                // sequence it is possible that we may get here
-                                // before WebCore has had a chance to process the events.
-                                // In this case, any call to preventDefault in the
-                                // JS touch handler will not have been executed yet.
-                                // Hence we will see both the UI (now) and WebCore
-                                // (when context switches) handling the event,
-                                // regardless of whether the web developer actually
-                                // doeses preventDefault in their touch handler. This
-                                // is the nature of our asynchronous touch model.
-
-                                // we will not rewrite drag code here, but we
-                                // will try fling if it applies.
-                                WebViewCore.reducePriority();
-                                // to get better performance, pause updating the
-                                // picture
-                                WebViewCore.pauseUpdatePicture(mWebViewCore);
-                                // fall through to TOUCH_DRAG_MODE
-                            } else {
-                                // WebKit may consume the touch event and modify
-                                // DOM. drawContentPicture() will be called with
-                                // animateSroll as true for better performance.
-                                // Force redraw in high-quality.
-                                invalidate();
-                                break;
-                            }
-                        } else {
-                            if (mSelectingText) {
-                                // tapping on selection or controls does nothing
-                                if (!mSelectionStarted) {
-                                    selectionDone();
-                                }
-                                break;
-                            }
-                            // only trigger double tap if the WebView is
-                            // scalable
-                            if (mTouchMode == TOUCH_INIT_MODE
-                                    && (canZoomIn() || canZoomOut())) {
-                                mPrivateHandler.sendEmptyMessageDelayed(
-                                        RELEASE_SINGLE_TAP, ViewConfiguration
-                                                .getDoubleTapTimeout());
-                            } else {
-                                doShortPress();
-                            }
-                            break;
-                        }
-                    case TOUCH_DRAG_MODE:
-                    case TOUCH_DRAG_LAYER_MODE:
-                        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                        // if the user waits a while w/o moving before the
-                        // up, we don't want to do a fling
-                        if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
-                            if (mVelocityTracker == null) {
-                                Log.e(LOGTAG, "Got null mVelocityTracker when "
-                                        + "mPreventDefault = "
-                                        + mPreventDefault
-                                        + " mDeferTouchProcess = "
-                                        + mDeferTouchProcess);
-                            } else {
-                                mVelocityTracker.addMovement(ev);
-                            }
-                            // set to MOTIONLESS_IGNORE so that it won't keep
-                            // removing and sending message in
-                            // drawCoreAndCursorRing()
-                            mHeldMotionless = MOTIONLESS_IGNORE;
-                            doFling();
-                            break;
-                        } else {
-                            if (mScroller.springBack(mScrollX, mScrollY, 0,
-                                    computeMaxScrollX(), 0,
-                                    computeMaxScrollY())) {
-                                invalidate();
-                            }
-                        }
-                        // redraw in high-quality, as we're done dragging
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        invalidate();
-                        // fall through
-                    case TOUCH_DRAG_START_MODE:
-                        // TOUCH_DRAG_START_MODE should not happen for the real
-                        // device as we almost certain will get a MOVE. But this
-                        // is possible on emulator.
-                        mLastVelocity = 0;
-                        WebViewCore.resumePriority();
-                        if (!mSelectingText) {
-                            WebViewCore.resumeUpdatePicture(mWebViewCore);
-                        }
-                        break;
-                }
-                stopTouch();
-                break;
-            }
-            case MotionEvent.ACTION_CANCEL: {
-                if (mTouchMode == TOUCH_DRAG_MODE) {
-                    mScroller.springBack(mScrollX, mScrollY, 0,
-                            computeMaxScrollX(), 0, computeMaxScrollY());
-                    invalidate();
-                }
-                cancelWebCoreTouchEvent(contentX, contentY, false);
-                cancelTouch();
-                break;
-            }
-        }
-        return true;
-    }
-
-    private void passMultiTouchToWebKit(MotionEvent ev, long sequence) {
-        TouchEventData ted = new TouchEventData();
-        ted.mAction = ev.getActionMasked();
-        final int count = ev.getPointerCount();
-        ted.mIds = new int[count];
-        ted.mPoints = new Point[count];
-        ted.mPointsInView = new Point[count];
-        for (int c = 0; c < count; c++) {
-            ted.mIds[c] = ev.getPointerId(c);
-            int x = viewToContentX((int) ev.getX(c) + mScrollX);
-            int y = viewToContentY((int) ev.getY(c) + mScrollY);
-            ted.mPoints[c] = new Point(x, y);
-            ted.mPointsInView[c] = new Point((int) ev.getX(c), (int) ev.getY(c));
-        }
-        if (ted.mAction == MotionEvent.ACTION_POINTER_DOWN
-            || ted.mAction == MotionEvent.ACTION_POINTER_UP) {
-            ted.mActionIndex = ev.getActionIndex();
-        }
-        ted.mMetaState = ev.getMetaState();
-        ted.mReprocess = true;
-        ted.mMotionEvent = MotionEvent.obtain(ev);
-        ted.mSequence = sequence;
-        mTouchEventQueue.preQueueTouchEventData(ted);
-        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-        cancelLongPress();
-        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-    }
-
-    void handleMultiTouchInWebView(MotionEvent ev) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "multi-touch: " + ev + " at " + ev.getEventTime()
-                + " mTouchMode=" + mTouchMode
-                + " numPointers=" + ev.getPointerCount()
-                + " scrolloffset=(" + mScrollX + "," + mScrollY + ")");
-        }
-
-        final ScaleGestureDetector detector =
-            mZoomManager.getMultiTouchGestureDetector();
-
-        // A few apps use WebView but don't instantiate gesture detector.
-        // We don't need to support multi touch for them.
-        if (detector == null) return;
-
-        float x = ev.getX();
-        float y = ev.getY();
-
-        if (mPreventDefault != PREVENT_DEFAULT_YES) {
-            detector.onTouchEvent(ev);
-
-            if (detector.isInProgress()) {
-                if (DebugFlags.WEB_VIEW) {
-                    Log.v(LOGTAG, "detector is in progress");
-                }
-                mLastTouchTime = ev.getEventTime();
-                x = detector.getFocusX();
-                y = detector.getFocusY();
-
-                cancelLongPress();
-                mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                if (!mZoomManager.supportsPanDuringZoom()) {
-                    return;
-                }
-                mTouchMode = TOUCH_DRAG_MODE;
-                if (mVelocityTracker == null) {
-                    mVelocityTracker = VelocityTracker.obtain();
-                }
-            }
-        }
-
-        int action = ev.getActionMasked();
-        if (action == MotionEvent.ACTION_POINTER_DOWN) {
-            cancelTouch();
-            action = MotionEvent.ACTION_DOWN;
-        } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() >= 2) {
-            // set mLastTouchX/Y to the remaining points for multi-touch.
-            mLastTouchX = Math.round(x);
-            mLastTouchY = Math.round(y);
-        } else if (action == MotionEvent.ACTION_MOVE) {
-            // negative x or y indicate it is on the edge, skip it.
-            if (x < 0 || y < 0) {
-                return;
-            }
-        }
-
-        handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
-    }
-
-    private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
-        if (shouldForwardTouchEvent()) {
-            if (removeEvents) {
-                mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
-            }
-            TouchEventData ted = new TouchEventData();
-            ted.mIds = new int[1];
-            ted.mIds[0] = 0;
-            ted.mPoints = new Point[1];
-            ted.mPoints[0] = new Point(x, y);
-            ted.mPointsInView = new Point[1];
-            int viewX = contentToViewX(x) - mScrollX;
-            int viewY = contentToViewY(y) - mScrollY;
-            ted.mPointsInView[0] = new Point(viewX, viewY);
-            ted.mAction = MotionEvent.ACTION_CANCEL;
-            ted.mNativeLayer = nativeScrollableLayer(
-                    x, y, ted.mNativeLayerRect, null);
-            ted.mSequence = mTouchEventQueue.nextTouchSequence();
-            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-            mPreventDefault = PREVENT_DEFAULT_IGNORE;
-
-            if (removeEvents) {
-                // Mark this after sending the message above; we should
-                // be willing to ignore the cancel event that we just sent.
-                mTouchEventQueue.ignoreCurrentlyMissingEvents();
-            }
-        }
-    }
-
-    private void startTouch(float x, float y, long eventTime) {
-        // Remember where the motion event started
-        mStartTouchX = mLastTouchX = Math.round(x);
-        mStartTouchY = mLastTouchY = Math.round(y);
-        mLastTouchTime = eventTime;
-        mVelocityTracker = VelocityTracker.obtain();
-        mSnapScrollMode = SNAP_NONE;
-        mPrivateHandler.sendEmptyMessageDelayed(UPDATE_SELECTION,
-                ViewConfiguration.getTapTimeout());
-    }
-
-    private void startDrag() {
-        WebViewCore.reducePriority();
-        // to get better performance, pause updating the picture
-        WebViewCore.pauseUpdatePicture(mWebViewCore);
-        nativeSetIsScrolling(true);
-
-        if (!mDragFromTextInput) {
-            nativeHideCursor();
-        }
-
-        if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
-                || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
-            mZoomManager.invokeZoomPicker();
-        }
-    }
-
-    private void doDrag(int deltaX, int deltaY) {
-        if ((deltaX | deltaY) != 0) {
-            int oldX = mScrollX;
-            int oldY = mScrollY;
-            int rangeX = computeMaxScrollX();
-            int rangeY = computeMaxScrollY();
-            // Check for the original scrolling layer in case we change
-            // directions.  mTouchMode might be TOUCH_DRAG_MODE if we have
-            // reached the edge of a layer but mScrollingLayer will be non-zero
-            // if we initiated the drag on a layer.
-            if (mCurrentScrollingLayerId != 0) {
-                final int contentX = viewToContentDimension(deltaX);
-                final int contentY = viewToContentDimension(deltaY);
-
-                // Check the scrolling bounds to see if we will actually do any
-                // scrolling.  The rectangle is in document coordinates.
-                final int maxX = mScrollingLayerRect.right;
-                final int maxY = mScrollingLayerRect.bottom;
-                final int resultX = Math.max(0,
-                        Math.min(mScrollingLayerRect.left + contentX, maxX));
-                final int resultY = Math.max(0,
-                        Math.min(mScrollingLayerRect.top + contentY, maxY));
-
-                if (resultX != mScrollingLayerRect.left ||
-                        resultY != mScrollingLayerRect.top) {
-                    // In case we switched to dragging the page.
-                    mTouchMode = TOUCH_DRAG_LAYER_MODE;
-                    deltaX = contentX;
-                    deltaY = contentY;
-                    oldX = mScrollingLayerRect.left;
-                    oldY = mScrollingLayerRect.top;
-                    rangeX = maxX;
-                    rangeY = maxY;
-                } else {
-                    // Scroll the main page if we are not going to scroll the
-                    // layer.  This does not reset mScrollingLayer in case the
-                    // user changes directions and the layer can scroll the
-                    // other way.
-                    mTouchMode = TOUCH_DRAG_MODE;
-                }
-            }
-
-            if (mOverScrollGlow != null) {
-                mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
-            }
-
-            overScrollBy(deltaX, deltaY, oldX, oldY,
-                    rangeX, rangeY,
-                    mOverscrollDistance, mOverscrollDistance, true);
-            if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
-                invalidate();
-            }
-        }
-        mZoomManager.keepZoomPickerVisible();
-    }
-
-    private void stopTouch() {
-        if (mScroller.isFinished() && !mSelectingText
-                && (mTouchMode == TOUCH_DRAG_MODE || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
-            WebViewCore.resumePriority();
-            WebViewCore.resumeUpdatePicture(mWebViewCore);
-            nativeSetIsScrolling(false);
-        }
-
-        // we also use mVelocityTracker == null to tell us that we are
-        // not "moving around", so we can take the slower/prettier
-        // mode in the drawing code
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-
-        // Release any pulled glows
-        if (mOverScrollGlow != null) {
-            mOverScrollGlow.releaseAll();
-        }
-
-        if (mSelectingText) {
-            mSelectionStarted = false;
-            syncSelectionCursors();
-            if (mIsCaretSelection) {
-                resetCaretTimer();
-                showPasteWindow();
-            }
-            invalidate();
-        }
-    }
-
-    private void cancelTouch() {
-        // we also use mVelocityTracker == null to tell us that we are
-        // not "moving around", so we can take the slower/prettier
-        // mode in the drawing code
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-
-        if ((mTouchMode == TOUCH_DRAG_MODE
-                || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
-            WebViewCore.resumePriority();
-            WebViewCore.resumeUpdatePicture(mWebViewCore);
-            nativeSetIsScrolling(false);
-        }
-        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-        if (sDisableNavcache) {
-            removeTouchHighlight();
-        }
-        mHeldMotionless = MOTIONLESS_TRUE;
-        mTouchMode = TOUCH_DONE_MODE;
-        nativeHideCursor();
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_SCROLL: {
-                    final float vscroll;
-                    final float hscroll;
-                    if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
-                        vscroll = 0;
-                        hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                    } else {
-                        vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                        hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
-                    }
-                    if (hscroll != 0 || vscroll != 0) {
-                        final int vdelta = (int) (vscroll * getVerticalScrollFactor());
-                        final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
-                        if (pinScrollBy(hdelta, vdelta, false, 0)) {
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return super.onGenericMotionEvent(event);
-    }
-
-    private long mTrackballFirstTime = 0;
-    private long mTrackballLastTime = 0;
-    private float mTrackballRemainsX = 0.0f;
-    private float mTrackballRemainsY = 0.0f;
-    private int mTrackballXMove = 0;
-    private int mTrackballYMove = 0;
-    private boolean mSelectingText = false;
-    private boolean mSelectionStarted = false;
-    private static final int TRACKBALL_KEY_TIMEOUT = 1000;
-    private static final int TRACKBALL_TIMEOUT = 200;
-    private static final int TRACKBALL_WAIT = 100;
-    private static final int TRACKBALL_SCALE = 400;
-    private static final int TRACKBALL_SCROLL_COUNT = 5;
-    private static final int TRACKBALL_MOVE_COUNT = 10;
-    private static final int TRACKBALL_MULTIPLIER = 3;
-    private static final int SELECT_CURSOR_OFFSET = 16;
-    private static final int SELECT_SCROLL = 5;
-    private int mSelectX = 0;
-    private int mSelectY = 0;
-    private boolean mFocusSizeChanged = false;
-    private boolean mTrackballDown = false;
-    private long mTrackballUpTime = 0;
-    private long mLastCursorTime = 0;
-    private Rect mLastCursorBounds;
-
-    // Set by default; BrowserActivity clears to interpret trackball data
-    // directly for movement. Currently, the framework only passes
-    // arrow key events, not trackball events, from one child to the next
-    private boolean mMapTrackballToArrowKeys = true;
-
-    private DrawData mDelaySetPicture;
-    private DrawData mLoadedPicture;
-
     public void setMapTrackballToArrowKeys(boolean setMap) {
         checkThread();
-        mMapTrackballToArrowKeys = setMap;
+        mProvider.setMapTrackballToArrowKeys(setMap);
     }
 
-    void resetTrackballTime() {
-        mTrackballLastTime = 0;
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent ev) {
-        long time = ev.getEventTime();
-        if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
-            if (ev.getY() > 0) pageDown(true);
-            if (ev.getY() < 0) pageUp(true);
-            return true;
-        }
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (mSelectingText) {
-                return true; // discard press if copy in progress
-            }
-            mTrackballDown = true;
-            if (mNativeClass == 0) {
-                return false;
-            }
-            if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
-                    && !mLastCursorBounds.equals(cursorRingBounds())) {
-                nativeSelectBestAt(mLastCursorBounds);
-            }
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
-                        + " time=" + time
-                        + " mLastCursorTime=" + mLastCursorTime);
-            }
-            if (isInTouchMode()) requestFocusFromTouch();
-            return false; // let common code in onKeyDown at it
-        }
-        if (ev.getAction() == MotionEvent.ACTION_UP) {
-            // LONG_PRESS_CENTER is set in common onKeyDown
-            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
-            mTrackballDown = false;
-            mTrackballUpTime = time;
-            if (mSelectingText) {
-                copySelection();
-                selectionDone();
-                return true; // discard press if copy in progress
-            }
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
-                        + " time=" + time
-                );
-            }
-            return false; // let common code in onKeyUp at it
-        }
-        if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
-                AccessibilityManager.getInstance(mContext).isEnabled()) {
-            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
-            return false;
-        }
-        if (mTrackballDown) {
-            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
-            return true; // discard move if trackball is down
-        }
-        if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
-            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
-            return true;
-        }
-        // TODO: alternatively we can do panning as touch does
-        switchOutDrawHistory();
-        if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "onTrackballEvent time="
-                        + time + " last=" + mTrackballLastTime);
-            }
-            mTrackballFirstTime = time;
-            mTrackballXMove = mTrackballYMove = 0;
-        }
-        mTrackballLastTime = time;
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
-        }
-        mTrackballRemainsX += ev.getX();
-        mTrackballRemainsY += ev.getY();
-        doTrackball(time, ev.getMetaState());
-        return true;
-    }
-
-    private int scaleTrackballX(float xRate, int width) {
-        int xMove = (int) (xRate / TRACKBALL_SCALE * width);
-        int nextXMove = xMove;
-        if (xMove > 0) {
-            if (xMove > mTrackballXMove) {
-                xMove -= mTrackballXMove;
-            }
-        } else if (xMove < mTrackballXMove) {
-            xMove -= mTrackballXMove;
-        }
-        mTrackballXMove = nextXMove;
-        return xMove;
-    }
-
-    private int scaleTrackballY(float yRate, int height) {
-        int yMove = (int) (yRate / TRACKBALL_SCALE * height);
-        int nextYMove = yMove;
-        if (yMove > 0) {
-            if (yMove > mTrackballYMove) {
-                yMove -= mTrackballYMove;
-            }
-        } else if (yMove < mTrackballYMove) {
-            yMove -= mTrackballYMove;
-        }
-        mTrackballYMove = nextYMove;
-        return yMove;
-    }
-
-    private int keyCodeToSoundsEffect(int keyCode) {
-        switch(keyCode) {
-            case KeyEvent.KEYCODE_DPAD_UP:
-                return SoundEffectConstants.NAVIGATION_UP;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                return SoundEffectConstants.NAVIGATION_RIGHT;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                return SoundEffectConstants.NAVIGATION_DOWN;
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                return SoundEffectConstants.NAVIGATION_LEFT;
-        }
-        throw new IllegalArgumentException("keyCode must be one of " +
-                "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
-                "KEYCODE_DPAD_LEFT}.");
-    }
-
-    private void doTrackball(long time, int metaState) {
-        int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
-        if (elapsed == 0) {
-            elapsed = TRACKBALL_TIMEOUT;
-        }
-        float xRate = mTrackballRemainsX * 1000 / elapsed;
-        float yRate = mTrackballRemainsY * 1000 / elapsed;
-        int viewWidth = getViewWidth();
-        int viewHeight = getViewHeight();
-        float ax = Math.abs(xRate);
-        float ay = Math.abs(yRate);
-        float maxA = Math.max(ax, ay);
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
-                    + " xRate=" + xRate
-                    + " yRate=" + yRate
-                    + " mTrackballRemainsX=" + mTrackballRemainsX
-                    + " mTrackballRemainsY=" + mTrackballRemainsY);
-        }
-        int width = mContentWidth - viewWidth;
-        int height = mContentHeight - viewHeight;
-        if (width < 0) width = 0;
-        if (height < 0) height = 0;
-        ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
-        ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
-        maxA = Math.max(ax, ay);
-        int count = Math.max(0, (int) maxA);
-        int oldScrollX = mScrollX;
-        int oldScrollY = mScrollY;
-        if (count > 0) {
-            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
-                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
-                    mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
-                    KeyEvent.KEYCODE_DPAD_RIGHT;
-            count = Math.min(count, TRACKBALL_MOVE_COUNT);
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
-                        + " count=" + count
-                        + " mTrackballRemainsX=" + mTrackballRemainsX
-                        + " mTrackballRemainsY=" + mTrackballRemainsY);
-            }
-            if (mNativeClass != 0 && nativePageShouldHandleShiftAndArrows()) {
-                for (int i = 0; i < count; i++) {
-                    letPageHandleNavKey(selectKeyCode, time, true, metaState);
-                }
-                letPageHandleNavKey(selectKeyCode, time, false, metaState);
-            } else if (navHandledKey(selectKeyCode, count, false, time)) {
-                playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
-            }
-            mTrackballRemainsX = mTrackballRemainsY = 0;
-        }
-        if (count >= TRACKBALL_SCROLL_COUNT) {
-            int xMove = scaleTrackballX(xRate, width);
-            int yMove = scaleTrackballY(yRate, height);
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "doTrackball pinScrollBy"
-                        + " count=" + count
-                        + " xMove=" + xMove + " yMove=" + yMove
-                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
-                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
-                        );
-            }
-            if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
-                xMove = 0;
-            }
-            if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
-                yMove = 0;
-            }
-            if (xMove != 0 || yMove != 0) {
-                pinScrollBy(xMove, yMove, true, 0);
-            }
-        }
-    }
-
-    /**
-     * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
-     * @return Maximum horizontal scroll position within real content
-     */
-    int computeMaxScrollX() {
-        return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
-    }
-
-    /**
-     * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
-     * @return Maximum vertical scroll position within real content
-     */
-    int computeMaxScrollY() {
-        return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
-                - getViewHeightWithTitle(), 0);
-    }
-
-    boolean updateScrollCoordinates(int x, int y) {
-        int oldX = mScrollX;
-        int oldY = mScrollY;
-        mScrollX = x;
-        mScrollY = y;
-        if (oldX != mScrollX || oldY != mScrollY) {
-            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-            return true;
-        } else {
-            return false;
-        }
-    }
 
     public void flingScroll(int vx, int vy) {
         checkThread();
-        mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
-                computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
-        invalidate();
-    }
-
-    private void doFling() {
-        if (mVelocityTracker == null) {
-            return;
-        }
-        int maxX = computeMaxScrollX();
-        int maxY = computeMaxScrollY();
-
-        mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
-        int vx = (int) mVelocityTracker.getXVelocity();
-        int vy = (int) mVelocityTracker.getYVelocity();
-
-        int scrollX = mScrollX;
-        int scrollY = mScrollY;
-        int overscrollDistance = mOverscrollDistance;
-        int overflingDistance = mOverflingDistance;
-
-        // Use the layer's scroll data if applicable.
-        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-            scrollX = mScrollingLayerRect.left;
-            scrollY = mScrollingLayerRect.top;
-            maxX = mScrollingLayerRect.right;
-            maxY = mScrollingLayerRect.bottom;
-            // No overscrolling for layers.
-            overscrollDistance = overflingDistance = 0;
-        }
-
-        if (mSnapScrollMode != SNAP_NONE) {
-            if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
-                vy = 0;
-            } else {
-                vx = 0;
-            }
-        }
-        if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
-            WebViewCore.resumePriority();
-            if (!mSelectingText) {
-                WebViewCore.resumeUpdatePicture(mWebViewCore);
-            }
-            if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
-                invalidate();
-            }
-            return;
-        }
-        float currentVelocity = mScroller.getCurrVelocity();
-        float velocity = (float) Math.hypot(vx, vy);
-        if (mLastVelocity > 0 && currentVelocity > 0 && velocity
-                > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
-            float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
-                    - Math.atan2(vy, vx)));
-            final float circle = (float) (Math.PI) * 2.0f;
-            if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
-                vx += currentVelocity * mLastVelX / mLastVelocity;
-                vy += currentVelocity * mLastVelY / mLastVelocity;
-                velocity = (float) Math.hypot(vx, vy);
-                if (DebugFlags.WEB_VIEW) {
-                    Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
-                }
-            } else if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "doFling missed " + deltaR / circle);
-            }
-        } else if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "doFling start last=" + mLastVelocity
-                    + " current=" + currentVelocity
-                    + " vx=" + vx + " vy=" + vy
-                    + " maxX=" + maxX + " maxY=" + maxY
-                    + " scrollX=" + scrollX + " scrollY=" + scrollY
-                    + " layer=" + mCurrentScrollingLayerId);
-        }
-
-        // Allow sloppy flings without overscrolling at the edges.
-        if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
-            vx = 0;
-        }
-        if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
-            vy = 0;
-        }
-
-        if (overscrollDistance < overflingDistance) {
-            if ((vx > 0 && scrollX == -overscrollDistance) ||
-                    (vx < 0 && scrollX == maxX + overscrollDistance)) {
-                vx = 0;
-            }
-            if ((vy > 0 && scrollY == -overscrollDistance) ||
-                    (vy < 0 && scrollY == maxY + overscrollDistance)) {
-                vy = 0;
-            }
-        }
-
-        mLastVelX = vx;
-        mLastVelY = vy;
-        mLastVelocity = velocity;
-
-        // no horizontal overscroll if the content just fits
-        mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
-                maxX == 0 ? 0 : overflingDistance, overflingDistance);
-        // Duration is calculated based on velocity. With range boundaries and overscroll
-        // we may not know how long the final animation will take. (Hence the deprecation
-        // warning on the call below.) It's not a big deal for scroll bars but if webcore
-        // resumes during this effect we will take a performance hit. See computeScroll;
-        // we resume webcore there when the animation is finished.
-        final int time = mScroller.getDuration();
-
-        // Suppress scrollbars for layer scrolling.
-        if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-            awakenScrollBars(time);
-        }
-
-        invalidate();
+        mProvider.flingScroll(vx, vy);
     }
 
     /**
@@ -7921,27 +1491,7 @@
     @Deprecated
     public View getZoomControls() {
         checkThread();
-        if (!getSettings().supportZoom()) {
-            Log.w(LOGTAG, "This WebView doesn't support zoom.");
-            return null;
-        }
-        return mZoomManager.getExternalZoomPicker();
-    }
-
-    void dismissZoomControl() {
-        mZoomManager.dismissZoomPicker();
-    }
-
-    float getDefaultZoomScale() {
-        return mZoomManager.getDefaultScale();
-    }
-
-    /**
-     * Return the overview scale of the WebView
-     * @return The overview scale.
-     */
-    float getZoomOverviewScale() {
-        return mZoomManager.getZoomOverviewScale();
+        return mProvider.getZoomControls();
     }
 
     /**
@@ -7949,7 +1499,7 @@
      */
     public boolean canZoomIn() {
         checkThread();
-        return mZoomManager.canZoomIn();
+        return mProvider.canZoomIn();
     }
 
     /**
@@ -7957,7 +1507,7 @@
      */
     public boolean canZoomOut() {
         checkThread();
-        return mZoomManager.canZoomOut();
+        return mProvider.canZoomOut();
     }
 
     /**
@@ -7966,7 +1516,7 @@
      */
     public boolean zoomIn() {
         checkThread();
-        return mZoomManager.zoomIn();
+        return mProvider.zoomIn();
     }
 
     /**
@@ -7975,2262 +1525,7 @@
      */
     public boolean zoomOut() {
         checkThread();
-        return mZoomManager.zoomOut();
-    }
-
-    /**
-     * This selects the best clickable target at mLastTouchX and mLastTouchY
-     * and calls showCursorTimed on the native side
-     */
-    private void updateSelection() {
-        if (mNativeClass == 0 || sDisableNavcache) {
-            return;
-        }
-        mPrivateHandler.removeMessages(UPDATE_SELECTION);
-        // mLastTouchX and mLastTouchY are the point in the current viewport
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        int slop = viewToContentDimension(mNavSlop);
-        Rect rect = new Rect(contentX - slop, contentY - slop,
-                contentX + slop, contentY + slop);
-        nativeSelectBestAt(rect);
-        mInitialHitTestResult = hitTestResult(null);
-    }
-
-    /**
-     * Scroll the focused text field to match the WebTextView
-     * @param xPercent New x position of the WebTextView from 0 to 1.
-     */
-    /*package*/ void scrollFocusedTextInputX(float xPercent) {
-        if (!inEditingMode() || mWebViewCore == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
-                new Float(xPercent));
-    }
-
-    /**
-     * Scroll the focused textarea vertically to match the WebTextView
-     * @param y New y position of the WebTextView in view coordinates
-     */
-    /* package */ void scrollFocusedTextInputY(int y) {
-        if (!inEditingMode() || mWebViewCore == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, viewToContentDimension(y));
-    }
-
-    /**
-     * Set our starting point and time for a drag from the WebTextView.
-     */
-    /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
-        if (!inEditingMode()) {
-            return;
-        }
-        mLastTouchX = Math.round(x + mWebTextView.getLeft() - mScrollX);
-        mLastTouchY = Math.round(y + mWebTextView.getTop() - mScrollY);
-        mLastTouchTime = eventTime;
-        if (!mScroller.isFinished()) {
-            abortAnimation();
-        }
-        mSnapScrollMode = SNAP_NONE;
-        mVelocityTracker = VelocityTracker.obtain();
-        mTouchMode = TOUCH_DRAG_START_MODE;
-    }
-
-    /**
-     * Given a motion event from the WebTextView, set its location to our
-     * coordinates, and handle the event.
-     */
-    /*package*/ boolean textFieldDrag(MotionEvent event) {
-        if (!inEditingMode()) {
-            return false;
-        }
-        mDragFromTextInput = true;
-        event.offsetLocation((mWebTextView.getLeft() - mScrollX),
-                (mWebTextView.getTop() - mScrollY));
-        boolean result = onTouchEvent(event);
-        mDragFromTextInput = false;
-        return result;
-    }
-
-    /**
-     * Due a touch up from a WebTextView.  This will be handled by webkit to
-     * change the selection.
-     * @param event MotionEvent in the WebTextView's coordinates.
-     */
-    /*package*/ void touchUpOnTextField(MotionEvent event) {
-        if (!inEditingMode()) {
-            return;
-        }
-        int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
-        int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
-        int slop = viewToContentDimension(mNavSlop);
-        nativeMotionUp(x, y, slop);
-    }
-
-    /**
-     * Called when pressing the center key or trackball on a textfield.
-     */
-    /*package*/ void centerKeyPressOnTextField() {
-        mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
-                    nativeCursorNodePointer());
-    }
-
-    private void doShortPress() {
-        if (mNativeClass == 0) {
-            return;
-        }
-        if (mPreventDefault == PREVENT_DEFAULT_YES) {
-            return;
-        }
-        mTouchMode = TOUCH_DONE_MODE;
-        updateSelection();
-        switchOutDrawHistory();
-        // mLastTouchX and mLastTouchY are the point in the current viewport
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        int slop = viewToContentDimension(mNavSlop);
-        if (sDisableNavcache && !mTouchHighlightRegion.isEmpty()) {
-            // set mTouchHighlightRequested to 0 to cause an immediate
-            // drawing of the touch rings
-            mTouchHighlightRequested = 0;
-            invalidate(mTouchHighlightRegion.getBounds());
-            mPrivateHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    removeTouchHighlight();
-                }
-            }, ViewConfiguration.getPressedStateDuration());
-        }
-        if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-            overrideLoading(mFocusedNode.mIntentUrl);
-        } else if (sDisableNavcache) {
-            WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
-            // use "0" as generation id to inform WebKit to use the same x/y as
-            // it used when processing GET_TOUCH_HIGHLIGHT_RECTS
-            touchUpData.mMoveGeneration = 0;
-            mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
-        } else if (nativePointInNavCache(contentX, contentY, slop)) {
-            WebViewCore.MotionUpData motionUpData = new WebViewCore
-                    .MotionUpData();
-            motionUpData.mFrame = nativeCacheHitFramePointer();
-            motionUpData.mNode = nativeCacheHitNodePointer();
-            motionUpData.mBounds = nativeCacheHitNodeBounds();
-            motionUpData.mX = contentX;
-            motionUpData.mY = contentY;
-            mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
-                    motionUpData);
-        } else {
-            doMotionUp(contentX, contentY);
-        }
-    }
-
-    private void doMotionUp(int contentX, int contentY) {
-        int slop = viewToContentDimension(mNavSlop);
-        if (nativeMotionUp(contentX, contentY, slop) && mLogEvent) {
-            EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
-        }
-        if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-        }
-    }
-
-    void sendPluginDrawMsg() {
-        mWebViewCore.sendMessage(EventHub.PLUGIN_SURFACE_READY);
-    }
-
-    /**
-     * Returns plugin bounds if x/y in content coordinates corresponds to a
-     * plugin. Otherwise a NULL rectangle is returned.
-     */
-    Rect getPluginBounds(int x, int y) {
-        int slop = viewToContentDimension(mNavSlop);
-        if (nativePointInNavCache(x, y, slop) && nativeCacheHitIsPlugin()) {
-            return nativeCacheHitNodeBounds();
-        } else {
-            return null;
-        }
-    }
-
-    /*
-     * Return true if the rect (e.g. plugin) is fully visible and maximized
-     * inside the WebView.
-     */
-    boolean isRectFitOnScreen(Rect rect) {
-        final int rectWidth = rect.width();
-        final int rectHeight = rect.height();
-        final int viewWidth = getViewWidth();
-        final int viewHeight = getViewHeightWithTitle();
-        float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
-        scale = mZoomManager.computeScaleWithLimits(scale);
-        return !mZoomManager.willScaleTriggerZoom(scale)
-                && contentToViewX(rect.left) >= mScrollX
-                && contentToViewX(rect.right) <= mScrollX + viewWidth
-                && contentToViewY(rect.top) >= mScrollY
-                && contentToViewY(rect.bottom) <= mScrollY + viewHeight;
-    }
-
-    /*
-     * Maximize and center the rectangle, specified in the document coordinate
-     * space, inside the WebView. If the zoom doesn't need to be changed, do an
-     * animated scroll to center it. If the zoom needs to be changed, find the
-     * zoom center and do a smooth zoom transition. The rect is in document
-     * coordinates
-     */
-    void centerFitRect(Rect rect) {
-        final int rectWidth = rect.width();
-        final int rectHeight = rect.height();
-        final int viewWidth = getViewWidth();
-        final int viewHeight = getViewHeightWithTitle();
-        float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
-                / rectHeight);
-        scale = mZoomManager.computeScaleWithLimits(scale);
-        if (!mZoomManager.willScaleTriggerZoom(scale)) {
-            pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
-                    contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
-                    true, 0);
-        } else {
-            float actualScale = mZoomManager.getScale();
-            float oldScreenX = rect.left * actualScale - mScrollX;
-            float rectViewX = rect.left * scale;
-            float rectViewWidth = rectWidth * scale;
-            float newMaxWidth = mContentWidth * scale;
-            float newScreenX = (viewWidth - rectViewWidth) / 2;
-            // pin the newX to the WebView
-            if (newScreenX > rectViewX) {
-                newScreenX = rectViewX;
-            } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
-                newScreenX = viewWidth - (newMaxWidth - rectViewX);
-            }
-            float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
-                    / (scale - actualScale);
-            float oldScreenY = rect.top * actualScale + getTitleHeight()
-                    - mScrollY;
-            float rectViewY = rect.top * scale + getTitleHeight();
-            float rectViewHeight = rectHeight * scale;
-            float newMaxHeight = mContentHeight * scale + getTitleHeight();
-            float newScreenY = (viewHeight - rectViewHeight) / 2;
-            // pin the newY to the WebView
-            if (newScreenY > rectViewY) {
-                newScreenY = rectViewY;
-            } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
-                newScreenY = viewHeight - (newMaxHeight - rectViewY);
-            }
-            float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
-                    / (scale - actualScale);
-            mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
-            mZoomManager.startZoomAnimation(scale, false);
-        }
-    }
-
-    // Called by JNI to handle a touch on a node representing an email address,
-    // address, or phone number
-    private void overrideLoading(String url) {
-        mCallbackProxy.uiOverrideUrlLoading(url);
-    }
-
-    @Override
-    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
-        // FIXME: If a subwindow is showing find, and the user touches the
-        // background window, it can steal focus.
-        if (mFindIsUp) return false;
-        boolean result = false;
-        if (inEditingMode()) {
-            result = mWebTextView.requestFocus(direction,
-                    previouslyFocusedRect);
-        } else {
-            result = super.requestFocus(direction, previouslyFocusedRect);
-            if (mWebViewCore.getSettings().getNeedInitialFocus() && !isInTouchMode()) {
-                // For cases such as GMail, where we gain focus from a direction,
-                // we want to move to the first available link.
-                // FIXME: If there are no visible links, we may not want to
-                int fakeKeyDirection = 0;
-                switch(direction) {
-                    case View.FOCUS_UP:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
-                        break;
-                    case View.FOCUS_DOWN:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
-                        break;
-                    case View.FOCUS_LEFT:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
-                        break;
-                    case View.FOCUS_RIGHT:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
-                        break;
-                    default:
-                        return result;
-                }
-                if (mNativeClass != 0 && !nativeHasCursorNode()) {
-                    navHandledKey(fakeKeyDirection, 1, true, 0);
-                }
-            }
-        }
-        return result;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-
-        int measuredHeight = heightSize;
-        int measuredWidth = widthSize;
-
-        // Grab the content size from WebViewCore.
-        int contentHeight = contentToViewDimension(mContentHeight);
-        int contentWidth = contentToViewDimension(mContentWidth);
-
-//        Log.d(LOGTAG, "------- measure " + heightMode);
-
-        if (heightMode != MeasureSpec.EXACTLY) {
-            mHeightCanMeasure = true;
-            measuredHeight = contentHeight;
-            if (heightMode == MeasureSpec.AT_MOST) {
-                // If we are larger than the AT_MOST height, then our height can
-                // no longer be measured and we should scroll internally.
-                if (measuredHeight > heightSize) {
-                    measuredHeight = heightSize;
-                    mHeightCanMeasure = false;
-                    measuredHeight |= MEASURED_STATE_TOO_SMALL;
-                }
-            }
-        } else {
-            mHeightCanMeasure = false;
-        }
-        if (mNativeClass != 0) {
-            nativeSetHeightCanMeasure(mHeightCanMeasure);
-        }
-        // For the width, always use the given size unless unspecified.
-        if (widthMode == MeasureSpec.UNSPECIFIED) {
-            mWidthCanMeasure = true;
-            measuredWidth = contentWidth;
-        } else {
-            if (measuredWidth < contentWidth) {
-                measuredWidth |= MEASURED_STATE_TOO_SMALL;
-            }
-            mWidthCanMeasure = false;
-        }
-
-        synchronized (this) {
-            setMeasuredDimension(measuredWidth, measuredHeight);
-        }
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(View child,
-                                                 Rect rect,
-                                                 boolean immediate) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        // don't scroll while in zoom animation. When it is done, we will adjust
-        // the necessary components (e.g., WebTextView if it is in editing mode)
-        if (mZoomManager.isFixedLengthAnimationInProgress()) {
-            return false;
-        }
-
-        rect.offset(child.getLeft() - child.getScrollX(),
-                child.getTop() - child.getScrollY());
-
-        Rect content = new Rect(viewToContentX(mScrollX),
-                viewToContentY(mScrollY),
-                viewToContentX(mScrollX + getWidth()
-                - getVerticalScrollbarWidth()),
-                viewToContentY(mScrollY + getViewHeightWithTitle()));
-        content = nativeSubtractLayers(content);
-        int screenTop = contentToViewY(content.top);
-        int screenBottom = contentToViewY(content.bottom);
-        int height = screenBottom - screenTop;
-        int scrollYDelta = 0;
-
-        if (rect.bottom > screenBottom) {
-            int oneThirdOfScreenHeight = height / 3;
-            if (rect.height() > 2 * oneThirdOfScreenHeight) {
-                // If the rectangle is too tall to fit in the bottom two thirds
-                // of the screen, place it at the top.
-                scrollYDelta = rect.top - screenTop;
-            } else {
-                // If the rectangle will still fit on screen, we want its
-                // top to be in the top third of the screen.
-                scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
-            }
-        } else if (rect.top < screenTop) {
-            scrollYDelta = rect.top - screenTop;
-        }
-
-        int screenLeft = contentToViewX(content.left);
-        int screenRight = contentToViewX(content.right);
-        int width = screenRight - screenLeft;
-        int scrollXDelta = 0;
-
-        if (rect.right > screenRight && rect.left > screenLeft) {
-            if (rect.width() > width) {
-                scrollXDelta += (rect.left - screenLeft);
-            } else {
-                scrollXDelta += (rect.right - screenRight);
-            }
-        } else if (rect.left < screenLeft) {
-            scrollXDelta -= (screenLeft - rect.left);
-        }
-
-        if ((scrollYDelta | scrollXDelta) != 0) {
-            return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
-        }
-
-        return false;
-    }
-
-    /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
-            String replace, int newStart, int newEnd) {
-        WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
-        arg.mReplace = replace;
-        arg.mNewStart = newStart;
-        arg.mNewEnd = newEnd;
-        mTextGeneration++;
-        arg.mTextGeneration = mTextGeneration;
-        mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
-    }
-
-    /* package */ void passToJavaScript(String currentText, KeyEvent event) {
-        // check if mWebViewCore has been destroyed
-        if (mWebViewCore == null) {
-            return;
-        }
-        WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
-        arg.mEvent = event;
-        arg.mCurrentText = currentText;
-        // Increase our text generation number, and pass it to webcore thread
-        mTextGeneration++;
-        mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
-        // WebKit's document state is not saved until about to leave the page.
-        // To make sure the host application, like Browser, has the up to date
-        // document state when it goes to background, we force to save the
-        // document state.
-        mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
-        mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
-                cursorData(), 1000);
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized WebViewCore getWebViewCore() {
-        return mWebViewCore;
-    }
-
-    /**
-     * Used only by TouchEventQueue to store pending touch events.
-     */
-    private static class QueuedTouch {
-        long mSequence;
-        MotionEvent mEvent; // Optional
-        TouchEventData mTed; // Optional
-
-        QueuedTouch mNext;
-
-        public QueuedTouch set(TouchEventData ted) {
-            mSequence = ted.mSequence;
-            mTed = ted;
-            mEvent = null;
-            mNext = null;
-            return this;
-        }
-
-        public QueuedTouch set(MotionEvent ev, long sequence) {
-            mEvent = MotionEvent.obtain(ev);
-            mSequence = sequence;
-            mTed = null;
-            mNext = null;
-            return this;
-        }
-
-        public QueuedTouch add(QueuedTouch other) {
-            if (other.mSequence < mSequence) {
-                other.mNext = this;
-                return other;
-            }
-
-            QueuedTouch insertAt = this;
-            while (insertAt.mNext != null && insertAt.mNext.mSequence < other.mSequence) {
-                insertAt = insertAt.mNext;
-            }
-            other.mNext = insertAt.mNext;
-            insertAt.mNext = other;
-            return this;
-        }
-    }
-
-    /**
-     * WebView handles touch events asynchronously since some events must be passed to WebKit
-     * for potentially slower processing. TouchEventQueue serializes touch events regardless
-     * of which path they take to ensure that no events are ever processed out of order
-     * by WebView.
-     */
-    private class TouchEventQueue {
-        private long mNextTouchSequence = Long.MIN_VALUE + 1;
-        private long mLastHandledTouchSequence = Long.MIN_VALUE;
-        private long mIgnoreUntilSequence = Long.MIN_VALUE + 1;
-
-        // Events waiting to be processed.
-        private QueuedTouch mTouchEventQueue;
-
-        // Known events that are waiting on a response before being enqueued.
-        private QueuedTouch mPreQueue;
-
-        // Pool of QueuedTouch objects saved for later use.
-        private QueuedTouch mQueuedTouchRecycleBin;
-        private int mQueuedTouchRecycleCount;
-
-        private long mLastEventTime = Long.MAX_VALUE;
-        private static final int MAX_RECYCLED_QUEUED_TOUCH = 15;
-
-        // milliseconds until we abandon hope of getting all of a previous gesture
-        private static final int QUEUED_GESTURE_TIMEOUT = 1000;
-
-        private QueuedTouch obtainQueuedTouch() {
-            if (mQueuedTouchRecycleBin != null) {
-                QueuedTouch result = mQueuedTouchRecycleBin;
-                mQueuedTouchRecycleBin = result.mNext;
-                mQueuedTouchRecycleCount--;
-                return result;
-            }
-            return new QueuedTouch();
-        }
-
-        /**
-         * Allow events with any currently missing sequence numbers to be skipped in processing.
-         */
-        public void ignoreCurrentlyMissingEvents() {
-            mIgnoreUntilSequence = mNextTouchSequence;
-
-            // Run any events we have available and complete, pre-queued or otherwise.
-            runQueuedAndPreQueuedEvents();
-        }
-
-        private void runQueuedAndPreQueuedEvents() {
-            QueuedTouch qd = mPreQueue;
-            boolean fromPreQueue = true;
-            while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
-                handleQueuedTouch(qd);
-                QueuedTouch recycleMe = qd;
-                if (fromPreQueue) {
-                    mPreQueue = qd.mNext;
-                } else {
-                    mTouchEventQueue = qd.mNext;
-                }
-                recycleQueuedTouch(recycleMe);
-                mLastHandledTouchSequence++;
-
-                long nextPre = mPreQueue != null ? mPreQueue.mSequence : Long.MAX_VALUE;
-                long nextQueued = mTouchEventQueue != null ?
-                        mTouchEventQueue.mSequence : Long.MAX_VALUE;
-                fromPreQueue = nextPre < nextQueued;
-                qd = fromPreQueue ? mPreQueue : mTouchEventQueue;
-            }
-        }
-
-        /**
-         * Add a TouchEventData to the pre-queue.
-         *
-         * An event in the pre-queue is an event that we know about that
-         * has been sent to webkit, but that we haven't received back and
-         * enqueued into the normal touch queue yet. If webkit ever times
-         * out and we need to ignore currently missing events, we'll run
-         * events from the pre-queue to patch the holes.
-         *
-         * @param ted TouchEventData to pre-queue
-         */
-        public void preQueueTouchEventData(TouchEventData ted) {
-            QueuedTouch newTouch = obtainQueuedTouch().set(ted);
-            if (mPreQueue == null) {
-                mPreQueue = newTouch;
-            } else {
-                QueuedTouch insertionPoint = mPreQueue;
-                while (insertionPoint.mNext != null &&
-                        insertionPoint.mNext.mSequence < newTouch.mSequence) {
-                    insertionPoint = insertionPoint.mNext;
-                }
-                newTouch.mNext = insertionPoint.mNext;
-                insertionPoint.mNext = newTouch;
-            }
-        }
-
-        private void recycleQueuedTouch(QueuedTouch qd) {
-            if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) {
-                qd.mNext = mQueuedTouchRecycleBin;
-                mQueuedTouchRecycleBin = qd;
-                mQueuedTouchRecycleCount++;
-            }
-        }
-
-        /**
-         * Reset the touch event queue. This will dump any pending events
-         * and reset the sequence numbering.
-         */
-        public void reset() {
-            mNextTouchSequence = Long.MIN_VALUE + 1;
-            mLastHandledTouchSequence = Long.MIN_VALUE;
-            mIgnoreUntilSequence = Long.MIN_VALUE + 1;
-            while (mTouchEventQueue != null) {
-                QueuedTouch recycleMe = mTouchEventQueue;
-                mTouchEventQueue = mTouchEventQueue.mNext;
-                recycleQueuedTouch(recycleMe);
-            }
-            while (mPreQueue != null) {
-                QueuedTouch recycleMe = mPreQueue;
-                mPreQueue = mPreQueue.mNext;
-                recycleQueuedTouch(recycleMe);
-            }
-        }
-
-        /**
-         * Return the next valid sequence number for tagging incoming touch events.
-         * @return The next touch event sequence number
-         */
-        public long nextTouchSequence() {
-            return mNextTouchSequence++;
-        }
-
-        /**
-         * Enqueue a touch event in the form of TouchEventData.
-         * The sequence number will be read from the mSequence field of the argument.
-         *
-         * If the touch event's sequence number is the next in line to be processed, it will
-         * be handled before this method returns. Any subsequent events that have already
-         * been queued will also be processed in their proper order.
-         *
-         * @param ted Touch data to be processed in order.
-         * @return true if the event was processed before returning, false if it was just enqueued.
-         */
-        public boolean enqueueTouchEvent(TouchEventData ted) {
-            // Remove from the pre-queue if present
-            QueuedTouch preQueue = mPreQueue;
-            if (preQueue != null) {
-                // On exiting this block, preQueue is set to the pre-queued QueuedTouch object
-                // if it was present in the pre-queue, and removed from the pre-queue itself.
-                if (preQueue.mSequence == ted.mSequence) {
-                    mPreQueue = preQueue.mNext;
-                } else {
-                    QueuedTouch prev = preQueue;
-                    preQueue = null;
-                    while (prev.mNext != null) {
-                        if (prev.mNext.mSequence == ted.mSequence) {
-                            preQueue = prev.mNext;
-                            prev.mNext = preQueue.mNext;
-                            break;
-                        } else {
-                            prev = prev.mNext;
-                        }
-                    }
-                }
-            }
-
-            if (ted.mSequence < mLastHandledTouchSequence) {
-                // Stale event and we already moved on; drop it. (Should not be common.)
-                Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) +
-                        " received from webcore; ignoring");
-                return false;
-            }
-
-            if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) {
-                return false;
-            }
-
-            // dropStaleGestures above might have fast-forwarded us to
-            // an event we have already.
-            runNextQueuedEvents();
-
-            if (mLastHandledTouchSequence + 1 == ted.mSequence) {
-                if (preQueue != null) {
-                    recycleQueuedTouch(preQueue);
-                    preQueue = null;
-                }
-                handleQueuedTouchEventData(ted);
-
-                mLastHandledTouchSequence++;
-
-                // Do we have any more? Run them if so.
-                runNextQueuedEvents();
-            } else {
-                // Reuse the pre-queued object if we had it.
-                QueuedTouch qd = preQueue != null ? preQueue : obtainQueuedTouch().set(ted);
-                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
-            }
-            return true;
-        }
-
-        /**
-         * Enqueue a touch event in the form of a MotionEvent from the framework.
-         *
-         * If the touch event's sequence number is the next in line to be processed, it will
-         * be handled before this method returns. Any subsequent events that have already
-         * been queued will also be processed in their proper order.
-         *
-         * @param ev MotionEvent to be processed in order
-         */
-        public void enqueueTouchEvent(MotionEvent ev) {
-            final long sequence = nextTouchSequence();
-
-            if (dropStaleGestures(ev, sequence)) {
-                return;
-            }
-
-            // dropStaleGestures above might have fast-forwarded us to
-            // an event we have already.
-            runNextQueuedEvents();
-
-            if (mLastHandledTouchSequence + 1 == sequence) {
-                handleQueuedMotionEvent(ev);
-
-                mLastHandledTouchSequence++;
-
-                // Do we have any more? Run them if so.
-                runNextQueuedEvents();
-            } else {
-                QueuedTouch qd = obtainQueuedTouch().set(ev, sequence);
-                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
-            }
-        }
-
-        private void runNextQueuedEvents() {
-            QueuedTouch qd = mTouchEventQueue;
-            while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
-                handleQueuedTouch(qd);
-                QueuedTouch recycleMe = qd;
-                qd = qd.mNext;
-                recycleQueuedTouch(recycleMe);
-                mLastHandledTouchSequence++;
-            }
-            mTouchEventQueue = qd;
-        }
-
-        private boolean dropStaleGestures(MotionEvent ev, long sequence) {
-            if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && !mConfirmMove) {
-                // This is to make sure that we don't attempt to process a tap
-                // or long press when webkit takes too long to get back to us.
-                // The movement will be properly confirmed when we process the
-                // enqueued event later.
-                final int dx = Math.round(ev.getX()) - mLastTouchX;
-                final int dy = Math.round(ev.getY()) - mLastTouchY;
-                if (dx * dx + dy * dy > mTouchSlopSquare) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                }
-            }
-
-            if (mTouchEventQueue == null) {
-                return sequence <= mLastHandledTouchSequence;
-            }
-
-            // If we have a new down event and it's been a while since the last event
-            // we saw, catch up as best we can and keep going.
-            if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN) {
-                long eventTime = ev.getEventTime();
-                long lastHandledEventTime = mLastEventTime;
-                if (eventTime > lastHandledEventTime + QUEUED_GESTURE_TIMEOUT) {
-                    Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " +
-                            "Catching up.");
-                    runQueuedAndPreQueuedEvents();
-
-                    // Drop leftovers that we truly don't have.
-                    QueuedTouch qd = mTouchEventQueue;
-                    while (qd != null && qd.mSequence < sequence) {
-                        QueuedTouch recycleMe = qd;
-                        qd = qd.mNext;
-                        recycleQueuedTouch(recycleMe);
-                    }
-                    mTouchEventQueue = qd;
-                    mLastHandledTouchSequence = sequence - 1;
-                }
-            }
-
-            if (mIgnoreUntilSequence - 1 > mLastHandledTouchSequence) {
-                QueuedTouch qd = mTouchEventQueue;
-                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
-                    QueuedTouch recycleMe = qd;
-                    qd = qd.mNext;
-                    recycleQueuedTouch(recycleMe);
-                }
-                mTouchEventQueue = qd;
-                mLastHandledTouchSequence = mIgnoreUntilSequence - 1;
-            }
-
-            if (mPreQueue != null) {
-                // Drop stale prequeued events
-                QueuedTouch qd = mPreQueue;
-                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
-                    QueuedTouch recycleMe = qd;
-                    qd = qd.mNext;
-                    recycleQueuedTouch(recycleMe);
-                }
-                mPreQueue = qd;
-            }
-
-            return sequence <= mLastHandledTouchSequence;
-        }
-
-        private void handleQueuedTouch(QueuedTouch qt) {
-            if (qt.mTed != null) {
-                handleQueuedTouchEventData(qt.mTed);
-            } else {
-                handleQueuedMotionEvent(qt.mEvent);
-                qt.mEvent.recycle();
-            }
-        }
-
-        private void handleQueuedMotionEvent(MotionEvent ev) {
-            mLastEventTime = ev.getEventTime();
-            int action = ev.getActionMasked();
-            if (ev.getPointerCount() > 1) {  // Multi-touch
-                handleMultiTouchInWebView(ev);
-            } else {
-                final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
-                if (detector != null && mPreventDefault != PREVENT_DEFAULT_YES) {
-                    // ScaleGestureDetector needs a consistent event stream to operate properly.
-                    // It won't take any action with fewer than two pointers, but it needs to
-                    // update internal bookkeeping state.
-                    detector.onTouchEvent(ev);
-                }
-
-                handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY()));
-            }
-        }
-
-        private void handleQueuedTouchEventData(TouchEventData ted) {
-            if (ted.mMotionEvent != null) {
-                mLastEventTime = ted.mMotionEvent.getEventTime();
-            }
-            if (!ted.mReprocess) {
-                if (ted.mAction == MotionEvent.ACTION_DOWN
-                        && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
-                    // if prevent default is called from WebCore, UI
-                    // will not handle the rest of the touch events any
-                    // more.
-                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
-                            : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
-                } else if (ted.mAction == MotionEvent.ACTION_MOVE
-                        && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
-                    // the return for the first ACTION_MOVE will decide
-                    // whether UI will handle touch or not. Currently no
-                    // support for alternating prevent default
-                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
-                            : PREVENT_DEFAULT_NO;
-                }
-                if (mPreventDefault == PREVENT_DEFAULT_YES) {
-                    mTouchHighlightRegion.setEmpty();
-                }
-            } else {
-                if (ted.mPoints.length > 1) {  // multi-touch
-                    if (!ted.mNativeResult && mPreventDefault != PREVENT_DEFAULT_YES) {
-                        mPreventDefault = PREVENT_DEFAULT_NO;
-                        handleMultiTouchInWebView(ted.mMotionEvent);
-                    } else {
-                        mPreventDefault = PREVENT_DEFAULT_YES;
-                    }
-                    return;
-                }
-
-                // prevent default is not called in WebCore, so the
-                // message needs to be reprocessed in UI
-                if (!ted.mNativeResult) {
-                    // Following is for single touch.
-                    switch (ted.mAction) {
-                        case MotionEvent.ACTION_DOWN:
-                            mLastDeferTouchX = ted.mPointsInView[0].x;
-                            mLastDeferTouchY = ted.mPointsInView[0].y;
-                            mDeferTouchMode = TOUCH_INIT_MODE;
-                            break;
-                        case MotionEvent.ACTION_MOVE: {
-                            // no snapping in defer process
-                            int x = ted.mPointsInView[0].x;
-                            int y = ted.mPointsInView[0].y;
-
-                            if (mDeferTouchMode != TOUCH_DRAG_MODE) {
-                                mDeferTouchMode = TOUCH_DRAG_MODE;
-                                mLastDeferTouchX = x;
-                                mLastDeferTouchY = y;
-                                startScrollingLayer(x, y);
-                                startDrag();
-                            }
-                            int deltaX = pinLocX((int) (mScrollX
-                                    + mLastDeferTouchX - x))
-                                    - mScrollX;
-                            int deltaY = pinLocY((int) (mScrollY
-                                    + mLastDeferTouchY - y))
-                                    - mScrollY;
-                            doDrag(deltaX, deltaY);
-                            if (deltaX != 0) mLastDeferTouchX = x;
-                            if (deltaY != 0) mLastDeferTouchY = y;
-                            break;
-                        }
-                        case MotionEvent.ACTION_UP:
-                        case MotionEvent.ACTION_CANCEL:
-                            if (mDeferTouchMode == TOUCH_DRAG_MODE) {
-                                // no fling in defer process
-                                mScroller.springBack(mScrollX, mScrollY, 0,
-                                        computeMaxScrollX(), 0,
-                                        computeMaxScrollY());
-                                invalidate();
-                                WebViewCore.resumePriority();
-                                WebViewCore.resumeUpdatePicture(mWebViewCore);
-                            }
-                            mDeferTouchMode = TOUCH_DONE_MODE;
-                            break;
-                        case WebViewCore.ACTION_DOUBLETAP:
-                            // doDoubleTap() needs mLastTouchX/Y as anchor
-                            mLastDeferTouchX = ted.mPointsInView[0].x;
-                            mLastDeferTouchY = ted.mPointsInView[0].y;
-                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
-                            mDeferTouchMode = TOUCH_DONE_MODE;
-                            break;
-                        case WebViewCore.ACTION_LONGPRESS:
-                            HitTestResult hitTest = getHitTestResult();
-                            if (hitTest != null && hitTest.mType
-                                    != HitTestResult.UNKNOWN_TYPE) {
-                                performLongClick();
-                            }
-                            mDeferTouchMode = TOUCH_DONE_MODE;
-                            break;
-                    }
-                }
-            }
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    // Methods can be called from a separate thread, like WebViewCore
-    // If it needs to call the View system, it has to send message.
-    //-------------------------------------------------------------------------
-
-    /**
-     * General handler to receive message coming from webkit thread
-     */
-    class PrivateHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            // exclude INVAL_RECT_MSG_ID since it is frequently output
-            if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
-                if (msg.what >= FIRST_PRIVATE_MSG_ID
-                        && msg.what <= LAST_PRIVATE_MSG_ID) {
-                    Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
-                            - FIRST_PRIVATE_MSG_ID]);
-                } else if (msg.what >= FIRST_PACKAGE_MSG_ID
-                        && msg.what <= LAST_PACKAGE_MSG_ID) {
-                    Log.v(LOGTAG, HandlerPackageDebugString[msg.what
-                            - FIRST_PACKAGE_MSG_ID]);
-                } else {
-                    Log.v(LOGTAG, Integer.toString(msg.what));
-                }
-            }
-            if (mWebViewCore == null) {
-                // after WebView's destroy() is called, skip handling messages.
-                return;
-            }
-            if (mBlockWebkitViewMessages
-                    && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
-                // Blocking messages from webkit
-                return;
-            }
-            switch (msg.what) {
-                case REMEMBER_PASSWORD: {
-                    mDatabase.setUsernamePassword(
-                            msg.getData().getString("host"),
-                            msg.getData().getString("username"),
-                            msg.getData().getString("password"));
-                    ((Message) msg.obj).sendToTarget();
-                    break;
-                }
-                case NEVER_REMEMBER_PASSWORD: {
-                    mDatabase.setUsernamePassword(
-                            msg.getData().getString("host"), null, null);
-                    ((Message) msg.obj).sendToTarget();
-                    break;
-                }
-                case PREVENT_DEFAULT_TIMEOUT: {
-                    // if timeout happens, cancel it so that it won't block UI
-                    // to continue handling touch events
-                    if ((msg.arg1 == MotionEvent.ACTION_DOWN
-                            && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES)
-                            || (msg.arg1 == MotionEvent.ACTION_MOVE
-                            && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) {
-                        cancelWebCoreTouchEvent(
-                                viewToContentX(mLastTouchX + mScrollX),
-                                viewToContentY(mLastTouchY + mScrollY),
-                                true);
-                    }
-                    break;
-                }
-                case SCROLL_SELECT_TEXT: {
-                    if (mAutoScrollX == 0 && mAutoScrollY == 0) {
-                        mSentAutoScrollMessage = false;
-                        break;
-                    }
-                    if (mCurrentScrollingLayerId == 0) {
-                        pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
-                    } else {
-                        scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX,
-                                mScrollingLayerRect.top + mAutoScrollY);
-                    }
-                    sendEmptyMessageDelayed(
-                            SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
-                    break;
-                }
-                case UPDATE_SELECTION: {
-                    if (mTouchMode == TOUCH_INIT_MODE
-                            || mTouchMode == TOUCH_SHORTPRESS_MODE
-                            || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
-                        updateSelection();
-                    }
-                    break;
-                }
-                case SWITCH_TO_SHORTPRESS: {
-                    if (mTouchMode == TOUCH_INIT_MODE) {
-                        if (!sDisableNavcache
-                                && mPreventDefault != PREVENT_DEFAULT_YES) {
-                            mTouchMode = TOUCH_SHORTPRESS_START_MODE;
-                            updateSelection();
-                        } else {
-                            // set to TOUCH_SHORTPRESS_MODE so that it won't
-                            // trigger double tap any more
-                            mTouchMode = TOUCH_SHORTPRESS_MODE;
-                        }
-                    } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
-                        mTouchMode = TOUCH_DONE_MODE;
-                    }
-                    break;
-                }
-                case SWITCH_TO_LONGPRESS: {
-                    if (sDisableNavcache) {
-                        removeTouchHighlight();
-                    }
-                    if (inFullScreenMode() || mDeferTouchProcess) {
-                        TouchEventData ted = new TouchEventData();
-                        ted.mAction = WebViewCore.ACTION_LONGPRESS;
-                        ted.mIds = new int[1];
-                        ted.mIds[0] = 0;
-                        ted.mPoints = new Point[1];
-                        ted.mPoints[0] = new Point(viewToContentX(mLastTouchX + mScrollX),
-                                                   viewToContentY(mLastTouchY + mScrollY));
-                        ted.mPointsInView = new Point[1];
-                        ted.mPointsInView[0] = new Point(mLastTouchX, mLastTouchY);
-                        // metaState for long press is tricky. Should it be the
-                        // state when the press started or when the press was
-                        // released? Or some intermediary key state? For
-                        // simplicity for now, we don't set it.
-                        ted.mMetaState = 0;
-                        ted.mReprocess = mDeferTouchProcess;
-                        ted.mNativeLayer = nativeScrollableLayer(
-                                ted.mPoints[0].x, ted.mPoints[0].y,
-                                ted.mNativeLayerRect, null);
-                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                        mTouchEventQueue.preQueueTouchEventData(ted);
-                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                    } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
-                        mTouchMode = TOUCH_DONE_MODE;
-                        performLongClick();
-                    }
-                    break;
-                }
-                case RELEASE_SINGLE_TAP: {
-                    doShortPress();
-                    break;
-                }
-                case SCROLL_TO_MSG_ID: {
-                    // arg1 = animate, arg2 = onlyIfImeIsShowing
-                    // obj = Point(x, y)
-                    if (msg.arg2 == 1) {
-                        // This scroll is intended to bring the textfield into
-                        // view, but is only necessary if the IME is showing
-                        InputMethodManager imm = InputMethodManager.peekInstance();
-                        if (imm == null || !imm.isAcceptingText()
-                                || (!imm.isActive(WebView.this) && (!inEditingMode()
-                                || !imm.isActive(mWebTextView)))) {
-                            break;
-                        }
-                    }
-                    final Point p = (Point) msg.obj;
-                    if (msg.arg1 == 1) {
-                        spawnContentScrollTo(p.x, p.y);
-                    } else {
-                        setContentScrollTo(p.x, p.y);
-                    }
-                    break;
-                }
-                case UPDATE_ZOOM_RANGE: {
-                    WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
-                    // mScrollX contains the new minPrefWidth
-                    mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
-                    break;
-                }
-                case UPDATE_ZOOM_DENSITY: {
-                    final float density = (Float) msg.obj;
-                    mZoomManager.updateDefaultZoomDensity(density);
-                    break;
-                }
-                case REPLACE_BASE_CONTENT: {
-                    nativeReplaceBaseContent(msg.arg1);
-                    break;
-                }
-                case NEW_PICTURE_MSG_ID: {
-                    // called for new content
-                    final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
-                    setNewPicture(draw, true);
-                    break;
-                }
-                case WEBCORE_INITIALIZED_MSG_ID:
-                    // nativeCreate sets mNativeClass to a non-zero value
-                    String drawableDir = BrowserFrame.getRawResFilename(
-                            BrowserFrame.DRAWABLEDIR, mContext);
-                    WindowManager windowManager =
-                            (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-                    Display display = windowManager.getDefaultDisplay();
-                    nativeCreate(msg.arg1, drawableDir,
-                            ActivityManager.isHighEndGfx(display));
-                    if (mDelaySetPicture != null) {
-                        setNewPicture(mDelaySetPicture, true);
-                        mDelaySetPicture = null;
-                    }
-                    if (mIsPaused) {
-                        nativeSetPauseDrawing(mNativeClass, true);
-                    }
-                    break;
-                case UPDATE_TEXTFIELD_TEXT_MSG_ID:
-                    // Make sure that the textfield is currently focused
-                    // and representing the same node as the pointer.
-                    if (msg.arg2 == mTextGeneration) {
-                        String text = (String) msg.obj;
-                        if (null == text) {
-                            text = "";
-                        }
-                        if (inEditingMode() &&
-                                mWebTextView.isSameTextField(msg.arg1)) {
-                            mWebTextView.setTextAndKeepSelection(text);
-                        } else if (mInputConnection != null &&
-                                mFieldPointer == msg.arg1) {
-                            mInputConnection.setTextAndKeepSelection(text);
-                        }
-                    }
-                    break;
-                case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
-                    displaySoftKeyboard(true);
-                    // fall through to UPDATE_TEXT_SELECTION_MSG_ID
-                case UPDATE_TEXT_SELECTION_MSG_ID:
-                    updateTextSelectionFromMessage(msg.arg1, msg.arg2,
-                            (WebViewCore.TextSelectionData) msg.obj);
-                    break;
-                case FORM_DID_BLUR:
-                    if (inEditingMode()
-                            && mWebTextView.isSameTextField(msg.arg1)) {
-                        hideSoftKeyboard();
-                    }
-                    break;
-                case RETURN_LABEL:
-                    if (inEditingMode()
-                            && mWebTextView.isSameTextField(msg.arg1)) {
-                        mWebTextView.setHint((String) msg.obj);
-                        InputMethodManager imm
-                                = InputMethodManager.peekInstance();
-                        // The hint is propagated to the IME in
-                        // onCreateInputConnection.  If the IME is already
-                        // active, restart it so that its hint text is updated.
-                        if (imm != null && imm.isActive(mWebTextView)) {
-                            imm.restartInput(mWebTextView);
-                        }
-                    }
-                    break;
-                case UNHANDLED_NAV_KEY:
-                    navHandledKey(msg.arg1, 1, false, 0);
-                    break;
-                case UPDATE_TEXT_ENTRY_MSG_ID:
-                    // this is sent after finishing resize in WebViewCore. Make
-                    // sure the text edit box is still on the  screen.
-                    if (inEditingMode() && nativeCursorIsTextInput()) {
-                        updateWebTextViewPosition();
-                    }
-                    break;
-                case CLEAR_TEXT_ENTRY:
-                    clearTextEntry();
-                    break;
-                case INVAL_RECT_MSG_ID: {
-                    Rect r = (Rect)msg.obj;
-                    if (r == null) {
-                        invalidate();
-                    } else {
-                        // we need to scale r from content into view coords,
-                        // which viewInvalidate() does for us
-                        viewInvalidate(r.left, r.top, r.right, r.bottom);
-                    }
-                    break;
-                }
-                case REQUEST_FORM_DATA:
-                    AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
-                    if (mWebTextView.isSameTextField(msg.arg1)) {
-                        mWebTextView.setAdapterCustom(adapter);
-                    }
-                    break;
-
-                case LONG_PRESS_CENTER:
-                    // as this is shared by keydown and trackballdown, reset all
-                    // the states
-                    mGotCenterDown = false;
-                    mTrackballDown = false;
-                    performLongClick();
-                    break;
-
-                case WEBCORE_NEED_TOUCH_EVENTS:
-                    mForwardTouchEvents = (msg.arg1 != 0);
-                    break;
-
-                case PREVENT_TOUCH_ID:
-                    if (inFullScreenMode()) {
-                        break;
-                    }
-                    TouchEventData ted = (TouchEventData) msg.obj;
-
-                    if (mTouchEventQueue.enqueueTouchEvent(ted)) {
-                        // WebCore is responding to us; remove pending timeout.
-                        // It will be re-posted when needed.
-                        removeMessages(PREVENT_DEFAULT_TIMEOUT);
-                    }
-                    break;
-
-                case REQUEST_KEYBOARD:
-                    if (msg.arg1 == 0) {
-                        hideSoftKeyboard();
-                    } else {
-                        displaySoftKeyboard(false);
-                    }
-                    break;
-
-                case DRAG_HELD_MOTIONLESS:
-                    mHeldMotionless = MOTIONLESS_TRUE;
-                    invalidate();
-                    // fall through to keep scrollbars awake
-
-                case AWAKEN_SCROLL_BARS:
-                    if (mTouchMode == TOUCH_DRAG_MODE
-                            && mHeldMotionless == MOTIONLESS_TRUE) {
-                        awakenScrollBars(ViewConfiguration
-                                .getScrollDefaultDelay(), false);
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(AWAKEN_SCROLL_BARS),
-                                ViewConfiguration.getScrollDefaultDelay());
-                    }
-                    break;
-
-                case DO_MOTION_UP:
-                    doMotionUp(msg.arg1, msg.arg2);
-                    break;
-
-                case SCREEN_ON:
-                    setKeepScreenOn(msg.arg1 == 1);
-                    break;
-
-                case ENTER_FULLSCREEN_VIDEO:
-                    int layerId = msg.arg1;
-
-                    String url = (String) msg.obj;
-                    if (mHTML5VideoViewProxy != null) {
-                        mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url);
-                    }
-                    break;
-
-                case EXIT_FULLSCREEN_VIDEO:
-                    if (mHTML5VideoViewProxy != null) {
-                        mHTML5VideoViewProxy.exitFullScreenVideo();
-                    }
-                    break;
-
-                case SHOW_FULLSCREEN: {
-                    View view = (View) msg.obj;
-                    int orientation = msg.arg1;
-                    int npp = msg.arg2;
-
-                    if (inFullScreenMode()) {
-                        Log.w(LOGTAG, "Should not have another full screen.");
-                        dismissFullScreenMode();
-                    }
-                    mFullScreenHolder = new PluginFullScreenHolder(WebView.this, orientation, npp);
-                    mFullScreenHolder.setContentView(view);
-                    mFullScreenHolder.show();
-                    invalidate();
-
-                    break;
-                }
-                case HIDE_FULLSCREEN:
-                    dismissFullScreenMode();
-                    break;
-
-                case DOM_FOCUS_CHANGED:
-                    if (inEditingMode()) {
-                        nativeClearCursor();
-                        rebuildWebTextView();
-                    }
-                    break;
-
-                case SHOW_RECT_MSG_ID: {
-                    WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
-                    int x = mScrollX;
-                    int left = contentToViewX(data.mLeft);
-                    int width = contentToViewDimension(data.mWidth);
-                    int maxWidth = contentToViewDimension(data.mContentWidth);
-                    int viewWidth = getViewWidth();
-                    if (width < viewWidth) {
-                        // center align
-                        x += left + width / 2 - mScrollX - viewWidth / 2;
-                    } else {
-                        x += (int) (left + data.mXPercentInDoc * width
-                                - mScrollX - data.mXPercentInView * viewWidth);
-                    }
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
-                              width + ",maxWidth=" + maxWidth +
-                              ",viewWidth=" + viewWidth + ",x="
-                              + x + ",xPercentInDoc=" + data.mXPercentInDoc +
-                              ",xPercentInView=" + data.mXPercentInView+ ")");
-                    }
-                    // use the passing content width to cap x as the current
-                    // mContentWidth may not be updated yet
-                    x = Math.max(0,
-                            (Math.min(maxWidth, x + viewWidth)) - viewWidth);
-                    int top = contentToViewY(data.mTop);
-                    int height = contentToViewDimension(data.mHeight);
-                    int maxHeight = contentToViewDimension(data.mContentHeight);
-                    int viewHeight = getViewHeight();
-                    int y = (int) (top + data.mYPercentInDoc * height -
-                                   data.mYPercentInView * viewHeight);
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
-                              height + ",maxHeight=" + maxHeight +
-                              ",viewHeight=" + viewHeight + ",y="
-                              + y + ",yPercentInDoc=" + data.mYPercentInDoc +
-                              ",yPercentInView=" + data.mYPercentInView+ ")");
-                    }
-                    // use the passing content height to cap y as the current
-                    // mContentHeight may not be updated yet
-                    y = Math.max(0,
-                            (Math.min(maxHeight, y + viewHeight) - viewHeight));
-                    // We need to take into account the visible title height
-                    // when scrolling since y is an absolute view position.
-                    y = Math.max(0, y - getVisibleTitleHeightImpl());
-                    scrollTo(x, y);
-                    }
-                    break;
-
-                case CENTER_FIT_RECT:
-                    centerFitRect((Rect)msg.obj);
-                    break;
-
-                case SET_SCROLLBAR_MODES:
-                    mHorizontalScrollBarMode = msg.arg1;
-                    mVerticalScrollBarMode = msg.arg2;
-                    break;
-
-                case SELECTION_STRING_CHANGED:
-                    if (mAccessibilityInjector != null) {
-                        String selectionString = (String) msg.obj;
-                        mAccessibilityInjector.onSelectionStringChange(selectionString);
-                    }
-                    break;
-
-                case HIT_TEST_RESULT:
-                    WebKitHitTest hit = (WebKitHitTest) msg.obj;
-                    mFocusedNode = hit;
-                    setTouchHighlightRects(hit);
-                    setHitTestResult(hit);
-                    break;
-
-                case SAVE_WEBARCHIVE_FINISHED:
-                    SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
-                    if (saveMessage.mCallback != null) {
-                        saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
-                    }
-                    break;
-
-                case SET_AUTOFILLABLE:
-                    mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
-                    if (mWebTextView != null) {
-                        mWebTextView.setAutoFillable(mAutoFillData.getQueryId());
-                        rebuildWebTextView();
-                    }
-                    break;
-
-                case AUTOFILL_COMPLETE:
-                    if (mWebTextView != null) {
-                        // Clear the WebTextView adapter when AutoFill finishes
-                        // so that the drop down gets cleared.
-                        mWebTextView.setAdapterCustom(null);
-                    }
-                    break;
-
-                case SELECT_AT:
-                    nativeSelectAt(msg.arg1, msg.arg2);
-                    break;
-
-                case COPY_TO_CLIPBOARD:
-                    copyToClipboard((String) msg.obj);
-                    break;
-
-                case INIT_EDIT_FIELD:
-                    if (mInputConnection != null) {
-                        TextFieldInitData initData = (TextFieldInitData) msg.obj;
-                        mTextGeneration = 0;
-                        mFieldPointer = initData.mFieldPointer;
-                        mInputConnection.initEditorInfo(initData);
-                        mInputConnection.setTextAndKeepSelection(initData.mText);
-                    }
-                    break;
-
-                case REPLACE_TEXT:{
-                    String text = (String)msg.obj;
-                    int start = msg.arg1;
-                    int end = msg.arg2;
-                    int cursorPosition = start + text.length();
-                    replaceTextfieldText(start, end, text,
-                            cursorPosition, cursorPosition);
-                    break;
-                }
-
-                case UPDATE_MATCH_COUNT: {
-                    if (mFindCallback != null) {
-                        mFindCallback.updateMatchCount(msg.arg1, msg.arg2,
-                            (String) msg.obj);
-                    }
-                    break;
-                }
-                case CLEAR_CARET_HANDLE:
-                    selectionDone();
-                    break;
-
-                case KEY_PRESS:
-                    mWebViewCore.sendMessage(EventHub.KEY_PRESS, msg.arg1);
-                    break;
-
-                default:
-                    super.handleMessage(msg);
-                    break;
-            }
-        }
-    }
-
-    private void setHitTestTypeFromUrl(String url) {
-        String substr = null;
-        if (url.startsWith(SCHEME_GEO)) {
-            mInitialHitTestResult.mType = HitTestResult.GEO_TYPE;
-            substr = url.substring(SCHEME_GEO.length());
-        } else if (url.startsWith(SCHEME_TEL)) {
-            mInitialHitTestResult.mType = HitTestResult.PHONE_TYPE;
-            substr = url.substring(SCHEME_TEL.length());
-        } else if (url.startsWith(SCHEME_MAILTO)) {
-            mInitialHitTestResult.mType = HitTestResult.EMAIL_TYPE;
-            substr = url.substring(SCHEME_MAILTO.length());
-        } else {
-            mInitialHitTestResult.mType = HitTestResult.SRC_ANCHOR_TYPE;
-            mInitialHitTestResult.mExtra = url;
-            return;
-        }
-        try {
-            mInitialHitTestResult.mExtra = URLDecoder.decode(substr, "UTF-8");
-        } catch (Throwable e) {
-            Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
-            mInitialHitTestResult.mType = HitTestResult.UNKNOWN_TYPE;
-        }
-    }
-
-    private void setHitTestResult(WebKitHitTest hit) {
-        if (hit == null) {
-            mInitialHitTestResult = null;
-            return;
-        }
-        mInitialHitTestResult = new HitTestResult();
-        if (hit.mLinkUrl != null) {
-            setHitTestTypeFromUrl(hit.mLinkUrl);
-            if (hit.mImageUrl != null
-                    && mInitialHitTestResult.mType == HitTestResult.SRC_ANCHOR_TYPE) {
-                mInitialHitTestResult.mType = HitTestResult.SRC_IMAGE_ANCHOR_TYPE;
-                mInitialHitTestResult.mExtra = hit.mImageUrl;
-            }
-        } else if (hit.mImageUrl != null) {
-            mInitialHitTestResult.mType = HitTestResult.IMAGE_TYPE;
-            mInitialHitTestResult.mExtra = hit.mImageUrl;
-        } else if (hit.mEditable) {
-            mInitialHitTestResult.mType = HitTestResult.EDIT_TEXT_TYPE;
-        } else if (hit.mIntentUrl != null) {
-            setHitTestTypeFromUrl(hit.mIntentUrl);
-        }
-    }
-
-    private boolean shouldDrawHighlightRect() {
-        if (mFocusedNode == null || mInitialHitTestResult == null) {
-            return false;
-        }
-        if (mTouchHighlightRegion.isEmpty()) {
-            return false;
-        }
-        if (mFocusedNode.mHasFocus && !isInTouchMode()) {
-            return !mFocusedNode.mEditable;
-        }
-        if (mInitialHitTestResult.mType == HitTestResult.UNKNOWN_TYPE) {
-            return false;
-        }
-        long delay = System.currentTimeMillis() - mTouchHighlightRequested;
-        if (delay < ViewConfiguration.getTapTimeout()) {
-            Rect r = mTouchHighlightRegion.getBounds();
-            postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
-            return false;
-        }
-        return true;
-    }
-
-
-    private FocusTransitionDrawable mFocusTransition = null;
-    static class FocusTransitionDrawable extends Drawable {
-        Region mPreviousRegion;
-        Region mNewRegion;
-        float mProgress = 0;
-        WebView mWebView;
-        Paint mPaint;
-        int mMaxAlpha;
-        Point mTranslate;
-
-        public FocusTransitionDrawable(WebView view) {
-            mWebView = view;
-            mPaint = new Paint(mWebView.mTouchHightlightPaint);
-            mMaxAlpha = mPaint.getAlpha();
-        }
-
-        @Override
-        public void setColorFilter(ColorFilter cf) {
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-        }
-
-        @Override
-        public int getOpacity() {
-            return 0;
-        }
-
-        public void setProgress(float p) {
-            mProgress = p;
-            if (mWebView.mFocusTransition == this) {
-                if (mProgress == 1f)
-                    mWebView.mFocusTransition = null;
-                mWebView.invalidate();
-            }
-        }
-
-        public float getProgress() {
-            return mProgress;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            if (mTranslate == null) {
-                Rect bounds = mPreviousRegion.getBounds();
-                Point from = new Point(bounds.centerX(), bounds.centerY());
-                mNewRegion.getBounds(bounds);
-                Point to = new Point(bounds.centerX(), bounds.centerY());
-                mTranslate = new Point(from.x - to.x, from.y - to.y);
-            }
-            int alpha = (int) (mProgress * mMaxAlpha);
-            RegionIterator iter = new RegionIterator(mPreviousRegion);
-            Rect r = new Rect();
-            mPaint.setAlpha(mMaxAlpha - alpha);
-            float tx = mTranslate.x * mProgress;
-            float ty = mTranslate.y * mProgress;
-            int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            canvas.translate(-tx, -ty);
-            while (iter.next(r)) {
-                canvas.drawRect(r, mPaint);
-            }
-            canvas.restoreToCount(save);
-            iter = new RegionIterator(mNewRegion);
-            r = new Rect();
-            mPaint.setAlpha(alpha);
-            save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            tx = mTranslate.x - tx;
-            ty = mTranslate.y - ty;
-            canvas.translate(tx, ty);
-            while (iter.next(r)) {
-                canvas.drawRect(r, mPaint);
-            }
-            canvas.restoreToCount(save);
-        }
-    };
-
-    private boolean shouldAnimateTo(WebKitHitTest hit) {
-        // TODO: Don't be annoying or throw out the animation entirely
-        return false;
-    }
-
-    private void setTouchHighlightRects(WebKitHitTest hit) {
-        FocusTransitionDrawable transition = null;
-        if (shouldAnimateTo(hit)) {
-            transition = new FocusTransitionDrawable(this);
-        }
-        Rect[] rects = hit != null ? hit.mTouchRects : null;
-        if (!mTouchHighlightRegion.isEmpty()) {
-            invalidate(mTouchHighlightRegion.getBounds());
-            if (transition != null) {
-                transition.mPreviousRegion = new Region(mTouchHighlightRegion);
-            }
-            mTouchHighlightRegion.setEmpty();
-        }
-        if (rects != null) {
-            mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
-            for (Rect rect : rects) {
-                Rect viewRect = contentToViewRect(rect);
-                // some sites, like stories in nytimes.com, set
-                // mouse event handler in the top div. It is not
-                // user friendly to highlight the div if it covers
-                // more than half of the screen.
-                if (viewRect.width() < getWidth() >> 1
-                        || viewRect.height() < getHeight() >> 1) {
-                    mTouchHighlightRegion.union(viewRect);
-                } else {
-                    Log.w(LOGTAG, "Skip the huge selection rect:"
-                            + viewRect);
-                }
-            }
-            invalidate(mTouchHighlightRegion.getBounds());
-            if (transition != null && transition.mPreviousRegion != null) {
-                transition.mNewRegion = new Region(mTouchHighlightRegion);
-                mFocusTransition = transition;
-                ObjectAnimator animator = ObjectAnimator.ofFloat(
-                        mFocusTransition, "progress", 1f);
-                animator.start();
-            }
-        }
-    }
-
-    /** @hide Called by JNI when pages are swapped (only occurs with hardware
-     * acceleration) */
-    protected void pageSwapCallback(boolean notifyAnimationStarted) {
-        mWebViewCore.resumeWebKitDraw();
-        if (inEditingMode()) {
-            didUpdateWebTextViewDimensions(ANYWHERE);
-        }
-        if (notifyAnimationStarted) {
-            mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
-        }
-    }
-
-    void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
-        if (mNativeClass == 0) {
-            if (mDelaySetPicture != null) {
-                throw new IllegalStateException("Tried to setNewPicture with"
-                        + " a delay picture already set! (memory leak)");
-            }
-            // Not initialized yet, delay set
-            mDelaySetPicture = draw;
-            return;
-        }
-        WebViewCore.ViewState viewState = draw.mViewState;
-        boolean isPictureAfterFirstLayout = viewState != null;
-
-        if (updateBaseLayer) {
-            setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
-                    getSettings().getShowVisualIndicator(),
-                    isPictureAfterFirstLayout);
-        }
-        final Point viewSize = draw.mViewSize;
-        // We update the layout (i.e. request a layout from the
-        // view system) if the last view size that we sent to
-        // WebCore matches the view size of the picture we just
-        // received in the fixed dimension.
-        final boolean updateLayout = viewSize.x == mLastWidthSent
-                && viewSize.y == mLastHeightSent;
-        // Don't send scroll event for picture coming from webkit,
-        // since the new picture may cause a scroll event to override
-        // the saved history scroll position.
-        mSendScrollEvent = false;
-        recordNewContentSize(draw.mContentSize.x,
-                draw.mContentSize.y, updateLayout);
-        if (isPictureAfterFirstLayout) {
-            // Reset the last sent data here since dealing with new page.
-            mLastWidthSent = 0;
-            mZoomManager.onFirstLayout(draw);
-            int scrollX = viewState.mShouldStartScrolledRight
-                    ? getContentWidth() : viewState.mScrollX;
-            int scrollY = viewState.mScrollY;
-            setContentScrollTo(scrollX, scrollY);
-            if (!mDrawHistory) {
-                // As we are on a new page, remove the WebTextView. This
-                // is necessary for page loads driven by webkit, and in
-                // particular when the user was on a password field, so
-                // the WebTextView was visible.
-                clearTextEntry();
-            }
-        }
-        mSendScrollEvent = true;
-
-        if (DebugFlags.WEB_VIEW) {
-            Rect b = draw.mInvalRegion.getBounds();
-            Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
-                    b.left+","+b.top+","+b.right+","+b.bottom+"}");
-        }
-        invalidateContentRect(draw.mInvalRegion.getBounds());
-
-        if (mPictureListener != null) {
-            mPictureListener.onNewPicture(WebView.this, capturePicture());
-        }
-
-        // update the zoom information based on the new picture
-        mZoomManager.onNewPicture(draw);
-
-        if (draw.mFocusSizeChanged && inEditingMode()) {
-            mFocusSizeChanged = true;
-        }
-        if (isPictureAfterFirstLayout) {
-            mViewManager.postReadyToDrawAll();
-        }
-    }
-
-    /**
-     * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
-     * and UPDATE_TEXT_SELECTION_MSG_ID.  Update the selection of WebTextView.
-     */
-    private void updateTextSelectionFromMessage(int nodePointer,
-            int textGeneration, WebViewCore.TextSelectionData data) {
-        if (textGeneration == mTextGeneration) {
-            if (inEditingMode()
-                    && mWebTextView.isSameTextField(nodePointer)) {
-                mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
-            } else if (mInputConnection != null && mFieldPointer == nodePointer) {
-                mInputConnection.setSelection(data.mStart, data.mEnd);
-            }
-        }
-        nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
-
-        if (data.mSelectTextPtr != 0 &&
-                (data.mStart != data.mEnd ||
-                (mFieldPointer == nodePointer && mFieldPointer != 0))) {
-            mIsCaretSelection = (data.mStart == data.mEnd);
-            if (!mSelectingText) {
-                setupWebkitSelect();
-            } else if (!mSelectionStarted) {
-                syncSelectionCursors();
-            }
-            if (mIsCaretSelection) {
-                resetCaretTimer();
-            }
-        } else {
-            selectionDone();
-        }
-        invalidate();
-    }
-
-    // Class used to use a dropdown for a <select> element
-    private class InvokeListBox implements Runnable {
-        // Whether the listbox allows multiple selection.
-        private boolean     mMultiple;
-        // Passed in to a list with multiple selection to tell
-        // which items are selected.
-        private int[]       mSelectedArray;
-        // Passed in to a list with single selection to tell
-        // where the initial selection is.
-        private int         mSelection;
-
-        private Container[] mContainers;
-
-        // Need these to provide stable ids to my ArrayAdapter,
-        // which normally does not have stable ids. (Bug 1250098)
-        private class Container extends Object {
-            /**
-             * Possible values for mEnabled.  Keep in sync with OptionStatus in
-             * WebViewCore.cpp
-             */
-            final static int OPTGROUP = -1;
-            final static int OPTION_DISABLED = 0;
-            final static int OPTION_ENABLED = 1;
-
-            String  mString;
-            int     mEnabled;
-            int     mId;
-
-            @Override
-            public String toString() {
-                return mString;
-            }
-        }
-
-        /**
-         *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
-         *  and allow filtering.
-         */
-        private class MyArrayListAdapter extends ArrayAdapter<Container> {
-            public MyArrayListAdapter() {
-                super(mContext,
-                        mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
-                        com.android.internal.R.layout.webview_select_singlechoice,
-                        mContainers);
-            }
-
-            @Override
-            public View getView(int position, View convertView,
-                    ViewGroup parent) {
-                // Always pass in null so that we will get a new CheckedTextView
-                // Otherwise, an item which was previously used as an <optgroup>
-                // element (i.e. has no check), could get used as an <option>
-                // element, which needs a checkbox/radio, but it would not have
-                // one.
-                convertView = super.getView(position, null, parent);
-                Container c = item(position);
-                if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
-                    // ListView does not draw dividers between disabled and
-                    // enabled elements.  Use a LinearLayout to provide dividers
-                    LinearLayout layout = new LinearLayout(mContext);
-                    layout.setOrientation(LinearLayout.VERTICAL);
-                    if (position > 0) {
-                        View dividerTop = new View(mContext);
-                        dividerTop.setBackgroundResource(
-                                android.R.drawable.divider_horizontal_bright);
-                        layout.addView(dividerTop);
-                    }
-
-                    if (Container.OPTGROUP == c.mEnabled) {
-                        // Currently select_dialog_multichoice uses CheckedTextViews.
-                        // If that changes, the class cast will no longer be valid.
-                        if (mMultiple) {
-                            Assert.assertTrue(convertView instanceof CheckedTextView);
-                            ((CheckedTextView) convertView).setCheckMarkDrawable(null);
-                        }
-                    } else {
-                        // c.mEnabled == Container.OPTION_DISABLED
-                        // Draw the disabled element in a disabled state.
-                        convertView.setEnabled(false);
-                    }
-
-                    layout.addView(convertView);
-                    if (position < getCount() - 1) {
-                        View dividerBottom = new View(mContext);
-                        dividerBottom.setBackgroundResource(
-                                android.R.drawable.divider_horizontal_bright);
-                        layout.addView(dividerBottom);
-                    }
-                    return layout;
-                }
-                return convertView;
-            }
-
-            @Override
-            public boolean hasStableIds() {
-                // AdapterView's onChanged method uses this to determine whether
-                // to restore the old state.  Return false so that the old (out
-                // of date) state does not replace the new, valid state.
-                return false;
-            }
-
-            private Container item(int position) {
-                if (position < 0 || position >= getCount()) {
-                    return null;
-                }
-                return getItem(position);
-            }
-
-            @Override
-            public long getItemId(int position) {
-                Container item = item(position);
-                if (item == null) {
-                    return -1;
-                }
-                return item.mId;
-            }
-
-            @Override
-            public boolean areAllItemsEnabled() {
-                return false;
-            }
-
-            @Override
-            public boolean isEnabled(int position) {
-                Container item = item(position);
-                if (item == null) {
-                    return false;
-                }
-                return Container.OPTION_ENABLED == item.mEnabled;
-            }
-        }
-
-        private InvokeListBox(String[] array, int[] enabled, int[] selected) {
-            mMultiple = true;
-            mSelectedArray = selected;
-
-            int length = array.length;
-            mContainers = new Container[length];
-            for (int i = 0; i < length; i++) {
-                mContainers[i] = new Container();
-                mContainers[i].mString = array[i];
-                mContainers[i].mEnabled = enabled[i];
-                mContainers[i].mId = i;
-            }
-        }
-
-        private InvokeListBox(String[] array, int[] enabled, int selection) {
-            mSelection = selection;
-            mMultiple = false;
-
-            int length = array.length;
-            mContainers = new Container[length];
-            for (int i = 0; i < length; i++) {
-                mContainers[i] = new Container();
-                mContainers[i].mString = array[i];
-                mContainers[i].mEnabled = enabled[i];
-                mContainers[i].mId = i;
-            }
-        }
-
-        /*
-         * Whenever the data set changes due to filtering, this class ensures
-         * that the checked item remains checked.
-         */
-        private class SingleDataSetObserver extends DataSetObserver {
-            private long        mCheckedId;
-            private ListView    mListView;
-            private Adapter     mAdapter;
-
-            /*
-             * Create a new observer.
-             * @param id The ID of the item to keep checked.
-             * @param l ListView for getting and clearing the checked states
-             * @param a Adapter for getting the IDs
-             */
-            public SingleDataSetObserver(long id, ListView l, Adapter a) {
-                mCheckedId = id;
-                mListView = l;
-                mAdapter = a;
-            }
-
-            @Override
-            public void onChanged() {
-                // The filter may have changed which item is checked.  Find the
-                // item that the ListView thinks is checked.
-                int position = mListView.getCheckedItemPosition();
-                long id = mAdapter.getItemId(position);
-                if (mCheckedId != id) {
-                    // Clear the ListView's idea of the checked item, since
-                    // it is incorrect
-                    mListView.clearChoices();
-                    // Search for mCheckedId.  If it is in the filtered list,
-                    // mark it as checked
-                    int count = mAdapter.getCount();
-                    for (int i = 0; i < count; i++) {
-                        if (mAdapter.getItemId(i) == mCheckedId) {
-                            mListView.setItemChecked(i, true);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            final ListView listView = (ListView) LayoutInflater.from(mContext)
-                    .inflate(com.android.internal.R.layout.select_dialog, null);
-            final MyArrayListAdapter adapter = new MyArrayListAdapter();
-            AlertDialog.Builder b = new AlertDialog.Builder(mContext)
-                    .setView(listView).setCancelable(true)
-                    .setInverseBackgroundForced(true);
-
-            if (mMultiple) {
-                b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mWebViewCore.sendMessage(
-                                EventHub.LISTBOX_CHOICES,
-                                adapter.getCount(), 0,
-                                listView.getCheckedItemPositions());
-                    }});
-                b.setNegativeButton(android.R.string.cancel,
-                        new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mWebViewCore.sendMessage(
-                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
-                }});
-            }
-            mListBoxDialog = b.create();
-            listView.setAdapter(adapter);
-            listView.setFocusableInTouchMode(true);
-            // There is a bug (1250103) where the checks in a ListView with
-            // multiple items selected are associated with the positions, not
-            // the ids, so the items do not properly retain their checks when
-            // filtered.  Do not allow filtering on multiple lists until
-            // that bug is fixed.
-
-            listView.setTextFilterEnabled(!mMultiple);
-            if (mMultiple) {
-                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-                int length = mSelectedArray.length;
-                for (int i = 0; i < length; i++) {
-                    listView.setItemChecked(mSelectedArray[i], true);
-                }
-            } else {
-                listView.setOnItemClickListener(new OnItemClickListener() {
-                    @Override
-                    public void onItemClick(AdapterView<?> parent, View v,
-                            int position, long id) {
-                        // Rather than sending the message right away, send it
-                        // after the page regains focus.
-                        mListBoxMessage = Message.obtain(null,
-                                EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
-                        mListBoxDialog.dismiss();
-                        mListBoxDialog = null;
-                    }
-                });
-                if (mSelection != -1) {
-                    listView.setSelection(mSelection);
-                    listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-                    listView.setItemChecked(mSelection, true);
-                    DataSetObserver observer = new SingleDataSetObserver(
-                            adapter.getItemId(mSelection), listView, adapter);
-                    adapter.registerDataSetObserver(observer);
-                }
-            }
-            mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
-                @Override
-                public void onCancel(DialogInterface dialog) {
-                    mWebViewCore.sendMessage(
-                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
-                    mListBoxDialog = null;
-                }
-            });
-            mListBoxDialog.show();
-        }
-    }
-
-    private Message mListBoxMessage;
-
-    /*
-     * Request a dropdown menu for a listbox with multiple selection.
-     *
-     * @param array Labels for the listbox.
-     * @param enabledArray  State for each element in the list.  See static
-     *      integers in Container class.
-     * @param selectedArray Which positions are initally selected.
-     */
-    void requestListBox(String[] array, int[] enabledArray, int[]
-            selectedArray) {
-        mPrivateHandler.post(
-                new InvokeListBox(array, enabledArray, selectedArray));
-    }
-
-    /*
-     * Request a dropdown menu for a listbox with single selection or a single
-     * <select> element.
-     *
-     * @param array Labels for the listbox.
-     * @param enabledArray  State for each element in the list.  See static
-     *      integers in Container class.
-     * @param selection Which position is initally selected.
-     */
-    void requestListBox(String[] array, int[] enabledArray, int selection) {
-        mPrivateHandler.post(
-                new InvokeListBox(array, enabledArray, selection));
-    }
-
-    // called by JNI
-    private void sendMoveFocus(int frame, int node) {
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
-                new WebViewCore.CursorData(frame, node, 0, 0));
-    }
-
-    // called by JNI
-    private void sendMoveMouse(int frame, int node, int x, int y) {
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
-                new WebViewCore.CursorData(frame, node, x, y));
-    }
-
-    /*
-     * Send a mouse move event to the webcore thread.
-     *
-     * @param removeFocus Pass true to remove the WebTextView, if present.
-     * @param stopPaintingCaret Stop drawing the blinking caret if true.
-     * called by JNI
-     */
-    @SuppressWarnings("unused")
-    private void sendMoveMouseIfLatest(boolean removeFocus, boolean stopPaintingCaret) {
-        if (removeFocus) {
-            clearTextEntry();
-        }
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
-                stopPaintingCaret ? 1 : 0, 0,
-                cursorData());
-    }
-
-    /**
-     * Called by JNI to send a message to the webcore thread that the user
-     * touched the webpage.
-     * @param touchGeneration Generation number of the touch, to ignore touches
-     *      after a new one has been generated.
-     * @param frame Pointer to the frame holding the node that was touched.
-     * @param node Pointer to the node touched.
-     * @param x x-position of the touch.
-     * @param y y-position of the touch.
-     */
-    private void sendMotionUp(int touchGeneration,
-            int frame, int node, int x, int y) {
-        WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
-        touchUpData.mMoveGeneration = touchGeneration;
-        touchUpData.mFrame = frame;
-        touchUpData.mNode = node;
-        touchUpData.mX = x;
-        touchUpData.mY = y;
-        touchUpData.mNativeLayer = nativeScrollableLayer(
-                x, y, touchUpData.mNativeLayerRect, null);
-        mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
-    }
-
-
-    private int getScaledMaxXScroll() {
-        int width;
-        if (mHeightCanMeasure == false) {
-            width = getViewWidth() / 4;
-        } else {
-            Rect visRect = new Rect();
-            calcOurVisibleRect(visRect);
-            width = visRect.width() / 2;
-        }
-        // FIXME the divisor should be retrieved from somewhere
-        return viewToContentX(width);
-    }
-
-    private int getScaledMaxYScroll() {
-        int height;
-        if (mHeightCanMeasure == false) {
-            height = getViewHeight() / 4;
-        } else {
-            Rect visRect = new Rect();
-            calcOurVisibleRect(visRect);
-            height = visRect.height() / 2;
-        }
-        // FIXME the divisor should be retrieved from somewhere
-        // the closest thing today is hard-coded into ScrollView.java
-        // (from ScrollView.java, line 363)   int maxJump = height/2;
-        return Math.round(height * mZoomManager.getInvScale());
-    }
-
-    /**
-     * Called by JNI to invalidate view
-     */
-    private void viewInvalidate() {
-        invalidate();
-    }
-
-    /**
-     * Pass the key directly to the page.  This assumes that
-     * nativePageShouldHandleShiftAndArrows() returned true.
-     */
-    private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
-        int keyEventAction;
-        int eventHubAction;
-        if (down) {
-            keyEventAction = KeyEvent.ACTION_DOWN;
-            eventHubAction = EventHub.KEY_DOWN;
-            playSoundEffect(keyCodeToSoundsEffect(keyCode));
-        } else {
-            keyEventAction = KeyEvent.ACTION_UP;
-            eventHubAction = EventHub.KEY_UP;
-        }
-
-        KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
-                1, (metaState & KeyEvent.META_SHIFT_ON)
-                | (metaState & KeyEvent.META_ALT_ON)
-                | (metaState & KeyEvent.META_SYM_ON)
-                , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
-        mWebViewCore.sendMessage(eventHubAction, event);
-    }
-
-    // return true if the key was handled
-    private boolean navHandledKey(int keyCode, int count, boolean noScroll,
-            long time) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        mInitialHitTestResult = null;
-        mLastCursorTime = time;
-        mLastCursorBounds = cursorRingBounds();
-        boolean keyHandled
-                = nativeMoveCursor(keyCode, count, noScroll) == false;
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
-                    + " mLastCursorTime=" + mLastCursorTime
-                    + " handled=" + keyHandled);
-        }
-        if (keyHandled == false) {
-            return keyHandled;
-        }
-        Rect contentCursorRingBounds = cursorRingBounds();
-        if (contentCursorRingBounds.isEmpty()) return keyHandled;
-        Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
-        // set last touch so that context menu related functions will work
-        mLastTouchX = (viewCursorRingBounds.left + viewCursorRingBounds.right) / 2;
-        mLastTouchY = (viewCursorRingBounds.top + viewCursorRingBounds.bottom) / 2;
-        if (mHeightCanMeasure == false) {
-            return keyHandled;
-        }
-        Rect visRect = new Rect();
-        calcOurVisibleRect(visRect);
-        Rect outset = new Rect(visRect);
-        int maxXScroll = visRect.width() / 2;
-        int maxYScroll = visRect.height() / 2;
-        outset.inset(-maxXScroll, -maxYScroll);
-        if (Rect.intersects(outset, viewCursorRingBounds) == false) {
-            return keyHandled;
-        }
-        // FIXME: Necessary because ScrollView/ListView do not scroll left/right
-        int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
-                maxXScroll);
-        if (maxH > 0) {
-            pinScrollBy(maxH, 0, true, 0);
-        } else {
-            maxH = Math.max(viewCursorRingBounds.left - visRect.left,
-                    -maxXScroll);
-            if (maxH < 0) {
-                pinScrollBy(maxH, 0, true, 0);
-            }
-        }
-        if (mLastCursorBounds.isEmpty()) return keyHandled;
-        if (mLastCursorBounds.equals(contentCursorRingBounds)) {
-            return keyHandled;
-        }
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
-                    + contentCursorRingBounds);
-        }
-        requestRectangleOnScreen(viewCursorRingBounds);
-        return keyHandled;
-    }
-
-    /**
-     * @return Whether accessibility script has been injected.
-     */
-    private boolean accessibilityScriptInjected() {
-        // TODO: Maybe the injected script should announce its presence in
-        // the page meta-tag so the nativePageShouldHandleShiftAndArrows
-        // will check that as one of the conditions it looks for
-        return mAccessibilityScriptInjected;
-    }
-
-    /**
-     * Set the background color. It's white by default. Pass
-     * zero to make the view transparent.
-     * @param color   the ARGB color described by Color.java
-     */
-    @Override
-    public void setBackgroundColor(int color) {
-        mBackgroundColor = color;
-        mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
+        return mProvider.zoomOut();
     }
 
     /**
@@ -10239,70 +1534,165 @@
     @Deprecated
     public void debugDump() {
         checkThread();
-        nativeDebugDump();
-        mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
+        mProvider.debugDump();
     }
 
+    //-------------------------------------------------------------------------
+    // Interface for WebView providers
+    //-------------------------------------------------------------------------
+
     /**
-     * Draw the HTML page into the specified canvas. This call ignores any
-     * view-specific zoom, scroll offset, or other changes. It does not draw
-     * any view-specific chrome, such as progress or URL bars.
+     * Used by providers to obtain the underlying implementation, e.g. when the appliction
+     * responds to WebViewClient.onCreateWindow() request.
      *
-     * @hide only needs to be accessible to Browser and testing
+     * @hide WebViewProvider is not public API.
      */
-    public void drawPage(Canvas canvas) {
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        nativeDraw(canvas, mVisibleContentRect, 0, 0, false);
+    public WebViewProvider getWebViewProvider() {
+        return mProvider;
     }
 
     /**
-     * Enable the communication b/t the webView and VideoViewProxy
-     *
-     * @hide only used by the Browser
+     * Callback interface, allows the provider implementation to access non-public methods
+     * and fields, and make super-class calls in this WebView instance.
+     * @hide Only for use by WebViewProvider implementations
      */
-    public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
-        mHTML5VideoViewProxy = proxy;
+    public class PrivateAccess {
+        // ---- Access to super-class methods ----
+        public int super_getScrollBarStyle() {
+            return WebView.super.getScrollBarStyle();
+        }
+
+        public void super_scrollTo(int scrollX, int scrollY) {
+            WebView.super.scrollTo(scrollX, scrollY);
+        }
+
+        public void super_computeScroll() {
+            WebView.super.computeScroll();
+        }
+
+        public boolean super_performLongClick() {
+            return WebView.super.performLongClick();
+        }
+
+        public boolean super_setFrame(int left, int top, int right, int bottom) {
+            return WebView.super.setFrame(left, top, right, bottom);
+        }
+
+        public boolean super_dispatchKeyEvent(KeyEvent event) {
+            return WebView.super.dispatchKeyEvent(event);
+        }
+
+        public boolean super_onGenericMotionEvent(MotionEvent event) {
+            return WebView.super.onGenericMotionEvent(event);
+        }
+
+        public boolean super_requestFocus(int direction, Rect previouslyFocusedRect) {
+            return WebView.super.requestFocus(direction, previouslyFocusedRect);
+        }
+
+        public void super_setLayoutParams(ViewGroup.LayoutParams params) {
+            WebView.super.setLayoutParams(params);
+        }
+
+        // ---- Access to non-public methods ----
+        public void overScrollBy(int deltaX, int deltaY,
+                int scrollX, int scrollY,
+                int scrollRangeX, int scrollRangeY,
+                int maxOverScrollX, int maxOverScrollY,
+                boolean isTouchEvent) {
+            WebView.this.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+                    maxOverScrollX, maxOverScrollY, isTouchEvent);
+        }
+
+        public void awakenScrollBars(int duration) {
+            WebView.this.awakenScrollBars(duration);
+        }
+
+        public void awakenScrollBars(int duration, boolean invalidate) {
+            WebView.this.awakenScrollBars(duration, invalidate);
+        }
+
+        public float getVerticalScrollFactor() {
+            return WebView.this.getVerticalScrollFactor();
+        }
+
+        public float getHorizontalScrollFactor() {
+            return WebView.this.getHorizontalScrollFactor();
+        }
+
+        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+            WebView.this.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        public void onScrollChanged(int l, int t, int oldl, int oldt) {
+            WebView.this.onScrollChanged(l, t, oldl, oldt);
+        }
+
+        public int getHorizontalScrollbarHeight() {
+            return WebView.this.getHorizontalScrollbarHeight();
+        }
+
+        // ---- Access to (non-public) fields ----
+        /** Raw setter for the scroll X value, without invoking onScrollChanged handlers etc. */
+        public void setScrollXRaw(int scrollX) {
+            WebView.this.mScrollX = scrollX;
+        }
+
+        /** Raw setter for the scroll Y value, without invoking onScrollChanged handlers etc. */
+        public void setScrollYRaw(int scrollY) {
+            WebView.this.mScrollY = scrollY;
+        }
+
     }
 
-    /**
-     * Set the time to wait between passing touches to WebCore. See also the
-     * TOUCH_SENT_INTERVAL member for further discussion.
-     *
-     * @hide This is only used by the DRT test application.
-     */
-    public void setTouchInterval(int interval) {
-        mCurrentTouchInterval = interval;
+    //-------------------------------------------------------------------------
+    // Private internal stuff
+    //-------------------------------------------------------------------------
+
+    // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
+    // same provider.
+    private static WebViewFactoryProvider sProviderFactory;
+
+    private WebViewProvider mProvider;
+
+    private void ensureProviderCreated() {
+        checkThread();
+        if (mProvider == null) {
+            if (DEBUG) Log.v(LOGTAG, "instantiating webview provider instance");
+            // As this can get called during the base class constructor chain, pass the minimum
+            // number of dependencies here; the rest are deferred to init().
+            mProvider = getFactory().createWebView(this, new PrivateAccess());
+        }
     }
 
-    /**
-     * Copy text into the clipboard. This is called indirectly from
-     * WebViewCore.
-     * @param text The text to put into the clipboard.
-     */
-    private void copyToClipboard(String text) {
-        ClipboardManager cm = (ClipboardManager)getContext()
-                .getSystemService(Context.CLIPBOARD_SERVICE);
-        ClipData clip = ClipData.newPlainText(getTitle(), text);
-        cm.setPrimaryClip(clip);
+    private static synchronized WebViewFactoryProvider getFactory() {
+        // For now the main purpose of this function (and the factory abstration) is to keep
+        // us honest and minimize usage of WebViewClassic internals when binding the proxy.
+        checkThread();
+        if (sProviderFactory != null) return sProviderFactory;
+
+        sProviderFactory = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY);
+        if (sProviderFactory == null) {
+            if (DEBUG) Log.v (LOGTAG, "Falling back to explicit linkage");
+            sProviderFactory = new WebViewClassic.Factory();
+        }
+        return sProviderFactory;
     }
 
-    /**
-     *  Update our cache with updatedText.
-     *  @param updatedText  The new text to put in our cache.
-     *  @hide
-     */
-    protected void updateCachedTextfield(String updatedText) {
-        // Also place our generation number so that when we look at the cache
-        // we recognize that it is up to date.
-        nativeUpdateCachedTextfield(updatedText, mTextGeneration);
-    }
-
-    /*package*/ void autoFillForm(int autoFillQueryId) {
-        mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
-    }
-
-    /* package */ ViewManager getViewManager() {
-        return mViewManager;
+    private static WebViewFactoryProvider getFactoryByName(String providerName) {
+        try {
+            if (DEBUG) Log.v(LOGTAG, "attempt to load class " + providerName);
+            Class<?> c = Class.forName(providerName);
+            if (DEBUG) Log.v(LOGTAG, "instantiating factory");
+            return (WebViewFactoryProvider) c.newInstance();
+        } catch (ClassNotFoundException e) {
+            Log.e(LOGTAG, "error loading " + providerName, e);
+        } catch (IllegalAccessException e) {
+            Log.e(LOGTAG, "error loading " + providerName, e);
+        } catch (InstantiationException e) {
+            Log.e(LOGTAG, "error loading " + providerName, e);
+        }
+        return null;
     }
 
     private static void checkThread() {
@@ -10317,234 +1707,252 @@
         }
     }
 
-    /** @hide send content invalidate */
-    protected void contentInvalidateAll() {
-        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
-            mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
-        }
+    //-------------------------------------------------------------------------
+    // Override View methods
+    //-------------------------------------------------------------------------
+
+    // TODO: Add a test that enumerates all methods in ViewDelegte & ScrollDelegate, and ensures
+    // there's a corresponding override (or better, caller) for each of them in here.
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mProvider.getViewDelegate().onAttachedToWindow();
     }
 
-    /** @hide discard all textures from tiles */
-    protected void discardAllTextures() {
-        nativeDiscardAllTextures();
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.getViewDelegate().onDetachedFromWindow();
+        super.onDetachedFromWindow();
     }
 
-    /**
-     * Begin collecting per-tile profiling data
-     *
-     * @hide only used by profiling tests
-     */
-    public void tileProfilingStart() {
-        nativeTileProfilingStart();
-    }
-    /**
-     * Return per-tile profiling data
-     *
-     * @hide only used by profiling tests
-     */
-    public float tileProfilingStop() {
-        return nativeTileProfilingStop();
+    @Override
+    public void setLayoutParams(ViewGroup.LayoutParams params) {
+        mProvider.getViewDelegate().setLayoutParams(params);
     }
 
-    /** @hide only used by profiling tests */
-    public void tileProfilingClear() {
-        nativeTileProfilingClear();
-    }
-    /** @hide only used by profiling tests */
-    public int tileProfilingNumFrames() {
-        return nativeTileProfilingNumFrames();
-    }
-    /** @hide only used by profiling tests */
-    public int tileProfilingNumTilesInFrame(int frame) {
-        return nativeTileProfilingNumTilesInFrame(frame);
-    }
-    /** @hide only used by profiling tests */
-    public int tileProfilingGetInt(int frame, int tile, String key) {
-        return nativeTileProfilingGetInt(frame, tile, key);
-    }
-    /** @hide only used by profiling tests */
-    public float tileProfilingGetFloat(int frame, int tile, String key) {
-        return nativeTileProfilingGetFloat(frame, tile, key);
+    @Override
+    public void setOverScrollMode(int mode) {
+        super.setOverScrollMode(mode);
+        // This method may called in the constructor chain, before the WebView provider is
+        // created. (Fortunately, this is the only method we override that can get called by
+        // any of the base class constructors).
+        ensureProviderCreated();
+        mProvider.getViewDelegate().setOverScrollMode(mode);
     }
 
-    /**
-     * Checks the focused content for an editable text field. This can be
-     * text input or ContentEditable.
-     * @return true if the focused item is an editable text field.
-     */
-    boolean focusCandidateIsEditableText() {
-        boolean isEditable = false;
-        // TODO: reverse sDisableNavcache so that its name is positive
-        boolean isNavcacheEnabled = !sDisableNavcache;
-        if (isNavcacheEnabled) {
-            isEditable = nativeFocusCandidateIsEditableText(mNativeClass);
-        } else if (mFocusedNode != null) {
-            isEditable = mFocusedNode.mEditable;
-        }
-        return isEditable;
+    @Override
+    public void setScrollBarStyle(int style) {
+        mProvider.getViewDelegate().setScrollBarStyle(style);
+        super.setScrollBarStyle(style);
     }
 
-    // TODO: Remove this
-    Rect cursorRingBounds() {
-        if (sDisableNavcache) {
-            return new Rect();
-        }
-        return nativeGetCursorRingBounds();
+    @Override
+    protected int computeHorizontalScrollRange() {
+        return mProvider.getScrollDelegate().computeHorizontalScrollRange();
     }
 
-    private native int nativeCacheHitFramePointer();
-    private native boolean  nativeCacheHitIsPlugin();
-    private native Rect nativeCacheHitNodeBounds();
-    private native int nativeCacheHitNodePointer();
-    /* package */ native void nativeClearCursor();
-    private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
-    private native int      nativeCursorFramePointer();
-    private native Rect     nativeCursorNodeBounds();
-    private native int nativeCursorNodePointer();
-    private native boolean  nativeCursorIntersects(Rect visibleRect);
-    private native boolean  nativeCursorIsAnchor();
-    private native boolean  nativeCursorIsTextInput();
-    private native Point    nativeCursorPosition();
-    private native String   nativeCursorText();
-    /**
-     * Returns true if the native cursor node says it wants to handle key events
-     * (ala plugins). This can only be called if mNativeClass is non-zero!
-     */
-    private native boolean  nativeCursorWantsKeyEvents();
-    private native void     nativeDebugDump();
-    private native void     nativeDestroy();
+    @Override
+    protected int computeHorizontalScrollOffset() {
+        return mProvider.getScrollDelegate().computeHorizontalScrollOffset();
+    }
 
-    /**
-     * Draw the picture set with a background color and extra. If
-     * "splitIfNeeded" is true and the return value is not 0, the return value
-     * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the
-     * native allocation can be freed.
-     */
-    private native int nativeDraw(Canvas canvas, RectF visibleRect,
-            int color, int extra, boolean splitIfNeeded);
-    private native void     nativeDumpDisplayTree(String urlOrNull);
-    private native boolean  nativeEvaluateLayersAnimations(int nativeInstance);
-    private native int      nativeGetDrawGLFunction(int nativeInstance, Rect rect,
-            Rect viewRect, RectF visibleRect, float scale, int extras);
-    private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect,
-            RectF visibleRect, float scale);
-    private native void     nativeExtendSelection(int x, int y);
-    /* package */ native int      nativeFocusCandidateFramePointer();
-    /* package */ native boolean  nativeFocusCandidateHasNextTextfield();
-    /* package */ native boolean  nativeFocusCandidateIsPassword();
-    private native boolean  nativeFocusCandidateIsRtlText();
-    private native boolean  nativeFocusCandidateIsTextInput();
-    private native boolean nativeFocusCandidateIsEditableText(int nativeClass);
-    /* package */ native int      nativeFocusCandidateMaxLength();
-    /* package */ native boolean  nativeFocusCandidateIsAutoComplete();
-    /* package */ native boolean  nativeFocusCandidateIsSpellcheck();
-    /* package */ native String   nativeFocusCandidateName();
-    private native Rect     nativeFocusCandidateNodeBounds();
-    /**
-     * @return A Rect with left, top, right, bottom set to the corresponding
-     * padding values in the focus candidate, if it is a textfield/textarea with
-     * a style.  Otherwise return null.  This is not actually a rectangle; Rect
-     * is being used to pass four integers.
-     */
-    private native Rect     nativeFocusCandidatePaddingRect();
-    /* package */ native int      nativeFocusCandidatePointer();
-    private native String   nativeFocusCandidateText();
-    /* package */ native float    nativeFocusCandidateTextSize();
-    /* package */ native int nativeFocusCandidateLineHeight();
-    /**
-     * Returns an integer corresponding to WebView.cpp::type.
-     * See WebTextView.setType()
-     */
-    private native int      nativeFocusCandidateType();
-    private native int      nativeFocusCandidateLayerId();
-    private native boolean  nativeFocusIsPlugin();
-    private native Rect     nativeFocusNodeBounds();
-    /* package */ native int nativeFocusNodePointer();
-    private native Rect     nativeGetCursorRingBounds();
-    private native String   nativeGetSelection();
-    private native boolean  nativeHasCursorNode();
-    private native boolean  nativeHasFocusNode();
-    private native void     nativeHideCursor();
-    private native boolean  nativeHitSelection(int x, int y);
-    private native String   nativeImageURI(int x, int y);
-    private native Rect     nativeLayerBounds(int layer);
-    /* package */ native boolean nativeMoveCursorToNextTextInput();
-    // return true if the page has been scrolled
-    private native boolean  nativeMotionUp(int x, int y, int slop);
-    // returns false if it handled the key
-    private native boolean  nativeMoveCursor(int keyCode, int count,
-            boolean noScroll);
-    private native int      nativeMoveGeneration();
-    /**
-     * @return true if the page should get the shift and arrow keys, rather
-     * than select text/navigation.
-     *
-     * If the focus is a plugin, or if the focus and cursor match and are
-     * a contentEditable element, then the page should handle these keys.
-     */
-    private native boolean  nativePageShouldHandleShiftAndArrows();
-    private native boolean  nativePointInNavCache(int x, int y, int slop);
-    private native void     nativeSelectBestAt(Rect rect);
-    private native void     nativeSelectAt(int x, int y);
-    private native void     nativeSetExtendSelection();
-    private native void     nativeSetFindIsUp(boolean isUp);
-    private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native boolean  nativeSetBaseLayer(int nativeInstance,
-            int layer, Region invalRegion,
-            boolean showVisualIndicator, boolean isPictureAfterFirstLayout);
-    private native int      nativeGetBaseLayer();
-    private native void     nativeShowCursorTimed();
-    private native void     nativeReplaceBaseContent(int content);
-    private native void     nativeCopyBaseContentToPicture(Picture pict);
-    private native boolean  nativeHasContent();
-    private native void     nativeSetSelectionPointer(int nativeInstance,
-            boolean set, float scale, int x, int y);
-    private native boolean  nativeStartSelection(int x, int y);
-    private native void     nativeStopGL();
-    private native Rect     nativeSubtractLayers(Rect content);
-    private native int      nativeTextGeneration();
-    private native void     nativeDiscardAllTextures();
-    private native void     nativeTileProfilingStart();
-    private native float    nativeTileProfilingStop();
-    private native void     nativeTileProfilingClear();
-    private native int      nativeTileProfilingNumFrames();
-    private native int      nativeTileProfilingNumTilesInFrame(int frame);
-    private native int      nativeTileProfilingGetInt(int frame, int tile, String key);
-    private native float    nativeTileProfilingGetFloat(int frame, int tile, String key);
-    // Never call this version except by updateCachedTextfield(String) -
-    // we always want to pass in our generation number.
-    private native void     nativeUpdateCachedTextfield(String updatedText,
-            int generation);
-    private native boolean  nativeWordSelection(int x, int y);
-    // return NO_LEFTEDGE means failure.
-    static final int NO_LEFTEDGE = -1;
-    native int nativeGetBlockLeftEdge(int x, int y, float scale);
+    @Override
+    protected int computeVerticalScrollRange() {
+        return mProvider.getScrollDelegate().computeVerticalScrollRange();
+    }
 
-    private native void     nativeUseHardwareAccelSkia(boolean enabled);
+    @Override
+    protected int computeVerticalScrollOffset() {
+        return mProvider.getScrollDelegate().computeVerticalScrollOffset();
+    }
 
-    // Returns a pointer to the scrollable LayerAndroid at the given point.
-    private native int      nativeScrollableLayer(int x, int y, Rect scrollRect,
-            Rect scrollBounds);
-    /**
-     * Scroll the specified layer.
-     * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
-     * @param newX Destination x position to which to scroll.
-     * @param newY Destination y position to which to scroll.
-     * @return True if the layer is successfully scrolled.
-     */
-    private native boolean  nativeScrollLayer(int layer, int newX, int newY);
-    private native void     nativeSetIsScrolling(boolean isScrolling);
-    private native int      nativeGetBackgroundColor();
-    native boolean  nativeSetProperty(String key, String value);
-    native String   nativeGetProperty(String key);
-    /**
-     * See {@link ComponentCallbacks2} for the trim levels and descriptions
-     */
-    private static native void     nativeOnTrimMemory(int level);
-    private static native void nativeSetPauseDrawing(int instance, boolean pause);
-    private static native boolean nativeDisableNavcache();
-    private static native void nativeSetTextSelection(int instance, int selection);
-    private static native int nativeGetHandleLayerId(int instance, int handle,
-            Rect cursorLocation);
-    private static native boolean nativeIsBaseFirst(int instance);
+    @Override
+    protected int computeVerticalScrollExtent() {
+        return mProvider.getScrollDelegate().computeVerticalScrollExtent();
+    }
+
+    @Override
+    public void computeScroll() {
+        mProvider.getScrollDelegate().computeScroll();
+    }
+
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onHoverEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onGenericMotionEvent(event);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onTrackballEvent(event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyMultiple(keyCode, repeatCount, event);
+    }
+
+    /*
+    TODO: These are not currently implemented in WebViewClassic, but it seems inconsistent not
+    to be delegating them too.
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyPreIme(keyCode, event);
+    }
+    @Override
+    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyLongPress(keyCode, event);
+    }
+    @Override
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyShortcut(keyCode, event);
+    }
+    */
+
+    @Deprecated
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return mProvider.getViewDelegate().shouldDelayChildPressedState();
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
+    }
+
+    /** @hide */
+    @Override
+    protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+            int l, int t, int r, int b) {
+        mProvider.getViewDelegate().onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+    }
+
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+        mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mProvider.getViewDelegate().onWindowVisibilityChanged(visibility);
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        // Not using short-circuit OR: provider does suppress base-class call.
+        return mProvider.getViewDelegate().drawChild(canvas, child, drawingTime) |
+                super.drawChild(canvas, child, drawingTime);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        mProvider.getViewDelegate().onDraw(canvas);
+    }
+
+    @Override
+    public boolean performLongClick() {
+        return mProvider.getViewDelegate().performLongClick();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        mProvider.getViewDelegate().onConfigurationChanged(newConfig);
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        return mProvider.getViewDelegate().onCreateInputConnection(outAttrs);
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility);
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        mProvider.getViewDelegate().onWindowFocusChanged(hasWindowFocus);
+        super.onWindowFocusChanged(hasWindowFocus);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        mProvider.getViewDelegate().onFocusChanged(focused, direction, previouslyFocusedRect);
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+    }
+
+    /** @hide */
+    @Override
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        return mProvider.getViewDelegate().setFrame(left, top, right, bottom);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int ow, int oh) {
+        super.onSizeChanged(w, h, ow, oh);
+        mProvider.getViewDelegate().onSizeChanged(w, h, ow, oh);
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mProvider.getViewDelegate().onScrollChanged(l, t, oldl, oldt);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        return mProvider.getViewDelegate().dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        return mProvider.getViewDelegate().requestFocus(direction, previouslyFocusedRect);
+    }
+
+    @Deprecated
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mProvider.getViewDelegate().onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
+        return mProvider.getViewDelegate().requestChildRectangleOnScreen(child, rect, immediate);
+    }
+
+    @Override
+    public void setBackgroundColor(int color) {
+        mProvider.getViewDelegate().setBackgroundColor(color);
+    }
 }
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 46c8353..e6cee42 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -84,6 +84,7 @@
 import android.view.SoundEffectConstants;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.View.MeasureSpec;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewParent;
@@ -98,6 +99,8 @@
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.HTML5VideoInline;
 import android.webkit.WebTextView.AutoCompleteAdapter;
+import android.webkit.WebView.HitTestResult;
+import android.webkit.WebView.PictureListener;
 import android.webkit.WebViewCore.DrawData;
 import android.webkit.WebViewCore.EventHub;
 import android.webkit.WebViewCore.TextFieldInitData;
@@ -343,17 +346,19 @@
  * {@link WebChromeClient#getVideoLoadingProgressView()} is optional.
  * </p>
  *
- *
+ * @hide
  */
+// TODO: Remove duplicated API documentation and @hide from fields and methods, and
+// checkThread() call. (All left in for now to ease branch merging.)
+// TODO: Check if any WebView published API methods are called from within here, and if so
+// we should bounce the call out via the proxy to enable any sub-class to override it.
 @Widget
-public class WebView extends AbsoluteLayout
-        implements ViewTreeObserver.OnGlobalFocusChangeListener,
-        ViewGroup.OnHierarchyChangeListener {
-
+public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
+        WebViewProvider.ViewDelegate {
     private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
         @Override
         public void onGlobalLayout() {
-            if (isShown()) {
+            if (mWebView.isShown()) {
                 setGLRectViewport();
             }
         }
@@ -362,7 +367,7 @@
     private class InnerScrollChangedListener implements ViewTreeObserver.OnScrollChangedListener {
         @Override
         public void onScrollChanged() {
-            if (isShown()) {
+            if (mWebView.isShown()) {
                 setGLRectViewport();
             }
         }
@@ -382,7 +387,7 @@
         private int mMaxLength;
 
         public WebViewInputConnection() {
-            super(WebView.this, true);
+            super(mWebView, true);
         }
 
         @Override
@@ -490,17 +495,17 @@
             boolean handled = true;
             switch (editorAction) {
             case EditorInfo.IME_ACTION_NEXT:
-                WebView.this.requestFocus(FOCUS_FORWARD);
+                mWebView.requestFocus(View.FOCUS_FORWARD);
                 break;
             case EditorInfo.IME_ACTION_PREVIOUS:
-                WebView.this.requestFocus(FOCUS_BACKWARD);
+                mWebView.requestFocus(View.FOCUS_BACKWARD);
                 break;
             case EditorInfo.IME_ACTION_DONE:
-                WebView.this.hideSoftKeyboard();
+                WebViewClassic.this.hideSoftKeyboard();
                 break;
             case EditorInfo.IME_ACTION_GO:
             case EditorInfo.IME_ACTION_SEARCH:
-                WebView.this.hideSoftKeyboard();
+                WebViewClassic.this.hideSoftKeyboard();
                 String text = getEditable().toString();
                 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN,
                         KeyEvent.KEYCODE_ENTER));
@@ -686,26 +691,26 @@
             if (imm != null) {
                 // Since the text has changed, do not allow the IME to replace the
                 // existing text as though it were a completion.
-                imm.restartInput(WebView.this);
+                imm.restartInput(mWebView);
             }
         }
     }
 
-    private class PastePopupWindow extends PopupWindow implements OnClickListener {
+    private class PastePopupWindow extends PopupWindow implements View.OnClickListener {
         private ViewGroup mContentView;
         private TextView mPasteTextView;
 
         public PastePopupWindow() {
-            super(WebView.this.mContext, null,
+            super(mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
             setClippingEnabled(true);
-            LinearLayout linearLayout = new LinearLayout(WebView.this.getContext());
+            LinearLayout linearLayout = new LinearLayout(mContext);
             linearLayout.setOrientation(LinearLayout.HORIZONTAL);
             mContentView = linearLayout;
             mContentView.setBackgroundResource(
                     com.android.internal.R.drawable.text_edit_paste_window);
 
-            LayoutInflater inflater = (LayoutInflater)WebView.this.mContext.
+            LayoutInflater inflater = (LayoutInflater)mContext.
                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
             ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams(
@@ -739,7 +744,7 @@
                 x = windowLeft;
             }
             if (!isShowing()) {
-                showAtLocation(WebView.this, Gravity.NO_GRAVITY, x, y);
+                showAtLocation(mWebView, Gravity.NO_GRAVITY, x, y);
             }
             update(x, y, width, height);
         }
@@ -792,29 +797,6 @@
     private int mFieldPointer;
     private PastePopupWindow mPasteWindow;
 
-    /**
-     *  Transportation object for returning WebView across thread boundaries.
-     */
-    public class WebViewTransport {
-        private WebView mWebview;
-
-        /**
-         * Set the WebView to the transportation object.
-         * @param webview The WebView to transport.
-         */
-        public synchronized void setWebView(WebView webview) {
-            mWebview = webview;
-        }
-
-        /**
-         * Return the WebView object.
-         * @return WebView The transported WebView object.
-         */
-        public synchronized WebView getWebView() {
-            return mWebview;
-        }
-    }
-
     private static class OnTrimMemoryListener implements ComponentCallbacks2 {
         private static OnTrimMemoryListener sInstance = null;
 
@@ -848,15 +830,15 @@
             // at native side.
             // Here we just need to clean up the Surface Texture which is static.
             HTML5VideoInline.cleanupSurfaceTexture();
-            WebView.nativeOnTrimMemory(level);
+            WebViewClassic.nativeOnTrimMemory(level);
         }
 
     }
 
     // A final CallbackProxy shared by WebViewCore and BrowserFrame.
-    private final CallbackProxy mCallbackProxy;
+    private CallbackProxy mCallbackProxy;
 
-    private final WebViewDatabase mDatabase;
+    private WebViewDatabase mDatabase;
 
     // SSL certificate for the main top-level page (if secure)
     private SslCertificate mCertificate;
@@ -877,7 +859,7 @@
     /* package */ void incrementTextGeneration() { mTextGeneration++; }
 
     // Used by WebViewCore to create child views.
-    /* package */ final ViewManager mViewManager;
+    /* package */ ViewManager mViewManager;
 
     // Used to display in full screen mode
     PluginFullScreenHolder mFullScreenHolder;
@@ -1373,102 +1355,6 @@
 
     // Used to notify listeners of a new picture.
     private PictureListener mPictureListener;
-    /**
-     * Interface to listen for new pictures as they change.
-     * @deprecated This interface is now obsolete.
-     */
-    @Deprecated
-    public interface PictureListener {
-        /**
-         * Notify the listener that the picture has changed.
-         * @param view The WebView that owns the picture.
-         * @param picture The new picture.
-         * @deprecated Due to internal changes, the picture does not include
-         * composited layers such as fixed position elements or scrollable divs.
-         * While the PictureListener API can still be used to detect changes in
-         * the WebView content, you are advised against its usage until a replacement
-         * is provided in a future Android release
-         */
-        @Deprecated
-        public void onNewPicture(WebView view, Picture picture);
-    }
-
-    public static class HitTestResult {
-        /**
-         * Default HitTestResult, where the target is unknown
-         */
-        public static final int UNKNOWN_TYPE = 0;
-        /**
-         * @deprecated This type is no longer used.
-         */
-        @Deprecated
-        public static final int ANCHOR_TYPE = 1;
-        /**
-         * HitTestResult for hitting a phone number
-         */
-        public static final int PHONE_TYPE = 2;
-        /**
-         * HitTestResult for hitting a map address
-         */
-        public static final int GEO_TYPE = 3;
-        /**
-         * HitTestResult for hitting an email address
-         */
-        public static final int EMAIL_TYPE = 4;
-        /**
-         * HitTestResult for hitting an HTML::img tag
-         */
-        public static final int IMAGE_TYPE = 5;
-        /**
-         * @deprecated This type is no longer used.
-         */
-        @Deprecated
-        public static final int IMAGE_ANCHOR_TYPE = 6;
-        /**
-         * HitTestResult for hitting a HTML::a tag with src=http
-         */
-        public static final int SRC_ANCHOR_TYPE = 7;
-        /**
-         * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
-         */
-        public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
-        /**
-         * HitTestResult for hitting an edit text area
-         */
-        public static final int EDIT_TEXT_TYPE = 9;
-
-        private int mType;
-        private String mExtra;
-
-        HitTestResult() {
-            mType = UNKNOWN_TYPE;
-        }
-
-        private void setType(int type) {
-            mType = type;
-        }
-
-        private void setExtra(String extra) {
-            mExtra = extra;
-        }
-
-        /**
-         * Gets the type of the hit test result.
-         * @return See the XXX_TYPE constants defined in this class.
-         */
-        public int getType() {
-            return mType;
-        }
-
-        /**
-         * Gets additional type-dependant information about the result, see
-         * {@link WebView#getHitTestResult()} for details.
-         * @return may either be null or contain extra information about this result.
-         */
-        public String getExtra() {
-            return mExtra;
-        }
-    }
 
     /**
      * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
@@ -1479,43 +1365,10 @@
         static final String SRC = "src";
     }
 
-    /**
-     * Construct a new WebView with a Context object.
-     * @param context A Context object used to access application assets.
-     */
-    public WebView(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Construct a new WebView with layout parameters.
-     * @param context A Context object used to access application assets.
-     * @param attrs An AttributeSet passed to our parent.
-     */
-    public WebView(Context context, AttributeSet attrs) {
-        this(context, attrs, com.android.internal.R.attr.webViewStyle);
-    }
-
-    /**
-     * Construct a new WebView with layout parameters and a default style.
-     * @param context A Context object used to access application assets.
-     * @param attrs An AttributeSet passed to our parent.
-     * @param defStyle The default style resource ID.
-     */
-    public WebView(Context context, AttributeSet attrs, int defStyle) {
-        this(context, attrs, defStyle, false);
-    }
-
-    /**
-     * Construct a new WebView with layout parameters and a default style.
-     * @param context A Context object used to access application assets.
-     * @param attrs An AttributeSet passed to our parent.
-     * @param defStyle The default style resource ID.
-     * @param privateBrowsing If true the web view will be initialized in private mode.
-     */
-    public WebView(Context context, AttributeSet attrs, int defStyle,
-            boolean privateBrowsing) {
-        this(context, attrs, defStyle, null, privateBrowsing);
+    public WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess) {
+        mWebView = webView;
+        mWebViewPrivate = privateAccess;
+        mContext = webView.getContext();
     }
 
     /**
@@ -1523,22 +1376,16 @@
      * of custom Javscript interfaces to be added to the WebView at initialization
      * time. This guarantees that these interfaces will be available when the JS
      * context is initialized.
-     * @param context A Context object used to access application assets.
-     * @param attrs An AttributeSet passed to our parent.
-     * @param defStyle The default style resource ID.
      * @param javaScriptInterfaces is a Map of interface names, as keys, and
      * object implementing those interfaces, as values.
      * @param privateBrowsing If true the web view will be initialized in private mode.
      * @hide This is an implementation detail.
      */
-    protected WebView(Context context, AttributeSet attrs, int defStyle,
-            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
-        super(context, attrs, defStyle);
+    @Override
+    public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
         checkThread();
 
-        if (context == null) {
-            throw new IllegalArgumentException("Invalid context argument");
-        }
+        Context context = mContext;
 
         // Used by the chrome stack to find application paths
         JniUtil.setContext(context);
@@ -1567,6 +1414,98 @@
         mAutoFillData = new WebViewCore.AutoFillData();
     }
 
+    // === START: WebView Proxy binding ===
+    // Keep the webview proxy / SPI related stuff in this section, to minimize merge conflicts.
+
+    static class Factory implements WebViewFactoryProvider,  WebViewFactoryProvider.Statics {
+        @Override
+        public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
+            return new WebViewClassic(webView, privateAccess);
+        }
+
+        @Override
+        public Statics getStatics() { return this; }
+
+        @Override
+        public String findAddress(String addr) {
+            return WebViewClassic.findAddress(addr);
+        }
+        @Override
+        public void setPlatformNotificationsEnabled(boolean enable) {
+            if (enable) {
+                WebViewClassic.enablePlatformNotifications();
+            } else {
+                WebViewClassic.disablePlatformNotifications();
+            }
+        }
+
+    }
+
+    // The webview that is bound to this WebViewClassic instance. Primarily needed for supplying
+    // as the first param in the WebViewClient and WebChromeClient callbacks.
+    final private WebView mWebView;
+    // Callback interface, provides priviledged access into the WebView instance.
+    final private WebView.PrivateAccess mWebViewPrivate;
+    // Cached reference to mWebView.getContext(), for convenience.
+    final private Context mContext;
+
+    /**
+     * @return The webview proxy that this classic webview is bound to.
+     */
+    public WebView getWebView() {
+        return mWebView;
+    }
+
+    @Override
+    public ViewDelegate getViewDelegate() {
+        return this;
+    }
+
+    @Override
+    public ScrollDelegate getScrollDelegate() {
+        return this;
+    }
+
+    public static WebViewClassic fromWebView(WebView webView) {
+        return webView == null ? null : (WebViewClassic) webView.getWebViewProvider();
+    }
+
+    // Accessors, purely for convenience (and to reduce code churn during webview proxy migration).
+    int getScrollX() {
+        return mWebView.getScrollX();
+    }
+
+    int getScrollY() {
+        return mWebView.getScrollY();
+    }
+
+    int getWidth() {
+        return mWebView.getWidth();
+    }
+
+    int getHeight() {
+        return mWebView.getHeight();
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    void invalidate() {
+        mWebView.invalidate();
+    }
+
+    // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths.
+    void setScrollXRaw(int mScrollX) {
+        mWebViewPrivate.setScrollXRaw(mScrollX);
+    }
+
+    void setScrollYRaw(int mScrollY) {
+        mWebViewPrivate.setScrollYRaw(mScrollY);
+    }
+
+    // === END: WebView Proxy binding ===
+
     private static class TrustStorageListener extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1703,7 +1642,7 @@
          * receiver to ensure that only ONE receiver exists for all WebView
          * instances.
          */
-        synchronized (WebView.class) {
+        synchronized (WebViewClassic.class) {
 
             // if the receiver already exists then we do not need to register it
             // again
@@ -1753,30 +1692,21 @@
         mZoomManager.updateMultiTouchSupport(context);
     }
 
-    // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths.
-    final void setScrollXRaw(int scrollX) {
-        mScrollX = scrollX;
-    }
-
-    final void setScrollYRaw(int scrollY) {
-        mScrollY = scrollY;
-    }
-
     private void init() {
-        OnTrimMemoryListener.init(getContext());
+        OnTrimMemoryListener.init(mContext);
         sDisableNavcache = nativeDisableNavcache();
-        setWillNotDraw(false);
-        setFocusable(true);
-        setFocusableInTouchMode(true);
-        setClickable(true);
-        setLongClickable(true);
+        mWebView.setWillNotDraw(false);
+        mWebView.setFocusable(true);
+        mWebView.setFocusableInTouchMode(true);
+        mWebView.setClickable(true);
+        mWebView.setLongClickable(true);
 
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+        final ViewConfiguration configuration = ViewConfiguration.get(mContext);
         int slop = configuration.getScaledTouchSlop();
         mTouchSlopSquare = slop * slop;
         slop = configuration.getScaledDoubleTapSlop();
         mDoubleTapSlopSquare = slop * slop;
-        final float density = getContext().getResources().getDisplayMetrics().density;
+        final float density = mContext.getResources().getDisplayMetrics().density;
         // use one line height, 16 based on our current default font, for how
         // far we allow a touch be away from the edge of a link
         mNavSlop = (int) (16 * density);
@@ -1789,7 +1719,7 @@
         mOverscrollDistance = configuration.getScaledOverscrollDistance();
         mOverflingDistance = configuration.getScaledOverflingDistance();
 
-        setScrollBarStyle(super.getScrollBarStyle());
+        setScrollBarStyle(mWebViewPrivate.super_getScrollBarStyle());
         // Initially use a size of two, since the user is likely to only hold
         // down two keys at a time (shift + another key)
         mKeysPressed = new Vector<Integer>(2);
@@ -1811,11 +1741,11 @@
         if (AccessibilityManager.getInstance(mContext).isEnabled()
                 && getSettings().getJavaScriptEnabled()) {
             // exposing the TTS for now ...
-            final Context ctx = getContext();
+            final Context ctx = mContext;
             if (ctx != null) {
                 final String packageName = ctx.getPackageName();
                 if (packageName != null) {
-                    mTextToSpeech = new TextToSpeech(getContext(), null, null,
+                    mTextToSpeech = new TextToSpeech(ctx, null, null,
                             packageName + ".**webview**", true);
                     addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
                 }
@@ -1837,33 +1767,34 @@
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
         info.setScrollable(isScrollableForAccessibility());
     }
 
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
         event.setScrollable(isScrollableForAccessibility());
         event.setScrollX(getScrollX());
         event.setScrollY(getScrollY());
         final int convertedContentWidth = contentToViewX(getContentWidth());
-        final int adjustedViewWidth = getWidth() - mPaddingLeft - mPaddingRight;
+        final int adjustedViewWidth = getWidth() - mWebView.getPaddingLeft()
+                - mWebView.getPaddingLeft();
         event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
         final int convertedContentHeight = contentToViewY(getContentHeight());
-        final int adjustedViewHeight = getHeight() - mPaddingTop - mPaddingBottom;
+        final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
+                - mWebView.getPaddingBottom();
         event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
     }
 
     private boolean isScrollableForAccessibility() {
-        return (contentToViewX(getContentWidth()) > getWidth() - mPaddingLeft - mPaddingRight
-                || contentToViewY(getContentHeight()) > getHeight() - mPaddingTop - mPaddingBottom);
+        return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft()
+                - mWebView.getPaddingRight()
+                || contentToViewY(getContentHeight()) > getHeight() - mWebView.getPaddingTop()
+                - mWebView.getPaddingBottom());
     }
 
     @Override
     public void setOverScrollMode(int mode) {
-        super.setOverScrollMode(mode);
-        if (mode != OVER_SCROLL_NEVER) {
+        if (mode != View.OVER_SCROLL_NEVER) {
             if (mOverScrollGlow == null) {
                 mOverScrollGlow = new OverScrollGlow(this);
             }
@@ -1904,7 +1835,7 @@
             neverRemember.getData().putString("password", password);
             neverRemember.obj = resumeMsg;
 
-            new AlertDialog.Builder(getContext())
+            new AlertDialog.Builder(mContext)
                     .setTitle(com.android.internal.R.string.save_password_label)
                     .setMessage(com.android.internal.R.string.save_password_message)
                     .setPositiveButton(com.android.internal.R.string.save_password_notnow,
@@ -1949,7 +1880,6 @@
         } else {
             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
         }
-        super.setScrollBarStyle(style);
     }
 
     /**
@@ -1994,19 +1924,28 @@
      * Note: this can be called from WebCoreThread.
      */
     /* package */ int getViewWidth() {
-        if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
+        if (!mWebView.isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
             return getWidth();
         } else {
-            return Math.max(0, getWidth() - getVerticalScrollbarWidth());
+            return Math.max(0, getWidth() - mWebView.getVerticalScrollbarWidth());
         }
     }
 
+    // Interface to enable the browser to override title bar handling.
+    public interface TitleBarDelegate {
+        int getTitleHeight();
+        public void onSetEmbeddedTitleBar(final View title);
+    }
+
     /**
      * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
      * scrolling
      * @hide
      */
     protected int getTitleHeight() {
+        if (mWebView instanceof TitleBarDelegate) {
+            return ((TitleBarDelegate) mWebView).getTitleHeight();
+        }
         return mTitleBar != null ? mTitleBar.getHeight() : 0;
     }
 
@@ -2037,7 +1976,7 @@
             return 0;
         }
         if (mCachedOverlappingActionModeHeight < 0) {
-            getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
+            mWebView.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
             mCachedOverlappingActionModeHeight = Math.max(0,
                     mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
         }
@@ -2055,8 +1994,8 @@
 
     int getViewHeightWithTitle() {
         int height = getHeight();
-        if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
-            height -= getHorizontalScrollbarHeight();
+        if (mWebView.isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
+            height -= mWebViewPrivate.getHorizontalScrollbarHeight();
         }
         return height;
     }
@@ -2215,7 +2154,7 @@
     @Deprecated
     public static void enablePlatformNotifications() {
         checkThread();
-        synchronized (WebView.class) {
+        synchronized (WebViewClassic.class) {
             sNotificationsEnabled = true;
             Context context = JniUtil.getContext();
             if (context != null)
@@ -2232,7 +2171,7 @@
     @Deprecated
     public static void disablePlatformNotifications() {
         checkThread();
-        synchronized (WebView.class) {
+        synchronized (WebViewClassic.class) {
             sNotificationsEnabled = false;
             Context context = JniUtil.getContext();
             if (context != null)
@@ -3268,12 +3207,15 @@
      * @hide
      */
     public void setEmbeddedTitleBar(View v) {
+        if (mWebView instanceof TitleBarDelegate) {
+            ((TitleBarDelegate) mWebView).onSetEmbeddedTitleBar(v);
+        }
         if (mTitleBar == v) return;
         if (mTitleBar != null) {
-            removeView(mTitleBar);
+            mWebView.removeView(mTitleBar);
         }
         if (null != v) {
-            addView(v, new AbsoluteLayout.LayoutParams(
+            mWebView.addView(v, new AbsoluteLayout.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
         }
@@ -3387,10 +3329,10 @@
     private void viewInvalidate(int l, int t, int r, int b) {
         final float scale = mZoomManager.getScale();
         final int dy = getTitleHeight();
-        invalidate((int)Math.floor(l * scale),
-                   (int)Math.floor(t * scale) + dy,
-                   (int)Math.ceil(r * scale),
-                   (int)Math.ceil(b * scale) + dy);
+        mWebView.invalidate((int)Math.floor(l * scale),
+                (int)Math.floor(t * scale) + dy,
+                (int)Math.ceil(r * scale),
+                (int)Math.ceil(b * scale) + dy);
     }
 
     // Called by JNI to invalidate the View after a delay, given rectangle
@@ -3398,11 +3340,11 @@
     private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
         final float scale = mZoomManager.getScale();
         final int dy = getTitleHeight();
-        postInvalidateDelayed(delay,
-                              (int)Math.floor(l * scale),
-                              (int)Math.floor(t * scale) + dy,
-                              (int)Math.ceil(r * scale),
-                              (int)Math.ceil(b * scale) + dy);
+        mWebView.postInvalidateDelayed(delay,
+                (int)Math.floor(l * scale),
+                (int)Math.floor(t * scale) + dy,
+                (int)Math.ceil(r * scale),
+                (int)Math.ceil(b * scale) + dy);
     }
 
     private void invalidateContentRect(Rect r) {
@@ -3467,7 +3409,7 @@
             mLastVisibleRectSent.set(mVisibleRect);
             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
         }
-        if (getGlobalVisibleRect(mGlobalVisibleRect)
+        if (mWebView.getGlobalVisibleRect(mGlobalVisibleRect)
                 && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
             if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
@@ -3488,7 +3430,7 @@
     private Point mGlobalVisibleOffset = new Point();
     // Sets r to be the visible rectangle of our webview in view coordinates
     private void calcOurVisibleRect(Rect r) {
-        getGlobalVisibleRect(r, mGlobalVisibleOffset);
+        mWebView.getGlobalVisibleRect(r, mGlobalVisibleOffset);
         r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
     }
 
@@ -3610,7 +3552,7 @@
     }
 
     @Override
-    protected int computeHorizontalScrollRange() {
+    public int computeHorizontalScrollRange() {
         int range = computeRealHorizontalScrollRange();
 
         // Adjust reported range if overscrolled to compress the scroll bars
@@ -3626,7 +3568,7 @@
     }
 
     @Override
-    protected int computeHorizontalScrollOffset() {
+    public int computeHorizontalScrollOffset() {
         return Math.max(getScrollX(), 0);
     }
 
@@ -3640,7 +3582,7 @@
     }
 
     @Override
-    protected int computeVerticalScrollRange() {
+    public int computeVerticalScrollRange() {
         int range = computeRealVerticalScrollRange();
 
         // Adjust reported range if overscrolled to compress the scroll bars
@@ -3656,18 +3598,18 @@
     }
 
     @Override
-    protected int computeVerticalScrollOffset() {
+    public int computeVerticalScrollOffset() {
         return Math.max(getScrollY() - getTitleHeight(), 0);
     }
 
     @Override
-    protected int computeVerticalScrollExtent() {
+    public int computeVerticalScrollExtent() {
         return getViewHeight();
     }
 
     /** @hide */
     @Override
-    protected void onDrawVerticalScrollBar(Canvas canvas,
+    public void onDrawVerticalScrollBar(Canvas canvas,
                                            Drawable scrollBar,
                                            int l, int t, int r, int b) {
         if (getScrollY() < 0) {
@@ -3678,7 +3620,7 @@
     }
 
     @Override
-    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
+    public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
             boolean clampedY) {
         // Special-case layer scrolling so that we do not trigger normal scroll
         // updating.
@@ -3702,7 +3644,7 @@
         int oldX = getScrollX();
         int oldY = getScrollY();
 
-        super.scrollTo(scrollX, scrollY);
+        mWebViewPrivate.super_scrollTo(scrollX, scrollY);
 
         if (mOverScrollGlow != null) {
             mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY);
@@ -3847,16 +3789,15 @@
     }
 
     @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
+    public void onWindowVisibilityChanged(int visibility) {
         updateDrawingState();
     }
 
     void updateDrawingState() {
         if (mNativeClass == 0 || mIsPaused) return;
-        if (getWindowVisibility() != VISIBLE) {
+        if (mWebView.getWindowVisibility() != View.VISIBLE) {
             nativeSetPauseDrawing(mNativeClass, true);
-        } else if (getVisibility() != VISIBLE) {
+        } else if (mWebView.getVisibility() != View.VISIBLE) {
             nativeSetPauseDrawing(mNativeClass, true);
         } else {
             nativeSetPauseDrawing(mNativeClass, false);
@@ -4030,7 +3971,7 @@
     public boolean showFindDialog(String text, boolean showIme) {
         checkThread();
         FindActionModeCallback callback = new FindActionModeCallback(mContext);
-        if (getParent() == null || startActionMode(callback) == null) {
+        if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) {
             // Could not start the action mode, so end Find on page
             return false;
         }
@@ -4206,7 +4147,7 @@
                     overflingDistance = 0;
                 }
 
-                overScrollBy(x - oldX, y - oldY, oldX, oldY,
+                mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY,
                         rangeX, rangeY,
                         overflingDistance, overflingDistance, false);
 
@@ -4234,7 +4175,7 @@
                 }
             }
         } else {
-            super.computeScroll();
+            mWebViewPrivate.super_computeScroll();
         }
     }
 
@@ -4257,7 +4198,7 @@
         mScrollingLayerRect.top = y;
         mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
                 mScrollingLayerRect);
-        onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
+        mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
         invalidate();
     }
 
@@ -4288,10 +4229,10 @@
             //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
             mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
                     animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
-            awakenScrollBars(mScroller.getDuration());
+            mWebViewPrivate.awakenScrollBars(mScroller.getDuration());
             invalidate();
         } else {
-            scrollTo(x, y);
+            mWebView.scrollTo(x, y);
         }
         return true;
     }
@@ -4314,7 +4255,7 @@
                 Rect tempRect = new Rect();
                 calcOurVisibleRect(tempRect);
                 tempRect.offset(cx, cy);
-                requestRectangleOnScreen(tempRect);
+                mWebView.requestRectangleOnScreen(tempRect);
             }
             // FIXME: We scroll horizontally no matter what because currently
             // ScrollView and ListView will not scroll horizontally.
@@ -4337,7 +4278,7 @@
         // will reload it and get a new certificate set;
         // if the new site is not secure, the certificate must be
         // null, and that will be the case
-        setCertificate(null);
+        mWebView.setCertificate(null);
 
         // reset the flag since we set to true in if need after
         // loading is see onPageFinished(Url)
@@ -4407,7 +4348,7 @@
             if (onDeviceScriptInjectionEnabled) {
                 ensureAccessibilityScriptInjectorInstance(false);
                 // neither script injected nor script injection opted out => we inject
-                loadUrl(getScreenReaderInjectingJs());
+                mWebView.loadUrl(getScreenReaderInjectingJs());
                 // TODO: Set this flag after successfull script injection. Maybe upon injection
                 // the chooser should update the meta tag and we check it to declare success
                 mAccessibilityScriptInjected = true;
@@ -4421,7 +4362,7 @@
         } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
             ensureAccessibilityScriptInjectorInstance(false);
             // the URL provides accessibility but we still need to add our generic script
-            loadUrl(getScreenReaderInjectingJs());
+            mWebView.loadUrl(getScreenReaderInjectingJs());
         } else {
             Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
         }
@@ -4573,14 +4514,14 @@
         }
 
         if (mHeightCanMeasure) {
-            if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
+            if (mWebView.getMeasuredHeight() != contentToViewDimension(mContentHeight)
                     || updateLayout) {
-                requestLayout();
+                mWebView.requestLayout();
             }
         } else if (mWidthCanMeasure) {
-            if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
+            if (mWebView.getMeasuredWidth() != contentToViewDimension(mContentWidth)
                     || updateLayout) {
-                requestLayout();
+                mWebView.requestLayout();
             }
         } else {
             // If we don't request a layout, try to send our view size to the
@@ -4744,7 +4685,7 @@
      * @return A WebSettings object that can be used to control this WebView's
      *         settings.
      */
-    public WebSettings getSettings() {
+    public WebSettingsClassic getSettings() {
         checkThread();
         return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
     }
@@ -4792,7 +4733,7 @@
     }
 
     @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
         if (child == mTitleBar) {
             // When drawing the title bar, move it horizontally to always show
             // at the top of the WebView.
@@ -4806,7 +4747,7 @@
             mTitleBar.setBottom(newTop + mTitleBar.getHeight());
             mTitleBar.setTop(newTop);
         }
-        return super.drawChild(canvas, child, drawingTime);
+        return false;  // We never call invalidate(), so unconditionally returning false.
     }
 
     private void drawContent(Canvas canvas, boolean drawRings) {
@@ -4849,7 +4790,7 @@
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
+    public void onDraw(Canvas canvas) {
         if (inFullScreenMode()) {
             return; // no need to draw anything if we aren't visible.
         }
@@ -4933,10 +4874,10 @@
 
     @Override
     public void setLayoutParams(ViewGroup.LayoutParams params) {
-        if (params.height == LayoutParams.WRAP_CONTENT) {
+        if (params.height == AbsoluteLayout.LayoutParams.WRAP_CONTENT) {
             mWrapContent = true;
         }
-        super.setLayoutParams(params);
+        mWebViewPrivate.super_setLayoutParams(params);
     }
 
     @Override
@@ -4944,7 +4885,7 @@
         // performLongClick() is the result of a delayed message. If we switch
         // to windows overview, the WebView will be temporarily removed from the
         // view system. In that case, do nothing.
-        if (getParent() == null) return false;
+        if (mWebView.getParent() == null) return false;
 
         // A multi-finger gesture can look like a long press; make sure we don't take
         // long press actions if we're scaling.
@@ -4979,7 +4920,7 @@
         /* if long click brings up a context menu, the super function
          * returns true and we're done. Otherwise, nothing happened when
          * the user clicked. */
-        if (super.performLongClick()) {
+        if (mWebViewPrivate.super_performLongClick()) {
             return true;
         }
         /* In the case where the application hasn't already handled the long
@@ -4988,12 +4929,12 @@
          * FIXME: no animation code yet */
         final boolean isSelecting = selectText();
         if (isSelecting) {
-            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         } else if (focusCandidateIsEditableText()) {
             mSelectCallback = new SelectActionModeCallback();
             mSelectCallback.setWebView(this);
             mSelectCallback.setTextSelected(false);
-            startActionMode(mSelectCallback);
+            mWebView.startActionMode(mSelectCallback);
         }
         return isSelecting;
     }
@@ -5020,7 +4961,7 @@
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(Configuration newConfig) {
         mCachedOverlappingActionModeHeight = -1;
         if (mSelectingText && mOrientation != newConfig.orientation) {
             selectionDone();
@@ -5131,7 +5072,7 @@
     private void onZoomAnimationStart() {
         // If it is in password mode, turn it off so it does not draw misplaced.
         if (inEditingMode()) {
-            mWebTextView.setVisibility(INVISIBLE);
+            mWebTextView.setVisibility(View.INVISIBLE);
         }
     }
 
@@ -5141,7 +5082,7 @@
                 && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)) {
             // If it is a password field, start drawing the WebTextView once
             // again.
-            mWebTextView.setVisibility(VISIBLE);
+            mWebTextView.setVisibility(View.VISIBLE);
         }
     }
 
@@ -5381,7 +5322,7 @@
             setScrollXRaw(pinLocX(getScrollX()));
             setScrollYRaw(pinLocY(getScrollY()));
             if (oldScrollX != getScrollX() || oldScrollY != getScrollY()) {
-                onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY);
+                mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY);
             } else {
                 sendOurVisibleRect();
             }
@@ -5449,7 +5390,7 @@
      */
     private void displaySoftKeyboard(boolean isTextView) {
         InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
 
         // bring it back to the default level scale so that user can enter text
         boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
@@ -5472,15 +5413,15 @@
         // does not recognize that a textfield is in focus.  In that
         // case, use WebView as the targeted view.
         // see http://b/issue?id=2457459
-        imm.showSoftInput(this, 0);
+        imm.showSoftInput(mWebView, 0);
     }
 
     // Called by WebKit to instruct the UI to hide the keyboard
     private void hideSoftKeyboard() {
         InputMethodManager imm = InputMethodManager.peekInstance();
-        if (imm != null && (imm.isActive(this)
+        if (imm != null && (imm.isActive(mWebView)
                 || (inEditingMode() && imm.isActive(mWebTextView)))) {
-            imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+            imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
         }
     }
 
@@ -5494,7 +5435,7 @@
             return; // always use WebKit's text entry
         }
         // If the WebView does not have focus, do nothing until it gains focus.
-        if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
+        if (!mWebView.hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
             return;
         }
         boolean alreadyThere = inEditingMode();
@@ -5509,7 +5450,7 @@
         // At this point, we know we have found an input field, so go ahead
         // and create the WebTextView if necessary.
         if (mWebTextView == null) {
-            mWebTextView = new WebTextView(mContext, WebView.this, mAutoFillData.getQueryId());
+            mWebTextView = new WebTextView(mContext, WebViewClassic.this, mAutoFillData.getQueryId());
             // Initialize our generation number.
             mTextGeneration = 0;
         }
@@ -5535,7 +5476,7 @@
             imm.restartInput(mWebTextView);
             mWebTextView.clearComposingText();
         }
-        if (isFocused()) {
+        if (mWebView.isFocused()) {
             mWebTextView.requestFocus();
         }
     }
@@ -5626,7 +5567,7 @@
         private Message mUpdateMessage;
         private boolean mAutoFillable;
         private boolean mAutoComplete;
-        private WebSettings mWebSettings;
+        private WebSettingsClassic mWebSettings;
 
         public RequestFormData(String name, String url, Message msg,
                 boolean autoFillable, boolean autoComplete) {
@@ -5647,7 +5588,7 @@
                 // on the AutoFill item being at the top of the drop down list. If you change
                 // the order, make sure to do it there too!
                 if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
-                    pastEntries.add(getResources().getText(
+                    pastEntries.add(mWebView.getResources().getText(
                             com.android.internal.R.string.autofill_this_form).toString() +
                             " " +
                             mAutoFillData.getPreviewString());
@@ -5655,7 +5596,7 @@
                 } else {
                     // There is no autofill profile set up yet, so add an option that
                     // will invite the user to set their profile up.
-                    pastEntries.add(getResources().getText(
+                    pastEntries.add(mWebView.getResources().getText(
                             com.android.internal.R.string.setup_autofill).toString());
                     mWebTextView.setAutoFillProfileIsSet(false);
                 }
@@ -5869,7 +5810,7 @@
                 }
             }
             if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
-                playSoundEffect(keyCodeToSoundsEffect(keyCode));
+                mWebView.playSoundEffect(keyCodeToSoundsEffect(keyCode));
                 return true;
             }
             // Bubble up the key event as WebView doesn't handle it
@@ -5964,7 +5905,7 @@
             if (!nativeCursorIsTextInput() && text != null
                     && text.startsWith(SCHEME_TEL)) {
                 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
-                getContext().startActivity(intent);
+                mContext.startActivity(intent);
                 return true;
             }
         }
@@ -6035,7 +5976,7 @@
                 }
                 WebViewCore.CursorData data = cursorData();
                 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
-                playSoundEffect(SoundEffectConstants.CLICK);
+                mWebView.playSoundEffect(SoundEffectConstants.CLICK);
                 if (nativeCursorIsTextInput()) {
                     rebuildWebTextView();
                     centerKeyPressOnTextField();
@@ -6073,13 +6014,13 @@
         mSelectCallback = new SelectActionModeCallback();
         mSelectCallback.setTextSelected(!mIsCaretSelection);
         mSelectCallback.setWebView(this);
-        if (startActionMode(mSelectCallback) == null) {
+        if (mWebView.startActionMode(mSelectCallback) == null) {
             // There is no ActionMode, so do not allow the user to modify a
             // selection.
             selectionDone();
             return false;
         }
-        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         return true;
     }
 
@@ -6089,8 +6030,8 @@
         if (cm.hasPrimaryClip()) {
             Rect cursorRect = contentToViewRect(mSelectCursorBase);
             int[] location = new int[2];
-            getLocationInWindow(location);
-            cursorRect.offset(location[0] - mScrollX, location[1] - mScrollY);
+            mWebView.getLocationInWindow(location);
+            cursorRect.offset(location[0] - getScrollX(), location[1] - getScrollY());
             if (mPasteWindow == null) {
                 mPasteWindow = new PastePopupWindow();
             }
@@ -6209,7 +6150,7 @@
                     , com.android.internal.R.string.text_copied
                     , Toast.LENGTH_SHORT).show();
             copiedSomething = true;
-            ClipboardManager cm = (ClipboardManager)getContext()
+            ClipboardManager cm = (ClipboardManager)mContext
                     .getSystemService(Context.CLIPBOARD_SERVICE);
             cm.setText(selection);
             int[] handles = new int[4];
@@ -6238,7 +6179,7 @@
      * @hide This is an implementation detail
      */
     public void pasteFromClipboard() {
-        ClipboardManager cm = (ClipboardManager)getContext()
+        ClipboardManager cm = (ClipboardManager)mContext
                 .getSystemService(Context.CLIPBOARD_SERVICE);
         ClipData clipData = cm.getPrimaryClip();
         if (clipData != null) {
@@ -6269,10 +6210,9 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (hasWindowFocus()) setActive(true);
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
+    public void onAttachedToWindow() {
+        if (mWebView.hasWindowFocus()) setActive(true);
+        final ViewTreeObserver treeObserver = mWebView.getViewTreeObserver();
         if (mGlobalLayoutListener == null) {
             mGlobalLayoutListener = new InnerGlobalLayoutListener();
             treeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
@@ -6288,12 +6228,12 @@
     }
 
     @Override
-    protected void onDetachedFromWindow() {
+    public void onDetachedFromWindow() {
         clearHelpers();
         mZoomManager.dismissZoomPicker();
-        if (hasWindowFocus()) setActive(false);
+        if (mWebView.hasWindowFocus()) setActive(false);
 
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
+        final ViewTreeObserver treeObserver = mWebView.getViewTreeObserver();
         if (mGlobalLayoutListener != null) {
             treeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
             mGlobalLayoutListener = null;
@@ -6304,13 +6244,10 @@
         }
 
         removeAccessibilityApisFromJavaScript();
-
-        super.onDetachedFromWindow();
     }
 
     @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
+    public void onVisibilityChanged(View changedView, int visibility) {
         // The zoomManager may be null if the webview is created from XML that
         // specifies the view's visibility param as not visible (see http://b/2794841)
         if (visibility != View.VISIBLE && mZoomManager != null) {
@@ -6319,37 +6256,9 @@
         updateDrawingState();
     }
 
-    /**
-     * @deprecated WebView no longer needs to implement
-     * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
-     */
-    @Override
-    // Cannot add @hide as this can always be accessed via the interface.
-    @Deprecated
-    public void onChildViewAdded(View parent, View child) {}
-
-    /**
-     * @deprecated WebView no longer needs to implement
-     * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
-     */
-    @Override
-    // Cannot add @hide as this can always be accessed via the interface.
-    @Deprecated
-    public void onChildViewRemoved(View p, View child) {}
-
-    /**
-     * @deprecated WebView should not have implemented
-     * ViewTreeObserver.OnGlobalFocusChangeListener. This method does nothing now.
-     */
-    @Override
-    // Cannot add @hide as this can always be accessed via the interface.
-    @Deprecated
-    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-    }
-
     void setActive(boolean active) {
         if (active) {
-            if (hasFocus()) {
+            if (mWebView.hasFocus()) {
                 // If our window regained focus, and we have focus, then begin
                 // drawing the cursor ring
                 mDrawCursorRing = !inEditingMode();
@@ -6404,7 +6313,6 @@
                 mPictureUpdatePausedForFocusChange = true;
             }
         }
-        super.onWindowFocusChanged(hasWindowFocus);
     }
 
     /*
@@ -6424,7 +6332,7 @@
     }
 
     @Override
-    protected void onFocusChanged(boolean focused, int direction,
+    public void onFocusChanged(boolean focused, int direction,
             Rect previouslyFocusedRect) {
         if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
@@ -6432,7 +6340,7 @@
         if (focused) {
             // When we regain focus, if we have window focus, resume drawing
             // the cursor ring
-            if (hasWindowFocus()) {
+            if (mWebView.hasWindowFocus()) {
                 mDrawCursorRing = !inEditingMode();
                 setFocusControllerActive(true);
             //} else {
@@ -6449,18 +6357,16 @@
             }
             mKeysPressed.clear();
         }
-
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
     }
 
     void setGLRectViewport() {
         // Use the getGlobalVisibleRect() to get the intersection among the parents
         // visible == false means we're clipped - send a null rect down to indicate that
         // we should not draw
-        boolean visible = getGlobalVisibleRect(mGLRectViewport);
+        boolean visible = mWebView.getGlobalVisibleRect(mGLRectViewport);
         if (visible) {
             // Then need to invert the Y axis, just for GL
-            View rootView = getRootView();
+            View rootView = mWebView.getRootView();
             int rootViewHeight = rootView.getHeight();
             mViewRectViewport.set(mGLRectViewport);
             int savedWebViewBottom = mGLRectViewport.bottom;
@@ -6480,8 +6386,8 @@
      * @hide
      */
     @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        boolean changed = super.setFrame(left, top, right, bottom);
+    public boolean setFrame(int left, int top, int right, int bottom) {
+        boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom);
         if (!changed && mHeightCanMeasure) {
             // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
             // in WebViewCore after we get the first layout. We do call
@@ -6496,9 +6402,7 @@
     }
 
     @Override
-    protected void onSizeChanged(int w, int h, int ow, int oh) {
-        super.onSizeChanged(w, h, ow, oh);
-
+    public void onSizeChanged(int w, int h, int ow, int oh) {
         // adjust the max viewport width depending on the view dimensions. This
         // is to ensure the scaling is not going insane. So do not shrink it if
         // the view size is temporarily smaller, e.g. when soft keyboard is up.
@@ -6518,8 +6422,7 @@
     }
 
     @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
+    public void onScrollChanged(int l, int t, int oldl, int oldt) {
         if (!mInOverScrollMode) {
             sendOurVisibleRect();
             // update WebKit if visible title bar height changed. The logic is same
@@ -6562,7 +6465,7 @@
             // not currently have a bounds.
             return mWebTextView.dispatchKeyEvent(event);
         } else {
-            return super.dispatchKeyEvent(event);
+            return mWebViewPrivate.super_dispatchKeyEvent(event);
         }
     }
 
@@ -6674,7 +6577,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
+        if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) {
             return false;
         }
 
@@ -6894,7 +6797,7 @@
                     if (DebugFlags.WEB_VIEW) {
                         Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
                     }
-                    ViewParent parent = getParent();
+                    ViewParent parent = mWebView.getParent();
                     if (parent != null) {
                         parent.requestDisallowInterceptTouchEvent(true);
                     }
@@ -7062,7 +6965,7 @@
                         invalidate();
                     }
                     // keep the scrollbar on the screen even there is no scroll
-                    awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
+                    mWebViewPrivate.awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
                             false);
                     // Post a message so that we'll keep them alive while we're not scrolling.
                     mPrivateHandler.sendMessageDelayed(mPrivateHandler
@@ -7077,7 +6980,7 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
-                if (!isFocused()) requestFocus();
+                if (!mWebView.isFocused()) mWebView.requestFocus();
                 // pass the touch events from UI thread to WebCore thread
                 if (shouldForwardTouchEvent()) {
                     TouchEventData ted = new TouchEventData();
@@ -7268,7 +7171,7 @@
         ted.mSequence = sequence;
         mTouchEventQueue.preQueueTouchEventData(ted);
         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-        cancelLongPress();
+        mWebView.cancelLongPress();
         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
     }
 
@@ -7301,7 +7204,7 @@
                 x = detector.getFocusX();
                 y = detector.getFocusY();
 
-                cancelLongPress();
+                mWebView.cancelLongPress();
                 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
                 if (!mZoomManager.supportsPanDuringZoom()) {
                     return;
@@ -7433,7 +7336,7 @@
                 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
             }
 
-            overScrollBy(deltaX, deltaY, oldX, oldY,
+            mWebViewPrivate.overScrollBy(deltaX, deltaY, oldX, oldY,
                     rangeX, rangeY,
                     mOverscrollDistance, mOverscrollDistance, true);
             if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
@@ -7517,8 +7420,10 @@
                         hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
                     }
                     if (hscroll != 0 || vscroll != 0) {
-                        final int vdelta = (int) (vscroll * getVerticalScrollFactor());
-                        final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
+                        final int vdelta = (int) (vscroll *
+                                mWebViewPrivate.getVerticalScrollFactor());
+                        final int hdelta = (int) (hscroll *
+                                mWebViewPrivate.getHorizontalScrollFactor());
                         if (pinScrollBy(hdelta, vdelta, false, 0)) {
                             return true;
                         }
@@ -7526,7 +7431,7 @@
                 }
             }
         }
-        return super.onGenericMotionEvent(event);
+        return mWebViewPrivate.super_onGenericMotionEvent(event);
     }
 
     private long mTrackballFirstTime = 0;
@@ -7596,7 +7501,7 @@
                         + " time=" + time
                         + " mLastCursorTime=" + mLastCursorTime);
             }
-            if (isInTouchMode()) requestFocusFromTouch();
+            if (mWebView.isInTouchMode()) mWebView.requestFocusFromTouch();
             return false; // let common code in onKeyDown at it
         }
         if (ev.getAction() == MotionEvent.ACTION_UP) {
@@ -7740,7 +7645,7 @@
                 }
                 letPageHandleNavKey(selectKeyCode, time, false, metaState);
             } else if (navHandledKey(selectKeyCode, count, false, time)) {
-                playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
+                mWebView.playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
             }
             mTrackballRemainsX = mTrackballRemainsY = 0;
         }
@@ -7790,7 +7695,7 @@
         setScrollXRaw(x);
         setScrollYRaw(y);
         if (oldX != getScrollX() || oldY != getScrollY()) {
-            onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
+            mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
             return true;
         } else {
             return false;
@@ -7908,7 +7813,7 @@
 
         // Suppress scrollbars for layer scrolling.
         if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-            awakenScrollBars(time);
+            mWebViewPrivate.awakenScrollBars(time);
         }
 
         invalidate();
@@ -8104,7 +8009,7 @@
             // set mTouchHighlightRequested to 0 to cause an immediate
             // drawing of the touch rings
             mTouchHighlightRequested = 0;
-            invalidate(mTouchHighlightRegion.getBounds());
+            mWebView.invalidate(mTouchHighlightRegion.getBounds());
             mPrivateHandler.postDelayed(new Runnable() {
                 @Override
                 public void run() {
@@ -8113,7 +8018,7 @@
             }, ViewConfiguration.getPressedStateDuration());
         }
         if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
-            playSoundEffect(SoundEffectConstants.CLICK);
+            mWebView.playSoundEffect(SoundEffectConstants.CLICK);
             overrideLoading(mFocusedNode.mIntentUrl);
         } else if (sDisableNavcache) {
             WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
@@ -8142,7 +8047,7 @@
             EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
         }
         if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
-            playSoundEffect(SoundEffectConstants.CLICK);
+            mWebView.playSoundEffect(SoundEffectConstants.CLICK);
         }
     }
 
@@ -8250,8 +8155,8 @@
             result = mWebTextView.requestFocus(direction,
                     previouslyFocusedRect);
         } else {
-            result = super.requestFocus(direction, previouslyFocusedRect);
-            if (mWebViewCore.getSettings().getNeedInitialFocus() && !isInTouchMode()) {
+            result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
+            if (mWebViewCore.getSettings().getNeedInitialFocus() && !mWebView.isInTouchMode()) {
                 // For cases such as GMail, where we gain focus from a direction,
                 // we want to move to the first available link.
                 // FIXME: If there are no visible links, we may not want to
@@ -8281,9 +8186,7 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
@@ -8307,7 +8210,7 @@
                 if (measuredHeight > heightSize) {
                     measuredHeight = heightSize;
                     mHeightCanMeasure = false;
-                    measuredHeight |= MEASURED_STATE_TOO_SMALL;
+                    measuredHeight |= View.MEASURED_STATE_TOO_SMALL;
                 }
             }
         } else {
@@ -8322,13 +8225,13 @@
             measuredWidth = contentWidth;
         } else {
             if (measuredWidth < contentWidth) {
-                measuredWidth |= MEASURED_STATE_TOO_SMALL;
+                measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
             }
             mWidthCanMeasure = false;
         }
 
         synchronized (this) {
-            setMeasuredDimension(measuredWidth, measuredHeight);
+            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
         }
     }
 
@@ -8351,7 +8254,7 @@
         Rect content = new Rect(viewToContentX(getScrollX()),
                 viewToContentY(getScrollY()),
                 viewToContentX(getScrollX() + getWidth()
-                - getVerticalScrollbarWidth()),
+                - mWebView.getVerticalScrollbarWidth()),
                 viewToContentY(getScrollY() + getViewHeightWithTitle()));
         content = nativeSubtractLayers(content);
         int screenTop = contentToViewY(content.top);
@@ -8901,7 +8804,7 @@
                             break;
                         case WebViewCore.ACTION_LONGPRESS:
                             HitTestResult hitTest = getHitTestResult();
-                            if (hitTest != null && hitTest.mType
+                            if (hitTest != null && hitTest.getType()
                                     != HitTestResult.UNKNOWN_TYPE) {
                                 performLongClick();
                             }
@@ -9059,7 +8962,7 @@
                         // view, but is only necessary if the IME is showing
                         InputMethodManager imm = InputMethodManager.peekInstance();
                         if (imm == null || !imm.isAcceptingText()
-                                || (!imm.isActive(WebView.this) && (!inEditingMode()
+                                || (!imm.isActive(mWebView) && (!inEditingMode()
                                 || !imm.isActive(mWebTextView)))) {
                             break;
                         }
@@ -9226,7 +9129,7 @@
                 case AWAKEN_SCROLL_BARS:
                     if (mTouchMode == TOUCH_DRAG_MODE
                             && mHeldMotionless == MOTIONLESS_TRUE) {
-                        awakenScrollBars(ViewConfiguration
+                        mWebViewPrivate.awakenScrollBars(ViewConfiguration
                                 .getScrollDefaultDelay(), false);
                         mPrivateHandler.sendMessageDelayed(mPrivateHandler
                                 .obtainMessage(AWAKEN_SCROLL_BARS),
@@ -9239,7 +9142,7 @@
                     break;
 
                 case SCREEN_ON:
-                    setKeepScreenOn(msg.arg1 == 1);
+                    mWebView.setKeepScreenOn(msg.arg1 == 1);
                     break;
 
                 case ENTER_FULLSCREEN_VIDEO:
@@ -9266,7 +9169,7 @@
                         Log.w(LOGTAG, "Should not have another full screen.");
                         dismissFullScreenMode();
                     }
-                    mFullScreenHolder = new PluginFullScreenHolder(WebView.this, orientation, npp);
+                    mFullScreenHolder = new PluginFullScreenHolder(WebViewClassic.this, orientation, npp);
                     mFullScreenHolder.setContentView(view);
                     mFullScreenHolder.show();
                     invalidate();
@@ -9329,7 +9232,7 @@
                     // We need to take into account the visible title height
                     // when scrolling since y is an absolute view position.
                     y = Math.max(0, y - getVisibleTitleHeightImpl());
-                    scrollTo(x, y);
+                    mWebView.scrollTo(x, y);
                     }
                     break;
 
@@ -9432,24 +9335,24 @@
     private void setHitTestTypeFromUrl(String url) {
         String substr = null;
         if (url.startsWith(SCHEME_GEO)) {
-            mInitialHitTestResult.mType = HitTestResult.GEO_TYPE;
+            mInitialHitTestResult.setType(HitTestResult.GEO_TYPE);
             substr = url.substring(SCHEME_GEO.length());
         } else if (url.startsWith(SCHEME_TEL)) {
-            mInitialHitTestResult.mType = HitTestResult.PHONE_TYPE;
+            mInitialHitTestResult.setType(HitTestResult.PHONE_TYPE);
             substr = url.substring(SCHEME_TEL.length());
         } else if (url.startsWith(SCHEME_MAILTO)) {
-            mInitialHitTestResult.mType = HitTestResult.EMAIL_TYPE;
+            mInitialHitTestResult.setType(HitTestResult.EMAIL_TYPE);
             substr = url.substring(SCHEME_MAILTO.length());
         } else {
-            mInitialHitTestResult.mType = HitTestResult.SRC_ANCHOR_TYPE;
-            mInitialHitTestResult.mExtra = url;
+            mInitialHitTestResult.setType(HitTestResult.SRC_ANCHOR_TYPE);
+            mInitialHitTestResult.setExtra(url);
             return;
         }
         try {
-            mInitialHitTestResult.mExtra = URLDecoder.decode(substr, "UTF-8");
+            mInitialHitTestResult.setExtra(URLDecoder.decode(substr, "UTF-8"));
         } catch (Throwable e) {
             Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
-            mInitialHitTestResult.mType = HitTestResult.UNKNOWN_TYPE;
+            mInitialHitTestResult.setType(HitTestResult.UNKNOWN_TYPE);
         }
     }
 
@@ -9462,15 +9365,15 @@
         if (hit.mLinkUrl != null) {
             setHitTestTypeFromUrl(hit.mLinkUrl);
             if (hit.mImageUrl != null
-                    && mInitialHitTestResult.mType == HitTestResult.SRC_ANCHOR_TYPE) {
-                mInitialHitTestResult.mType = HitTestResult.SRC_IMAGE_ANCHOR_TYPE;
-                mInitialHitTestResult.mExtra = hit.mImageUrl;
+                    && mInitialHitTestResult.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
+                mInitialHitTestResult.setType(HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+                mInitialHitTestResult.setExtra(hit.mImageUrl);
             }
         } else if (hit.mImageUrl != null) {
-            mInitialHitTestResult.mType = HitTestResult.IMAGE_TYPE;
-            mInitialHitTestResult.mExtra = hit.mImageUrl;
+            mInitialHitTestResult.setType(HitTestResult.IMAGE_TYPE);
+            mInitialHitTestResult.setExtra(hit.mImageUrl);
         } else if (hit.mEditable) {
-            mInitialHitTestResult.mType = HitTestResult.EDIT_TEXT_TYPE;
+            mInitialHitTestResult.setType(HitTestResult.EDIT_TEXT_TYPE);
         } else if (hit.mIntentUrl != null) {
             setHitTestTypeFromUrl(hit.mIntentUrl);
         }
@@ -9483,16 +9386,16 @@
         if (mTouchHighlightRegion.isEmpty()) {
             return false;
         }
-        if (mFocusedNode.mHasFocus && !isInTouchMode()) {
+        if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
             return !mFocusedNode.mEditable;
         }
-        if (mInitialHitTestResult.mType == HitTestResult.UNKNOWN_TYPE) {
+        if (mInitialHitTestResult.getType() == HitTestResult.UNKNOWN_TYPE) {
             return false;
         }
         long delay = System.currentTimeMillis() - mTouchHighlightRequested;
         if (delay < ViewConfiguration.getTapTimeout()) {
             Rect r = mTouchHighlightRegion.getBounds();
-            postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
+            mWebView.postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
             return false;
         }
         return true;
@@ -9504,12 +9407,12 @@
         Region mPreviousRegion;
         Region mNewRegion;
         float mProgress = 0;
-        WebView mWebView;
+        WebViewClassic mWebView;
         Paint mPaint;
         int mMaxAlpha;
         Point mTranslate;
 
-        public FocusTransitionDrawable(WebView view) {
+        public FocusTransitionDrawable(WebViewClassic view) {
             mWebView = view;
             mPaint = new Paint(mWebView.mTouchHightlightPaint);
             mMaxAlpha = mPaint.getAlpha();
@@ -9588,7 +9491,7 @@
         }
         Rect[] rects = hit != null ? hit.mTouchRects : null;
         if (!mTouchHighlightRegion.isEmpty()) {
-            invalidate(mTouchHighlightRegion.getBounds());
+            mWebView.invalidate(mTouchHighlightRegion.getBounds());
             if (transition != null) {
                 transition.mPreviousRegion = new Region(mTouchHighlightRegion);
             }
@@ -9610,7 +9513,7 @@
                             + viewRect);
                 }
             }
-            invalidate(mTouchHighlightRegion.getBounds());
+            mWebView.invalidate(mTouchHighlightRegion.getBounds());
             if (transition != null && transition.mPreviousRegion != null) {
                 transition.mNewRegion = new Region(mTouchHighlightRegion);
                 mFocusTransition = transition;
@@ -9621,6 +9524,11 @@
         }
     }
 
+    // Interface to allow the profiled WebView to hook the page swap notifications.
+    public interface PageSwapDelegate {
+        void onPageSwapOccurred(boolean notifyAnimationStarted);
+    }
+
     /** @hide Called by JNI when pages are swapped (only occurs with hardware
      * acceleration) */
     protected void pageSwapCallback(boolean notifyAnimationStarted) {
@@ -9631,6 +9539,10 @@
         if (notifyAnimationStarted) {
             mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
         }
+        if (mWebView instanceof PageSwapDelegate) {
+            // This provides a hook for ProfiledWebView to observe the tile page swaps.
+            ((PageSwapDelegate) mWebView).onPageSwapOccurred(notifyAnimationStarted);
+        }
     }
 
     void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
@@ -9690,7 +9602,7 @@
         invalidateContentRect(draw.mInvalRegion.getBounds());
 
         if (mPictureListener != null) {
-            mPictureListener.onNewPicture(WebView.this, capturePicture());
+            mPictureListener.onNewPicture(getWebView(), capturePicture());
         }
 
         // update the zoom information based on the new picture
@@ -9778,7 +9690,7 @@
          */
         private class MyArrayListAdapter extends ArrayAdapter<Container> {
             public MyArrayListAdapter() {
-                super(mContext,
+                super(WebViewClassic.this.mContext,
                         mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
                         com.android.internal.R.layout.webview_select_singlechoice,
                         mContainers);
@@ -10146,7 +10058,7 @@
         if (down) {
             keyEventAction = KeyEvent.ACTION_DOWN;
             eventHubAction = EventHub.KEY_DOWN;
-            playSoundEffect(keyCodeToSoundsEffect(keyCode));
+            mWebView.playSoundEffect(keyCodeToSoundsEffect(keyCode));
         } else {
             keyEventAction = KeyEvent.ACTION_UP;
             eventHubAction = EventHub.KEY_UP;
@@ -10217,7 +10129,7 @@
             Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
                     + contentCursorRingBounds);
         }
-        requestRectangleOnScreen(viewCursorRingBounds);
+        mWebView.requestRectangleOnScreen(viewCursorRingBounds);
         return keyHandled;
     }
 
@@ -10289,7 +10201,7 @@
      * @param text The text to put into the clipboard.
      */
     private void copyToClipboard(String text) {
-        ClipboardManager cm = (ClipboardManager)getContext()
+        ClipboardManager cm = (ClipboardManager)mContext
                 .getSystemService(Context.CLIPBOARD_SERVICE);
         ClipData clip = ClipData.newPlainText(getTitle(), text);
         cm.setPrimaryClip(clip);
@@ -10333,8 +10245,8 @@
         }
     }
 
-    /** @hide discard all textures from tiles */
-    protected void discardAllTextures() {
+    /** @hide discard all textures from tiles. Used in Profiled WebView */
+    public void discardAllTextures() {
         nativeDiscardAllTextures();
     }
 
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 93fd92b..4bda5ef 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -39,7 +39,7 @@
 import android.view.MotionEvent;
 import android.view.SurfaceView;
 import android.view.View;
-import android.webkit.WebView.FocusNodeHref;
+import android.webkit.WebViewClassic.FocusNodeHref;
 
 import junit.framework.Assert;
 
@@ -72,11 +72,12 @@
      */
 
     // The WebView that corresponds to this WebViewCore.
-    private WebView mWebView;
+    // TODO: rename this field (and its getter) to mWebViewClassic or  mWebViewImpl.
+    private WebViewClassic mWebView;
     // Proxy for handling callbacks from native code
     private final CallbackProxy mCallbackProxy;
     // Settings object for maintaining all settings
-    private final WebSettings mSettings;
+    private final WebSettingsClassic mSettings;
     // Context for initializing the BrowserFrame with the proper assets.
     private final Context mContext;
     // The pointer to a native view object.
@@ -142,7 +143,7 @@
     // debugging other classes that require operation within the WebCore thread.
     /* package */ static final String THREAD_NAME = "WebViewCoreThread";
 
-    public WebViewCore(Context context, WebView w, CallbackProxy proxy,
+    public WebViewCore(Context context, WebViewClassic w, CallbackProxy proxy,
             Map<String, Object> javascriptInterfaces) {
         // No need to assign this in the WebCore thread.
         mCallbackProxy = proxy;
@@ -179,7 +180,7 @@
         // ready.
         mEventHub = new EventHub();
         // Create a WebSettings object for maintaining all settings
-        mSettings = new WebSettings(mContext, mWebView);
+        mSettings = new WebSettingsClassic(mContext, mWebView);
         // The WebIconDatabase needs to be initialized within the UI thread so
         // just request the instance here.
         WebIconDatabase.getInstance();
@@ -234,7 +235,7 @@
         // WebCore thread.
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.WEBCORE_INITIALIZED_MSG_ID,
+                    WebViewClassic.WEBCORE_INITIALIZED_MSG_ID,
                     mNativeClass, 0).sendToTarget();
         }
 
@@ -284,7 +285,7 @@
         BrowserFrame.sJavaBridge.resume();
     }
 
-    public WebSettings getSettings() {
+    public WebSettingsClassic getSettings() {
         return mSettings;
     }
 
@@ -329,7 +330,7 @@
      */
     private void formDidBlur(int nodePointer) {
         if (mWebView == null) return;
-        Message.obtain(mWebView.mPrivateHandler, WebView.FORM_DID_BLUR,
+        Message.obtain(mWebView.mPrivateHandler, WebViewClassic.FORM_DID_BLUR,
                 nodePointer, 0).sendToTarget();
     }
 
@@ -338,7 +339,7 @@
      */
     private void focusNodeChanged(WebKitHitTest hitTest) {
         if (mWebView == null) return;
-        mWebView.mPrivateHandler.obtainMessage(WebView.HIT_TEST_RESULT, hitTest)
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIT_TEST_RESULT, hitTest)
                 .sendToTarget();
     }
 
@@ -508,7 +509,7 @@
     protected void enterFullscreenForVideoLayer(int layerId, String url) {
         if (mWebView == null) return;
         Message message = Message.obtain(mWebView.mPrivateHandler,
-                       WebView.ENTER_FULLSCREEN_VIDEO, layerId, 0);
+                       WebViewClassic.ENTER_FULLSCREEN_VIDEO, layerId, 0);
         message.obj = url;
         message.sendToTarget();
     }
@@ -520,7 +521,7 @@
     protected void exitFullscreenVideo() {
         if (mWebView == null) return;
         Message message = Message.obtain(mWebView.mPrivateHandler,
-                       WebView.EXIT_FULLSCREEN_VIDEO);
+                       WebViewClassic.EXIT_FULLSCREEN_VIDEO);
         message.sendToTarget();
     }
 
@@ -886,7 +887,7 @@
         String mTitle;
         Rect[] mTouchRects;
         boolean mEditable;
-        int mTapHighlightColor = WebView.HIGHLIGHT_COLOR;
+        int mTapHighlightColor = WebViewClassic.HIGHLIGHT_COLOR;
         Rect[] mEnclosingParentRects;
         boolean mHasFocus;
 
@@ -1270,7 +1271,7 @@
                                         msg.arg1, nodePointer);
                                 if (label != null && label.length() > 0) {
                                     Message.obtain(mWebView.mPrivateHandler,
-                                            WebView.RETURN_LABEL, nodePointer,
+                                            WebViewClassic.RETURN_LABEL, nodePointer,
                                             0, label).sendToTarget();
                                 }
                             }
@@ -1373,7 +1374,7 @@
                             break;
 
                         case VIEW_SIZE_CHANGED: {
-                            viewSizeChanged((WebView.ViewSizeData) msg.obj);
+                            viewSizeChanged((WebViewClassic.ViewSizeData) msg.obj);
                             break;
                         }
                         case SET_SCROLL_OFFSET:
@@ -1529,7 +1530,7 @@
                                     ted.mMetaState);
                             Message.obtain(
                                     mWebView.mPrivateHandler,
-                                    WebView.PREVENT_TOUCH_ID,
+                                    WebViewClassic.PREVENT_TOUCH_ID,
                                     ted.mAction,
                                     ted.mNativeResult ? 1 : 0,
                                     ted).sendToTarget();
@@ -1594,7 +1595,7 @@
                             nativeUpdateFrameCache(mNativeClass);
                             // FIXME: this should provide a minimal rectangle
                             if (mWebView != null) {
-                                mWebView.postInvalidate();
+                                mWebView.getWebView().postInvalidate();
                             }
                             sendUpdateTextEntry();
                             break;
@@ -1621,7 +1622,8 @@
                             String modifiedSelectionString =
                                 nativeModifySelection(mNativeClass, msg.arg1,
                                         msg.arg2);
-                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
+                            mWebView.mPrivateHandler.obtainMessage(
+                                    WebViewClassic.SELECTION_STRING_CHANGED,
                                     modifiedSelectionString).sendToTarget();
                             break;
 
@@ -1666,12 +1668,12 @@
                             break;
 
                         case SAVE_WEBARCHIVE:
-                            WebView.SaveWebArchiveMessage saveMessage =
-                                (WebView.SaveWebArchiveMessage)msg.obj;
+                            WebViewClassic.SaveWebArchiveMessage saveMessage =
+                                (WebViewClassic.SaveWebArchiveMessage)msg.obj;
                             saveMessage.mResultFile =
                                 saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
                             mWebView.mPrivateHandler.obtainMessage(
-                                WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
+                                WebViewClassic.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
                             break;
 
                         case GEOLOCATION_PERMISSIONS_PROVIDE:
@@ -1684,7 +1686,7 @@
                         case SPLIT_PICTURE_SET:
                             nativeSplitContent(mNativeClass, msg.arg1);
                             mWebView.mPrivateHandler.obtainMessage(
-                                    WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
+                                    WebViewClassic.REPLACE_BASE_CONTENT, msg.arg1, 0);
                             mSplitPictureIsScheduled = false;
                             break;
 
@@ -1711,7 +1713,7 @@
                                 nativeUpdateFrameCache(mNativeClass);
                             }
                             Message message = mWebView.mPrivateHandler
-                                    .obtainMessage(WebView.DO_MOTION_UP,
+                                    .obtainMessage(WebViewClassic.DO_MOTION_UP,
                                     motionUpData.mX, motionUpData.mY);
                             mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
                                     message);
@@ -1747,7 +1749,7 @@
                             }
                             WebKitHitTest hit = performHitTest(d.mX, d.mY, d.mSlop, true);
                             mWebView.mPrivateHandler.obtainMessage(
-                                    WebView.HIT_TEST_RESULT, hit)
+                                    WebViewClassic.HIT_TEST_RESULT, hit)
                                     .sendToTarget();
                             break;
 
@@ -1757,7 +1759,7 @@
 
                         case AUTOFILL_FORM:
                             nativeAutoFillForm(mNativeClass, msg.arg1);
-                            mWebView.mPrivateHandler.obtainMessage(WebView.AUTOFILL_COMPLETE, null)
+                            mWebView.mPrivateHandler.obtainMessage(WebViewClassic.AUTOFILL_COMPLETE, null)
                                     .sendToTarget();
                             break;
 
@@ -1788,7 +1790,7 @@
                                     handles[0], handles[1], handles[2],
                                     handles[3]);
                             if (copiedText != null) {
-                                mWebView.mPrivateHandler.obtainMessage(WebView.COPY_TO_CLIPBOARD, copiedText)
+                                mWebView.mPrivateHandler.obtainMessage(WebViewClassic.COPY_TO_CLIPBOARD, copiedText)
                                         .sendToTarget();
                             }
                             break;
@@ -2058,7 +2060,7 @@
                 }
                 if (mWebView != null && evt.isDown()) {
                     Message.obtain(mWebView.mPrivateHandler,
-                            WebView.UNHANDLED_NAV_KEY, keyCode,
+                            WebViewClassic.UNHANDLED_NAV_KEY, keyCode,
                             0).sendToTarget();
                 }
                 return;
@@ -2081,7 +2083,7 @@
     private float mCurrentViewScale = 1.0f;
 
     // notify webkit that our virtual view size changed size (after inv-zoom)
-    private void viewSizeChanged(WebView.ViewSizeData data) {
+    private void viewSizeChanged(WebViewClassic.ViewSizeData data) {
         int w = data.mWidth;
         int h = data.mHeight;
         int textwrapWidth = data.mTextWrapWidth;
@@ -2125,7 +2127,7 @@
         if (mSettings.getUseWideViewPort()) {
             if (mViewportWidth == -1) {
                 // Fixed viewport width.
-                width = WebView.DEFAULT_VIEWPORT_WIDTH;
+                width = WebViewClassic.DEFAULT_VIEWPORT_WIDTH;
             } else if (mViewportWidth > 0) {
                 // Use website specified or desired fixed viewport width.
                 width = mViewportWidth;
@@ -2140,7 +2142,7 @@
     private void sendUpdateTextEntry() {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
+                    WebViewClassic.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
         }
     }
 
@@ -2230,9 +2232,9 @@
             // If anything more complex than position has been touched, let's do a full draw
             webkitDraw();
         }
-        mWebView.mPrivateHandler.removeMessages(WebView.INVAL_RECT_MSG_ID);
+        mWebView.mPrivateHandler.removeMessages(WebViewClassic.INVAL_RECT_MSG_ID);
         mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(mWebView.mPrivateHandler
-                .obtainMessage(WebView.INVAL_RECT_MSG_ID));
+                .obtainMessage(WebViewClassic.INVAL_RECT_MSG_ID));
     }
 
     private Boolean m_skipDrawFlag = false;
@@ -2289,7 +2291,7 @@
             draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
             if (mSettings.getUseWideViewPort()) {
                 draw.mMinPrefWidth = Math.max(
-                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
+                        mViewportWidth == -1 ? WebViewClassic.DEFAULT_VIEWPORT_WIDTH
                                 : (mViewportWidth == 0 ? mCurrentViewWidth
                                         : mViewportWidth),
                         nativeGetContentMinPrefWidth(mNativeClass));
@@ -2304,7 +2306,7 @@
             }
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
+                    WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
         }
     }
 
@@ -2433,7 +2435,7 @@
         }
         if (mWebView != null) {
             Message msg = Message.obtain(mWebView.mPrivateHandler,
-                    WebView.SCROLL_TO_MSG_ID, animate ? 1 : 0,
+                    WebViewClassic.SCROLL_TO_MSG_ID, animate ? 1 : 0,
                     onlyIfImeIsShowing ? 1 : 0, new Point(x, y));
             if (mDrawIsScheduled) {
                 mEventHub.sendMessage(Message.obtain(null,
@@ -2457,7 +2459,7 @@
     private void sendViewInvalidate(int left, int top, int right, int bottom) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                           WebView.INVAL_RECT_MSG_ID,
+                           WebViewClassic.INVAL_RECT_MSG_ID,
                            new Rect(left, top, right, bottom)).sendToTarget();
         }
     }
@@ -2473,7 +2475,7 @@
 
     // Gets the WebView corresponding to this WebViewCore. Note that the
     // WebView object must only be used on the UI thread.
-    /* package */ WebView getWebView() {
+    /* package */ WebViewClassic getWebView() {
         return mWebView;
     }
 
@@ -2499,9 +2501,9 @@
         }
 
         // remove the touch highlight when moving to a new page
-        if (WebView.sDisableNavcache) {
+        if (WebViewClassic.sDisableNavcache) {
             mWebView.mPrivateHandler.sendEmptyMessage(
-                    WebView.HIT_TEST_RESULT);
+                    WebViewClassic.HIT_TEST_RESULT);
         }
 
         // reset the scroll position, the restored offset and scales
@@ -2567,7 +2569,7 @@
         }
         if (adjust != mWebView.getDefaultZoomScale()) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
+                    WebViewClassic.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
         }
         int defaultScale = (int) (adjust * 100);
 
@@ -2618,7 +2620,7 @@
             viewState.mScrollX = 0;
             viewState.mShouldStartScrolledRight = false;
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
+                    WebViewClassic.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
             return;
         }
 
@@ -2689,7 +2691,7 @@
             mWebView.mLastHeightSent = 0;
             // Send a negative scale to indicate that WebCore should reuse
             // the current scale
-            WebView.ViewSizeData data = new WebView.ViewSizeData();
+            WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
             data.mWidth = mWebView.mLastWidthSent;
             data.mHeight = 0;
             // if mHeightCanMeasure is true, getUseWideViewPort() can't be
@@ -2713,7 +2715,7 @@
                 // to WebViewCore
                 mWebView.mLastWidthSent = 0;
             } else {
-                WebView.ViewSizeData data = new WebView.ViewSizeData();
+                WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
                 // mViewScale as 0 means it is in zoom overview mode. So we don't
                 // know the exact scale. If mRestoredScale is non-zero, use it;
                 // otherwise just use mTextWrapScale as the initial scale.
@@ -2791,7 +2793,7 @@
     private void needTouchEvents(boolean need) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
+                    WebViewClassic.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
                     .sendToTarget();
         }
     }
@@ -2801,7 +2803,7 @@
             String text, int textGeneration) {
         if (mWebView != null) {
             Message msg = Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
+                    WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
                     textGeneration, text);
             msg.getData().putBoolean("password", changeToPassword);
             msg.sendToTarget();
@@ -2813,7 +2815,7 @@
             int textGeneration, int selectionPtr) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
+                WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
                 new TextSelectionData(start, end, selectionPtr)).sendToTarget();
         }
     }
@@ -2822,7 +2824,7 @@
     private void clearTextEntry() {
         if (mWebView == null) return;
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
+                WebViewClassic.CLEAR_TEXT_ENTRY).sendToTarget();
     }
 
     // called by JNI
@@ -2836,9 +2838,9 @@
                 text, inputType, isSpellCheckEnabled, nextFieldIsText, label,
                 maxLength);
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.INIT_EDIT_FIELD, initData).sendToTarget();
+                WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget();
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+                WebViewClassic.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
                 0, new TextSelectionData(start, end, selectionPtr))
                 .sendToTarget();
     }
@@ -2850,7 +2852,7 @@
             return;
         }
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.UPDATE_MATCH_COUNT, matchIndex, matchCount,
+                WebViewClassic.UPDATE_MATCH_COUNT, matchIndex, matchCount,
                 findText).sendToTarget();
     }
 
@@ -2892,14 +2894,14 @@
     private void requestKeyboard(boolean showKeyboard) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
+                    WebViewClassic.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
                     .sendToTarget();
         }
     }
 
     private void setWebTextViewAutoFillable(int queryId, String preview) {
         if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE,
+            Message.obtain(mWebView.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE,
                     new AutoFillData(queryId, preview))
                     .sendToTarget();
         }
@@ -2912,7 +2914,7 @@
     // called by JNI
     private void keepScreenOn(boolean screenOn) {
         if (mWebView != null) {
-            Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SCREEN_ON);
+            Message message = mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SCREEN_ON);
             message.arg1 = screenOn ? 1 : 0;
             message.sendToTarget();
         }
@@ -2952,7 +2954,7 @@
             return;
         }
 
-        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
+        Message message = mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_FULLSCREEN);
         message.obj = childView.mView;
         message.arg1 = orientation;
         message.arg2 = npp;
@@ -2964,7 +2966,7 @@
         if (mWebView == null) {
             return;
         }
-        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
                 .sendToTarget();
     }
 
@@ -3036,7 +3038,7 @@
             data.mXPercentInView = xPercentInView;
             data.mYPercentInDoc = yPercentInDoc;
             data.mYPercentInView = yPercentInView;
-            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
+            Message.obtain(mWebView.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID,
                     data).sendToTarget();
         }
     }
@@ -3046,7 +3048,7 @@
         if (mWebView == null) {
             return;
         }
-        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT,
                 new Rect(x, y, x + width, y + height)).sendToTarget();
     }
 
@@ -3055,7 +3057,7 @@
         if (mWebView == null) {
             return;
         }
-        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES,
                 hMode, vMode).sendToTarget();
     }
 
@@ -3063,7 +3065,7 @@
     @SuppressWarnings("unused")
     private void selectAt(int x, int y) {
         if (mWebView != null) {
-            mWebView.mPrivateHandler.obtainMessage(WebView.SELECT_AT, x, y).sendToTarget();
+            mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SELECT_AT, x, y).sendToTarget();
         }
     }
 
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
new file mode 100644
index 0000000..22bf0bf
--- /dev/null
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This is the main entry-point into the WebView back end implementations, which the WebView
+ * proxy class uses to instantiate all the other objects as needed. The backend must provide an
+ * implementation of this interface, and make it available to the WebView via mechanism TBD.
+ * @hide
+ */
+public interface WebViewFactoryProvider {
+
+    /**
+     * Construct a new WebView provider.
+     * @param webView the WebView instance bound to this implementation instance. Note it will not
+     * necessarily be fully constructed at the point of this call: defer real initialization to
+     * WebViewProvider.init().
+     * @param privateAccess provides access into WebView internal methods.
+     */
+    WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess);
+
+    Statics getStatics();
+
+    /**
+     * This Interface provides glue for implementing the backend of WebView static methods which
+     * cannot be implemented in-situ in the proxy class.
+     */
+    interface Statics {
+        /**
+         * Implements the API method:
+         * {@link android.webkit.WebView#findAddress(String)}
+         */
+        String findAddress(String addr);
+
+        /**
+         * Implements the API methods:
+         * {@link android.webkit.WebView#enablePlatformNotifications()}
+         * {@link android.webkit.WebView#disablePlatformNotifications()}
+         */
+        void setPlatformNotificationsEnabled(boolean enable);
+    }
+}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
new file mode 100644
index 0000000..2e8ad6d
--- /dev/null
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Picture;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.http.SslCertificate;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.webkit.WebView.HitTestResult;
+import android.webkit.WebView.PictureListener;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * WebView backend provider interface: this interface is the abstract backend to a WebView
+ * instance; each WebView object is bound to exactly one WebViewProvider object which implements
+ * the runtime behavior of that WebView.
+ *
+ * All methods must behave as per their namesake in {@link WebView}, unless otherwise noted.
+ *
+ * @hide Not part of the public API; only required by system implementors.
+ */
+public interface WebViewProvider {
+    //-------------------------------------------------------------------------
+    // Main interface for backend provider of the WebView class.
+    //-------------------------------------------------------------------------
+    /**
+     * Initialize this WebViewProvider instance. Called after the WebView has fully constructed.
+     * @param javaScriptInterfaces is a Map of interface names, as keys, and
+     * object implementing those interfaces, as values.
+     * @param privateBrowsing If true the web view will be initialized in private / incognito mode.
+     */
+    public void init(Map<String, Object> javaScriptInterfaces,
+            boolean privateBrowsing);
+
+    public void setHorizontalScrollbarOverlay(boolean overlay);
+
+    public void setVerticalScrollbarOverlay(boolean overlay);
+
+    public boolean overlayHorizontalScrollbar();
+
+    public boolean overlayVerticalScrollbar();
+
+    public int getVisibleTitleHeight();
+
+    public SslCertificate getCertificate();
+
+    public void setCertificate(SslCertificate certificate);
+
+    public void savePassword(String host, String username, String password);
+
+    public void setHttpAuthUsernamePassword(String host, String realm,
+            String username, String password);
+
+    public String[] getHttpAuthUsernamePassword(String host, String realm);
+
+    /**
+     * See {@link WebView#destroy()}.
+     * As well as releasing the internal state and resources held by the implementation,
+     * the provider should null all references it holds on the WebView proxy class, and ensure
+     * no further method calls are made to it.
+     */
+    public void destroy();
+
+    public void setNetworkAvailable(boolean networkUp);
+
+    public WebBackForwardList saveState(Bundle outState);
+
+    public boolean savePicture(Bundle b, final File dest);
+
+    public boolean restorePicture(Bundle b, File src);
+
+    public WebBackForwardList restoreState(Bundle inState);
+
+    public void loadUrl(String url, Map<String, String> additionalHttpHeaders);
+
+    public void loadUrl(String url);
+
+    public void postUrl(String url, byte[] postData);
+
+    public void loadData(String data, String mimeType, String encoding);
+
+    public void loadDataWithBaseURL(String baseUrl, String data,
+            String mimeType, String encoding, String historyUrl);
+
+    public void saveWebArchive(String filename);
+
+    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback);
+
+    public void stopLoading();
+
+    public void reload();
+
+    public boolean canGoBack();
+
+    public void goBack();
+
+    public boolean canGoForward();
+
+    public void goForward();
+
+    public boolean canGoBackOrForward(int steps);
+
+    public void goBackOrForward(int steps);
+
+    public boolean isPrivateBrowsingEnabled();
+
+    public boolean pageUp(boolean top);
+
+    public boolean pageDown(boolean bottom);
+
+    public void clearView();
+
+    public Picture capturePicture();
+
+    public float getScale();
+
+    public void setInitialScale(int scaleInPercent);
+
+    public void invokeZoomPicker();
+
+    public HitTestResult getHitTestResult();
+
+    public void requestFocusNodeHref(Message hrefMsg);
+
+    public void requestImageRef(Message msg);
+
+    public String getUrl();
+
+    public String getOriginalUrl();
+
+    public String getTitle();
+
+    public Bitmap getFavicon();
+
+    public String getTouchIconUrl();
+
+    public int getProgress();
+
+    public int getContentHeight();
+
+    public int getContentWidth();
+
+    public void pauseTimers();
+
+    public void resumeTimers();
+
+    public void onPause();
+
+    public void onResume();
+
+    public boolean isPaused();
+
+    public void freeMemory();
+
+    public void clearCache(boolean includeDiskFiles);
+
+    public void clearFormData();
+
+    public void clearHistory();
+
+    public void clearSslPreferences();
+
+    public WebBackForwardList copyBackForwardList();
+
+    public void findNext(boolean forward);
+
+    public int findAll(String find);
+
+    public boolean showFindDialog(String text, boolean showIme);
+
+    public void clearMatches();
+
+    public void documentHasImages(Message response);
+
+    public void setWebViewClient(WebViewClient client);
+
+    public void setDownloadListener(DownloadListener listener);
+
+    public void setWebChromeClient(WebChromeClient client);
+
+    public void setPictureListener(PictureListener listener);
+
+    public void addJavascriptInterface(Object obj, String interfaceName);
+
+    public void removeJavascriptInterface(String interfaceName);
+
+    public WebSettings getSettings();
+
+    public void emulateShiftHeld();
+
+    public void setMapTrackballToArrowKeys(boolean setMap);
+
+    public void flingScroll(int vx, int vy);
+
+    public View getZoomControls();
+
+    public boolean canZoomIn();
+
+    public boolean canZoomOut();
+
+    public boolean zoomIn();
+
+    public boolean zoomOut();
+
+    public void debugDump();
+
+    //-------------------------------------------------------------------------
+    // Provider glue methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * @return the ViewDelegate implementation. This provides the functionality to back all of
+     * the name-sake functions from the View and ViewGroup base classes of WebView.
+     */
+    /* package */ ViewDelegate getViewDelegate();
+
+    /**
+     * @return a ScrollDelegate implementation. Normally this would be same object as is
+     * returned by getViewDelegate().
+     */
+    /* package */ ScrollDelegate getScrollDelegate();
+
+    //-------------------------------------------------------------------------
+    // View / ViewGroup delegation methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Provides mechanism for the name-sake methods declared in View and ViewGroup to be delegated
+     * into the WebViewProvider instance.
+     * NOTE For many of these methods, the WebView will provide a super.Foo() call before or after
+     * making the call into the provider instance. This is done for convenience in the common case
+     * of maintaining backward compatibility. For remaining super class calls (e.g. where the
+     * provider may need to only conditionally make the call based on some internal state) see the
+     * {@link WebView.PrivateAccess} callback class.
+     */
+    // TODO: See if the pattern of the super-class calls can be rationalized at all, and document
+    // the remainder on the methods below.
+    interface ViewDelegate {
+        public boolean shouldDelayChildPressedState();
+
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
+
+        public void onInitializeAccessibilityEvent(AccessibilityEvent event);
+
+        public void setOverScrollMode(int mode);
+
+        public void setScrollBarStyle(int style);
+
+        public void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t,
+                int r, int b);
+
+        public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY);
+
+        public void onWindowVisibilityChanged(int visibility);
+
+        public boolean drawChild(Canvas canvas, View child, long drawingTime);
+
+        public void onDraw(Canvas canvas);
+
+        public void setLayoutParams(LayoutParams layoutParams);
+
+        public boolean performLongClick();
+
+        public void onConfigurationChanged(Configuration newConfig);
+
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs);
+
+        public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
+
+        public boolean onKeyDown(int keyCode, KeyEvent event);
+
+        public boolean onKeyUp(int keyCode, KeyEvent event);
+
+        public void onAttachedToWindow();
+
+        public void onDetachedFromWindow();
+
+        public void onVisibilityChanged(View changedView, int visibility);
+
+        public void onWindowFocusChanged(boolean hasWindowFocus);
+
+        public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect);
+
+        public boolean setFrame(int left, int top, int right, int bottom);
+
+        public void onSizeChanged(int w, int h, int ow, int oh);
+
+        public void onScrollChanged(int l, int t, int oldl, int oldt);
+
+        public boolean dispatchKeyEvent(KeyEvent event);
+
+        public boolean onTouchEvent(MotionEvent ev);
+
+        public boolean onHoverEvent(MotionEvent event);
+
+        public boolean onGenericMotionEvent(MotionEvent event);
+
+        public boolean onTrackballEvent(MotionEvent ev);
+
+        public boolean requestFocus(int direction, Rect previouslyFocusedRect);
+
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
+
+        public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate);
+
+        public void setBackgroundColor(int color);
+    }
+
+    interface ScrollDelegate {
+        // These methods are declared protected in the ViewGroup base class. This interface
+        // exists to promote them to public so they may be called by the WebView proxy class.
+        // TODO: Combine into ViewDelegate?
+        /**
+         * See {@link android.webkit.WebView#computeHorizontalScrollRange}
+         */
+        public int computeHorizontalScrollRange();
+
+        /**
+         * See {@link android.webkit.WebView#computeHorizontalScrollOffset}
+         */
+        public int computeHorizontalScrollOffset();
+
+        /**
+         * See {@link android.webkit.WebView#computeVerticalScrollRange}
+         */
+        public int computeVerticalScrollRange();
+
+        /**
+         * See {@link android.webkit.WebView#computeVerticalScrollOffset}
+         */
+        public int computeVerticalScrollOffset();
+
+        /**
+         * See {@link android.webkit.WebView#computeVerticalScrollExtent}
+         */
+        public int computeVerticalScrollExtent();
+
+        /**
+         * See {@link android.webkit.WebView#computeScroll}
+         */
+        public void computeScroll();
+    }
+}
diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java
index e505614..d2a0561 100644
--- a/core/java/android/webkit/ZoomControlEmbedded.java
+++ b/core/java/android/webkit/ZoomControlEmbedded.java
@@ -25,12 +25,12 @@
 class ZoomControlEmbedded implements ZoomControlBase {
 
     private final ZoomManager mZoomManager;
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
 
     // The controller is lazily initialized in getControls() for performance.
     private ZoomButtonsController mZoomButtonsController;
 
-    public ZoomControlEmbedded(ZoomManager zoomManager, WebView webView) {
+    public ZoomControlEmbedded(ZoomManager zoomManager, WebViewClassic webView) {
         mZoomManager = zoomManager;
         mWebView = webView;
     }
@@ -41,7 +41,7 @@
             mZoomButtonsController.setVisible(true);
 
             if (mZoomManager.isDoubleTapEnabled()) {
-                WebSettings settings = mWebView.getSettings();
+                WebSettingsClassic settings = mWebView.getSettings();
                 int count = settings.getDoubleTapToastCount();
                 if (mZoomManager.isInZoomOverview() && count > 0) {
                     settings.setDoubleTapToastCount(--count);
@@ -82,7 +82,7 @@
 
     private ZoomButtonsController getControls() {
         if (mZoomButtonsController == null) {
-            mZoomButtonsController = new ZoomButtonsController(mWebView);
+            mZoomButtonsController = new ZoomButtonsController(mWebView.getWebView());
             mZoomButtonsController.setOnZoomListener(new ZoomListener());
             // ZoomButtonsController positions the buttons at the bottom, but in
             // the middle. Change their layout parameters so they appear on the
diff --git a/core/java/android/webkit/ZoomControlExternal.java b/core/java/android/webkit/ZoomControlExternal.java
index d75313e..f5bfc05 100644
--- a/core/java/android/webkit/ZoomControlExternal.java
+++ b/core/java/android/webkit/ZoomControlExternal.java
@@ -35,9 +35,9 @@
     private Runnable mZoomControlRunnable;
     private final Handler mPrivateHandler = new Handler();
 
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
 
-    public ZoomControlExternal(WebView webView) {
+    public ZoomControlExternal(WebViewClassic webView) {
         mWebView = webView;
     }
 
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 369e883..e7b049e 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -50,7 +50,7 @@
 
     static final String LOGTAG = "webviewZoom";
 
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
     private final CallbackProxy mCallbackProxy;
 
     // Widgets responsible for the on-screen zoom functions of the WebView.
@@ -211,7 +211,7 @@
     private boolean mHardwareAccelerated = false;
     private boolean mInHWAcceleratedZoom = false;
 
-    public ZoomManager(WebView webView, CallbackProxy callbackProxy) {
+    public ZoomManager(WebViewClassic webView, CallbackProxy callbackProxy) {
         mWebView = webView;
         mCallbackProxy = callbackProxy;
 
@@ -220,7 +220,7 @@
          * ESPN and Engadget always have wider mContentWidth no matter what the
          * viewport size is.
          */
-        setZoomOverviewWidth(WebView.DEFAULT_VIEWPORT_WIDTH);
+        setZoomOverviewWidth(WebViewClassic.DEFAULT_VIEWPORT_WIDTH);
 
         mFocusMovementQueue = new FocusMovementQueue();
     }
@@ -487,13 +487,13 @@
         // zoomScale, we can't use the WebView's pinLocX/Y functions directly.
         float scale = zoomScale * mInvInitialZoomScale;
         int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX) - mZoomCenterX);
-        tx = -WebView.pinLoc(tx, mWebView.getViewWidth(), Math.round(mWebView.getContentWidth()
+        tx = -WebViewClassic.pinLoc(tx, mWebView.getViewWidth(), Math.round(mWebView.getContentWidth()
                 * zoomScale)) + mWebView.getScrollX();
         int titleHeight = mWebView.getTitleHeight();
         int ty = Math.round(scale
                 * (mInitialScrollY + mZoomCenterY - titleHeight)
                 - (mZoomCenterY - titleHeight));
-        ty = -(ty <= titleHeight ? Math.max(ty, 0) : WebView.pinLoc(ty
+        ty = -(ty <= titleHeight ? Math.max(ty, 0) : WebViewClassic.pinLoc(ty
                 - titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight()
                 * zoomScale)) + titleHeight) + mWebView.getScrollY();
 
@@ -630,7 +630,7 @@
     public void handleDoubleTap(float lastTouchX, float lastTouchY) {
         // User takes action, set initial zoom overview to false.
         mInitialZoomOverview = false;
-        WebSettings settings = mWebView.getSettings();
+        WebSettingsClassic settings = mWebView.getSettings();
         if (!isDoubleTapEnabled()) {
             return;
         }
@@ -690,7 +690,7 @@
 
     private void setZoomOverviewWidth(int width) {
         if (width == 0) {
-            mZoomOverviewWidth = WebView.DEFAULT_VIEWPORT_WIDTH;
+            mZoomOverviewWidth = WebViewClassic.DEFAULT_VIEWPORT_WIDTH;
         } else {
             mZoomOverviewWidth = width;
         }
@@ -719,7 +719,7 @@
         final float readingScale = getReadingLevelScale();
 
         int left = mWebView.getBlockLeftEdge(mAnchorX, mAnchorY, readingScale);
-        if (left != WebView.NO_LEFTEDGE) {
+        if (left != WebViewClassic.NO_LEFTEDGE) {
             // add a 5pt padding to the left edge.
             int viewLeft = mWebView.contentToViewX(left < 5 ? 0 : (left - 5))
                     - mWebView.getScrollX();
@@ -728,7 +728,7 @@
             if (viewLeft > 0) {
                 mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
             } else {
-                mWebView.scrollBy(viewLeft, 0);
+                mWebView.getWebView().scrollBy(viewLeft, 0);
                 mZoomCenterX = 0;
             }
         }
@@ -955,7 +955,7 @@
         // cause its child View to reposition itself through ViewManager's
         // scaleAll(), we need to post a Runnable to ensure requestLayout().
         // Additionally, only update the text wrap scale if the width changed.
-        mWebView.post(new PostScale(w != ow &&
+        mWebView.getWebView().post(new PostScale(w != ow &&
             !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview, w < ow));
     }
 
@@ -1027,7 +1027,7 @@
         final int viewWidth = mWebView.getViewWidth();
         final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth);
         final float newZoomOverviewScale = getZoomOverviewScale();
-        WebSettings settings = mWebView.getSettings();
+        WebSettingsClassic settings = mWebView.getSettings();
         if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() &&
             settings.getUseFixedViewport() &&
             (mInitialZoomOverview || mInZoomOverview)) {
@@ -1085,7 +1085,7 @@
             if (drawData.mContentSize.x > 0) {
                 // The webkitDraw for layers will not populate contentSize, and it'll be
                 // ignored for zoom overview width update.
-                newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth,
+                newZoomOverviewWidth = Math.min(WebViewClassic.sMaxViewportWidth,
                     drawData.mContentSize.x);
             }
         } else {
@@ -1117,7 +1117,7 @@
         updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
         setupZoomOverviewWidth(drawData, mWebView.getViewWidth());
         final float overviewScale = getZoomOverviewScale();
-        WebSettings settings = mWebView.getSettings();
+        WebSettingsClassic settings = mWebView.getSettings();
         if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) {
             mMinZoomScale = (mInitialScale > 0) ?
                     Math.min(mInitialScale, overviewScale) : overviewScale;
diff --git a/core/tests/coretests/src/android/webkit/ZoomManagerTest.java b/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
index 1c9defe..7e0e0b2 100644
--- a/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
+++ b/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
@@ -24,8 +24,9 @@
     @Override
     public void setUp() {
         WebView webView = new WebView(this.getContext());
-        CallbackProxy callbackProxy = new CallbackProxy(this.getContext(), webView);
-        zoomManager = new ZoomManager(webView, callbackProxy);
+        WebViewClassic webViewClassic = WebViewClassic.fromWebView(webView);
+        CallbackProxy callbackProxy = new CallbackProxy(this.getContext(), webViewClassic);
+        zoomManager = new ZoomManager(webViewClassic, callbackProxy);
 
         zoomManager.init(1.00f);
     }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 83c9c3d..856ebcc 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -45,8 +45,10 @@
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
+import android.webkit.WebSettingsClassic;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
+import android.webkit.WebViewClassic;
 import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
 
@@ -112,10 +114,10 @@
             case DUMP_AS_TEXT:
                 callback.arg1 = mDumpTopFrameAsText ? 1 : 0;
                 callback.arg2 = mDumpChildFramesAsText ? 1 : 0;
-                mWebView.documentAsText(callback);
+                mWebViewClassic.documentAsText(callback);
                 break;
             case EXT_REPR:
-                mWebView.externalRepresentation(callback);
+                mWebViewClassic.externalRepresentation(callback);
                 break;
             default:
                 finished();
@@ -144,6 +146,7 @@
 
         CookieManager.setAcceptFileSchemeCookies(true);
         mWebView = new WebView(this);
+        mWebViewClassic = WebViewClassic.fromWebView(mWebView);
         mEventSender = new WebViewEventSender(mWebView);
         mCallbackProxy = new CallbackProxy(mEventSender, this);
 
@@ -158,7 +161,7 @@
         // Expose window.gc function to JavaScript. JSC build exposes
         // this function by default, but V8 requires the flag to turn it on.
         // WebView::setJsFlags is noop in JSC build.
-        mWebView.setJsFlags("--expose_gc");
+        mWebViewClassic.setJsFlags("--expose_gc");
 
         mHandler = new AsyncHandler();
 
@@ -168,7 +171,7 @@
         }
 
         // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
-        mWebView.useMockDeviceOrientation();
+        mWebViewClassic.useMockDeviceOrientation();
     }
 
     @Override
@@ -290,6 +293,7 @@
         super.onDestroy();
         mWebView.destroy();
         mWebView = null;
+        mWebViewClassic = null;
     }
 
     @Override
@@ -531,8 +535,8 @@
 
     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
-        mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
-                canProvideGamma, gamma);
+        WebViewClassic.fromWebView(mWebView).setMockDeviceOrientation(canProvideAlpha, alpha,
+                canProvideBeta, beta, canProvideGamma, gamma);
     }
 
     public void overridePreference(String key, boolean value) {
@@ -541,10 +545,10 @@
         // WebView for the main frame. EventSender suffers from the same
         // problem.
         if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
-            mWebView.getSettings().setAppCacheEnabled(value);
+            mWebViewClassic.getSettings().setAppCacheEnabled(value);
         } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
             // Cache the maximum possible number of pages.
-            mWebView.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
+            mWebViewClassic.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
         } else {
             Log.w(LOGTAG, "LayoutTestController.overridePreference(): " +
                   "Unsupported preference '" + key + "'");
@@ -552,7 +556,7 @@
     }
 
     public void setXSSAuditorEnabled (boolean flag) {
-        mWebView.getSettings().setXSSAuditorEnabled(flag);
+        mWebViewClassic.getSettings().setXSSAuditorEnabled(flag);
     }
 
     private final WebViewClient mViewClient = new WebViewClient(){
@@ -855,7 +859,7 @@
         Bitmap bitmap = Bitmap.createBitmap(view.getContentWidth(), view.getContentHeight(),
                 Config.ARGB_8888);
         canvas.setBitmap(bitmap);
-        view.drawPage(canvas);
+        WebViewClassic.fromWebView(view).drawPage(canvas);
         try {
             FileOutputStream fos = new FileOutputStream(fileName);
             if(!bitmap.compress(CompressFormat.PNG, 90, fos)) {
@@ -885,11 +889,11 @@
         // single event rather than a stream of events (like what would generally happen in
         // a real use of touch events in a WebView)  and so if the WebView drops the event,
         // the test will fail as the test expects one callback for every touch it synthesizes.
-        webview.setTouchInterval(-1);
+        WebViewClassic.fromWebView(webview).setTouchInterval(-1);
     }
 
     public void setDefaultWebSettings(WebView webview) {
-        WebSettings settings = webview.getSettings();
+        WebSettingsClassic settings = WebViewClassic.fromWebView(webview).getSettings();
         settings.setAppCacheEnabled(true);
         settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
         settings.setAppCacheMaxSize(Long.MAX_VALUE);
@@ -906,6 +910,7 @@
         settings.setProperty("use_minimal_memory", "false");
     }
 
+    private WebViewClassic mWebViewClassic;
     private WebView mWebView;
     private WebViewEventSender mEventSender;
     private AsyncHandler mHandler;
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index f59da37..fc22472 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -41,9 +41,11 @@
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
+import android.webkit.WebSettingsClassic;
 import android.webkit.WebStorage;
 import android.webkit.WebStorage.QuotaUpdater;
 import android.webkit.WebView;
+import android.webkit.WebViewClassic;
 import android.webkit.WebViewClient;
 
 import java.lang.Thread.UncaughtExceptionHandler;
@@ -369,11 +371,12 @@
          * a real use of touch events in a WebView)  and so if the WebView drops the event,
          * the test will fail as the test expects one callback for every touch it synthesizes.
          */
-        webView.setTouchInterval(-1);
+        WebViewClassic webViewClassic = WebViewClassic.fromWebView(webView);
+        webViewClassic.setTouchInterval(-1);
 
-        webView.clearCache(true);
+        webViewClassic.clearCache(true);
 
-        WebSettings webViewSettings = webView.getSettings();
+        WebSettingsClassic webViewSettings = webViewClassic.getSettings();
         webViewSettings.setAppCacheEnabled(true);
         webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
         // Use of larger values causes unexplained AppCache database corruption.
@@ -391,7 +394,7 @@
         webViewSettings.setPageCacheCapacity(0);
 
         // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
-        mCurrentWebView.useMockDeviceOrientation();
+        WebViewClassic.fromWebView(mCurrentWebView).useMockDeviceOrientation();
 
         // Must do this after setting the AppCache path.
         WebStorage.getInstance().deleteAllData();
@@ -625,10 +628,12 @@
                     String key = msg.getData().getString("key");
                     boolean value = msg.getData().getBoolean("value");
                     if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
-                        mCurrentWebView.getSettings().setAppCacheEnabled(value);
+                        WebViewClassic.fromWebView(mCurrentWebView).getSettings().
+                                setAppCacheEnabled(value);
                     } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
                         // Cache the maximum possible number of pages.
-                        mCurrentWebView.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
+                        WebViewClassic.fromWebView(mCurrentWebView).getSettings().
+                                setPageCacheCapacity(Integer.MAX_VALUE);
                     } else {
                         Log.w(LOG_TAG, "LayoutTestController.overridePreference(): " +
                               "Unsupported preference '" + key + "'");
@@ -656,7 +661,8 @@
                     break;
 
                 case MSG_SET_XSS_AUDITOR_ENABLED:
-                    mCurrentWebView.getSettings().setXSSAuditorEnabled(msg.arg1 == 1);
+                    WebViewClassic.fromWebView(mCurrentWebView).getSettings().
+                            setXSSAuditorEnabled(msg.arg1 == 1);
                     break;
 
                 case MSG_WAIT_UNTIL_DONE:
@@ -728,8 +734,8 @@
         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
                 ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
                 ", " + gamma + ")");
-        mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
-                canProvideGamma, gamma);
+        WebViewClassic.fromWebView(mCurrentWebView).setMockDeviceOrientation(canProvideAlpha,
+                alpha, canProvideBeta, beta, canProvideGamma, gamma);
     }
 
     public void setXSSAuditorEnabled(boolean flag) {
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
index 3d2b98b..fd1c0ad 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.webkit.WebView;
+import android.webkit.WebViewClassic;
 
 import name.fraser.neil.plaintext.diff_match_patch;
 
@@ -233,7 +234,7 @@
          */
         msg.arg1 = 1;
         msg.arg2 = mDumpChildFramesAsText ? 1 : 0;
-        webview.documentAsText(msg);
+        WebViewClassic.fromWebView(webview).documentAsText(msg);
     }
 
     @Override
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index a38ac25..87baf76 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -20,8 +20,9 @@
 import android.os.CountDownTimer;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.webkit.WebSettings;
+import android.webkit.WebSettingsClassic;
 import android.webkit.WebView;
+import android.webkit.WebViewClassic;
 import android.widget.Toast;
 
 import java.util.ArrayList;
@@ -29,7 +30,7 @@
 import com.test.tilebenchmark.ProfileActivity.ProfileCallback;
 import com.test.tilebenchmark.RunData.TileData;
 
-public class ProfiledWebView extends WebView {
+public class ProfiledWebView extends WebView implements WebViewClassic.PageSwapDelegate {
     private static final String LOGTAG = "ProfiledWebView";
 
     private int mSpeed;
@@ -80,7 +81,7 @@
     }
 
     public void init(Context c) {
-        WebSettings settings = getSettings();
+        WebSettingsClassic settings = getWebViewClassic().getSettings();
         settings.setJavaScriptEnabled(true);
         settings.setSupportZoom(true);
         settings.setEnableSmoothTransition(true);
@@ -118,7 +119,7 @@
         mCallback = callback;
         mIsTesting = false;
         mIsScrolling = false;
-        WebSettings settings = getSettings();
+        WebSettingsClassic settings = getWebViewClassic().getSettings();
         settings.setProperty("tree_updates", "0");
 
 
@@ -134,7 +135,7 @@
                     // invalidate all content, and kick off redraw
                     Log.d("ProfiledWebView",
                             "kicking off test with callback registration, and tile discard...");
-                    discardAllTextures();
+                    getWebViewClassic().discardAllTextures();
                     invalidate();
                     mIsScrolling = true;
                     mContentInvalMillis = System.currentTimeMillis();
@@ -142,30 +143,29 @@
             }.start();
         } else {
             mIsTesting = true;
-            tileProfilingStart();
+            getWebViewClassic().tileProfilingStart();
         }
     }
 
     /*
      * Called after the manual contentInvalidateAll, after the tiles have all
      * been redrawn.
+     * From PageSwapDelegate.
      */
     @Override
-    protected void pageSwapCallback(boolean startAnim) {
-        super.pageSwapCallback(startAnim);
-
+    public void onPageSwapOccurred(boolean startAnim) {
         if (!mIsTesting && mIsScrolling) {
             // kick off testing
             mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis;
             Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis");
             mIsTesting = true;
             invalidate(); // ensure a redraw so that auto-scrolling can occur
-            tileProfilingStart();
+            getWebViewClassic().tileProfilingStart();
         }
     }
 
     private double animFramerate() {
-        WebSettings settings = getSettings();
+        WebSettingsClassic settings = getWebViewClassic().getSettings();
         String updatesString = settings.getProperty("tree_updates");
         int updates = (updatesString == null) ? -1 : Integer.parseInt(updatesString);
 
@@ -180,7 +180,7 @@
     }
 
     public void setDoubleBuffering(boolean useDoubleBuffering) {
-        WebSettings settings = getSettings();
+        WebSettingsClassic settings = getWebViewClassic().getSettings();
         settings.setProperty("use_double_buffering", useDoubleBuffering ? "true" : "false");
     }
 
@@ -188,15 +188,15 @@
      * Called once the page has stopped scrolling
      */
     public void stopScrollTest() {
-        tileProfilingStop();
+        getWebViewClassic().tileProfilingStop();
         mIsTesting = false;
 
         if (mCallback == null) {
-            tileProfilingClear();
+            getWebViewClassic().tileProfilingClear();
             return;
         }
 
-        RunData data = new RunData(super.tileProfilingNumFrames());
+        RunData data = new RunData(getWebViewClassic().tileProfilingNumFrames());
         // record the time spent (before scrolling) rendering the page
         data.singleStats.put(getResources().getString(R.string.render_millis),
                 (double)mContentInvalMillis);
@@ -209,24 +209,24 @@
 
         for (int frame = 0; frame < data.frames.length; frame++) {
             data.frames[frame] = new TileData[
-                    tileProfilingNumTilesInFrame(frame)];
+                    getWebViewClassic().tileProfilingNumTilesInFrame(frame)];
             for (int tile = 0; tile < data.frames[frame].length; tile++) {
-                int left = tileProfilingGetInt(frame, tile, "left");
-                int top = tileProfilingGetInt(frame, tile, "top");
-                int right = tileProfilingGetInt(frame, tile, "right");
-                int bottom = tileProfilingGetInt(frame, tile, "bottom");
+                int left = getWebViewClassic().tileProfilingGetInt(frame, tile, "left");
+                int top = getWebViewClassic().tileProfilingGetInt(frame, tile, "top");
+                int right = getWebViewClassic().tileProfilingGetInt(frame, tile, "right");
+                int bottom = getWebViewClassic().tileProfilingGetInt(frame, tile, "bottom");
 
-                boolean isReady = super.tileProfilingGetInt(
+                boolean isReady = getWebViewClassic().tileProfilingGetInt(
                         frame, tile, "isReady") == 1;
-                int level = tileProfilingGetInt(frame, tile, "level");
+                int level = getWebViewClassic().tileProfilingGetInt(frame, tile, "level");
 
-                float scale = tileProfilingGetFloat(frame, tile, "scale");
+                float scale = getWebViewClassic().tileProfilingGetFloat(frame, tile, "scale");
 
                 data.frames[frame][tile] = data.new TileData(left, top, right, bottom,
                         isReady, level, scale);
             }
         }
-        tileProfilingClear();
+        getWebViewClassic().tileProfilingClear();
 
         mCallback.profileCallback(data);
     }
@@ -244,4 +244,8 @@
     public void setAutoScrollSpeed(int speedInt) {
         mSpeed = speedInt;
     }
+
+    public WebViewClassic getWebViewClassic() {
+        return WebViewClassic.fromWebView(this);
+    }
 }