Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / android_webview / java / src / org / chromium / android_webview / AwContents.java
index d962d9f..e47e0cc 100644 (file)
@@ -4,16 +4,18 @@
 
 package org.chromium.android_webview;
 
+import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
 import android.graphics.Picture;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.net.http.SslCertificate;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -21,11 +23,11 @@ import android.os.Bundle;
 import android.os.Message;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
@@ -35,32 +37,39 @@ import android.webkit.GeolocationPermissions;
 import android.webkit.ValueCallback;
 import android.widget.OverScroller;
 
-import com.google.common.annotations.VisibleForTesting;
-
+import org.chromium.android_webview.permission.AwPermissionRequest;
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
+import org.chromium.components.navigation_interception.NavigationParams;
 import org.chromium.content.browser.ContentSettings;
-import org.chromium.content.browser.ContentVideoView;
 import org.chromium.content.browser.ContentViewClient;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content.browser.ContentViewStatics;
-import org.chromium.content.browser.LoadUrlParams;
-import org.chromium.content.browser.NavigationHistory;
-import org.chromium.content.browser.PageTransitionTypes;
+import org.chromium.content.browser.WebContentsObserverAndroid;
 import org.chromium.content.common.CleanupReference;
-import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
-import org.chromium.components.navigation_interception.NavigationParams;
-import org.chromium.net.GURLUtils;
+import org.chromium.content_public.browser.GestureStateListener;
+import org.chromium.content_public.browser.JavaScriptCallback;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.NavigationController;
+import org.chromium.content_public.browser.NavigationHistory;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.common.Referrer;
+import org.chromium.ui.base.ActivityWindowAndroid;
+import org.chromium.ui.base.PageTransitionTypes;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.gfx.DeviceDisplayInfo;
 
 import java.io.File;
 import java.lang.annotation.Annotation;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.Callable;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Exposes the native AwContents class, and together these classes wrap the ContentViewCore
@@ -126,29 +135,66 @@ public class AwContents {
          * @see View#getScrollBarStyle()
          */
         int super_getScrollBarStyle();
+    }
 
+    /**
+     * Interface that consumers of {@link AwContents} must implement to support
+     * native GL rendering.
+     */
+    public interface NativeGLDelegate {
         /**
          * Requests a callback on the native DrawGL method (see getAwDrawGLFunction)
          * if called from within onDraw, |canvas| will be non-null and hardware accelerated.
-         * otherwise, |canvas| will be null, and the container view itself will be hardware
-         * accelerated.
+         * Otherwise, |canvas| will be null, and the container view itself will be hardware
+         * accelerated. If |waitForCompletion| is true, this method will not return until
+         * functor has returned.
+         * Should avoid setting |waitForCompletion| when |canvas| is not null.
+         * |containerView| is the view where the AwContents should be drawn.
          *
          * @return false indicates the GL draw request was not accepted, and the caller
          *         should fallback to the SW path.
          */
-        boolean requestDrawGL(Canvas canvas);
+        boolean requestDrawGL(Canvas canvas, boolean waitForCompletion, View containerView);
+
+        /**
+         * Detaches the GLFunctor from the view tree.
+         */
+        void detachGLFunctor();
     }
 
-    private int mNativeAwContents;
+    /**
+     * Class to facilitate dependency injection. Subclasses by test code to provide mock versions of
+     * certain AwContents dependencies.
+     */
+    public static class DependencyFactory {
+        public AwLayoutSizer createLayoutSizer() {
+            return new AwLayoutSizer();
+        }
+
+        public AwScrollOffsetManager createScrollOffsetManager(
+                AwScrollOffsetManager.Delegate delegate, OverScroller overScroller) {
+            return new AwScrollOffsetManager(delegate, overScroller);
+        }
+    }
+
+    private long mNativeAwContents;
     private final AwBrowserContext mBrowserContext;
-    private final ViewGroup mContainerView;
+    private ViewGroup mContainerView;
+    private final AwLayoutChangeListener mLayoutChangeListener;
+    private final Context mContext;
     private ContentViewCore mContentViewCore;
+    private WindowAndroid mWindowAndroid;
+    private WebContents mWebContents;
+    private NavigationController mNavigationController;
     private final AwContentsClient mContentsClient;
+    private final AwContentViewClient mContentViewClient;
+    private WebContentsObserverAndroid mWebContentsObserver;
     private final AwContentsClientBridge mContentsClientBridge;
-    private final AwWebContentsDelegate mWebContentsDelegate;
+    private final AwWebContentsDelegateAdapter mWebContentsDelegate;
     private final AwContentsIoThreadClient mIoThreadClient;
     private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
-    private final InternalAccessDelegate mInternalAccessAdapter;
+    private InternalAccessDelegate mInternalAccessAdapter;
+    private final NativeGLDelegate mNativeGLDelegate;
     private final AwLayoutSizer mLayoutSizer;
     private final AwZoomControls mZoomControls;
     private final AwScrollOffsetManager mScrollOffsetManager;
@@ -172,7 +218,7 @@ public class AwContents {
     // Must call nativeUpdateLastHitTestData first to update this before use.
     private final HitTestData mPossiblyStaleHitTestData = new HitTestData();
 
-    private DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler;
+    private final DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler;
 
     // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the
     // picture listener API has not yet been enabled, or if it is using invalidation-only mode.
@@ -181,43 +227,97 @@ public class AwContents {
     private boolean mContainerViewFocused;
     private boolean mWindowFocused;
 
-    private boolean mClearViewActive;
-    private boolean mPictureListenerEnabled;
-
-    // These come from the compositor and are updated immediately (in contrast to the values in
+    // These come from the compositor and are updated synchronously (in contrast to the values in
     // ContentViewCore, which are updated at end of every frame).
     private float mPageScaleFactor = 1.0f;
+    private float mMinPageScaleFactor = 1.0f;
+    private float mMaxPageScaleFactor = 1.0f;
     private float mContentWidthDip;
     private float mContentHeightDip;
 
-    private AwAutofillManagerDelegate mAwAutofillManagerDelegate;
+    private AwAutofillClient mAwAutofillClient;
+
+    private AwPdfExporter mAwPdfExporter;
+
+    private AwViewMethods mAwViewMethods;
+    private final FullScreenTransitionsState mFullScreenTransitionsState;
+
+    // This flag indicates that ShouldOverrideUrlNavigation should be posted
+    // through the resourcethrottle. This is only used for popup windows.
+    private boolean mDeferredShouldOverrideUrlLoadingIsPendingForPopup;
+
+    // The framework may temporarily detach our container view, for example during layout if
+    // we are a child of a ListView. This may cause many toggles of View focus, which we suppress
+    // when in this state.
+    private boolean mTemporarilyDetached;
 
-    private ComponentCallbacks2 mComponentCallbacks;
+    // True when this AwContents has been destroyed.
+    // Do not use directly, call isDestroyed() instead.
+    private boolean mIsDestroyed = false;
 
     private static final class DestroyRunnable implements Runnable {
-        private int mNativeAwContents;
-        private DestroyRunnable(int nativeAwContents) {
+        private final long mNativeAwContents;
+        private DestroyRunnable(long nativeAwContents) {
             mNativeAwContents = nativeAwContents;
         }
         @Override
         public void run() {
-            // This is a no-op if not currently attached.
-            nativeOnDetachedFromWindow(mNativeAwContents);
             nativeDestroy(mNativeAwContents);
         }
     }
 
+    /**
+     * A class that stores the state needed to enter and exit fullscreen.
+     */
+    private static class FullScreenTransitionsState {
+        private final ViewGroup mInitialContainerView;
+        private final InternalAccessDelegate mInitialInternalAccessAdapter;
+        private final AwViewMethods mInitialAwViewMethods;
+        private FullScreenView mFullScreenView;
+
+        private FullScreenTransitionsState(ViewGroup initialContainerView,
+                InternalAccessDelegate initialInternalAccessAdapter,
+                AwViewMethods initialAwViewMethods) {
+            mInitialContainerView = initialContainerView;
+            mInitialInternalAccessAdapter = initialInternalAccessAdapter;
+            mInitialAwViewMethods = initialAwViewMethods;
+        }
+
+        private void enterFullScreen(FullScreenView fullScreenView) {
+            mFullScreenView = fullScreenView;
+        }
+
+        private void exitFullScreen() {
+            mFullScreenView = null;
+        }
+
+        private boolean isFullScreen() {
+            return mFullScreenView != null;
+        }
+
+        private ViewGroup getInitialContainerView() {
+            return mInitialContainerView;
+        }
+
+        private InternalAccessDelegate getInitialInternalAccessDelegate() {
+            return mInitialInternalAccessAdapter;
+        }
+
+        private AwViewMethods getInitialAwViewMethods() {
+            return mInitialAwViewMethods;
+        }
+
+        private FullScreenView getFullScreenView() {
+            return mFullScreenView;
+        }
+    }
+
     // Reference to the active mNativeAwContents pointer while it is active use
     // (ie before it is destroyed).
     private CleanupReference mCleanupReference;
 
-    // A list of references to native pointers where the Java counterpart has been
-    // destroyed, but are held here because they are waiting for onDetachFromWindow
-    // to release GL resources. This is cleared inside onDetachFromWindow.
-    private List<CleanupReference> mPendingDetachCleanupReferences;
-
     //--------------------------------------------------------------------------------------------
-    private class IoThreadClientImpl implements AwContentsIoThreadClient {
+    private class IoThreadClientImpl extends AwContentsIoThreadClient {
         // All methods are called on the IO thread.
 
         @Override
@@ -226,21 +326,22 @@ public class AwContents {
         }
 
         @Override
-        public InterceptedRequestData shouldInterceptRequest(final String url,
-                boolean isMainFrame) {
-            InterceptedRequestData interceptedRequestData;
+        public AwWebResourceResponse shouldInterceptRequest(
+                AwContentsClient.ShouldInterceptRequestParams params) {
+            String url = params.url;
+            AwWebResourceResponse awWebResourceResponse;
             // Return the response directly if the url is default video poster url.
-            interceptedRequestData = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url);
-            if (interceptedRequestData != null) return interceptedRequestData;
+            awWebResourceResponse = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url);
+            if (awWebResourceResponse != null) return awWebResourceResponse;
 
-            interceptedRequestData = mContentsClient.shouldInterceptRequest(url);
+            awWebResourceResponse = mContentsClient.shouldInterceptRequest(params);
 
-            if (interceptedRequestData == null) {
+            if (awWebResourceResponse == null) {
                 mContentsClient.getCallbackHelper().postOnLoadResource(url);
             }
 
-            if (isMainFrame && interceptedRequestData != null &&
-                    interceptedRequestData.getData() == null) {
+            if (params.isMainFrame && awWebResourceResponse != null &&
+                    awWebResourceResponse.getData() == null) {
                 // In this case the intercepted URLRequest job will simulate an empty response
                 // which doesn't trigger the onReceivedError callback. For WebViewClassic
                 // compatibility we synthesize that callback. http://crbug.com/180950
@@ -248,7 +349,7 @@ public class AwContents {
                         ErrorCodeConversionHelper.ERROR_UNKNOWN,
                         null /* filled in by the glue layer */, url);
             }
-            return interceptedRequestData;
+            return awWebResourceResponse;
         }
 
         @Override
@@ -267,11 +368,13 @@ public class AwContents {
         }
 
         @Override
-        public void onDownloadStart(String url,
-                                    String userAgent,
-                                    String contentDisposition,
-                                    String mimeType,
-                                    long contentLength) {
+        public boolean shouldAcceptThirdPartyCookies() {
+            return mSettings.getAcceptThirdPartyCookies();
+        }
+
+        @Override
+        public void onDownloadStart(String url, String userAgent,
+                String contentDisposition, String mimeType, long contentLength) {
             mContentsClient.getCallbackHelper().postOnDownloadStart(url, userAgent,
                     contentDisposition, mimeType, contentLength);
         }
@@ -283,62 +386,33 @@ public class AwContents {
     }
 
     //--------------------------------------------------------------------------------------------
+    // When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation
+    // here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading
+    // callback to the correct WebViewClient that is associated with the WebView.
+    // Otherwise, use this delegate only to post onPageStarted messages.
+    //
+    // We are not using WebContentsObserver.didStartLoading because of stale URLs, out of order
+    // onPageStarted's and double onPageStarted's.
+    //
     private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
-        private String mLastLoadUrlAddress;
-
-        public void onUrlLoadRequested(String url) {
-            mLastLoadUrlAddress = url;
-        }
-
         @Override
         public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
             final String url = navigationParams.url;
-            final int transitionType = navigationParams.pageTransitionType;
-            final boolean isLoadUrl =
-                    (transitionType & PageTransitionTypes.PAGE_TRANSITION_FROM_API) != 0;
-            final boolean isBackForward =
-                    (transitionType & PageTransitionTypes.PAGE_TRANSITION_FORWARD_BACK) != 0;
-            final boolean isReload =
-                    (transitionType & PageTransitionTypes.PAGE_TRANSITION_CORE_MASK) ==
-                    PageTransitionTypes.PAGE_TRANSITION_RELOAD;
-            final boolean isRedirect = navigationParams.isRedirect;
-
             boolean ignoreNavigation = false;
-
-            // Any navigation from loadUrl, goBack/Forward, or reload, are considered application
-            // initiated and hence will not yield a shouldOverrideUrlLoading() callback.
-            // TODO(joth): Using PageTransitionTypes should be sufficient to determine all app
-            // initiated navigations, and so mLastLoadUrlAddress should be removed.
-            if ((isLoadUrl && !isRedirect) || isBackForward || isReload ||
-                    mLastLoadUrlAddress != null && mLastLoadUrlAddress.equals(url)) {
-                // Support the case where the user clicks on a link that takes them back to the
-                // same page.
-                mLastLoadUrlAddress = null;
-
-                // If the embedder requested the load of a certain URL via the loadUrl API, then we
-                // do not offer it to AwContentsClient.shouldOverrideUrlLoading.
-                // The embedder is also not allowed to intercept POST requests because of
-                // crbug.com/155250.
-            } else if (!navigationParams.isPost) {
-                ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url);
+            if (mDeferredShouldOverrideUrlLoadingIsPendingForPopup) {
+                mDeferredShouldOverrideUrlLoadingIsPendingForPopup = false;
+                // If this is used for all navigations in future, cases for application initiated
+                // load, redirect and backforward should also be filtered out.
+                if (!navigationParams.isPost) {
+                    ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url);
+                }
             }
-
-            // The existing contract is that shouldOverrideUrlLoading callbacks are delivered before
-            // onPageStarted callbacks; third party apps depend on this behavior.
-            // Using a ResouceThrottle to implement the navigation interception feature results in
-            // the WebContentsObserver.didStartLoading callback happening before the
-            // ResourceThrottle has a chance to run.
-            // To preserve the ordering the onPageStarted callback is synthesized from the
-            // shouldOverrideUrlLoading, and only if the navigation was not ignored (this
-            // balances out with the onPageFinished callback, which is suppressed in the
-            // AwContentsClient if the navigation was ignored).
+            // The shouldOverrideUrlLoading call might have resulted in posting messages to the
+            // UI thread. Using sendMessage here (instead of calling onPageStarted directly)
+            // will allow those to run in order.
             if (!ignoreNavigation) {
-                // The shouldOverrideUrlLoading call might have resulted in posting messages to the
-                // UI thread. Using sendMessage here (instead of calling onPageStarted directly)
-                // will allow those to run in order.
                 mContentsClient.getCallbackHelper().postOnPageStarted(url);
             }
-
             return ignoreNavigation;
         }
     }
@@ -356,15 +430,14 @@ public class AwContents {
         }
 
         @Override
-        public void setFixedLayoutSize(int widthDip, int heightDip) {
-            if (mNativeAwContents == 0) return;
-            nativeSetFixedLayoutSize(mNativeAwContents, widthDip, heightDip);
+        public boolean isLayoutParamsHeightWrapContent() {
+            return mContainerView.getLayoutParams() != null &&
+                    mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
         }
 
         @Override
-        public boolean isLayoutParamsHeightWrapContent() {
-            return mContainerView.getLayoutParams() != null &&
-                mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
+        public void setForceZeroLayoutHeight(boolean forceZeroHeight) {
+            getSettings().setForceZeroLayoutHeight(forceZeroHeight);
         }
     }
 
@@ -384,8 +457,7 @@ public class AwContents {
 
         @Override
         public void scrollNativeTo(int x, int y) {
-            if (mNativeAwContents == 0) return;
-            nativeScrollTo(mNativeAwContents, x, y);
+            if (!isDestroyed()) nativeScrollTo(mNativeAwContents, x, y);
         }
 
         @Override
@@ -405,9 +477,9 @@ public class AwContents {
     }
 
     //--------------------------------------------------------------------------------------------
-    private class AwGestureStateListener implements ContentViewCore.GestureStateListener {
+    private class AwGestureStateListener extends GestureStateListener {
         @Override
-        public void onPinchGestureStart() {
+        public void onPinchStarted() {
             // While it's possible to re-layout the view during a pinch gesture, the effect is very
             // janky (especially that the page scale update notification comes from the renderer
             // main thread, not from the impl thread, so it's usually out of sync with what's on
@@ -418,23 +490,18 @@ public class AwContents {
         }
 
         @Override
-        public void onPinchGestureEnd() {
+        public void onPinchEnded() {
             mLayoutSizer.unfreezeLayoutRequests();
         }
 
         @Override
-        public void onFlingStartGesture(int velocityX, int velocityY) {
-            mScrollOffsetManager.onFlingStartGesture(velocityX, velocityY);
-        }
-
-        @Override
         public void onFlingCancelGesture() {
             mScrollOffsetManager.onFlingCancelGesture();
         }
 
         @Override
-        public void onUnhandledFlingStartEvent() {
-            mScrollOffsetManager.onUnhandledFlingStartEvent();
+        public void onUnhandledFlingStartEvent(int velocityX, int velocityY) {
+            mScrollOffsetManager.onUnhandledFlingStartEvent(velocityX, velocityY);
         }
 
         @Override
@@ -445,19 +512,19 @@ public class AwContents {
 
     //--------------------------------------------------------------------------------------------
     private class AwComponentCallbacks implements ComponentCallbacks2 {
-          @Override
-          public void onTrimMemory(int level) {
-              if (mNativeAwContents == 0) return;
-              nativeTrimMemory(mNativeAwContents, level);
-          }
-
-          @Override
-          public void onLowMemory() {
-          }
-
-          @Override
-          public void onConfigurationChanged(Configuration configuration) {
-          }
+        @Override
+        public void onTrimMemory(final int level) {
+            if (isDestroyed()) return;
+            boolean visibleRectEmpty = getGlobalVisibleRect().isEmpty();
+            final boolean visible = mIsViewVisible && mIsWindowVisible && !visibleRectEmpty;
+            nativeTrimMemory(mNativeAwContents, level, visible);
+        }
+
+        @Override
+        public void onLowMemory() {}
+
+        @Override
+        public void onConfigurationChanged(Configuration configuration) {}
     };
 
     //--------------------------------------------------------------------------------------------
@@ -473,71 +540,51 @@ public class AwContents {
     /**
      * @param browserContext the browsing context to associate this view contents with.
      * @param containerView the view-hierarchy item this object will be bound to.
+     * @param context the context to use, usually containerView.getContext().
      * @param internalAccessAdapter to access private methods on containerView.
-     * @param contentsClient will receive API callbacks from this WebView Contents
-     * @param isAccessFromFileURLsGrantedByDefault passed to AwSettings.
+     * @param nativeGLDelegate to access the GL functor provided by the WebView.
+     * @param contentsClient will receive API callbacks from this WebView Contents.
+     * @param awSettings AwSettings instance used to configure the AwContents.
      *
      * This constructor uses the default view sizing policy.
      */
-    public AwContents(AwBrowserContext browserContext, ViewGroup containerView,
-            InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient,
-            boolean isAccessFromFileURLsGrantedByDefault) {
-        this(browserContext, containerView, internalAccessAdapter, contentsClient,
-                isAccessFromFileURLsGrantedByDefault, new AwLayoutSizer());
-    }
-
-    public AwContents(AwBrowserContext browserContext, ViewGroup containerView,
-            InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient,
-            boolean isAccessFromFileURLsGrantedByDefault, AwLayoutSizer layoutSizer) {
-        this(browserContext, containerView, internalAccessAdapter, contentsClient,
-                isAccessFromFileURLsGrantedByDefault, layoutSizer, false);
-    }
-
-    private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView,
-            InternalAccessDelegate internalDispatcher, int nativeWebContents,
-            ContentViewCore.GestureStateListener pinchGestureStateListener,
-            ContentViewClient contentViewClient,
-            ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) {
-      ContentViewCore contentViewCore = new ContentViewCore(containerView.getContext());
-      // Note INPUT_EVENTS_DELIVERED_IMMEDIATELY is passed to avoid triggering vsync in the
-      // compositor, not because input events are delivered immediately.
-      contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, null,
-                ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
-      contentViewCore.setGestureStateListener(pinchGestureStateListener);
-      contentViewCore.setContentViewClient(contentViewClient);
-      contentViewCore.setZoomControlsDelegate(zoomControlsDelegate);
-      return contentViewCore;
+    public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
+            InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate,
+            AwContentsClient contentsClient, AwSettings awSettings) {
+        this(browserContext, containerView, context, internalAccessAdapter, nativeGLDelegate,
+                contentsClient, awSettings, new DependencyFactory());
     }
 
     /**
-     * @param layoutSizer the AwLayoutSizer instance implementing the sizing policy for the view.
+     * @param dependencyFactory an instance of the DependencyFactory used to provide instances of
+     *                          classes that this class depends on.
      *
      * This version of the constructor is used in test code to inject test versions of the above
-     * documented classes
+     * documented classes.
      */
-    public AwContents(AwBrowserContext browserContext, ViewGroup containerView,
-            InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient,
-            boolean isAccessFromFileURLsGrantedByDefault, AwLayoutSizer layoutSizer,
-            boolean supportsLegacyQuirks) {
-        this(browserContext, containerView, internalAccessAdapter, contentsClient,
-                layoutSizer, new AwSettings(containerView.getContext(),
-                        isAccessFromFileURLsGrantedByDefault, supportsLegacyQuirks));
-    }
-
-    public AwContents(AwBrowserContext browserContext, ViewGroup containerView,
-            InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient,
-            AwLayoutSizer layoutSizer, AwSettings settings) {
+    public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
+            InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate,
+            AwContentsClient contentsClient, AwSettings settings,
+            DependencyFactory dependencyFactory) {
         mBrowserContext = browserContext;
         mContainerView = containerView;
+        mContext = context;
         mInternalAccessAdapter = internalAccessAdapter;
+        mNativeGLDelegate = nativeGLDelegate;
         mContentsClient = contentsClient;
-        mLayoutSizer = layoutSizer;
+        mAwViewMethods = new AwViewMethodsImpl();
+        mFullScreenTransitionsState = new FullScreenTransitionsState(
+                mContainerView, mInternalAccessAdapter, mAwViewMethods);
+        mContentViewClient = new AwContentViewClient(contentsClient, settings, this, mContext);
+        mLayoutSizer = dependencyFactory.createLayoutSizer();
         mSettings = settings;
-        mDIPScale = DeviceDisplayInfo.create(mContainerView.getContext()).getDIPScale();
+        mDIPScale = DeviceDisplayInfo.create(mContext).getDIPScale();
         mLayoutSizer.setDelegate(new AwLayoutSizerDelegate());
         mLayoutSizer.setDIPScale(mDIPScale);
-        mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient, mContainerView);
-        mContentsClientBridge = new AwContentsClientBridge(contentsClient);
+        mWebContentsDelegate = new AwWebContentsDelegateAdapter(
+                contentsClient, mContainerView, mContext);
+        mContentsClientBridge = new AwContentsClientBridge(contentsClient,
+                mBrowserContext.getKeyStore(), AwContentsStatics.getClientCertLookupTable());
         mZoomControls = new AwZoomControls(this);
         mIoThreadClient = new IoThreadClientImpl();
         mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl();
@@ -545,9 +592,11 @@ public class AwContents {
         AwSettings.ZoomSupportChangeListener zoomListener =
                 new AwSettings.ZoomSupportChangeListener() {
                     @Override
-                    public void onGestureZoomSupportChanged(boolean supportsGestureZoom) {
-                        mContentViewCore.updateMultiTouchZoomSupport(supportsGestureZoom);
-                        mContentViewCore.updateDoubleTapSupport(supportsGestureZoom);
+                    public void onGestureZoomSupportChanged(
+                            boolean supportsDoubleTapZoom, boolean supportsMultiTouchZoom) {
+                        if (isDestroyed()) return;
+                        mContentViewCore.updateDoubleTapSupport(supportsDoubleTapZoom);
+                        mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom);
                     }
 
                 };
@@ -556,31 +605,158 @@ public class AwContents {
         mSettings.setDefaultVideoPosterURL(
                 mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL());
         mSettings.setDIPScale(mDIPScale);
-        mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate(),
-                new OverScroller(mContainerView.getContext()));
+        mScrollOffsetManager = dependencyFactory.createScrollOffsetManager(
+                new AwScrollOffsetManagerDelegate(), new OverScroller(mContext));
         mScrollAccessibilityHelper = new ScrollAccessibilityHelper(mContainerView);
 
         setOverScrollMode(mContainerView.getOverScrollMode());
         setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle());
-        mContainerView.addOnLayoutChangeListener(new AwLayoutChangeListener());
+        mLayoutChangeListener = new AwLayoutChangeListener();
+        mContainerView.addOnLayoutChangeListener(mLayoutChangeListener);
 
         setNewAwContents(nativeInit(mBrowserContext));
 
-        onVisibilityChanged(mContainerView, mContainerView.getVisibility());
-        onWindowVisibilityChanged(mContainerView.getWindowVisibility());
+        onContainerViewChanged();
+    }
+
+    private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView,
+            Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents,
+            GestureStateListener gestureStateListener,
+            ContentViewClient contentViewClient,
+            ContentViewCore.ZoomControlsDelegate zoomControlsDelegate,
+            WindowAndroid windowAndroid) {
+        ContentViewCore contentViewCore = new ContentViewCore(context);
+        contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents,
+                windowAndroid);
+        contentViewCore.addGestureStateListener(gestureStateListener);
+        contentViewCore.setContentViewClient(contentViewClient);
+        contentViewCore.setZoomControlsDelegate(zoomControlsDelegate);
+        return contentViewCore;
+    }
+
+    boolean isFullScreen() {
+        return mFullScreenTransitionsState.isFullScreen();
     }
 
     /**
-     * Common initialization routine for adopting a native AwContents instance into this
+     * Transitions this {@link AwContents} to fullscreen mode and returns the
+     * {@link View} where the contents will be drawn while in fullscreen, or null
+     * if this AwContents has already been destroyed.
+     */
+    View enterFullScreen() {
+        assert !isFullScreen();
+        if (isDestroyed()) return null;
+
+        // Detach to tear down the GL functor if this is still associated with the old
+        // container view. It will be recreated during the next call to onDraw attached to
+        // the new container view.
+        onDetachedFromWindow();
+
+        // In fullscreen mode FullScreenView owns the AwViewMethodsImpl and AwContents
+        // a NullAwViewMethods.
+        FullScreenView fullScreenView = new FullScreenView(mContext, mAwViewMethods);
+        mFullScreenTransitionsState.enterFullScreen(fullScreenView);
+        mAwViewMethods = new NullAwViewMethods(this, mInternalAccessAdapter, mContainerView);
+        mContainerView.removeOnLayoutChangeListener(mLayoutChangeListener);
+        fullScreenView.addOnLayoutChangeListener(mLayoutChangeListener);
+
+        // Associate this AwContents with the FullScreenView.
+        setInternalAccessAdapter(fullScreenView.getInternalAccessAdapter());
+        setContainerView(fullScreenView);
+
+        return fullScreenView;
+    }
+
+    /**
+     * Returns this {@link AwContents} to embedded mode, where the {@link AwContents} are drawn
+     * in the WebView.
+     */
+    void exitFullScreen() {
+        if (!isFullScreen() || isDestroyed())
+            // exitFullScreen() can be called without a prior call to enterFullScreen() if a
+            // "misbehave" app overrides onShowCustomView but does not add the custom view to
+            // the window. Exiting avoids a crash.
+            return;
+
+        // Detach to tear down the GL functor if this is still associated with the old
+        // container view. It will be recreated during the next call to onDraw attached to
+        // the new container view.
+        // NOTE: we cannot use mAwViewMethods here because its type is NullAwViewMethods.
+        AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods();
+        awViewMethodsImpl.onDetachedFromWindow();
+
+        // Swap the view delegates. In embedded mode the FullScreenView owns a
+        // NullAwViewMethods and AwContents the AwViewMethodsImpl.
+        FullScreenView fullscreenView = mFullScreenTransitionsState.getFullScreenView();
+        fullscreenView.setAwViewMethods(new NullAwViewMethods(
+                this, fullscreenView.getInternalAccessAdapter(), fullscreenView));
+        mAwViewMethods = awViewMethodsImpl;
+        ViewGroup initialContainerView = mFullScreenTransitionsState.getInitialContainerView();
+        initialContainerView.addOnLayoutChangeListener(mLayoutChangeListener);
+        fullscreenView.removeOnLayoutChangeListener(mLayoutChangeListener);
+
+        // Re-associate this AwContents with the WebView.
+        setInternalAccessAdapter(mFullScreenTransitionsState.getInitialInternalAccessDelegate());
+        setContainerView(initialContainerView);
+
+        mFullScreenTransitionsState.exitFullScreen();
+    }
+
+    private void setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter) {
+        mInternalAccessAdapter = internalAccessAdapter;
+        mContentViewCore.setContainerViewInternals(mInternalAccessAdapter);
+    }
+
+    private void setContainerView(ViewGroup newContainerView) {
+        mContainerView = newContainerView;
+        mContentViewCore.setContainerView(mContainerView);
+        if (mAwPdfExporter != null) {
+            mAwPdfExporter.setContainerView(mContainerView);
+        }
+        mWebContentsDelegate.setContainerView(mContainerView);
+
+        onContainerViewChanged();
+    }
+
+    /**
+     * Reconciles the state of this AwContents object with the state of the new container view.
+     */
+    private void onContainerViewChanged() {
+        // NOTE: mAwViewMethods is used by the old container view, the WebView, so it might refer
+        // to a NullAwViewMethods when in fullscreen. To ensure that the state is reconciled with
+        // the new container view correctly, we bypass mAwViewMethods and use the real
+        // implementation directly.
+        AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods();
+        awViewMethodsImpl.onVisibilityChanged(mContainerView, mContainerView.getVisibility());
+        awViewMethodsImpl.onWindowVisibilityChanged(mContainerView.getWindowVisibility());
+
+        // We should stop running WebView tests in JellyBean devices, see crbug/161864.
+        // Until then we skip calling isAttachedToWindow() as it has only been introduced in K.
+        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT
+                || mContainerView.isAttachedToWindow()) {
+            awViewMethodsImpl.onAttachedToWindow();
+        } else {
+            awViewMethodsImpl.onDetachedFromWindow();
+        }
+        awViewMethodsImpl.onSizeChanged(
+                mContainerView.getWidth(), mContainerView.getHeight(), 0, 0);
+        awViewMethodsImpl.onWindowFocusChanged(mContainerView.hasWindowFocus());
+        awViewMethodsImpl.onFocusChanged(mContainerView.hasFocus(), 0, null);
+        mContainerView.requestLayout();
+    }
+
+    /* Common initialization routine for adopting a native AwContents instance into this
      * java instance.
      *
      * TAKE CARE! This method can get called multiple times per java instance. Code accordingly.
      * ^^^^^^^^^  See the native class declaration for more details on relative object lifetimes.
      */
-    private void setNewAwContents(int newAwContentsPtr) {
+    private void setNewAwContents(long newAwContentsPtr) {
         if (mNativeAwContents != 0) {
-            destroy();
+            destroyNatives();
             mContentViewCore = null;
+            mWebContents = null;
+            mNavigationController = null;
         }
 
         assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null;
@@ -594,28 +770,37 @@ public class AwContents {
         // bind all the native->java relationships.
         mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents));
 
-        int nativeWebContents = nativeGetWebContents(mNativeAwContents);
+        long nativeWebContents = nativeGetWebContents(mNativeAwContents);
+
+        mWindowAndroid = mContext instanceof Activity ?
+                new ActivityWindowAndroid((Activity) mContext) :
+                new WindowAndroid(mContext.getApplicationContext());
         mContentViewCore = createAndInitializeContentViewCore(
-                mContainerView, mInternalAccessAdapter, nativeWebContents,
-                new AwGestureStateListener(), mContentsClient.getContentViewClient(),
-                mZoomControls);
+                mContainerView, mContext, mInternalAccessAdapter, nativeWebContents,
+                new AwGestureStateListener(), mContentViewClient, mZoomControls, mWindowAndroid);
         nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
                 mIoThreadClient, mInterceptNavigationDelegate);
-        mContentsClient.installWebContentsObserver(mContentViewCore);
+        mWebContents = mContentViewCore.getWebContents();
+        mNavigationController = mWebContents.getNavigationController();
+        installWebContentsObserver();
         mSettings.setWebContents(nativeWebContents);
         nativeSetDipScale(mNativeAwContents, (float) mDIPScale);
-        updateGlobalVisibleRect();
-
-        // The only call to onShow. onHide should never be called.
         mContentViewCore.onShow();
-   }
+    }
+
+    private void installWebContentsObserver() {
+        if (mWebContentsObserver != null) {
+            mWebContentsObserver.detachFromWebContents();
+        }
+        mWebContentsObserver = new AwWebContentsObserver(mWebContents, mContentsClient);
+    }
 
     /**
      * Called on the "source" AwContents that is opening the popup window to
      * provide the AwContents to host the pop up content.
      */
     public void supplyContentsForPopup(AwContents newContents) {
-        int popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents);
+        long popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents);
         if (popupNativeAwContents == 0) {
             Log.w(TAG, "Popup WebView bind failed: no pending content.");
             if (newContents != null) newContents.destroy();
@@ -631,7 +816,8 @@ public class AwContents {
 
     // Recap: supplyContentsForPopup() is called on the parent window's content, this method is
     // called on the popup window's content.
-    private void receivePopupContents(int popupNativeAwContents) {
+    private void receivePopupContents(long popupNativeAwContents) {
+        mDeferredShouldOverrideUrlLoadingIsPendingForPopup = true;
         // Save existing view state.
         final boolean wasAttached = mIsAttachedToWindow;
         final boolean wasViewVisible = mIsViewVisible;
@@ -645,75 +831,132 @@ public class AwContents {
         if (wasWindowFocused) onWindowFocusChanged(false);
         if (wasViewVisible) setViewVisibilityInternal(false);
         if (wasWindowVisible) setWindowVisibilityInternal(false);
+        if (wasAttached) onDetachedFromWindow();
         if (!wasPaused) onPause();
-        // Not calling onDetachedFromWindow here because native code requires GL context to release
-        // GL resources. This case is properly handled when destroy is called while still attached
-        // to window.
+
+        // Save injected JavaScript interfaces.
+        Map<String, Pair<Object, Class>> javascriptInterfaces =
+                new HashMap<String, Pair<Object, Class>>();
+        if (mContentViewCore != null) {
+            javascriptInterfaces.putAll(mContentViewCore.getJavascriptInterfaces());
+        }
 
         setNewAwContents(popupNativeAwContents);
 
         // Finally refresh all view state for mContentViewCore and mNativeAwContents.
         if (!wasPaused) onResume();
-        if (wasAttached) onAttachedToWindow();
+        if (wasAttached) {
+            onAttachedToWindow();
+            postInvalidateOnAnimation();
+        }
         onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0);
         if (wasWindowVisible) setWindowVisibilityInternal(true);
         if (wasViewVisible) setViewVisibilityInternal(true);
         if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused);
         if (wasFocused) onFocusChanged(true, 0, null);
+
+        // Restore injected JavaScript interfaces.
+        for (Map.Entry<String, Pair<Object, Class>> entry : javascriptInterfaces.entrySet()) {
+            @SuppressWarnings("unchecked")
+            Class<? extends Annotation> requiredAnnotation = entry.getValue().second;
+            mContentViewCore.addPossiblyUnsafeJavascriptInterface(
+                    entry.getValue().first,
+                    entry.getKey(),
+                    requiredAnnotation);
+        }
     }
 
     /**
-     * Deletes the native counterpart of this object. Normally happens immediately,
-     * but maybe deferred until the appropriate time for GL resource cleanup. Either way
-     * this is transparent to the caller: after this function returns the object is
-     * effectively dead and methods are no-ops.
+     * Destroys this object and deletes its native counterpart.
      */
     public void destroy() {
+        mIsDestroyed = true;
+        destroyNatives();
+    }
+
+    /**
+     * Deletes the native counterpart of this object.
+     */
+    private void destroyNatives() {
         if (mCleanupReference != null) {
-            // We explicitly do not null out the mContentViewCore reference here
-            // because ContentViewCore already has code to deal with the case
-            // methods are called on it after it's been destroyed, and other
-            // code relies on AwContents.mContentViewCore to be non-null.
+            assert mNativeAwContents != 0;
+            // If we are attached, we have to call native detach to clean up
+            // hardware resources.
+            if (mIsAttachedToWindow) {
+                nativeOnDetachedFromWindow(mNativeAwContents);
+            }
+
+            mWebContentsObserver.detachFromWebContents();
+            mWebContentsObserver = null;
             mContentViewCore.destroy();
+            mContentViewCore = null;
             mNativeAwContents = 0;
+            mWebContents = null;
+            mNavigationController = null;
 
-            // We cannot destroy immediately if we are still attached to the window.
-            // Instead if we make sure to null out the native pointer so there is no more native
-            // calls, and delay the actual destroy until onDetachedFromWindow.
-            if (mIsAttachedToWindow) {
-                if (mPendingDetachCleanupReferences == null) {
-                    mPendingDetachCleanupReferences = new ArrayList<CleanupReference>();
-                }
-                mPendingDetachCleanupReferences.add(mCleanupReference);
-            } else {
-                mCleanupReference.cleanupNow();
-            }
+            mCleanupReference.cleanupNow();
             mCleanupReference = null;
         }
 
-        assert !mContentViewCore.isAlive();
+        assert mContentViewCore == null;
+        assert mWebContents == null;
+        assert mNavigationController == null;
         assert mNativeAwContents == 0;
     }
 
+    private boolean isDestroyed() {
+        if (mIsDestroyed) {
+            assert mContentViewCore == null;
+            assert mWebContents == null;
+            assert mNavigationController == null;
+            assert mNativeAwContents == 0;
+        } else {
+            assert mContentViewCore != null;
+            assert mWebContents != null;
+            assert mNavigationController != null;
+            assert mNativeAwContents != 0;
+        }
+        return mIsDestroyed;
+    }
+
     @VisibleForTesting
     public ContentViewCore getContentViewCore() {
         return mContentViewCore;
     }
 
+    @VisibleForTesting
+    public WebContents getWebContents() {
+        return mWebContents;
+    }
+
+    @VisibleForTesting
+    public NavigationController getNavigationController() {
+        return mNavigationController;
+    }
+
     // Can be called from any thread.
     public AwSettings getSettings() {
         return mSettings;
     }
 
-    public static void setAwDrawSWFunctionTable(int functionTablePointer) {
+    public AwPdfExporter getPdfExporter() {
+        if (isDestroyed()) return null;
+        if (mAwPdfExporter == null) {
+            mAwPdfExporter = new AwPdfExporter(mContainerView);
+            nativeCreatePdfExporter(mNativeAwContents, mAwPdfExporter);
+        }
+        return mAwPdfExporter;
+    }
+
+    public static void setAwDrawSWFunctionTable(long functionTablePointer) {
         nativeSetAwDrawSWFunctionTable(functionTablePointer);
     }
 
-    public static void setAwDrawGLFunctionTable(int functionTablePointer) {
+    public static void setAwDrawGLFunctionTable(long functionTablePointer) {
         nativeSetAwDrawGLFunctionTable(functionTablePointer);
     }
 
-    public static int getAwDrawGLFunction() {
+    public static long getAwDrawGLFunction() {
         return nativeGetAwDrawGLFunction();
     }
 
@@ -722,6 +965,16 @@ public class AwContents {
     }
 
     /**
+     * Disables contents of JS-to-Java bridge objects to be inspectable using
+     * Object.keys() method and "for .. in" loops. This is intended for applications
+     * targeting earlier Android releases where this was not possible, and we want
+     * to ensure backwards compatible behavior.
+     */
+    public void disableJavascriptInterfacesInspection() {
+        if (!isDestroyed()) mContentViewCore.setAllowJavascriptInterfacesInspection(false);
+    }
+
+    /**
      * Intended for test code.
      * @return the number of native instances of this class.
      */
@@ -730,67 +983,37 @@ public class AwContents {
         return nativeGetNativeInstanceCount();
     }
 
-    public int getAwDrawGLViewContext() {
+    public long getAwDrawGLViewContext() {
         // Only called during early construction, so client should not have had a chance to
         // call destroy yet.
-        assert mNativeAwContents != 0;
+        assert !isDestroyed();
 
         // Using the native pointer as the returned viewContext. This is matched by the
         // reinterpret_cast back to BrowserViewRenderer pointer in the native DrawGLFunction.
         return nativeGetAwDrawGLViewContext(mNativeAwContents);
     }
 
-    // This is only to avoid heap allocations inside updateGLobalVisibleRect. It should treated
+    // This is only to avoid heap allocations inside getGlobalVisibleRect. It should treated
     // as a local variable in the function and not used anywhere else.
     private static final Rect sLocalGlobalVisibleRect = new Rect();
 
-    @CalledByNative
-    private void updateGlobalVisibleRect() {
-        if (mNativeAwContents == 0) return;
+    private Rect getGlobalVisibleRect() {
         if (!mContainerView.getGlobalVisibleRect(sLocalGlobalVisibleRect)) {
             sLocalGlobalVisibleRect.setEmpty();
         }
-
-        nativeSetGlobalVisibleRect(mNativeAwContents, sLocalGlobalVisibleRect.left,
-                sLocalGlobalVisibleRect.top, sLocalGlobalVisibleRect.right,
-                sLocalGlobalVisibleRect.bottom);
+        return sLocalGlobalVisibleRect;
     }
 
     //--------------------------------------------------------------------------------------------
     //  WebView[Provider] method implementations (where not provided by ContentViewCore)
     //--------------------------------------------------------------------------------------------
 
-    // Only valid within onDraw().
-    private final Rect mClipBoundsTemporary = new Rect();
-
     public void onDraw(Canvas canvas) {
-        if (mNativeAwContents == 0) {
-            canvas.drawColor(getEffectiveBackgroundColor());
-            return;
-        }
-
-        mScrollOffsetManager.syncScrollOffsetFromOnDraw();
-        canvas.getClipBounds(mClipBoundsTemporary);
-
-        if (mClearViewActive) {
-            canvas.drawColor(getEffectiveBackgroundColor());
-        } else if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(),
-                mContainerView.getScrollX(), mContainerView.getScrollY(),
-                mClipBoundsTemporary.left, mClipBoundsTemporary.top,
-                mClipBoundsTemporary.right, mClipBoundsTemporary.bottom)) {
-            Log.w(TAG, "nativeOnDraw failed; clearing to background color.");
-            canvas.drawColor(getEffectiveBackgroundColor());
-        }
-
-        if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas,
-                    mScrollOffsetManager.computeMaximumHorizontalScrollOffset(),
-                    mScrollOffsetManager.computeMaximumVerticalScrollOffset())) {
-            mContainerView.invalidate();
-        }
+        mAwViewMethods.onDraw(canvas);
     }
 
     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
     public int getContentHeightCss() {
@@ -802,16 +1025,14 @@ public class AwContents {
     }
 
     public Picture capturePicture() {
-        if (mNativeAwContents == 0) return null;
+        if (isDestroyed()) return null;
         return new AwPicture(nativeCapturePicture(mNativeAwContents,
-                    mScrollOffsetManager.computeHorizontalScrollRange(),
-                    mScrollOffsetManager.computeVerticalScrollRange()));
+                mScrollOffsetManager.computeHorizontalScrollRange(),
+                mScrollOffsetManager.computeVerticalScrollRange()));
     }
 
     public void clearView() {
-        mClearViewActive = true;
-        syncOnNewPictureStateToNative();
-        mContainerView.invalidate();
+        if (!isDestroyed()) nativeClearView(mNativeAwContents);
     }
 
     /**
@@ -820,7 +1041,7 @@ public class AwContents {
      * @param invalidationOnly Flag to call back only on invalidation without providing a picture.
      */
     public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) {
-        if (mNativeAwContents == 0) return;
+        if (isDestroyed()) return;
         if (invalidationOnly) {
             mPictureListenerContentProvider = null;
         } else if (enabled && mPictureListenerContentProvider == null) {
@@ -831,27 +1052,19 @@ public class AwContents {
                 }
             };
         }
-        mPictureListenerEnabled = enabled;
-        syncOnNewPictureStateToNative();
-    }
-
-    private void syncOnNewPictureStateToNative() {
-        nativeEnableOnNewPicture(mNativeAwContents, mPictureListenerEnabled || mClearViewActive);
+        nativeEnableOnNewPicture(mNativeAwContents, enabled);
     }
 
     public void findAllAsync(String searchString) {
-        if (mNativeAwContents == 0) return;
-        nativeFindAllAsync(mNativeAwContents, searchString);
+        if (!isDestroyed()) nativeFindAllAsync(mNativeAwContents, searchString);
     }
 
     public void findNext(boolean forward) {
-        if (mNativeAwContents == 0) return;
-        nativeFindNext(mNativeAwContents, forward);
+        if (!isDestroyed()) nativeFindNext(mNativeAwContents, forward);
     }
 
     public void clearMatches() {
-        if (mNativeAwContents == 0) return;
-        nativeClearMatches(mNativeAwContents);
+        if (!isDestroyed()) nativeClearMatches(mNativeAwContents);
     }
 
     /**
@@ -873,8 +1086,7 @@ public class AwContents {
                 ThreadUtils.runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
-                        if (mNativeAwContents == 0) return;
-                        nativeAddVisitedLinks(mNativeAwContents, value);
+                        if (!isDestroyed()) nativeAddVisitedLinks(mNativeAwContents, value);
                     }
                 });
             }
@@ -887,11 +1099,13 @@ public class AwContents {
      * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
      * off during user input).
      *
-     * @param pararms Parameters for this load.
+     * @param params Parameters for this load.
      */
     public void loadUrl(LoadUrlParams params) {
+        if (isDestroyed()) return;
+
         if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA &&
-            !params.isBaseUrlDataScheme()) {
+                !params.isBaseUrlDataScheme()) {
             // This allows data URLs with a non-data base URL access to file:///android_asset/ and
             // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also
             // allow access to file:// URLs (subject to OS level permission checks).
@@ -900,8 +1114,8 @@ public class AwContents {
 
         // If we are reloading the same url, then set transition type as reload.
         if (params.getUrl() != null &&
-            params.getUrl().equals(mContentViewCore.getUrl()) &&
-            params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) {
+                params.getUrl().equals(mWebContents.getUrl()) &&
+                params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) {
             params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_RELOAD);
         }
         params.setTransitionType(
@@ -911,24 +1125,42 @@ public class AwContents {
         // every time the user agent in AwSettings is modified.
         params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
 
-        mContentViewCore.loadUrl(params);
 
-        suppressInterceptionForThisNavigation();
+        // We don't pass extra headers to the content layer, as WebViewClassic
+        // was adding them in a very narrow set of conditions. See http://crbug.com/306873
+        // However, if the embedder is attempting to inject a Referer header for their
+        // loadUrl call, then we set that separately and remove it from the extra headers map/
+        final String referer = "referer";
+        Map<String, String> extraHeaders = params.getExtraHeaders();
+        if (extraHeaders != null) {
+            for (String header : extraHeaders.keySet()) {
+                if (referer.equals(header.toLowerCase(Locale.US))) {
+                    params.setReferrer(new Referrer(extraHeaders.remove(header), 1));
+                    params.setExtraHeaders(extraHeaders);
+                    break;
+                }
+            }
+        }
+
+        nativeSetExtraHeadersForUrl(
+                mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString());
+        params.setExtraHeaders(new HashMap<String, String>());
+
+        mNavigationController.loadUrl(params);
 
         // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit.
         // Chromium does not use this use code path and the best emulation of this behavior to call
         // request visited links once on the first URL load of the WebView.
         if (!mHasRequestedVisitedHistoryFromClient) {
-          mHasRequestedVisitedHistoryFromClient = true;
-          requestVisitedHistoryFromClient();
+            mHasRequestedVisitedHistoryFromClient = true;
+            requestVisitedHistoryFromClient();
         }
-    }
 
-    private void suppressInterceptionForThisNavigation() {
-        if (mInterceptNavigationDelegate != null) {
-            // getUrl returns a sanitized address in the same format that will be used for
-            // callbacks, so it's safe to use string comparison as an equality check later on.
-            mInterceptNavigationDelegate.onUrlLoadRequested(mContentViewCore.getUrl());
+        if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA &&
+                params.getBaseUrl() != null) {
+            // Data loads with a base url will be resolved in Blink, and not cause an onPageStarted
+            // event to be sent. Sending the callback directly from here.
+            mContentsClient.getCallbackHelper().postOnPageStarted(params.getBaseUrl());
         }
     }
 
@@ -938,35 +1170,40 @@ public class AwContents {
      * @return The URL of the current page or null if it's empty.
      */
     public String getUrl() {
-        String url =  mContentViewCore.getUrl();
+        if (isDestroyed()) return null;
+        String url =  mWebContents.getUrl();
         if (url == null || url.trim().isEmpty()) return null;
         return url;
     }
 
     public void requestFocus() {
-        if (mNativeAwContents == 0) return;
-        if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) {
-            nativeFocusFirstNode(mNativeAwContents);
-        }
+        mAwViewMethods.requestFocus();
     }
 
     public void setBackgroundColor(int color) {
         mBaseBackgroundColor = color;
-        if (mNativeAwContents != 0) nativeSetBackgroundColor(mNativeAwContents, color);
+        if (!isDestroyed()) nativeSetBackgroundColor(mNativeAwContents, color);
     }
 
-    private int getEffectiveBackgroundColor() {
+    /**
+     * @see android.view.View#setLayerType()
+     */
+    public void setLayerType(int layerType, Paint paint) {
+        mAwViewMethods.setLayerType(layerType, paint);
+    }
+
+    int getEffectiveBackgroundColor() {
         // Do not ask the ContentViewCore for the background color, as it will always
         // report white prior to initial navigation or post destruction,  whereas we want
         // to use the client supplied base value in those cases.
-        if (mNativeAwContents == 0 || !mContentsClient.isCachedRendererBackgroundColorValid()) {
+        if (isDestroyed() || !mContentsClient.isCachedRendererBackgroundColorValid()) {
             return mBaseBackgroundColor;
         }
         return mContentsClient.getCachedRendererBackgroundColor();
     }
 
     public boolean isMultiTouchZoomSupported() {
-        return mSettings.supportsGestureZoom();
+        return mSettings.supportsMultiTouchZoom();
     }
 
     public View getZoomControlsForTest() {
@@ -977,7 +1214,7 @@ public class AwContents {
      * @see ContentViewCore#getContentSettings()
      */
     public ContentSettings getContentSettings() {
-        return mContentViewCore.getContentSettings();
+        return isDestroyed() ? null : mContentViewCore.getContentSettings();
     }
 
     /**
@@ -985,7 +1222,7 @@ public class AwContents {
      */
     public void setOverScrollMode(int mode) {
         if (mode != View.OVER_SCROLL_NEVER) {
-            mOverScrollGlow = new OverScrollGlow(mContainerView);
+            mOverScrollGlow = new OverScrollGlow(mContext, mContainerView);
         } else {
             mOverScrollGlow = null;
         }
@@ -1071,7 +1308,7 @@ public class AwContents {
     }
 
     /**
-     * @see WebView#requestChildRectangleOnScreen(View, Rect, boolean)
+     * @see android.webkit.WebView#requestChildRectangleOnScreen(View, Rect, boolean)
      */
     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
         return mScrollOffsetManager.requestChildRectangleOnScreen(
@@ -1125,94 +1362,90 @@ public class AwContents {
      * @see android.webkit.WebView#stopLoading()
      */
     public void stopLoading() {
-        mContentViewCore.stopLoading();
+        if (!isDestroyed()) mWebContents.stop();
     }
 
     /**
      * @see android.webkit.WebView#reload()
      */
     public void reload() {
-        mContentViewCore.reload();
+        if (!isDestroyed()) mNavigationController.reload(true);
     }
 
     /**
      * @see android.webkit.WebView#canGoBack()
      */
     public boolean canGoBack() {
-        return mContentViewCore.canGoBack();
+        return isDestroyed() ? false : mNavigationController.canGoBack();
     }
 
     /**
      * @see android.webkit.WebView#goBack()
      */
     public void goBack() {
-        mContentViewCore.goBack();
-
-        suppressInterceptionForThisNavigation();
+        if (!isDestroyed()) mNavigationController.goBack();
     }
 
     /**
      * @see android.webkit.WebView#canGoForward()
      */
     public boolean canGoForward() {
-        return mContentViewCore.canGoForward();
+        return isDestroyed() ? false : mNavigationController.canGoForward();
     }
 
     /**
      * @see android.webkit.WebView#goForward()
      */
     public void goForward() {
-        mContentViewCore.goForward();
-
-        suppressInterceptionForThisNavigation();
+        if (!isDestroyed()) mNavigationController.goForward();
     }
 
     /**
      * @see android.webkit.WebView#canGoBackOrForward(int)
      */
     public boolean canGoBackOrForward(int steps) {
-        return mContentViewCore.canGoToOffset(steps);
+        return isDestroyed() ? false : mNavigationController.canGoToOffset(steps);
     }
 
     /**
      * @see android.webkit.WebView#goBackOrForward(int)
      */
     public void goBackOrForward(int steps) {
-        mContentViewCore.goToOffset(steps);
-
-        suppressInterceptionForThisNavigation();
+        if (!isDestroyed()) mNavigationController.goToOffset(steps);
     }
 
     /**
      * @see android.webkit.WebView#pauseTimers()
      */
     public void pauseTimers() {
-        ContentViewStatics.setWebKitSharedTimersSuspended(true);
+        if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(true);
     }
 
     /**
      * @see android.webkit.WebView#resumeTimers()
      */
     public void resumeTimers() {
-        ContentViewStatics.setWebKitSharedTimersSuspended(false);
+        if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(false);
     }
 
     /**
      * @see android.webkit.WebView#onPause()
      */
     public void onPause() {
-        if (mIsPaused || mNativeAwContents == 0) return;
+        if (mIsPaused || isDestroyed()) return;
         mIsPaused = true;
         nativeSetIsPaused(mNativeAwContents, mIsPaused);
+        mContentViewCore.onHide();
     }
 
     /**
      * @see android.webkit.WebView#onResume()
      */
     public void onResume() {
-        if (!mIsPaused || mNativeAwContents == 0) return;
+        if (!mIsPaused || isDestroyed()) return;
         mIsPaused = false;
         nativeSetIsPaused(mNativeAwContents, mIsPaused);
+        mContentViewCore.onShow();
     }
 
     /**
@@ -1226,38 +1459,21 @@ public class AwContents {
      * @see android.webkit.WebView#onCreateInputConnection(EditorInfo)
      */
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return mContentViewCore.onCreateInputConnection(outAttrs);
+        return mAwViewMethods.onCreateInputConnection(outAttrs);
     }
 
     /**
      * @see android.webkit.WebView#onKeyUp(int, KeyEvent)
      */
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        return mContentViewCore.onKeyUp(keyCode, event);
-    }
-
-    private boolean isDpadEvent(KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            switch (event.getKeyCode()) {
-                case KeyEvent.KEYCODE_DPAD_CENTER:
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                case KeyEvent.KEYCODE_DPAD_UP:
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    return true;
-            }
-        }
-        return false;
+        return mAwViewMethods.onKeyUp(keyCode, event);
     }
 
     /**
      * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent)
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (isDpadEvent(event)) {
-            mSettings.setSpatialNavigationEnabled(true);
-        }
-        return mContentViewCore.dispatchKeyEvent(event);
+        return mAwViewMethods.dispatchKeyEvent(event);
     }
 
     /**
@@ -1267,13 +1483,11 @@ public class AwContents {
      * @param includeDiskFiles if false, only the RAM cache is cleared
      */
     public void clearCache(boolean includeDiskFiles) {
-        if (mNativeAwContents == 0) return;
-        nativeClearCache(mNativeAwContents, includeDiskFiles);
+        if (!isDestroyed()) nativeClearCache(mNativeAwContents, includeDiskFiles);
     }
 
     public void documentHasImages(Message message) {
-        if (mNativeAwContents == 0) return;
-        nativeDocumentHasImages(mNativeAwContents, message);
+        if (!isDestroyed()) nativeDocumentHasImages(mNativeAwContents, message);
     }
 
     public void saveWebArchive(
@@ -1298,7 +1512,8 @@ public class AwContents {
     }
 
     public String getOriginalUrl() {
-        NavigationHistory history = mContentViewCore.getNavigationHistory();
+        if (isDestroyed()) return null;
+        NavigationHistory history = mNavigationController.getNavigationHistory();
         int currentIndex = history.getCurrentEntryIndex();
         if (currentIndex >= 0 && currentIndex < history.getEntryCount()) {
             return history.getEntryAtIndex(currentIndex).getOriginalUrl();
@@ -1310,31 +1525,31 @@ public class AwContents {
      * @see ContentViewCore#getNavigationHistory()
      */
     public NavigationHistory getNavigationHistory() {
-        return mContentViewCore.getNavigationHistory();
+        return isDestroyed() ? null : mNavigationController.getNavigationHistory();
     }
 
     /**
      * @see android.webkit.WebView#getTitle()
      */
     public String getTitle() {
-        return mContentViewCore.getTitle();
+        return isDestroyed() ? null : mWebContents.getTitle();
     }
 
     /**
      * @see android.webkit.WebView#clearHistory()
      */
     public void clearHistory() {
-        mContentViewCore.clearHistory();
+        if (!isDestroyed()) mNavigationController.clearHistory();
     }
 
     public String[] getHttpAuthUsernamePassword(String host, String realm) {
-        return mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext())
+        return mBrowserContext.getHttpAuthDatabase(mContext)
                 .getHttpAuthUsernamePassword(host, realm);
     }
 
     public void setHttpAuthUsernamePassword(String host, String realm, String username,
             String password) {
-        mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext())
+        mBrowserContext.getHttpAuthDatabase(mContext)
                 .setHttpAuthUsernamePassword(host, realm, username, password);
     }
 
@@ -1342,17 +1557,20 @@ public class AwContents {
      * @see android.webkit.WebView#getCertificate()
      */
     public SslCertificate getCertificate() {
-        if (mNativeAwContents == 0) return null;
-        return SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents));
+        return isDestroyed() ? null
+                : SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents));
     }
 
     /**
      * @see android.webkit.WebView#clearSslPreferences()
      */
     public void clearSslPreferences() {
-        mContentViewCore.clearSslPreferences();
+        if (!isDestroyed()) mNavigationController.clearSslPreferences();
     }
 
+    // TODO(sgurun) remove after this rolls in. To keep internal tree happy.
+    public void clearClientCertPreferences() { }
+
     /**
      * Method to return all hit test values relevant to public WebView API.
      * Note that this expose more data than needed for WebView.getHitTestResult.
@@ -1360,7 +1578,7 @@ public class AwContents {
      * garbage allocation on repeated calls.
      */
     public HitTestData getLastHitTestResult() {
-        if (mNativeAwContents == 0) return null;
+        if (isDestroyed()) return null;
         nativeUpdateLastHitTestData(mNativeAwContents);
         return mPossiblyStaleHitTestData;
     }
@@ -1369,7 +1587,7 @@ public class AwContents {
      * @see android.webkit.WebView#requestFocusNodeHref()
      */
     public void requestFocusNodeHref(Message msg) {
-        if (msg == null || mNativeAwContents == 0) return;
+        if (msg == null || isDestroyed()) return;
 
         nativeUpdateLastHitTestData(mNativeAwContents);
         Bundle data = msg.getData();
@@ -1377,7 +1595,7 @@ public class AwContents {
         // In order to maintain compatibility with the old WebView's implementation,
         // the absolute (full) url is passed in the |url| field, not only the href attribute.
         // Note: HitTestData could be cleaned up at this point. See http://crbug.com/290992.
-        data.putString("url", mPossiblyStaleHitTestData.hitTestResultExtraData);
+        data.putString("url", mPossiblyStaleHitTestData.href);
         data.putString("title", mPossiblyStaleHitTestData.anchorText);
         data.putString("src", mPossiblyStaleHitTestData.imgSrc);
         msg.setData(data);
@@ -1388,7 +1606,7 @@ public class AwContents {
      * @see android.webkit.WebView#requestImageRef()
      */
     public void requestImageRef(Message msg) {
-        if (msg == null || mNativeAwContents == 0) return;
+        if (msg == null || isDestroyed()) return;
 
         nativeUpdateLastHitTestData(mNativeAwContents);
         Bundle data = msg.getData();
@@ -1397,6 +1615,11 @@ public class AwContents {
         msg.sendToTarget();
     }
 
+    @VisibleForTesting
+    public float getPageScaleFactor() {
+        return mPageScaleFactor;
+    }
+
     /**
      * @see android.webkit.WebView#getScale()
      *
@@ -1404,7 +1627,7 @@ public class AwContents {
      * the screen density factor. See CTS WebViewTest.testSetInitialScale.
      */
     public float getScale() {
-        return (float)(mPageScaleFactor * mDIPScale);
+        return (float) (mPageScaleFactor * mDIPScale);
     }
 
     /**
@@ -1434,8 +1657,7 @@ public class AwContents {
     // This method uses the term 'zoom' for legacy reasons, but relates
     // to what chrome calls the 'page scale factor'.
     public boolean canZoomIn() {
-        final float zoomInExtent = mContentViewCore.getRenderCoordinates().getMaxPageScaleFactor()
-                - mPageScaleFactor;
+        final float zoomInExtent = mMaxPageScaleFactor - mPageScaleFactor;
         return zoomInExtent > ZOOM_CONTROLS_EPSILON;
     }
 
@@ -1445,8 +1667,7 @@ public class AwContents {
     // This method uses the term 'zoom' for legacy reasons, but relates
     // to what chrome calls the 'page scale factor'.
     public boolean canZoomOut() {
-        final float zoomOutExtent = mPageScaleFactor
-                - mContentViewCore.getRenderCoordinates().getMinPageScaleFactor();
+        final float zoomOutExtent = mPageScaleFactor - mMinPageScaleFactor;
         return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
     }
 
@@ -1459,7 +1680,7 @@ public class AwContents {
         if (!canZoomIn()) {
             return false;
         }
-        return mContentViewCore.pinchByDelta(1.25f);
+        return zoomBy(1.25f);
     }
 
     /**
@@ -1471,23 +1692,45 @@ public class AwContents {
         if (!canZoomOut()) {
             return false;
         }
-        return mContentViewCore.pinchByDelta(0.8f);
+        return zoomBy(0.8f);
+    }
+
+    /**
+     * @see android.webkit.WebView#zoomBy()
+     */
+    // This method uses the term 'zoom' for legacy reasons, but relates
+    // to what chrome calls the 'page scale factor'.
+    public boolean zoomBy(float delta) {
+        if (isDestroyed()) return false;
+        if (delta < 0.01f || delta > 100.0f) {
+            throw new IllegalStateException("zoom delta value outside [0.01, 100] range.");
+        }
+        return mContentViewCore.pinchByDelta(delta);
     }
 
     /**
      * @see android.webkit.WebView#invokeZoomPicker()
      */
     public void invokeZoomPicker() {
-        mContentViewCore.invokeZoomPicker();
+        if (!isDestroyed()) mContentViewCore.invokeZoomPicker();
     }
 
     /**
-     * @see ContentViewCore.evaluateJavaScript(String, ContentViewCore.JavaScriptCallback)
+     * @see android.webkit.WebView#preauthorizePermission(Uri, long)
+     */
+    public void preauthorizePermission(Uri origin, long resources) {
+        if (isDestroyed()) return;
+        nativePreauthorizePermission(mNativeAwContents, origin.toString(), resources);
+    }
+
+    /**
+     * @see ContentViewCore.evaluateJavaScript(String, JavaScriptCallback)
      */
     public void evaluateJavaScript(String script, final ValueCallback<String> callback) {
-        ContentViewCore.JavaScriptCallback jsCallback = null;
+        if (isDestroyed()) return;
+        JavaScriptCallback jsCallback = null;
         if (callback != null) {
-            jsCallback = new ContentViewCore.JavaScriptCallback() {
+            jsCallback = new JavaScriptCallback() {
                 @Override
                 public void handleJavaScriptResult(String jsonResult) {
                     callback.onReceiveValue(jsonResult);
@@ -1495,14 +1738,12 @@ public class AwContents {
             };
         }
 
-        mContentViewCore.evaluateJavaScript(script, jsCallback);
+        mWebContents.evaluateJavaScript(script, jsCallback);
     }
 
-    /**
-     * @see ContentViewCore.evaluateJavaScriptEvenIfNotYetNavigated(String)
-     */
+    // TODO(boliu): Remove this once Android side no longer calls this.
     public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
-        mContentViewCore.evaluateJavaScriptEvenIfNotYetNavigated(script);
+        if (!isDestroyed()) mWebContents.evaluateJavaScript(script, null);
     }
 
     //--------------------------------------------------------------------------------------------
@@ -1513,156 +1754,105 @@ public class AwContents {
      * @see android.webkit.View#onTouchEvent()
      */
     public boolean onTouchEvent(MotionEvent event) {
-        if (mNativeAwContents == 0) return false;
-
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mSettings.setSpatialNavigationEnabled(false);
-        }
-
-        mScrollOffsetManager.setProcessingTouchEvent(true);
-        boolean rv = mContentViewCore.onTouchEvent(event);
-        mScrollOffsetManager.setProcessingTouchEvent(false);
-
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            int actionIndex = event.getActionIndex();
-
-            // Note this will trigger IPC back to browser even if nothing is hit.
-            nativeRequestNewHitTestDataAt(mNativeAwContents,
-                                          (int)Math.round(event.getX(actionIndex) / mDIPScale),
-                                          (int)Math.round(event.getY(actionIndex) / mDIPScale));
-        }
-
-        if (mOverScrollGlow != null && event.getActionMasked() == MotionEvent.ACTION_UP) {
-            mOverScrollGlow.releaseAll();
-        }
-
-        return rv;
+        return mAwViewMethods.onTouchEvent(event);
     }
 
     /**
      * @see android.view.View#onHoverEvent()
      */
     public boolean onHoverEvent(MotionEvent event) {
-        return mContentViewCore.onHoverEvent(event);
+        return mAwViewMethods.onHoverEvent(event);
     }
 
     /**
      * @see android.view.View#onGenericMotionEvent()
      */
     public boolean onGenericMotionEvent(MotionEvent event) {
-        return mContentViewCore.onGenericMotionEvent(event);
+        return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event);
     }
 
     /**
      * @see android.view.View#onConfigurationChanged()
      */
     public void onConfigurationChanged(Configuration newConfig) {
-        mContentViewCore.onConfigurationChanged(newConfig);
+        mAwViewMethods.onConfigurationChanged(newConfig);
     }
 
     /**
      * @see android.view.View#onAttachedToWindow()
-     *
-     * Note that this is also called from receivePopupContents.
      */
     public void onAttachedToWindow() {
-        if (mNativeAwContents == 0) return;
-        mIsAttachedToWindow = true;
-
-        mContentViewCore.onAttachedToWindow();
-        nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(),
-                mContainerView.getHeight());
-
-        if (mComponentCallbacks != null) return;
-        mComponentCallbacks = new AwComponentCallbacks();
-        mContainerView.getContext().registerComponentCallbacks(mComponentCallbacks);
+        mTemporarilyDetached = false;
+        mAwViewMethods.onAttachedToWindow();
     }
 
     /**
      * @see android.view.View#onDetachedFromWindow()
      */
+    @SuppressLint("MissingSuperCall")
     public void onDetachedFromWindow() {
-        mIsAttachedToWindow = false;
-        hideAutofillPopup();
-        if (mNativeAwContents != 0) {
-            nativeOnDetachedFromWindow(mNativeAwContents);
-        }
-
-        mContentViewCore.onDetachedFromWindow();
-
-        if (mComponentCallbacks != null) {
-          mContainerView.getContext().unregisterComponentCallbacks(mComponentCallbacks);
-          mComponentCallbacks = null;
-        }
-
-        mScrollAccessibilityHelper.removePostedCallbacks();
-
-        if (mPendingDetachCleanupReferences != null) {
-            for (int i = 0; i < mPendingDetachCleanupReferences.size(); ++i) {
-                mPendingDetachCleanupReferences.get(i).cleanupNow();
-            }
-            mPendingDetachCleanupReferences = null;
-        }
+        mAwViewMethods.onDetachedFromWindow();
     }
 
     /**
      * @see android.view.View#onWindowFocusChanged()
      */
     public void onWindowFocusChanged(boolean hasWindowFocus) {
-        mWindowFocused = hasWindowFocus;
-        mContentViewCore.onWindowFocusChanged(hasWindowFocus);
+        mAwViewMethods.onWindowFocusChanged(hasWindowFocus);
     }
 
     /**
      * @see android.view.View#onFocusChanged()
      */
     public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-        mContainerViewFocused = focused;
-        mContentViewCore.onFocusChanged(focused);
+        if (!mTemporarilyDetached) {
+            mAwViewMethods.onFocusChanged(focused, direction, previouslyFocusedRect);
+        }
+    }
+
+    /**
+     * @see android.view.View#onStartTemporaryDetach()
+     */
+    public void onStartTemporaryDetach() {
+        mTemporarilyDetached = true;
+    }
+
+    /**
+     * @see android.view.View#onFinishTemporaryDetach()
+     */
+    public void onFinishTemporaryDetach() {
+        mTemporarilyDetached = false;
     }
 
     /**
      * @see android.view.View#onSizeChanged()
      */
     public void onSizeChanged(int w, int h, int ow, int oh) {
-        if (mNativeAwContents == 0) return;
-        mScrollOffsetManager.setContainerViewSize(w, h);
-        // The AwLayoutSizer needs to go first so that if we're in fixedLayoutSize mode the update
-        // to enter fixedLayoutSize mode is sent before the first resize update.
-        mLayoutSizer.onSizeChanged(w, h, ow, oh);
-        mContentViewCore.onPhysicalBackingSizeChanged(w, h);
-        mContentViewCore.onSizeChanged(w, h, ow, oh);
-        nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh);
+        mAwViewMethods.onSizeChanged(w, h, ow, oh);
     }
 
     /**
      * @see android.view.View#onVisibilityChanged()
      */
     public void onVisibilityChanged(View changedView, int visibility) {
-        boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE;
-        if (mIsViewVisible == viewVisible) return;
-        setViewVisibilityInternal(viewVisible);
+        mAwViewMethods.onVisibilityChanged(changedView, visibility);
     }
 
     /**
      * @see android.view.View#onWindowVisibilityChanged()
      */
     public void onWindowVisibilityChanged(int visibility) {
-        boolean windowVisible = visibility == View.VISIBLE;
-        if (mIsWindowVisible == windowVisible) return;
-        setWindowVisibilityInternal(windowVisible);
+        mAwViewMethods.onWindowVisibilityChanged(visibility);
     }
 
     private void setViewVisibilityInternal(boolean visible) {
         mIsViewVisible = visible;
-        if (mNativeAwContents == 0) return;
-        nativeSetViewVisibility(mNativeAwContents, mIsViewVisible);
+        if (!isDestroyed()) nativeSetViewVisibility(mNativeAwContents, mIsViewVisible);
     }
 
     private void setWindowVisibilityInternal(boolean visible) {
         mIsWindowVisible = visible;
-        if (mNativeAwContents == 0) return;
-        nativeSetWindowVisibility(mNativeAwContents, mIsWindowVisible);
+        if (!isDestroyed()) nativeSetWindowVisibility(mNativeAwContents, mIsWindowVisible);
     }
 
     /**
@@ -1675,7 +1865,7 @@ public class AwContents {
      * @return False if saving state failed.
      */
     public boolean saveState(Bundle outState) {
-        if (mNativeAwContents == 0 || outState == null) return false;
+        if (isDestroyed() || outState == null) return false;
 
         byte[] state = nativeGetOpaqueState(mNativeAwContents);
         if (state == null) return false;
@@ -1690,7 +1880,7 @@ public class AwContents {
      * @return False if restoring state failed.
      */
     public boolean restoreState(Bundle inState) {
-        if (mNativeAwContents == 0 || inState == null) return false;
+        if (isDestroyed() || inState == null) return false;
 
         byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY);
         if (state == null) return false;
@@ -1701,7 +1891,7 @@ public class AwContents {
         // but is optimized out in the restoreState case because the title is
         // already restored. See WebContentsImpl::UpdateTitleForEntry. So we
         // call the callback explicitly here.
-        if (result) mContentsClient.onReceivedTitle(mContentViewCore.getTitle());
+        if (result) mContentsClient.onReceivedTitle(mWebContents.getTitle());
 
         return result;
     }
@@ -1711,6 +1901,7 @@ public class AwContents {
      */
     public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
             Class<? extends Annotation> requiredAnnotation) {
+        if (isDestroyed()) return;
         mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation);
     }
 
@@ -1718,7 +1909,7 @@ public class AwContents {
      * @see android.webkit.WebView#removeJavascriptInterface(String)
      */
     public void removeJavascriptInterface(String interfaceName) {
-        mContentViewCore.removeJavascriptInterface(interfaceName);
+        if (!isDestroyed()) mContentViewCore.removeJavascriptInterface(interfaceName);
     }
 
     /**
@@ -1728,45 +1919,46 @@ public class AwContents {
      * @return The AccessibilityNodeProvider, if available, or null otherwise.
      */
     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
-        return mContentViewCore.getAccessibilityNodeProvider();
+        return isDestroyed() ? null : mContentViewCore.getAccessibilityNodeProvider();
     }
 
     /**
      * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
      */
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        mContentViewCore.onInitializeAccessibilityNodeInfo(info);
+        if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityNodeInfo(info);
     }
 
     /**
      * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent)
      */
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        mContentViewCore.onInitializeAccessibilityEvent(event);
+        if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityEvent(event);
     }
 
     public boolean supportsAccessibilityAction(int action) {
-        return mContentViewCore.supportsAccessibilityAction(action);
+        return isDestroyed() ? false : mContentViewCore.supportsAccessibilityAction(action);
     }
 
     /**
      * @see android.webkit.WebView#performAccessibilityAction(int, Bundle)
      */
     public boolean performAccessibilityAction(int action, Bundle arguments) {
-        return mContentViewCore.performAccessibilityAction(action, arguments);
+        return isDestroyed() ? false
+                : mContentViewCore.performAccessibilityAction(action, arguments);
     }
 
     /**
      * @see android.webkit.WebView#clearFormData()
      */
     public void hideAutofillPopup() {
-        if (mAwAutofillManagerDelegate != null)
-            mAwAutofillManagerDelegate.hideAutofillPopup();
+        if (mAwAutofillClient != null) {
+            mAwAutofillClient.hideAutofillPopup();
+        }
     }
 
     public void setNetworkAvailable(boolean networkUp) {
-        if (mNativeAwContents == 0) return;
-        nativeSetJsOnlineProperty(mNativeAwContents, networkUp);
+        if (!isDestroyed()) nativeSetJsOnlineProperty(mNativeAwContents, networkUp);
     }
 
     //--------------------------------------------------------------------------------------------
@@ -1817,7 +2009,7 @@ public class AwContents {
                             mBrowserContext.getGeolocationPermissions().deny(origin);
                         }
                     }
-                    if (mNativeAwContents == 0) return;
+                    if (isDestroyed()) return;
                     nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin);
                 }
             });
@@ -1826,7 +2018,7 @@ public class AwContents {
 
     @CalledByNative
     private void onGeolocationPermissionsShowPrompt(String origin) {
-        if (mNativeAwContents == 0) return;
+        if (isDestroyed()) return;
         AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions();
         // Reject if geoloaction is disabled, or the origin has a retained deny
         if (!mSettings.getGeolocationEnabled()) {
@@ -1849,6 +2041,16 @@ public class AwContents {
     }
 
     @CalledByNative
+    private void onPermissionRequest(AwPermissionRequest awPermissionRequest) {
+        mContentsClient.onPermissionRequest(awPermissionRequest);
+    }
+
+    @CalledByNative
+    private void onPermissionRequestCanceled(AwPermissionRequest awPermissionRequest) {
+        mContentsClient.onPermissionRequestCanceled(awPermissionRequest);
+    }
+
+    @CalledByNative
     public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
             boolean isDoneCounting) {
         mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
@@ -1856,13 +2058,6 @@ public class AwContents {
 
     @CalledByNative
     public void onNewPicture() {
-        // Clear up any results from a previous clearView call
-        if (mClearViewActive) {
-            mClearViewActive = false;
-            mContainerView.invalidate();
-            syncOnNewPictureStateToNative();
-        }
-
         // Don't call capturePicture() here but instead defer it until the posted task runs within
         // the callback helper, to avoid doubling back into the renderer compositor in the middle
         // of the notification it is sending up to here.
@@ -1881,8 +2076,8 @@ public class AwContents {
     }
 
     @CalledByNative
-    private boolean requestDrawGL(Canvas canvas) {
-        return mInternalAccessAdapter.requestDrawGL(canvas);
+    private boolean requestDrawGL(Canvas canvas, boolean waitForCompletion) {
+        return mNativeGLDelegate.requestDrawGL(canvas, waitForCompletion, mContainerView);
     }
 
     private static final boolean SUPPORTS_ON_ANIMATION =
@@ -1890,13 +2085,20 @@ public class AwContents {
 
     @CalledByNative
     private void postInvalidateOnAnimation() {
-        if (SUPPORTS_ON_ANIMATION) {
+        if (SUPPORTS_ON_ANIMATION && !mWindowAndroid.isInsideVSync()) {
             mContainerView.postInvalidateOnAnimation();
         } else {
-            mContainerView.postInvalidate();
+            mContainerView.invalidate();
         }
     }
 
+    // Call postInvalidateOnAnimation for invalidations. This is only used to synchronize
+    // draw functor destruction.
+    @CalledByNative
+    private void invalidateOnFunctorDestroy() {
+        mContainerView.invalidate();
+    }
+
     @CalledByNative
     private int[] getLocationOnScreen() {
         int[] result = new int[2];
@@ -1917,11 +2119,6 @@ public class AwContents {
     }
 
     @CalledByNative
-    private void setMaxContainerViewScrollOffset(int maxX, int maxY) {
-        mScrollOffsetManager.setMaxScrollOffset(maxX, maxY);
-    }
-
-    @CalledByNative
     private void scrollContainerViewTo(int x, int y) {
         mScrollOffsetManager.scrollContainerViewTo(x, y);
     }
@@ -1932,25 +2129,20 @@ public class AwContents {
     }
 
     @CalledByNative
-    private void setContentsSize(int widthDip, int heightDip) {
-        mContentWidthDip = widthDip;
-        mContentHeightDip = heightDip;
-    }
-
-    @CalledByNative
-    private void setPageScaleFactor(float pageScaleFactor) {
-        if (mPageScaleFactor == pageScaleFactor)
-            return;
-        float oldPageScaleFactor = mPageScaleFactor;
-        mPageScaleFactor = pageScaleFactor;
-        mContentsClient.getCallbackHelper().postOnScaleChangedScaled(
-                (float)(oldPageScaleFactor * mDIPScale), (float)(mPageScaleFactor * mDIPScale));
+    private void updateScrollState(int maxContainerViewScrollOffsetX,
+            int maxContainerViewScrollOffsetY, int contentWidthDip, int contentHeightDip,
+            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) {
+        mContentWidthDip = contentWidthDip;
+        mContentHeightDip = contentHeightDip;
+        mScrollOffsetManager.setMaxScrollOffset(maxContainerViewScrollOffsetX,
+            maxContainerViewScrollOffsetY);
+        setPageScaleFactorAndLimits(pageScaleFactor, minPageScaleFactor, maxPageScaleFactor);
     }
 
     @CalledByNative
-    private void setAwAutofillManagerDelegate(AwAutofillManagerDelegate delegate) {
-        mAwAutofillManagerDelegate = delegate;
-        delegate.init(mContentViewCore);
+    private void setAwAutofillClient(AwAutofillClient client) {
+        mAwAutofillClient = client;
+        client.init(mContentViewCore);
     }
 
     @CalledByNative
@@ -1970,8 +2162,26 @@ public class AwContents {
     // Helper methods
     // -------------------------------------------------------------------------------------------
 
+    private void setPageScaleFactorAndLimits(
+            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) {
+        if (mPageScaleFactor == pageScaleFactor &&
+                mMinPageScaleFactor == minPageScaleFactor &&
+                mMaxPageScaleFactor == maxPageScaleFactor) {
+            return;
+        }
+        mMinPageScaleFactor = minPageScaleFactor;
+        mMaxPageScaleFactor = maxPageScaleFactor;
+        if (mPageScaleFactor != pageScaleFactor) {
+            float oldPageScaleFactor = mPageScaleFactor;
+            mPageScaleFactor = pageScaleFactor;
+            mContentsClient.getCallbackHelper().postOnScaleChangedScaled(
+                    (float) (oldPageScaleFactor * mDIPScale),
+                    (float) (mPageScaleFactor * mDIPScale));
+        }
+    }
+
     private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) {
-        if (path == null || mNativeAwContents == 0) {
+        if (path == null || isDestroyed()) {
             ThreadUtils.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -2017,72 +2227,331 @@ public class AwContents {
         return null;
     }
 
+    public void extractSmartClipData(int x, int y, int width, int height) {
+        if (!isDestroyed()) mContentViewCore.extractSmartClipData(x, y, width, height);
+    }
+
+    public void setSmartClipDataListener(ContentViewCore.SmartClipDataListener listener) {
+        if (!isDestroyed()) mContentViewCore.setSmartClipDataListener(listener);
+    }
+
+    // --------------------------------------------------------------------------------------------
+    // This is the AwViewMethods implementation that does real work. The AwViewMethodsImpl is
+    // hooked up to the WebView in embedded mode and to the FullScreenView in fullscreen mode,
+    // but not to both at the same time.
+    private class AwViewMethodsImpl implements AwViewMethods {
+        private int mLayerType = View.LAYER_TYPE_NONE;
+        private ComponentCallbacks2 mComponentCallbacks;
+
+        // Only valid within software onDraw().
+        private final Rect mClipBoundsTemporary = new Rect();
+
+        @Override
+        public void onDraw(Canvas canvas) {
+            if (isDestroyed()) {
+                canvas.drawColor(getEffectiveBackgroundColor());
+                return;
+            }
+
+            // For hardware draws, the clip at onDraw time could be different
+            // from the clip during DrawGL.
+            if (!canvas.isHardwareAccelerated() && !canvas.getClipBounds(mClipBoundsTemporary)) {
+                return;
+            }
+
+            mScrollOffsetManager.syncScrollOffsetFromOnDraw();
+            Rect globalVisibleRect = getGlobalVisibleRect();
+            if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(),
+                    mContainerView.getScrollX(), mContainerView.getScrollY(),
+                    globalVisibleRect.left, globalVisibleRect.top,
+                    globalVisibleRect.right, globalVisibleRect.bottom)) {
+                // Can happen during initialization when compositor is not set
+                // up. Or when clearView
+                // is in effect. Just draw background color instead.
+                canvas.drawColor(getEffectiveBackgroundColor());
+            }
+
+            if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas,
+                    mScrollOffsetManager.computeMaximumHorizontalScrollOffset(),
+                    mScrollOffsetManager.computeMaximumVerticalScrollOffset())) {
+                mContainerView.invalidate();
+            }
+        }
+
+        @Override
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public void requestFocus() {
+            if (isDestroyed()) return;
+            if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) {
+                nativeFocusFirstNode(mNativeAwContents);
+            }
+        }
+
+        @Override
+        public void setLayerType(int layerType, Paint paint) {
+            mLayerType = layerType;
+            updateHardwareAcceleratedFeaturesToggle();
+        }
+
+        private void updateHardwareAcceleratedFeaturesToggle() {
+            mSettings.setEnableSupportedHardwareAcceleratedFeatures(
+                    mIsAttachedToWindow && mContainerView.isHardwareAccelerated() &&
+                            (mLayerType == View.LAYER_TYPE_NONE
+                            || mLayerType == View.LAYER_TYPE_HARDWARE));
+        }
+
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            return isDestroyed() ? null : mContentViewCore.onCreateInputConnection(outAttrs);
+        }
+
+        @Override
+        public boolean onKeyUp(int keyCode, KeyEvent event) {
+            return isDestroyed() ? false : mContentViewCore.onKeyUp(keyCode, event);
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            if (isDestroyed()) return false;
+            if (isDpadEvent(event)) {
+                mSettings.setSpatialNavigationEnabled(true);
+            }
+            return mContentViewCore.dispatchKeyEvent(event);
+        }
+
+        private boolean isDpadEvent(KeyEvent event) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                switch (event.getKeyCode()) {
+                    case KeyEvent.KEYCODE_DPAD_CENTER:
+                    case KeyEvent.KEYCODE_DPAD_DOWN:
+                    case KeyEvent.KEYCODE_DPAD_UP:
+                    case KeyEvent.KEYCODE_DPAD_LEFT:
+                    case KeyEvent.KEYCODE_DPAD_RIGHT:
+                        return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (isDestroyed()) return false;
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mSettings.setSpatialNavigationEnabled(false);
+            }
+
+            mScrollOffsetManager.setProcessingTouchEvent(true);
+            boolean rv = mContentViewCore.onTouchEvent(event);
+            mScrollOffsetManager.setProcessingTouchEvent(false);
+
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                int actionIndex = event.getActionIndex();
+
+                // Note this will trigger IPC back to browser even if nothing is
+                // hit.
+                nativeRequestNewHitTestDataAt(mNativeAwContents,
+                        (int) Math.round(event.getX(actionIndex) / mDIPScale),
+                        (int) Math.round(event.getY(actionIndex) / mDIPScale));
+            }
+
+            if (mOverScrollGlow != null) {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                    mOverScrollGlow.setShouldPull(true);
+                } else if (event.getActionMasked() == MotionEvent.ACTION_UP ||
+                        event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+                    mOverScrollGlow.setShouldPull(false);
+                    mOverScrollGlow.releaseAll();
+                }
+            }
+
+            return rv;
+        }
+
+        @Override
+        public boolean onHoverEvent(MotionEvent event) {
+            return isDestroyed() ? false : mContentViewCore.onHoverEvent(event);
+        }
+
+        @Override
+        public boolean onGenericMotionEvent(MotionEvent event) {
+            return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event);
+        }
+
+        @Override
+        public void onConfigurationChanged(Configuration newConfig) {
+            if (!isDestroyed()) mContentViewCore.onConfigurationChanged(newConfig);
+        }
+
+        @Override
+        public void onAttachedToWindow() {
+            if (isDestroyed()) return;
+            if (mIsAttachedToWindow) {
+                Log.w(TAG, "onAttachedToWindow called when already attached. Ignoring");
+                return;
+            }
+            mIsAttachedToWindow = true;
+
+            mContentViewCore.onAttachedToWindow();
+            nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(),
+                    mContainerView.getHeight());
+            updateHardwareAcceleratedFeaturesToggle();
+
+            if (mComponentCallbacks != null) return;
+            mComponentCallbacks = new AwComponentCallbacks();
+            mContext.registerComponentCallbacks(mComponentCallbacks);
+        }
+
+        @Override
+        public void onDetachedFromWindow() {
+            if (isDestroyed()) return;
+            if (!mIsAttachedToWindow) {
+                Log.w(TAG, "onDetachedFromWindow called when already detached. Ignoring");
+                return;
+            }
+            mIsAttachedToWindow = false;
+            hideAutofillPopup();
+            nativeOnDetachedFromWindow(mNativeAwContents);
+
+            mContentViewCore.onDetachedFromWindow();
+            updateHardwareAcceleratedFeaturesToggle();
+
+            if (mComponentCallbacks != null) {
+                mContext.unregisterComponentCallbacks(mComponentCallbacks);
+                mComponentCallbacks = null;
+            }
+
+            mScrollAccessibilityHelper.removePostedCallbacks();
+            mNativeGLDelegate.detachGLFunctor();
+        }
+
+        @Override
+        public void onWindowFocusChanged(boolean hasWindowFocus) {
+            if (isDestroyed()) return;
+            mWindowFocused = hasWindowFocus;
+            mContentViewCore.onWindowFocusChanged(hasWindowFocus);
+        }
+
+        @Override
+        public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+            if (isDestroyed()) return;
+            mContainerViewFocused = focused;
+            mContentViewCore.onFocusChanged(focused);
+        }
+
+        @Override
+        public void onSizeChanged(int w, int h, int ow, int oh) {
+            if (isDestroyed()) return;
+            mScrollOffsetManager.setContainerViewSize(w, h);
+            // The AwLayoutSizer needs to go first so that if we're in
+            // fixedLayoutSize mode the update
+            // to enter fixedLayoutSize mode is sent before the first resize
+            // update.
+            mLayoutSizer.onSizeChanged(w, h, ow, oh);
+            mContentViewCore.onPhysicalBackingSizeChanged(w, h);
+            mContentViewCore.onSizeChanged(w, h, ow, oh);
+            nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh);
+        }
+
+        @Override
+        public void onVisibilityChanged(View changedView, int visibility) {
+            boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE;
+            if (mIsViewVisible == viewVisible) return;
+            setViewVisibilityInternal(viewVisible);
+        }
+
+        @Override
+        public void onWindowVisibilityChanged(int visibility) {
+            boolean windowVisible = visibility == View.VISIBLE;
+            if (mIsWindowVisible == windowVisible) return;
+            setWindowVisibilityInternal(windowVisible);
+        }
+    }
+
+    // Return true if the GeolocationPermissionAPI should be used.
+    @CalledByNative
+    private boolean useLegacyGeolocationPermissionAPI() {
+        // Always return true since we are not ready to swap the geolocation yet.
+        // TODO: If we decide not to migrate the geolocation, there are some unreachable
+        // code need to remove. http://crbug.com/396184.
+        return true;
+    }
+
     //--------------------------------------------------------------------------------------------
     //  Native methods
     //--------------------------------------------------------------------------------------------
 
-    private static native int nativeInit(AwBrowserContext browserContext);
-    private static native void nativeDestroy(int nativeAwContents);
-    private static native void nativeSetAwDrawSWFunctionTable(int functionTablePointer);
-    private static native void nativeSetAwDrawGLFunctionTable(int functionTablePointer);
-    private static native int nativeGetAwDrawGLFunction();
+    private static native long nativeInit(AwBrowserContext browserContext);
+    private static native void nativeDestroy(long nativeAwContents);
+    private static native void nativeSetAwDrawSWFunctionTable(long functionTablePointer);
+    private static native void nativeSetAwDrawGLFunctionTable(long functionTablePointer);
+    private static native long nativeGetAwDrawGLFunction();
     private static native int nativeGetNativeInstanceCount();
     private static native void nativeSetShouldDownloadFavicons();
-    private native void nativeSetJavaPeers(int nativeAwContents, AwContents awContents,
+
+    private native void nativeSetJavaPeers(long nativeAwContents, AwContents awContents,
             AwWebContentsDelegate webViewWebContentsDelegate,
             AwContentsClientBridge contentsClientBridge,
             AwContentsIoThreadClient ioThreadClient,
             InterceptNavigationDelegate navigationInterceptionDelegate);
-    private native int nativeGetWebContents(int nativeAwContents);
+    private native long nativeGetWebContents(long nativeAwContents);
 
-    private native void nativeDocumentHasImages(int nativeAwContents, Message message);
+    private native void nativeDocumentHasImages(long nativeAwContents, Message message);
     private native void nativeGenerateMHTML(
-            int nativeAwContents, String path, ValueCallback<String> callback);
-
-    private native void nativeAddVisitedLinks(int nativeAwContents, String[] visitedLinks);
-    private native boolean nativeOnDraw(int nativeAwContents, Canvas canvas,
-            boolean isHardwareAccelerated, int scrollX, int ScrollY,
-            int clipLeft, int clipTop, int clipRight, int clipBottom);
-    private native void nativeSetGlobalVisibleRect(int nativeAwContents, int visibleLeft,
-            int visibleTop, int visibleRight, int visibleBottom);
-    private native void nativeFindAllAsync(int nativeAwContents, String searchString);
-    private native void nativeFindNext(int nativeAwContents, boolean forward);
-    private native void nativeClearMatches(int nativeAwContents);
-    private native void nativeClearCache(int nativeAwContents, boolean includeDiskFiles);
-    private native byte[] nativeGetCertificate(int nativeAwContents);
+            long nativeAwContents, String path, ValueCallback<String> callback);
+
+    private native void nativeAddVisitedLinks(long nativeAwContents, String[] visitedLinks);
+    private native boolean nativeOnDraw(long nativeAwContents, Canvas canvas,
+            boolean isHardwareAccelerated, int scrollX, int scrollY,
+            int visibleLeft, int visibleTop, int visibleRight, int visibleBottom);
+    private native void nativeFindAllAsync(long nativeAwContents, String searchString);
+    private native void nativeFindNext(long nativeAwContents, boolean forward);
+    private native void nativeClearMatches(long nativeAwContents);
+    private native void nativeClearCache(long nativeAwContents, boolean includeDiskFiles);
+    private native byte[] nativeGetCertificate(long nativeAwContents);
 
     // Coordinates in desity independent pixels.
-    private native void nativeRequestNewHitTestDataAt(int nativeAwContents, int x, int y);
-    private native void nativeUpdateLastHitTestData(int nativeAwContents);
-
-    private native void nativeOnSizeChanged(int nativeAwContents, int w, int h, int ow, int oh);
-    private native void nativeScrollTo(int nativeAwContents, int x, int y);
-    private native void nativeSetViewVisibility(int nativeAwContents, boolean visible);
-    private native void nativeSetWindowVisibility(int nativeAwContents, boolean visible);
-    private native void nativeSetIsPaused(int nativeAwContents, boolean paused);
-    private native void nativeOnAttachedToWindow(int nativeAwContents, int w, int h);
-    private static native void nativeOnDetachedFromWindow(int nativeAwContents);
-    private native void nativeSetDipScale(int nativeAwContents, float dipScale);
-    private native void nativeSetFixedLayoutSize(int nativeAwContents, int widthDip, int heightDip);
+    private native void nativeRequestNewHitTestDataAt(long nativeAwContents, int x, int y);
+    private native void nativeUpdateLastHitTestData(long nativeAwContents);
+
+    private native void nativeOnSizeChanged(long nativeAwContents, int w, int h, int ow, int oh);
+    private native void nativeScrollTo(long nativeAwContents, int x, int y);
+    private native void nativeSetViewVisibility(long nativeAwContents, boolean visible);
+    private native void nativeSetWindowVisibility(long nativeAwContents, boolean visible);
+    private native void nativeSetIsPaused(long nativeAwContents, boolean paused);
+    private native void nativeOnAttachedToWindow(long nativeAwContents, int w, int h);
+    private static native void nativeOnDetachedFromWindow(long nativeAwContents);
+    private native void nativeSetDipScale(long nativeAwContents, float dipScale);
 
     // Returns null if save state fails.
-    private native byte[] nativeGetOpaqueState(int nativeAwContents);
+    private native byte[] nativeGetOpaqueState(long nativeAwContents);
 
     // Returns false if restore state fails.
-    private native boolean nativeRestoreFromOpaqueState(int nativeAwContents, byte[] state);
+    private native boolean nativeRestoreFromOpaqueState(long nativeAwContents, byte[] state);
 
-    private native int nativeReleasePopupAwContents(int nativeAwContents);
-    private native void nativeFocusFirstNode(int nativeAwContents);
-    private native void nativeSetBackgroundColor(int nativeAwContents, int color);
+    private native long nativeReleasePopupAwContents(long nativeAwContents);
+    private native void nativeFocusFirstNode(long nativeAwContents);
+    private native void nativeSetBackgroundColor(long nativeAwContents, int color);
 
-    private native int nativeGetAwDrawGLViewContext(int nativeAwContents);
-    private native int nativeCapturePicture(int nativeAwContents, int width, int height);
-    private native void nativeEnableOnNewPicture(int nativeAwContents, boolean enabled);
+    private native long nativeGetAwDrawGLViewContext(long nativeAwContents);
+    private native long nativeCapturePicture(long nativeAwContents, int width, int height);
+    private native void nativeEnableOnNewPicture(long nativeAwContents, boolean enabled);
+    private native void nativeClearView(long nativeAwContents);
+    private native void nativeSetExtraHeadersForUrl(long nativeAwContents,
+            String url, String extraHeaders);
 
     private native void nativeInvokeGeolocationCallback(
-            int nativeAwContents, boolean value, String requestingFrame);
+            long nativeAwContents, boolean value, String requestingFrame);
+
+    private native void nativeSetJsOnlineProperty(long nativeAwContents, boolean networkUp);
+
+    private native void nativeTrimMemory(long nativeAwContents, int level, boolean visible);
 
-    private native void nativeSetJsOnlineProperty(int nativeAwContents, boolean networkUp);
+    private native void nativeCreatePdfExporter(long nativeAwContents, AwPdfExporter awPdfExporter);
 
-    private native void nativeTrimMemory(int nativeAwContents, int level);
+    private native void nativePreauthorizePermission(long nativeAwContents, String origin,
+            long resources);
 }