Home | History | Annotate | Download | only in android_webview
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.android_webview;
      6 
      7 import android.annotation.SuppressLint;
      8 import android.app.Activity;
      9 import android.content.ComponentCallbacks2;
     10 import android.content.Context;
     11 import android.content.res.Configuration;
     12 import android.graphics.Bitmap;
     13 import android.graphics.Canvas;
     14 import android.graphics.Color;
     15 import android.graphics.Paint;
     16 import android.graphics.Picture;
     17 import android.graphics.Rect;
     18 import android.net.Uri;
     19 import android.net.http.SslCertificate;
     20 import android.os.AsyncTask;
     21 import android.os.Build;
     22 import android.os.Bundle;
     23 import android.os.Handler;
     24 import android.os.Message;
     25 import android.text.TextUtils;
     26 import android.util.Log;
     27 import android.util.Pair;
     28 import android.view.KeyEvent;
     29 import android.view.MotionEvent;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.view.accessibility.AccessibilityEvent;
     33 import android.view.accessibility.AccessibilityNodeInfo;
     34 import android.view.accessibility.AccessibilityNodeProvider;
     35 import android.view.inputmethod.EditorInfo;
     36 import android.view.inputmethod.InputConnection;
     37 import android.webkit.GeolocationPermissions;
     38 import android.webkit.ValueCallback;
     39 import android.widget.OverScroller;
     40 
     41 import org.chromium.android_webview.permission.AwPermissionRequest;
     42 import org.chromium.base.CalledByNative;
     43 import org.chromium.base.JNINamespace;
     44 import org.chromium.base.ThreadUtils;
     45 import org.chromium.base.VisibleForTesting;
     46 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
     47 import org.chromium.components.navigation_interception.NavigationParams;
     48 import org.chromium.content.browser.ContentSettings;
     49 import org.chromium.content.browser.ContentViewClient;
     50 import org.chromium.content.browser.ContentViewCore;
     51 import org.chromium.content.browser.ContentViewStatics;
     52 import org.chromium.content.browser.SmartClipProvider;
     53 import org.chromium.content.browser.WebContentsObserverAndroid;
     54 import org.chromium.content.common.CleanupReference;
     55 import org.chromium.content_public.browser.GestureStateListener;
     56 import org.chromium.content_public.browser.JavaScriptCallback;
     57 import org.chromium.content_public.browser.LoadUrlParams;
     58 import org.chromium.content_public.browser.NavigationController;
     59 import org.chromium.content_public.browser.NavigationHistory;
     60 import org.chromium.content_public.browser.WebContents;
     61 import org.chromium.content_public.common.Referrer;
     62 import org.chromium.ui.base.ActivityWindowAndroid;
     63 import org.chromium.ui.base.PageTransitionTypes;
     64 import org.chromium.ui.base.WindowAndroid;
     65 import org.chromium.ui.gfx.DeviceDisplayInfo;
     66 
     67 import java.io.File;
     68 import java.lang.annotation.Annotation;
     69 import java.net.MalformedURLException;
     70 import java.net.URL;
     71 import java.util.HashMap;
     72 import java.util.Locale;
     73 import java.util.Map;
     74 import java.util.concurrent.Callable;
     75 
     76 /**
     77  * Exposes the native AwContents class, and together these classes wrap the ContentViewCore
     78  * and Browser components that are required to implement Android WebView API. This is the
     79  * primary entry point for the WebViewProvider implementation; it holds a 1:1 object
     80  * relationship with application WebView instances.
     81  * (We define this class independent of the hidden WebViewProvider interfaces, to allow
     82  * continuous build & test in the open source SDK-based tree).
     83  */
     84 @JNINamespace("android_webview")
     85 public class AwContents implements SmartClipProvider {
     86     private static final String TAG = "AwContents";
     87 
     88     private static final String WEB_ARCHIVE_EXTENSION = ".mht";
     89 
     90     // Used to avoid enabling zooming in / out if resulting zooming will
     91     // produce little visible difference.
     92     private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
     93 
     94     /**
     95      * WebKit hit test related data strcutre. These are used to implement
     96      * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView.
     97      * All values should be updated together. The native counterpart is
     98      * AwHitTestData.
     99      */
    100     public static class HitTestData {
    101         // Used in getHitTestResult.
    102         public int hitTestResultType;
    103         public String hitTestResultExtraData;
    104 
    105         // Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc).
    106         public String href;
    107         public String anchorText;
    108         public String imgSrc;
    109     }
    110 
    111     /**
    112      * Interface that consumers of {@link AwContents} must implement to allow the proper
    113      * dispatching of view methods through the containing view.
    114      */
    115     public interface InternalAccessDelegate extends ContentViewCore.InternalAccessDelegate {
    116 
    117         /**
    118          * @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean);
    119          */
    120         void overScrollBy(int deltaX, int deltaY,
    121                 int scrollX, int scrollY,
    122                 int scrollRangeX, int scrollRangeY,
    123                 int maxOverScrollX, int maxOverScrollY,
    124                 boolean isTouchEvent);
    125 
    126         /**
    127          * @see View#scrollTo(int, int)
    128          */
    129         void super_scrollTo(int scrollX, int scrollY);
    130 
    131         /**
    132          * @see View#setMeasuredDimension(int, int)
    133          */
    134         void setMeasuredDimension(int measuredWidth, int measuredHeight);
    135 
    136         /**
    137          * @see View#getScrollBarStyle()
    138          */
    139         int super_getScrollBarStyle();
    140     }
    141 
    142     /**
    143      * Interface that consumers of {@link AwContents} must implement to support
    144      * native GL rendering.
    145      */
    146     public interface NativeGLDelegate {
    147         /**
    148          * Requests a callback on the native DrawGL method (see getAwDrawGLFunction)
    149          * if called from within onDraw, |canvas| will be non-null and hardware accelerated.
    150          * Otherwise, |canvas| will be null, and the container view itself will be hardware
    151          * accelerated. If |waitForCompletion| is true, this method will not return until
    152          * functor has returned.
    153          * Should avoid setting |waitForCompletion| when |canvas| is not null.
    154          * |containerView| is the view where the AwContents should be drawn.
    155          *
    156          * @return false indicates the GL draw request was not accepted, and the caller
    157          *         should fallback to the SW path.
    158          */
    159         boolean requestDrawGL(Canvas canvas, boolean waitForCompletion, View containerView);
    160 
    161         /**
    162          * Detaches the GLFunctor from the view tree.
    163          */
    164         void detachGLFunctor();
    165     }
    166 
    167     /**
    168      * Class to facilitate dependency injection. Subclasses by test code to provide mock versions of
    169      * certain AwContents dependencies.
    170      */
    171     public static class DependencyFactory {
    172         public AwLayoutSizer createLayoutSizer() {
    173             return new AwLayoutSizer();
    174         }
    175 
    176         public AwScrollOffsetManager createScrollOffsetManager(
    177                 AwScrollOffsetManager.Delegate delegate, OverScroller overScroller) {
    178             return new AwScrollOffsetManager(delegate, overScroller);
    179         }
    180     }
    181 
    182     private long mNativeAwContents;
    183     private final AwBrowserContext mBrowserContext;
    184     private ViewGroup mContainerView;
    185     private final AwLayoutChangeListener mLayoutChangeListener;
    186     private final Context mContext;
    187     private ContentViewCore mContentViewCore;
    188     private WindowAndroid mWindowAndroid;
    189     private WebContents mWebContents;
    190     private NavigationController mNavigationController;
    191     private final AwContentsClient mContentsClient;
    192     private final AwContentViewClient mContentViewClient;
    193     private WebContentsObserverAndroid mWebContentsObserver;
    194     private final AwContentsClientBridge mContentsClientBridge;
    195     private final AwWebContentsDelegateAdapter mWebContentsDelegate;
    196     private final AwContentsIoThreadClient mIoThreadClient;
    197     private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
    198     private InternalAccessDelegate mInternalAccessAdapter;
    199     private final NativeGLDelegate mNativeGLDelegate;
    200     private final AwLayoutSizer mLayoutSizer;
    201     private final AwZoomControls mZoomControls;
    202     private final AwScrollOffsetManager mScrollOffsetManager;
    203     private OverScrollGlow mOverScrollGlow;
    204     // This can be accessed on any thread after construction. See AwContentsIoThreadClient.
    205     private final AwSettings mSettings;
    206     private final ScrollAccessibilityHelper mScrollAccessibilityHelper;
    207 
    208     private boolean mIsPaused;
    209     private boolean mIsViewVisible;
    210     private boolean mIsWindowVisible;
    211     private boolean mIsAttachedToWindow;
    212     private Bitmap mFavicon;
    213     private boolean mHasRequestedVisitedHistoryFromClient;
    214     // TODO(boliu): This should be in a global context, not per webview.
    215     private final double mDIPScale;
    216 
    217     // The base background color, i.e. not accounting for any CSS body from the current page.
    218     private int mBaseBackgroundColor = Color.WHITE;
    219 
    220     // Must call nativeUpdateLastHitTestData first to update this before use.
    221     private final HitTestData mPossiblyStaleHitTestData = new HitTestData();
    222 
    223     private final DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler;
    224 
    225     // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the
    226     // picture listener API has not yet been enabled, or if it is using invalidation-only mode.
    227     private Callable<Picture> mPictureListenerContentProvider;
    228 
    229     private boolean mContainerViewFocused;
    230     private boolean mWindowFocused;
    231 
    232     // These come from the compositor and are updated synchronously (in contrast to the values in
    233     // ContentViewCore, which are updated at end of every frame).
    234     private float mPageScaleFactor = 1.0f;
    235     private float mMinPageScaleFactor = 1.0f;
    236     private float mMaxPageScaleFactor = 1.0f;
    237     private float mContentWidthDip;
    238     private float mContentHeightDip;
    239 
    240     private AwAutofillClient mAwAutofillClient;
    241 
    242     private AwPdfExporter mAwPdfExporter;
    243 
    244     private AwViewMethods mAwViewMethods;
    245     private final FullScreenTransitionsState mFullScreenTransitionsState;
    246 
    247     // This flag indicates that ShouldOverrideUrlNavigation should be posted
    248     // through the resourcethrottle. This is only used for popup windows.
    249     private boolean mDeferredShouldOverrideUrlLoadingIsPendingForPopup;
    250 
    251     // The framework may temporarily detach our container view, for example during layout if
    252     // we are a child of a ListView. This may cause many toggles of View focus, which we suppress
    253     // when in this state.
    254     private boolean mTemporarilyDetached;
    255 
    256     // True when this AwContents has been destroyed.
    257     // Do not use directly, call isDestroyed() instead.
    258     private boolean mIsDestroyed = false;
    259 
    260     private static final class DestroyRunnable implements Runnable {
    261         private final long mNativeAwContents;
    262         private DestroyRunnable(long nativeAwContents) {
    263             mNativeAwContents = nativeAwContents;
    264         }
    265         @Override
    266         public void run() {
    267             nativeDestroy(mNativeAwContents);
    268         }
    269     }
    270 
    271     /**
    272      * A class that stores the state needed to enter and exit fullscreen.
    273      */
    274     private static class FullScreenTransitionsState {
    275         private final ViewGroup mInitialContainerView;
    276         private final InternalAccessDelegate mInitialInternalAccessAdapter;
    277         private final AwViewMethods mInitialAwViewMethods;
    278         private FullScreenView mFullScreenView;
    279 
    280         private FullScreenTransitionsState(ViewGroup initialContainerView,
    281                 InternalAccessDelegate initialInternalAccessAdapter,
    282                 AwViewMethods initialAwViewMethods) {
    283             mInitialContainerView = initialContainerView;
    284             mInitialInternalAccessAdapter = initialInternalAccessAdapter;
    285             mInitialAwViewMethods = initialAwViewMethods;
    286         }
    287 
    288         private void enterFullScreen(FullScreenView fullScreenView) {
    289             mFullScreenView = fullScreenView;
    290         }
    291 
    292         private void exitFullScreen() {
    293             mFullScreenView = null;
    294         }
    295 
    296         private boolean isFullScreen() {
    297             return mFullScreenView != null;
    298         }
    299 
    300         private ViewGroup getInitialContainerView() {
    301             return mInitialContainerView;
    302         }
    303 
    304         private InternalAccessDelegate getInitialInternalAccessDelegate() {
    305             return mInitialInternalAccessAdapter;
    306         }
    307 
    308         private AwViewMethods getInitialAwViewMethods() {
    309             return mInitialAwViewMethods;
    310         }
    311 
    312         private FullScreenView getFullScreenView() {
    313             return mFullScreenView;
    314         }
    315     }
    316 
    317     // Reference to the active mNativeAwContents pointer while it is active use
    318     // (ie before it is destroyed).
    319     private CleanupReference mCleanupReference;
    320 
    321     //--------------------------------------------------------------------------------------------
    322     private class IoThreadClientImpl extends AwContentsIoThreadClient {
    323         // All methods are called on the IO thread.
    324 
    325         @Override
    326         public int getCacheMode() {
    327             return mSettings.getCacheMode();
    328         }
    329 
    330         @Override
    331         public AwWebResourceResponse shouldInterceptRequest(
    332                 AwContentsClient.ShouldInterceptRequestParams params) {
    333             String url = params.url;
    334             AwWebResourceResponse awWebResourceResponse;
    335             // Return the response directly if the url is default video poster url.
    336             awWebResourceResponse = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url);
    337             if (awWebResourceResponse != null) return awWebResourceResponse;
    338 
    339             awWebResourceResponse = mContentsClient.shouldInterceptRequest(params);
    340 
    341             if (awWebResourceResponse == null) {
    342                 mContentsClient.getCallbackHelper().postOnLoadResource(url);
    343             }
    344 
    345             if (params.isMainFrame && awWebResourceResponse != null &&
    346                     awWebResourceResponse.getData() == null) {
    347                 // In this case the intercepted URLRequest job will simulate an empty response
    348                 // which doesn't trigger the onReceivedError callback. For WebViewClassic
    349                 // compatibility we synthesize that callback. http://crbug.com/180950
    350                 mContentsClient.getCallbackHelper().postOnReceivedError(
    351                         ErrorCodeConversionHelper.ERROR_UNKNOWN,
    352                         null /* filled in by the glue layer */, url);
    353             }
    354             return awWebResourceResponse;
    355         }
    356 
    357         @Override
    358         public boolean shouldBlockContentUrls() {
    359             return !mSettings.getAllowContentAccess();
    360         }
    361 
    362         @Override
    363         public boolean shouldBlockFileUrls() {
    364             return !mSettings.getAllowFileAccess();
    365         }
    366 
    367         @Override
    368         public boolean shouldBlockNetworkLoads() {
    369             return mSettings.getBlockNetworkLoads();
    370         }
    371 
    372         @Override
    373         public boolean shouldAcceptThirdPartyCookies() {
    374             return mSettings.getAcceptThirdPartyCookies();
    375         }
    376 
    377         @Override
    378         public void onDownloadStart(String url, String userAgent,
    379                 String contentDisposition, String mimeType, long contentLength) {
    380             mContentsClient.getCallbackHelper().postOnDownloadStart(url, userAgent,
    381                     contentDisposition, mimeType, contentLength);
    382         }
    383 
    384         @Override
    385         public void newLoginRequest(String realm, String account, String args) {
    386             mContentsClient.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args);
    387         }
    388     }
    389 
    390     //--------------------------------------------------------------------------------------------
    391     // When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation
    392     // here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading
    393     // callback to the correct WebViewClient that is associated with the WebView.
    394     // Otherwise, use this delegate only to post onPageStarted messages.
    395     //
    396     // We are not using WebContentsObserver.didStartLoading because of stale URLs, out of order
    397     // onPageStarted's and double onPageStarted's.
    398     //
    399     private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
    400         @Override
    401         public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
    402             final String url = navigationParams.url;
    403             boolean ignoreNavigation = false;
    404             if (mDeferredShouldOverrideUrlLoadingIsPendingForPopup) {
    405                 mDeferredShouldOverrideUrlLoadingIsPendingForPopup = false;
    406                 // If this is used for all navigations in future, cases for application initiated
    407                 // load, redirect and backforward should also be filtered out.
    408                 if (!navigationParams.isPost) {
    409                     ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url);
    410                 }
    411             }
    412             // The shouldOverrideUrlLoading call might have resulted in posting messages to the
    413             // UI thread. Using sendMessage here (instead of calling onPageStarted directly)
    414             // will allow those to run in order.
    415             if (!ignoreNavigation) {
    416                 mContentsClient.getCallbackHelper().postOnPageStarted(url);
    417             }
    418             return ignoreNavigation;
    419         }
    420     }
    421 
    422     //--------------------------------------------------------------------------------------------
    423     private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate {
    424         @Override
    425         public void requestLayout() {
    426             mContainerView.requestLayout();
    427         }
    428 
    429         @Override
    430         public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    431             mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight);
    432         }
    433 
    434         @Override
    435         public boolean isLayoutParamsHeightWrapContent() {
    436             return mContainerView.getLayoutParams() != null &&
    437                     mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
    438         }
    439 
    440         @Override
    441         public void setForceZeroLayoutHeight(boolean forceZeroHeight) {
    442             getSettings().setForceZeroLayoutHeight(forceZeroHeight);
    443         }
    444     }
    445 
    446     //--------------------------------------------------------------------------------------------
    447     private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate {
    448         @Override
    449         public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY,
    450                 int scrollRangeX, int scrollRangeY, boolean isTouchEvent) {
    451             mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY,
    452                     scrollRangeX, scrollRangeY, 0, 0, isTouchEvent);
    453         }
    454 
    455         @Override
    456         public void scrollContainerViewTo(int x, int y) {
    457             mInternalAccessAdapter.super_scrollTo(x, y);
    458         }
    459 
    460         @Override
    461         public void scrollNativeTo(int x, int y) {
    462             if (!isDestroyed()) nativeScrollTo(mNativeAwContents, x, y);
    463         }
    464 
    465         @Override
    466         public int getContainerViewScrollX() {
    467             return mContainerView.getScrollX();
    468         }
    469 
    470         @Override
    471         public int getContainerViewScrollY() {
    472             return mContainerView.getScrollY();
    473         }
    474 
    475         @Override
    476         public void invalidate() {
    477             mContainerView.invalidate();
    478         }
    479     }
    480 
    481     //--------------------------------------------------------------------------------------------
    482     private class AwGestureStateListener extends GestureStateListener {
    483         @Override
    484         public void onPinchStarted() {
    485             // While it's possible to re-layout the view during a pinch gesture, the effect is very
    486             // janky (especially that the page scale update notification comes from the renderer
    487             // main thread, not from the impl thread, so it's usually out of sync with what's on
    488             // screen). It's also quite expensive to do a re-layout, so we simply postpone
    489             // re-layout for the duration of the gesture. This is compatible with what
    490             // WebViewClassic does.
    491             mLayoutSizer.freezeLayoutRequests();
    492         }
    493 
    494         @Override
    495         public void onPinchEnded() {
    496             mLayoutSizer.unfreezeLayoutRequests();
    497         }
    498 
    499         @Override
    500         public void onFlingCancelGesture() {
    501             mScrollOffsetManager.onFlingCancelGesture();
    502         }
    503 
    504         @Override
    505         public void onUnhandledFlingStartEvent(int velocityX, int velocityY) {
    506             mScrollOffsetManager.onUnhandledFlingStartEvent(velocityX, velocityY);
    507         }
    508 
    509         @Override
    510         public void onScrollUpdateGestureConsumed() {
    511             mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback();
    512         }
    513     }
    514 
    515     //--------------------------------------------------------------------------------------------
    516     private class AwComponentCallbacks implements ComponentCallbacks2 {
    517         @Override
    518         public void onTrimMemory(final int level) {
    519             if (isDestroyed()) return;
    520             boolean visibleRectEmpty = getGlobalVisibleRect().isEmpty();
    521             final boolean visible = mIsViewVisible && mIsWindowVisible && !visibleRectEmpty;
    522             nativeTrimMemory(mNativeAwContents, level, visible);
    523         }
    524 
    525         @Override
    526         public void onLowMemory() {}
    527 
    528         @Override
    529         public void onConfigurationChanged(Configuration configuration) {}
    530     };
    531 
    532     //--------------------------------------------------------------------------------------------
    533     private class AwLayoutChangeListener implements View.OnLayoutChangeListener {
    534         @Override
    535         public void onLayoutChange(View v, int left, int top, int right, int bottom,
    536                 int oldLeft, int oldTop, int oldRight, int oldBottom) {
    537             assert v == mContainerView;
    538             mLayoutSizer.onLayoutChange();
    539         }
    540     }
    541 
    542     /**
    543      * @param browserContext the browsing context to associate this view contents with.
    544      * @param containerView the view-hierarchy item this object will be bound to.
    545      * @param context the context to use, usually containerView.getContext().
    546      * @param internalAccessAdapter to access private methods on containerView.
    547      * @param nativeGLDelegate to access the GL functor provided by the WebView.
    548      * @param contentsClient will receive API callbacks from this WebView Contents.
    549      * @param awSettings AwSettings instance used to configure the AwContents.
    550      *
    551      * This constructor uses the default view sizing policy.
    552      */
    553     public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
    554             InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate,
    555             AwContentsClient contentsClient, AwSettings awSettings) {
    556         this(browserContext, containerView, context, internalAccessAdapter, nativeGLDelegate,
    557                 contentsClient, awSettings, new DependencyFactory());
    558     }
    559 
    560     /**
    561      * @param dependencyFactory an instance of the DependencyFactory used to provide instances of
    562      *                          classes that this class depends on.
    563      *
    564      * This version of the constructor is used in test code to inject test versions of the above
    565      * documented classes.
    566      */
    567     public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
    568             InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate,
    569             AwContentsClient contentsClient, AwSettings settings,
    570             DependencyFactory dependencyFactory) {
    571         mBrowserContext = browserContext;
    572         mContainerView = containerView;
    573         mContext = context;
    574         mInternalAccessAdapter = internalAccessAdapter;
    575         mNativeGLDelegate = nativeGLDelegate;
    576         mContentsClient = contentsClient;
    577         mAwViewMethods = new AwViewMethodsImpl();
    578         mFullScreenTransitionsState = new FullScreenTransitionsState(
    579                 mContainerView, mInternalAccessAdapter, mAwViewMethods);
    580         mContentViewClient = new AwContentViewClient(contentsClient, settings, this, mContext);
    581         mLayoutSizer = dependencyFactory.createLayoutSizer();
    582         mSettings = settings;
    583         mDIPScale = DeviceDisplayInfo.create(mContext).getDIPScale();
    584         mLayoutSizer.setDelegate(new AwLayoutSizerDelegate());
    585         mLayoutSizer.setDIPScale(mDIPScale);
    586         mWebContentsDelegate = new AwWebContentsDelegateAdapter(
    587                 contentsClient, mContainerView, mContext);
    588         mContentsClientBridge = new AwContentsClientBridge(contentsClient,
    589                 mBrowserContext.getKeyStore(), AwContentsStatics.getClientCertLookupTable());
    590         mZoomControls = new AwZoomControls(this);
    591         mIoThreadClient = new IoThreadClientImpl();
    592         mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl();
    593 
    594         AwSettings.ZoomSupportChangeListener zoomListener =
    595                 new AwSettings.ZoomSupportChangeListener() {
    596                     @Override
    597                     public void onGestureZoomSupportChanged(
    598                             boolean supportsDoubleTapZoom, boolean supportsMultiTouchZoom) {
    599                         if (isDestroyed()) return;
    600                         mContentViewCore.updateDoubleTapSupport(supportsDoubleTapZoom);
    601                         mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom);
    602                     }
    603 
    604                 };
    605         mSettings.setZoomListener(zoomListener);
    606         mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient);
    607         mSettings.setDefaultVideoPosterURL(
    608                 mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL());
    609         mSettings.setDIPScale(mDIPScale);
    610         mScrollOffsetManager = dependencyFactory.createScrollOffsetManager(
    611                 new AwScrollOffsetManagerDelegate(), new OverScroller(mContext));
    612         mScrollAccessibilityHelper = new ScrollAccessibilityHelper(mContainerView);
    613 
    614         setOverScrollMode(mContainerView.getOverScrollMode());
    615         setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle());
    616         mLayoutChangeListener = new AwLayoutChangeListener();
    617         mContainerView.addOnLayoutChangeListener(mLayoutChangeListener);
    618 
    619         setNewAwContents(nativeInit(mBrowserContext));
    620 
    621         onContainerViewChanged();
    622     }
    623 
    624     private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView,
    625             Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents,
    626             GestureStateListener gestureStateListener,
    627             ContentViewClient contentViewClient,
    628             ContentViewCore.ZoomControlsDelegate zoomControlsDelegate,
    629             WindowAndroid windowAndroid) {
    630         ContentViewCore contentViewCore = new ContentViewCore(context);
    631         contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents,
    632                 windowAndroid);
    633         contentViewCore.addGestureStateListener(gestureStateListener);
    634         contentViewCore.setContentViewClient(contentViewClient);
    635         contentViewCore.setZoomControlsDelegate(zoomControlsDelegate);
    636         return contentViewCore;
    637     }
    638 
    639     boolean isFullScreen() {
    640         return mFullScreenTransitionsState.isFullScreen();
    641     }
    642 
    643     /**
    644      * Transitions this {@link AwContents} to fullscreen mode and returns the
    645      * {@link View} where the contents will be drawn while in fullscreen, or null
    646      * if this AwContents has already been destroyed.
    647      */
    648     View enterFullScreen() {
    649         assert !isFullScreen();
    650         if (isDestroyed()) return null;
    651 
    652         // Detach to tear down the GL functor if this is still associated with the old
    653         // container view. It will be recreated during the next call to onDraw attached to
    654         // the new container view.
    655         onDetachedFromWindow();
    656 
    657         // In fullscreen mode FullScreenView owns the AwViewMethodsImpl and AwContents
    658         // a NullAwViewMethods.
    659         FullScreenView fullScreenView = new FullScreenView(mContext, mAwViewMethods);
    660         mFullScreenTransitionsState.enterFullScreen(fullScreenView);
    661         mAwViewMethods = new NullAwViewMethods(this, mInternalAccessAdapter, mContainerView);
    662         mContainerView.removeOnLayoutChangeListener(mLayoutChangeListener);
    663         fullScreenView.addOnLayoutChangeListener(mLayoutChangeListener);
    664 
    665         // Associate this AwContents with the FullScreenView.
    666         setInternalAccessAdapter(fullScreenView.getInternalAccessAdapter());
    667         setContainerView(fullScreenView);
    668 
    669         return fullScreenView;
    670     }
    671 
    672     /**
    673      * Returns this {@link AwContents} to embedded mode, where the {@link AwContents} are drawn
    674      * in the WebView.
    675      */
    676     void exitFullScreen() {
    677         if (!isFullScreen() || isDestroyed())
    678             // exitFullScreen() can be called without a prior call to enterFullScreen() if a
    679             // "misbehave" app overrides onShowCustomView but does not add the custom view to
    680             // the window. Exiting avoids a crash.
    681             return;
    682 
    683         // Detach to tear down the GL functor if this is still associated with the old
    684         // container view. It will be recreated during the next call to onDraw attached to
    685         // the new container view.
    686         // NOTE: we cannot use mAwViewMethods here because its type is NullAwViewMethods.
    687         AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods();
    688         awViewMethodsImpl.onDetachedFromWindow();
    689 
    690         // Swap the view delegates. In embedded mode the FullScreenView owns a
    691         // NullAwViewMethods and AwContents the AwViewMethodsImpl.
    692         FullScreenView fullscreenView = mFullScreenTransitionsState.getFullScreenView();
    693         fullscreenView.setAwViewMethods(new NullAwViewMethods(
    694                 this, fullscreenView.getInternalAccessAdapter(), fullscreenView));
    695         mAwViewMethods = awViewMethodsImpl;
    696         ViewGroup initialContainerView = mFullScreenTransitionsState.getInitialContainerView();
    697         initialContainerView.addOnLayoutChangeListener(mLayoutChangeListener);
    698         fullscreenView.removeOnLayoutChangeListener(mLayoutChangeListener);
    699 
    700         // Re-associate this AwContents with the WebView.
    701         setInternalAccessAdapter(mFullScreenTransitionsState.getInitialInternalAccessDelegate());
    702         setContainerView(initialContainerView);
    703 
    704         mFullScreenTransitionsState.exitFullScreen();
    705     }
    706 
    707     private void setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter) {
    708         mInternalAccessAdapter = internalAccessAdapter;
    709         mContentViewCore.setContainerViewInternals(mInternalAccessAdapter);
    710     }
    711 
    712     private void setContainerView(ViewGroup newContainerView) {
    713         mContainerView = newContainerView;
    714         mContentViewCore.setContainerView(mContainerView);
    715         if (mAwPdfExporter != null) {
    716             mAwPdfExporter.setContainerView(mContainerView);
    717         }
    718         mWebContentsDelegate.setContainerView(mContainerView);
    719 
    720         onContainerViewChanged();
    721     }
    722 
    723     /**
    724      * Reconciles the state of this AwContents object with the state of the new container view.
    725      */
    726     private void onContainerViewChanged() {
    727         // NOTE: mAwViewMethods is used by the old container view, the WebView, so it might refer
    728         // to a NullAwViewMethods when in fullscreen. To ensure that the state is reconciled with
    729         // the new container view correctly, we bypass mAwViewMethods and use the real
    730         // implementation directly.
    731         AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods();
    732         awViewMethodsImpl.onVisibilityChanged(mContainerView, mContainerView.getVisibility());
    733         awViewMethodsImpl.onWindowVisibilityChanged(mContainerView.getWindowVisibility());
    734 
    735         // We should stop running WebView tests in JellyBean devices, see crbug/161864.
    736         // Until then we skip calling isAttachedToWindow() as it has only been introduced in K.
    737         if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT
    738                 || mContainerView.isAttachedToWindow()) {
    739             awViewMethodsImpl.onAttachedToWindow();
    740         } else {
    741             awViewMethodsImpl.onDetachedFromWindow();
    742         }
    743         awViewMethodsImpl.onSizeChanged(
    744                 mContainerView.getWidth(), mContainerView.getHeight(), 0, 0);
    745         awViewMethodsImpl.onWindowFocusChanged(mContainerView.hasWindowFocus());
    746         awViewMethodsImpl.onFocusChanged(mContainerView.hasFocus(), 0, null);
    747         mContainerView.requestLayout();
    748     }
    749 
    750     /* Common initialization routine for adopting a native AwContents instance into this
    751      * java instance.
    752      *
    753      * TAKE CARE! This method can get called multiple times per java instance. Code accordingly.
    754      * ^^^^^^^^^  See the native class declaration for more details on relative object lifetimes.
    755      */
    756     private void setNewAwContents(long newAwContentsPtr) {
    757         if (mNativeAwContents != 0) {
    758             destroyNatives();
    759             mContentViewCore = null;
    760             mWebContents = null;
    761             mNavigationController = null;
    762         }
    763 
    764         assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null;
    765 
    766         mNativeAwContents = newAwContentsPtr;
    767         // TODO(joth): when the native and java counterparts of AwBrowserContext are hooked up to
    768         // each other, we should update |mBrowserContext| according to the newly received native
    769         // WebContent's browser context.
    770 
    771         // The native side object has been bound to this java instance, so now is the time to
    772         // bind all the native->java relationships.
    773         mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents));
    774 
    775         long nativeWebContents = nativeGetWebContents(mNativeAwContents);
    776 
    777         mWindowAndroid = mContext instanceof Activity ?
    778                 new ActivityWindowAndroid((Activity) mContext) :
    779                 new WindowAndroid(mContext.getApplicationContext());
    780         mContentViewCore = createAndInitializeContentViewCore(
    781                 mContainerView, mContext, mInternalAccessAdapter, nativeWebContents,
    782                 new AwGestureStateListener(), mContentViewClient, mZoomControls, mWindowAndroid);
    783         nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
    784                 mIoThreadClient, mInterceptNavigationDelegate);
    785         mWebContents = mContentViewCore.getWebContents();
    786         mNavigationController = mWebContents.getNavigationController();
    787         installWebContentsObserver();
    788         mSettings.setWebContents(nativeWebContents);
    789         nativeSetDipScale(mNativeAwContents, (float) mDIPScale);
    790         mContentViewCore.onShow();
    791     }
    792 
    793     private void installWebContentsObserver() {
    794         if (mWebContentsObserver != null) {
    795             mWebContentsObserver.detachFromWebContents();
    796         }
    797         mWebContentsObserver = new AwWebContentsObserver(mWebContents, mContentsClient);
    798     }
    799 
    800     /**
    801      * Called on the "source" AwContents that is opening the popup window to
    802      * provide the AwContents to host the pop up content.
    803      */
    804     public void supplyContentsForPopup(AwContents newContents) {
    805         long popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents);
    806         if (popupNativeAwContents == 0) {
    807             Log.w(TAG, "Popup WebView bind failed: no pending content.");
    808             if (newContents != null) newContents.destroy();
    809             return;
    810         }
    811         if (newContents == null) {
    812             nativeDestroy(popupNativeAwContents);
    813             return;
    814         }
    815 
    816         newContents.receivePopupContents(popupNativeAwContents);
    817     }
    818 
    819     // Recap: supplyContentsForPopup() is called on the parent window's content, this method is
    820     // called on the popup window's content.
    821     private void receivePopupContents(long popupNativeAwContents) {
    822         mDeferredShouldOverrideUrlLoadingIsPendingForPopup = true;
    823         // Save existing view state.
    824         final boolean wasAttached = mIsAttachedToWindow;
    825         final boolean wasViewVisible = mIsViewVisible;
    826         final boolean wasWindowVisible = mIsWindowVisible;
    827         final boolean wasPaused = mIsPaused;
    828         final boolean wasFocused = mContainerViewFocused;
    829         final boolean wasWindowFocused = mWindowFocused;
    830 
    831         // Properly clean up existing mContentViewCore and mNativeAwContents.
    832         if (wasFocused) onFocusChanged(false, 0, null);
    833         if (wasWindowFocused) onWindowFocusChanged(false);
    834         if (wasViewVisible) setViewVisibilityInternal(false);
    835         if (wasWindowVisible) setWindowVisibilityInternal(false);
    836         if (wasAttached) onDetachedFromWindow();
    837         if (!wasPaused) onPause();
    838 
    839         // Save injected JavaScript interfaces.
    840         Map<String, Pair<Object, Class>> javascriptInterfaces =
    841                 new HashMap<String, Pair<Object, Class>>();
    842         if (mContentViewCore != null) {
    843             javascriptInterfaces.putAll(mContentViewCore.getJavascriptInterfaces());
    844         }
    845 
    846         setNewAwContents(popupNativeAwContents);
    847 
    848         // Finally refresh all view state for mContentViewCore and mNativeAwContents.
    849         if (!wasPaused) onResume();
    850         if (wasAttached) {
    851             onAttachedToWindow();
    852             postInvalidateOnAnimation();
    853         }
    854         onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0);
    855         if (wasWindowVisible) setWindowVisibilityInternal(true);
    856         if (wasViewVisible) setViewVisibilityInternal(true);
    857         if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused);
    858         if (wasFocused) onFocusChanged(true, 0, null);
    859 
    860         // Restore injected JavaScript interfaces.
    861         for (Map.Entry<String, Pair<Object, Class>> entry : javascriptInterfaces.entrySet()) {
    862             @SuppressWarnings("unchecked")
    863             Class<? extends Annotation> requiredAnnotation = entry.getValue().second;
    864             mContentViewCore.addPossiblyUnsafeJavascriptInterface(
    865                     entry.getValue().first,
    866                     entry.getKey(),
    867                     requiredAnnotation);
    868         }
    869     }
    870 
    871     /**
    872      * Destroys this object and deletes its native counterpart.
    873      */
    874     public void destroy() {
    875         mIsDestroyed = true;
    876         destroyNatives();
    877     }
    878 
    879     /**
    880      * Deletes the native counterpart of this object.
    881      */
    882     private void destroyNatives() {
    883         if (mCleanupReference != null) {
    884             assert mNativeAwContents != 0;
    885             // If we are attached, we have to call native detach to clean up
    886             // hardware resources.
    887             if (mIsAttachedToWindow) {
    888                 nativeOnDetachedFromWindow(mNativeAwContents);
    889             }
    890 
    891             mWebContentsObserver.detachFromWebContents();
    892             mWebContentsObserver = null;
    893             mContentViewCore.destroy();
    894             mContentViewCore = null;
    895             mNativeAwContents = 0;
    896             mWebContents = null;
    897             mNavigationController = null;
    898 
    899             mCleanupReference.cleanupNow();
    900             mCleanupReference = null;
    901         }
    902 
    903         assert mContentViewCore == null;
    904         assert mWebContents == null;
    905         assert mNavigationController == null;
    906         assert mNativeAwContents == 0;
    907     }
    908 
    909     private boolean isDestroyed() {
    910         if (mIsDestroyed) {
    911             assert mContentViewCore == null;
    912             assert mWebContents == null;
    913             assert mNavigationController == null;
    914             assert mNativeAwContents == 0;
    915         } else {
    916             assert mContentViewCore != null;
    917             assert mWebContents != null;
    918             assert mNavigationController != null;
    919             assert mNativeAwContents != 0;
    920         }
    921         return mIsDestroyed;
    922     }
    923 
    924     @VisibleForTesting
    925     public ContentViewCore getContentViewCore() {
    926         return mContentViewCore;
    927     }
    928 
    929     @VisibleForTesting
    930     public WebContents getWebContents() {
    931         return mWebContents;
    932     }
    933 
    934     @VisibleForTesting
    935     public NavigationController getNavigationController() {
    936         return mNavigationController;
    937     }
    938 
    939     // Can be called from any thread.
    940     public AwSettings getSettings() {
    941         return mSettings;
    942     }
    943 
    944     public AwPdfExporter getPdfExporter() {
    945         if (isDestroyed()) return null;
    946         if (mAwPdfExporter == null) {
    947             mAwPdfExporter = new AwPdfExporter(mContainerView);
    948             nativeCreatePdfExporter(mNativeAwContents, mAwPdfExporter);
    949         }
    950         return mAwPdfExporter;
    951     }
    952 
    953     public static void setAwDrawSWFunctionTable(long functionTablePointer) {
    954         nativeSetAwDrawSWFunctionTable(functionTablePointer);
    955     }
    956 
    957     public static void setAwDrawGLFunctionTable(long functionTablePointer) {
    958         nativeSetAwDrawGLFunctionTable(functionTablePointer);
    959     }
    960 
    961     public static long getAwDrawGLFunction() {
    962         return nativeGetAwDrawGLFunction();
    963     }
    964 
    965     public static void setShouldDownloadFavicons() {
    966         nativeSetShouldDownloadFavicons();
    967     }
    968 
    969     /**
    970      * Disables contents of JS-to-Java bridge objects to be inspectable using
    971      * Object.keys() method and "for .. in" loops. This is intended for applications
    972      * targeting earlier Android releases where this was not possible, and we want
    973      * to ensure backwards compatible behavior.
    974      */
    975     public void disableJavascriptInterfacesInspection() {
    976         if (!isDestroyed()) mContentViewCore.setAllowJavascriptInterfacesInspection(false);
    977     }
    978 
    979     /**
    980      * Intended for test code.
    981      * @return the number of native instances of this class.
    982      */
    983     @VisibleForTesting
    984     public static int getNativeInstanceCount() {
    985         return nativeGetNativeInstanceCount();
    986     }
    987 
    988     public long getAwDrawGLViewContext() {
    989         // Only called during early construction, so client should not have had a chance to
    990         // call destroy yet.
    991         assert !isDestroyed();
    992 
    993         // Using the native pointer as the returned viewContext. This is matched by the
    994         // reinterpret_cast back to BrowserViewRenderer pointer in the native DrawGLFunction.
    995         return nativeGetAwDrawGLViewContext(mNativeAwContents);
    996     }
    997 
    998     // This is only to avoid heap allocations inside getGlobalVisibleRect. It should treated
    999     // as a local variable in the function and not used anywhere else.
   1000     private static final Rect sLocalGlobalVisibleRect = new Rect();
   1001 
   1002     private Rect getGlobalVisibleRect() {
   1003         if (!mContainerView.getGlobalVisibleRect(sLocalGlobalVisibleRect)) {
   1004             sLocalGlobalVisibleRect.setEmpty();
   1005         }
   1006         return sLocalGlobalVisibleRect;
   1007     }
   1008 
   1009     //--------------------------------------------------------------------------------------------
   1010     //  WebView[Provider] method implementations (where not provided by ContentViewCore)
   1011     //--------------------------------------------------------------------------------------------
   1012 
   1013     public void onDraw(Canvas canvas) {
   1014         mAwViewMethods.onDraw(canvas);
   1015     }
   1016 
   1017     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   1018         mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec);
   1019     }
   1020 
   1021     public int getContentHeightCss() {
   1022         return (int) Math.ceil(mContentHeightDip);
   1023     }
   1024 
   1025     public int getContentWidthCss() {
   1026         return (int) Math.ceil(mContentWidthDip);
   1027     }
   1028 
   1029     public Picture capturePicture() {
   1030         if (isDestroyed()) return null;
   1031         return new AwPicture(nativeCapturePicture(mNativeAwContents,
   1032                 mScrollOffsetManager.computeHorizontalScrollRange(),
   1033                 mScrollOffsetManager.computeVerticalScrollRange()));
   1034     }
   1035 
   1036     public void clearView() {
   1037         if (!isDestroyed()) nativeClearView(mNativeAwContents);
   1038     }
   1039 
   1040     /**
   1041      * Enable the onNewPicture callback.
   1042      * @param enabled Flag to enable the callback.
   1043      * @param invalidationOnly Flag to call back only on invalidation without providing a picture.
   1044      */
   1045     public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) {
   1046         if (isDestroyed()) return;
   1047         if (invalidationOnly) {
   1048             mPictureListenerContentProvider = null;
   1049         } else if (enabled && mPictureListenerContentProvider == null) {
   1050             mPictureListenerContentProvider = new Callable<Picture>() {
   1051                 @Override
   1052                 public Picture call() {
   1053                     return capturePicture();
   1054                 }
   1055             };
   1056         }
   1057         nativeEnableOnNewPicture(mNativeAwContents, enabled);
   1058     }
   1059 
   1060     public void findAllAsync(String searchString) {
   1061         if (!isDestroyed()) nativeFindAllAsync(mNativeAwContents, searchString);
   1062     }
   1063 
   1064     public void findNext(boolean forward) {
   1065         if (!isDestroyed()) nativeFindNext(mNativeAwContents, forward);
   1066     }
   1067 
   1068     public void clearMatches() {
   1069         if (!isDestroyed()) nativeClearMatches(mNativeAwContents);
   1070     }
   1071 
   1072     /**
   1073      * @return load progress of the WebContents.
   1074      */
   1075     public int getMostRecentProgress() {
   1076         // WebContentsDelegateAndroid conveniently caches the most recent notified value for us.
   1077         return mWebContentsDelegate.getMostRecentProgress();
   1078     }
   1079 
   1080     public Bitmap getFavicon() {
   1081         return mFavicon;
   1082     }
   1083 
   1084     private void requestVisitedHistoryFromClient() {
   1085         ValueCallback<String[]> callback = new ValueCallback<String[]>() {
   1086             @Override
   1087             public void onReceiveValue(final String[] value) {
   1088                 ThreadUtils.runOnUiThread(new Runnable() {
   1089                     @Override
   1090                     public void run() {
   1091                         if (!isDestroyed()) nativeAddVisitedLinks(mNativeAwContents, value);
   1092                     }
   1093                 });
   1094             }
   1095         };
   1096         mContentsClient.getVisitedHistory(callback);
   1097     }
   1098 
   1099     /**
   1100      * Load url without fixing up the url string. Consumers of ContentView are responsible for
   1101      * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
   1102      * off during user input).
   1103      *
   1104      * @param params Parameters for this load.
   1105      */
   1106     public void loadUrl(LoadUrlParams params) {
   1107         if (isDestroyed()) return;
   1108 
   1109         if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA &&
   1110                 !params.isBaseUrlDataScheme()) {
   1111             // This allows data URLs with a non-data base URL access to file:///android_asset/ and
   1112             // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also
   1113             // allow access to file:// URLs (subject to OS level permission checks).
   1114             params.setCanLoadLocalResources(true);
   1115         }
   1116 
   1117         // If we are reloading the same url, then set transition type as reload.
   1118         if (params.getUrl() != null &&
   1119                 params.getUrl().equals(mWebContents.getUrl()) &&
   1120                 params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) {
   1121             params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_RELOAD);
   1122         }
   1123         params.setTransitionType(
   1124                 params.getTransitionType() | PageTransitionTypes.PAGE_TRANSITION_FROM_API);
   1125 
   1126         // For WebView, always use the user agent override, which is set
   1127         // every time the user agent in AwSettings is modified.
   1128         params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
   1129 
   1130 
   1131         // We don't pass extra headers to the content layer, as WebViewClassic
   1132         // was adding them in a very narrow set of conditions. See http://crbug.com/306873
   1133         // However, if the embedder is attempting to inject a Referer header for their
   1134         // loadUrl call, then we set that separately and remove it from the extra headers map/
   1135         final String referer = "referer";
   1136         Map<String, String> extraHeaders = params.getExtraHeaders();
   1137         if (extraHeaders != null) {
   1138             for (String header : extraHeaders.keySet()) {
   1139                 if (referer.equals(header.toLowerCase(Locale.US))) {
   1140                     params.setReferrer(new Referrer(extraHeaders.remove(header), 1));
   1141                     params.setExtraHeaders(extraHeaders);
   1142                     break;
   1143                 }
   1144             }
   1145         }
   1146 
   1147         nativeSetExtraHeadersForUrl(
   1148                 mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString());
   1149         params.setExtraHeaders(new HashMap<String, String>());
   1150 
   1151         mNavigationController.loadUrl(params);
   1152 
   1153         // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit.
   1154         // Chromium does not use this use code path and the best emulation of this behavior to call
   1155         // request visited links once on the first URL load of the WebView.
   1156         if (!mHasRequestedVisitedHistoryFromClient) {
   1157             mHasRequestedVisitedHistoryFromClient = true;
   1158             requestVisitedHistoryFromClient();
   1159         }
   1160 
   1161         if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA &&
   1162                 params.getBaseUrl() != null) {
   1163             // Data loads with a base url will be resolved in Blink, and not cause an onPageStarted
   1164             // event to be sent. Sending the callback directly from here.
   1165             mContentsClient.getCallbackHelper().postOnPageStarted(params.getBaseUrl());
   1166         }
   1167     }
   1168 
   1169     /**
   1170      * Get the URL of the current page.
   1171      *
   1172      * @return The URL of the current page or null if it's empty.
   1173      */
   1174     public String getUrl() {
   1175         if (isDestroyed()) return null;
   1176         String url =  mWebContents.getUrl();
   1177         if (url == null || url.trim().isEmpty()) return null;
   1178         return url;
   1179     }
   1180 
   1181     public void requestFocus() {
   1182         mAwViewMethods.requestFocus();
   1183     }
   1184 
   1185     public void setBackgroundColor(int color) {
   1186         mBaseBackgroundColor = color;
   1187         if (!isDestroyed()) nativeSetBackgroundColor(mNativeAwContents, color);
   1188     }
   1189 
   1190     /**
   1191      * @see android.view.View#setLayerType()
   1192      */
   1193     public void setLayerType(int layerType, Paint paint) {
   1194         mAwViewMethods.setLayerType(layerType, paint);
   1195     }
   1196 
   1197     int getEffectiveBackgroundColor() {
   1198         // Do not ask the ContentViewCore for the background color, as it will always
   1199         // report white prior to initial navigation or post destruction,  whereas we want
   1200         // to use the client supplied base value in those cases.
   1201         if (isDestroyed() || !mContentsClient.isCachedRendererBackgroundColorValid()) {
   1202             return mBaseBackgroundColor;
   1203         }
   1204         return mContentsClient.getCachedRendererBackgroundColor();
   1205     }
   1206 
   1207     public boolean isMultiTouchZoomSupported() {
   1208         return mSettings.supportsMultiTouchZoom();
   1209     }
   1210 
   1211     public View getZoomControlsForTest() {
   1212         return mZoomControls.getZoomControlsViewForTest();
   1213     }
   1214 
   1215     /**
   1216      * @see ContentViewCore#getContentSettings()
   1217      */
   1218     public ContentSettings getContentSettings() {
   1219         return isDestroyed() ? null : mContentViewCore.getContentSettings();
   1220     }
   1221 
   1222     /**
   1223      * @see View#setOverScrollMode(int)
   1224      */
   1225     public void setOverScrollMode(int mode) {
   1226         if (mode != View.OVER_SCROLL_NEVER) {
   1227             mOverScrollGlow = new OverScrollGlow(mContext, mContainerView);
   1228         } else {
   1229             mOverScrollGlow = null;
   1230         }
   1231     }
   1232 
   1233     // TODO(mkosiba): In WebViewClassic these appear in some of the scroll extent calculation
   1234     // methods but toggling them has no visiual effect on the content (in other words the scrolling
   1235     // code behaves as if the scrollbar-related padding is in place but the onDraw code doesn't
   1236     // take that into consideration).
   1237     // http://crbug.com/269032
   1238     private boolean mOverlayHorizontalScrollbar = true;
   1239     private boolean mOverlayVerticalScrollbar = false;
   1240 
   1241     /**
   1242      * @see View#setScrollBarStyle(int)
   1243      */
   1244     public void setScrollBarStyle(int style) {
   1245         if (style == View.SCROLLBARS_INSIDE_OVERLAY
   1246                 || style == View.SCROLLBARS_OUTSIDE_OVERLAY) {
   1247             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
   1248         } else {
   1249             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
   1250         }
   1251     }
   1252 
   1253     /**
   1254      * @see View#setHorizontalScrollbarOverlay(boolean)
   1255      */
   1256     public void setHorizontalScrollbarOverlay(boolean overlay) {
   1257         mOverlayHorizontalScrollbar = overlay;
   1258     }
   1259 
   1260     /**
   1261      * @see View#setVerticalScrollbarOverlay(boolean)
   1262      */
   1263     public void setVerticalScrollbarOverlay(boolean overlay) {
   1264         mOverlayVerticalScrollbar = overlay;
   1265     }
   1266 
   1267     /**
   1268      * @see View#overlayHorizontalScrollbar()
   1269      */
   1270     public boolean overlayHorizontalScrollbar() {
   1271         return mOverlayHorizontalScrollbar;
   1272     }
   1273 
   1274     /**
   1275      * @see View#overlayVerticalScrollbar()
   1276      */
   1277     public boolean overlayVerticalScrollbar() {
   1278         return mOverlayVerticalScrollbar;
   1279     }
   1280 
   1281     /**
   1282      * Called by the embedder when the scroll offset of the containing view has changed.
   1283      * @see View#onScrollChanged(int,int)
   1284      */
   1285     public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) {
   1286         // A side-effect of View.onScrollChanged is that the scroll accessibility event being sent
   1287         // by the base class implementation. This is completely hidden from the base classes and
   1288         // cannot be prevented, which is why we need the code below.
   1289         mScrollAccessibilityHelper.removePostedViewScrolledAccessibilityEventCallback();
   1290         mScrollOffsetManager.onContainerViewScrollChanged(l, t);
   1291     }
   1292 
   1293     /**
   1294      * Called by the embedder when the containing view is to be scrolled or overscrolled.
   1295      * @see View#onOverScrolled(int,int,int,int)
   1296      */
   1297     public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX,
   1298             boolean clampedY) {
   1299         int oldX = mContainerView.getScrollX();
   1300         int oldY = mContainerView.getScrollY();
   1301 
   1302         mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
   1303 
   1304         if (mOverScrollGlow != null) {
   1305             mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(),
   1306                     oldX, oldY,
   1307                     mScrollOffsetManager.computeMaximumHorizontalScrollOffset(),
   1308                     mScrollOffsetManager.computeMaximumVerticalScrollOffset());
   1309         }
   1310     }
   1311 
   1312     /**
   1313      * @see android.webkit.WebView#requestChildRectangleOnScreen(View, Rect, boolean)
   1314      */
   1315     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
   1316         return mScrollOffsetManager.requestChildRectangleOnScreen(
   1317                 child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY(),
   1318                 rect, immediate);
   1319     }
   1320 
   1321     /**
   1322      * @see View.computeScroll()
   1323      */
   1324     public void computeScroll() {
   1325         mScrollOffsetManager.computeScrollAndAbsorbGlow(mOverScrollGlow);
   1326     }
   1327 
   1328     /**
   1329      * @see View#computeHorizontalScrollRange()
   1330      */
   1331     public int computeHorizontalScrollRange() {
   1332         return mScrollOffsetManager.computeHorizontalScrollRange();
   1333     }
   1334 
   1335     /**
   1336      * @see View#computeHorizontalScrollOffset()
   1337      */
   1338     public int computeHorizontalScrollOffset() {
   1339         return mScrollOffsetManager.computeHorizontalScrollOffset();
   1340     }
   1341 
   1342     /**
   1343      * @see View#computeVerticalScrollRange()
   1344      */
   1345     public int computeVerticalScrollRange() {
   1346         return mScrollOffsetManager.computeVerticalScrollRange();
   1347     }
   1348 
   1349     /**
   1350      * @see View#computeVerticalScrollOffset()
   1351      */
   1352     public int computeVerticalScrollOffset() {
   1353         return mScrollOffsetManager.computeVerticalScrollOffset();
   1354     }
   1355 
   1356     /**
   1357      * @see View#computeVerticalScrollExtent()
   1358      */
   1359     public int computeVerticalScrollExtent() {
   1360         return mScrollOffsetManager.computeVerticalScrollExtent();
   1361     }
   1362 
   1363     /**
   1364      * @see android.webkit.WebView#stopLoading()
   1365      */
   1366     public void stopLoading() {
   1367         if (!isDestroyed()) mWebContents.stop();
   1368     }
   1369 
   1370     /**
   1371      * @see android.webkit.WebView#reload()
   1372      */
   1373     public void reload() {
   1374         if (!isDestroyed()) mNavigationController.reload(true);
   1375     }
   1376 
   1377     /**
   1378      * @see android.webkit.WebView#canGoBack()
   1379      */
   1380     public boolean canGoBack() {
   1381         return isDestroyed() ? false : mNavigationController.canGoBack();
   1382     }
   1383 
   1384     /**
   1385      * @see android.webkit.WebView#goBack()
   1386      */
   1387     public void goBack() {
   1388         if (!isDestroyed()) mNavigationController.goBack();
   1389     }
   1390 
   1391     /**
   1392      * @see android.webkit.WebView#canGoForward()
   1393      */
   1394     public boolean canGoForward() {
   1395         return isDestroyed() ? false : mNavigationController.canGoForward();
   1396     }
   1397 
   1398     /**
   1399      * @see android.webkit.WebView#goForward()
   1400      */
   1401     public void goForward() {
   1402         if (!isDestroyed()) mNavigationController.goForward();
   1403     }
   1404 
   1405     /**
   1406      * @see android.webkit.WebView#canGoBackOrForward(int)
   1407      */
   1408     public boolean canGoBackOrForward(int steps) {
   1409         return isDestroyed() ? false : mNavigationController.canGoToOffset(steps);
   1410     }
   1411 
   1412     /**
   1413      * @see android.webkit.WebView#goBackOrForward(int)
   1414      */
   1415     public void goBackOrForward(int steps) {
   1416         if (!isDestroyed()) mNavigationController.goToOffset(steps);
   1417     }
   1418 
   1419     /**
   1420      * @see android.webkit.WebView#pauseTimers()
   1421      */
   1422     public void pauseTimers() {
   1423         if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(true);
   1424     }
   1425 
   1426     /**
   1427      * @see android.webkit.WebView#resumeTimers()
   1428      */
   1429     public void resumeTimers() {
   1430         if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(false);
   1431     }
   1432 
   1433     /**
   1434      * @see android.webkit.WebView#onPause()
   1435      */
   1436     public void onPause() {
   1437         if (mIsPaused || isDestroyed()) return;
   1438         mIsPaused = true;
   1439         nativeSetIsPaused(mNativeAwContents, mIsPaused);
   1440         mContentViewCore.onHide();
   1441     }
   1442 
   1443     /**
   1444      * @see android.webkit.WebView#onResume()
   1445      */
   1446     public void onResume() {
   1447         if (!mIsPaused || isDestroyed()) return;
   1448         mIsPaused = false;
   1449         nativeSetIsPaused(mNativeAwContents, mIsPaused);
   1450         mContentViewCore.onShow();
   1451     }
   1452 
   1453     /**
   1454      * @see android.webkit.WebView#isPaused()
   1455      */
   1456     public boolean isPaused() {
   1457         return mIsPaused;
   1458     }
   1459 
   1460     /**
   1461      * @see android.webkit.WebView#onCreateInputConnection(EditorInfo)
   1462      */
   1463     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
   1464         return mAwViewMethods.onCreateInputConnection(outAttrs);
   1465     }
   1466 
   1467     /**
   1468      * @see android.webkit.WebView#onKeyUp(int, KeyEvent)
   1469      */
   1470     public boolean onKeyUp(int keyCode, KeyEvent event) {
   1471         return mAwViewMethods.onKeyUp(keyCode, event);
   1472     }
   1473 
   1474     /**
   1475      * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent)
   1476      */
   1477     public boolean dispatchKeyEvent(KeyEvent event) {
   1478         return mAwViewMethods.dispatchKeyEvent(event);
   1479     }
   1480 
   1481     /**
   1482      * Clears the resource cache. Note that the cache is per-application, so this will clear the
   1483      * cache for all WebViews used.
   1484      *
   1485      * @param includeDiskFiles if false, only the RAM cache is cleared
   1486      */
   1487     public void clearCache(boolean includeDiskFiles) {
   1488         if (!isDestroyed()) nativeClearCache(mNativeAwContents, includeDiskFiles);
   1489     }
   1490 
   1491     public void documentHasImages(Message message) {
   1492         if (!isDestroyed()) nativeDocumentHasImages(mNativeAwContents, message);
   1493     }
   1494 
   1495     public void saveWebArchive(
   1496             final String basename, boolean autoname, final ValueCallback<String> callback) {
   1497         if (!autoname) {
   1498             saveWebArchiveInternal(basename, callback);
   1499             return;
   1500         }
   1501         // If auto-generating the file name, handle the name generation on a background thread
   1502         // as it will require I/O access for checking whether previous files existed.
   1503         new AsyncTask<Void, Void, String>() {
   1504             @Override
   1505             protected String doInBackground(Void... params) {
   1506                 return generateArchiveAutoNamePath(getOriginalUrl(), basename);
   1507             }
   1508 
   1509             @Override
   1510             protected void onPostExecute(String result) {
   1511                 saveWebArchiveInternal(result, callback);
   1512             }
   1513         }.execute();
   1514     }
   1515 
   1516     public String getOriginalUrl() {
   1517         if (isDestroyed()) return null;
   1518         NavigationHistory history = mNavigationController.getNavigationHistory();
   1519         int currentIndex = history.getCurrentEntryIndex();
   1520         if (currentIndex >= 0 && currentIndex < history.getEntryCount()) {
   1521             return history.getEntryAtIndex(currentIndex).getOriginalUrl();
   1522         }
   1523         return null;
   1524     }
   1525 
   1526     /**
   1527      * @see ContentViewCore#getNavigationHistory()
   1528      */
   1529     public NavigationHistory getNavigationHistory() {
   1530         return isDestroyed() ? null : mNavigationController.getNavigationHistory();
   1531     }
   1532 
   1533     /**
   1534      * @see android.webkit.WebView#getTitle()
   1535      */
   1536     public String getTitle() {
   1537         return isDestroyed() ? null : mWebContents.getTitle();
   1538     }
   1539 
   1540     /**
   1541      * @see android.webkit.WebView#clearHistory()
   1542      */
   1543     public void clearHistory() {
   1544         if (!isDestroyed()) mNavigationController.clearHistory();
   1545     }
   1546 
   1547     public String[] getHttpAuthUsernamePassword(String host, String realm) {
   1548         return mBrowserContext.getHttpAuthDatabase(mContext)
   1549                 .getHttpAuthUsernamePassword(host, realm);
   1550     }
   1551 
   1552     public void setHttpAuthUsernamePassword(String host, String realm, String username,
   1553             String password) {
   1554         mBrowserContext.getHttpAuthDatabase(mContext)
   1555                 .setHttpAuthUsernamePassword(host, realm, username, password);
   1556     }
   1557 
   1558     /**
   1559      * @see android.webkit.WebView#getCertificate()
   1560      */
   1561     public SslCertificate getCertificate() {
   1562         return isDestroyed() ? null
   1563                 : SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents));
   1564     }
   1565 
   1566     /**
   1567      * @see android.webkit.WebView#clearSslPreferences()
   1568      */
   1569     public void clearSslPreferences() {
   1570         if (!isDestroyed()) mNavigationController.clearSslPreferences();
   1571     }
   1572 
   1573     // TODO(sgurun) remove after this rolls in. To keep internal tree happy.
   1574     public void clearClientCertPreferences() { }
   1575 
   1576     /**
   1577      * Method to return all hit test values relevant to public WebView API.
   1578      * Note that this expose more data than needed for WebView.getHitTestResult.
   1579      * Unsafely returning reference to mutable internal object to avoid excessive
   1580      * garbage allocation on repeated calls.
   1581      */
   1582     public HitTestData getLastHitTestResult() {
   1583         if (isDestroyed()) return null;
   1584         nativeUpdateLastHitTestData(mNativeAwContents);
   1585         return mPossiblyStaleHitTestData;
   1586     }
   1587 
   1588     /**
   1589      * @see android.webkit.WebView#requestFocusNodeHref()
   1590      */
   1591     public void requestFocusNodeHref(Message msg) {
   1592         if (msg == null || isDestroyed()) return;
   1593 
   1594         nativeUpdateLastHitTestData(mNativeAwContents);
   1595         Bundle data = msg.getData();
   1596 
   1597         // In order to maintain compatibility with the old WebView's implementation,
   1598         // the absolute (full) url is passed in the |url| field, not only the href attribute.
   1599         // Note: HitTestData could be cleaned up at this point. See http://crbug.com/290992.
   1600         data.putString("url", mPossiblyStaleHitTestData.href);
   1601         data.putString("title", mPossiblyStaleHitTestData.anchorText);
   1602         data.putString("src", mPossiblyStaleHitTestData.imgSrc);
   1603         msg.setData(data);
   1604         msg.sendToTarget();
   1605     }
   1606 
   1607     /**
   1608      * @see android.webkit.WebView#requestImageRef()
   1609      */
   1610     public void requestImageRef(Message msg) {
   1611         if (msg == null || isDestroyed()) return;
   1612 
   1613         nativeUpdateLastHitTestData(mNativeAwContents);
   1614         Bundle data = msg.getData();
   1615         data.putString("url", mPossiblyStaleHitTestData.imgSrc);
   1616         msg.setData(data);
   1617         msg.sendToTarget();
   1618     }
   1619 
   1620     @VisibleForTesting
   1621     public float getPageScaleFactor() {
   1622         return mPageScaleFactor;
   1623     }
   1624 
   1625     /**
   1626      * @see android.webkit.WebView#getScale()
   1627      *
   1628      * Please note that the scale returned is the page scale multiplied by
   1629      * the screen density factor. See CTS WebViewTest.testSetInitialScale.
   1630      */
   1631     public float getScale() {
   1632         return (float) (mPageScaleFactor * mDIPScale);
   1633     }
   1634 
   1635     /**
   1636      * @see android.webkit.WebView#flingScroll(int, int)
   1637      */
   1638     public void flingScroll(int velocityX, int velocityY) {
   1639         mScrollOffsetManager.flingScroll(velocityX, velocityY);
   1640     }
   1641 
   1642     /**
   1643      * @see android.webkit.WebView#pageUp(boolean)
   1644      */
   1645     public boolean pageUp(boolean top) {
   1646         return mScrollOffsetManager.pageUp(top);
   1647     }
   1648 
   1649     /**
   1650      * @see android.webkit.WebView#pageDown(boolean)
   1651      */
   1652     public boolean pageDown(boolean bottom) {
   1653         return mScrollOffsetManager.pageDown(bottom);
   1654     }
   1655 
   1656     /**
   1657      * @see android.webkit.WebView#canZoomIn()
   1658      */
   1659     // This method uses the term 'zoom' for legacy reasons, but relates
   1660     // to what chrome calls the 'page scale factor'.
   1661     public boolean canZoomIn() {
   1662         final float zoomInExtent = mMaxPageScaleFactor - mPageScaleFactor;
   1663         return zoomInExtent > ZOOM_CONTROLS_EPSILON;
   1664     }
   1665 
   1666     /**
   1667      * @see android.webkit.WebView#canZoomOut()
   1668      */
   1669     // This method uses the term 'zoom' for legacy reasons, but relates
   1670     // to what chrome calls the 'page scale factor'.
   1671     public boolean canZoomOut() {
   1672         final float zoomOutExtent = mPageScaleFactor - mMinPageScaleFactor;
   1673         return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
   1674     }
   1675 
   1676     /**
   1677      * @see android.webkit.WebView#zoomIn()
   1678      */
   1679     // This method uses the term 'zoom' for legacy reasons, but relates
   1680     // to what chrome calls the 'page scale factor'.
   1681     public boolean zoomIn() {
   1682         if (!canZoomIn()) {
   1683             return false;
   1684         }
   1685         return zoomBy(1.25f);
   1686     }
   1687 
   1688     /**
   1689      * @see android.webkit.WebView#zoomOut()
   1690      */
   1691     // This method uses the term 'zoom' for legacy reasons, but relates
   1692     // to what chrome calls the 'page scale factor'.
   1693     public boolean zoomOut() {
   1694         if (!canZoomOut()) {
   1695             return false;
   1696         }
   1697         return zoomBy(0.8f);
   1698     }
   1699 
   1700     /**
   1701      * @see android.webkit.WebView#zoomBy()
   1702      */
   1703     // This method uses the term 'zoom' for legacy reasons, but relates
   1704     // to what chrome calls the 'page scale factor'.
   1705     public boolean zoomBy(float delta) {
   1706         if (isDestroyed()) return false;
   1707         if (delta < 0.01f || delta > 100.0f) {
   1708             throw new IllegalStateException("zoom delta value outside [0.01, 100] range.");
   1709         }
   1710         return mContentViewCore.pinchByDelta(delta);
   1711     }
   1712 
   1713     /**
   1714      * @see android.webkit.WebView#invokeZoomPicker()
   1715      */
   1716     public void invokeZoomPicker() {
   1717         if (!isDestroyed()) mContentViewCore.invokeZoomPicker();
   1718     }
   1719 
   1720     /**
   1721      * @see android.webkit.WebView#preauthorizePermission(Uri, long)
   1722      */
   1723     public void preauthorizePermission(Uri origin, long resources) {
   1724         if (isDestroyed()) return;
   1725         nativePreauthorizePermission(mNativeAwContents, origin.toString(), resources);
   1726     }
   1727 
   1728     /**
   1729      * @see ContentViewCore.evaluateJavaScript(String, JavaScriptCallback)
   1730      */
   1731     public void evaluateJavaScript(String script, final ValueCallback<String> callback) {
   1732         if (isDestroyed()) return;
   1733         JavaScriptCallback jsCallback = null;
   1734         if (callback != null) {
   1735             jsCallback = new JavaScriptCallback() {
   1736                 @Override
   1737                 public void handleJavaScriptResult(String jsonResult) {
   1738                     callback.onReceiveValue(jsonResult);
   1739                 }
   1740             };
   1741         }
   1742 
   1743         mWebContents.evaluateJavaScript(script, jsCallback);
   1744     }
   1745 
   1746     // TODO(boliu): Remove this once Android side no longer calls this.
   1747     public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
   1748         if (!isDestroyed()) mWebContents.evaluateJavaScript(script, null);
   1749     }
   1750 
   1751     //--------------------------------------------------------------------------------------------
   1752     //  View and ViewGroup method implementations
   1753     //--------------------------------------------------------------------------------------------
   1754 
   1755     /**
   1756      * @see android.webkit.View#onTouchEvent()
   1757      */
   1758     public boolean onTouchEvent(MotionEvent event) {
   1759         return mAwViewMethods.onTouchEvent(event);
   1760     }
   1761 
   1762     /**
   1763      * @see android.view.View#onHoverEvent()
   1764      */
   1765     public boolean onHoverEvent(MotionEvent event) {
   1766         return mAwViewMethods.onHoverEvent(event);
   1767     }
   1768 
   1769     /**
   1770      * @see android.view.View#onGenericMotionEvent()
   1771      */
   1772     public boolean onGenericMotionEvent(MotionEvent event) {
   1773         return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event);
   1774     }
   1775 
   1776     /**
   1777      * @see android.view.View#onConfigurationChanged()
   1778      */
   1779     public void onConfigurationChanged(Configuration newConfig) {
   1780         mAwViewMethods.onConfigurationChanged(newConfig);
   1781     }
   1782 
   1783     /**
   1784      * @see android.view.View#onAttachedToWindow()
   1785      */
   1786     public void onAttachedToWindow() {
   1787         mTemporarilyDetached = false;
   1788         mAwViewMethods.onAttachedToWindow();
   1789     }
   1790 
   1791     /**
   1792      * @see android.view.View#onDetachedFromWindow()
   1793      */
   1794     @SuppressLint("MissingSuperCall")
   1795     public void onDetachedFromWindow() {
   1796         mAwViewMethods.onDetachedFromWindow();
   1797     }
   1798 
   1799     /**
   1800      * @see android.view.View#onWindowFocusChanged()
   1801      */
   1802     public void onWindowFocusChanged(boolean hasWindowFocus) {
   1803         mAwViewMethods.onWindowFocusChanged(hasWindowFocus);
   1804     }
   1805 
   1806     /**
   1807      * @see android.view.View#onFocusChanged()
   1808      */
   1809     public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
   1810         if (!mTemporarilyDetached) {
   1811             mAwViewMethods.onFocusChanged(focused, direction, previouslyFocusedRect);
   1812         }
   1813     }
   1814 
   1815     /**
   1816      * @see android.view.View#onStartTemporaryDetach()
   1817      */
   1818     public void onStartTemporaryDetach() {
   1819         mTemporarilyDetached = true;
   1820     }
   1821 
   1822     /**
   1823      * @see android.view.View#onFinishTemporaryDetach()
   1824      */
   1825     public void onFinishTemporaryDetach() {
   1826         mTemporarilyDetached = false;
   1827     }
   1828 
   1829     /**
   1830      * @see android.view.View#onSizeChanged()
   1831      */
   1832     public void onSizeChanged(int w, int h, int ow, int oh) {
   1833         mAwViewMethods.onSizeChanged(w, h, ow, oh);
   1834     }
   1835 
   1836     /**
   1837      * @see android.view.View#onVisibilityChanged()
   1838      */
   1839     public void onVisibilityChanged(View changedView, int visibility) {
   1840         mAwViewMethods.onVisibilityChanged(changedView, visibility);
   1841     }
   1842 
   1843     /**
   1844      * @see android.view.View#onWindowVisibilityChanged()
   1845      */
   1846     public void onWindowVisibilityChanged(int visibility) {
   1847         mAwViewMethods.onWindowVisibilityChanged(visibility);
   1848     }
   1849 
   1850     private void setViewVisibilityInternal(boolean visible) {
   1851         mIsViewVisible = visible;
   1852         if (!isDestroyed()) nativeSetViewVisibility(mNativeAwContents, mIsViewVisible);
   1853     }
   1854 
   1855     private void setWindowVisibilityInternal(boolean visible) {
   1856         mIsWindowVisible = visible;
   1857         if (!isDestroyed()) nativeSetWindowVisibility(mNativeAwContents, mIsWindowVisible);
   1858     }
   1859 
   1860     /**
   1861      * Key for opaque state in bundle. Note this is only public for tests.
   1862      */
   1863     public static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE";
   1864 
   1865     /**
   1866      * Save the state of this AwContents into provided Bundle.
   1867      * @return False if saving state failed.
   1868      */
   1869     public boolean saveState(Bundle outState) {
   1870         if (isDestroyed() || outState == null) return false;
   1871 
   1872         byte[] state = nativeGetOpaqueState(mNativeAwContents);
   1873         if (state == null) return false;
   1874 
   1875         outState.putByteArray(SAVE_RESTORE_STATE_KEY, state);
   1876         return true;
   1877     }
   1878 
   1879     /**
   1880      * Restore the state of this AwContents into provided Bundle.
   1881      * @param inState Must be a bundle returned by saveState.
   1882      * @return False if restoring state failed.
   1883      */
   1884     public boolean restoreState(Bundle inState) {
   1885         if (isDestroyed() || inState == null) return false;
   1886 
   1887         byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY);
   1888         if (state == null) return false;
   1889 
   1890         boolean result = nativeRestoreFromOpaqueState(mNativeAwContents, state);
   1891 
   1892         // The onUpdateTitle callback normally happens when a page is loaded,
   1893         // but is optimized out in the restoreState case because the title is
   1894         // already restored. See WebContentsImpl::UpdateTitleForEntry. So we
   1895         // call the callback explicitly here.
   1896         if (result) mContentsClient.onReceivedTitle(mWebContents.getTitle());
   1897 
   1898         return result;
   1899     }
   1900 
   1901     /**
   1902      * @see ContentViewCore#addPossiblyUnsafeJavascriptInterface(Object, String, Class)
   1903      */
   1904     public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
   1905             Class<? extends Annotation> requiredAnnotation) {
   1906         if (isDestroyed()) return;
   1907         mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation);
   1908     }
   1909 
   1910     /**
   1911      * @see android.webkit.WebView#removeJavascriptInterface(String)
   1912      */
   1913     public void removeJavascriptInterface(String interfaceName) {
   1914         if (!isDestroyed()) mContentViewCore.removeJavascriptInterface(interfaceName);
   1915     }
   1916 
   1917     /**
   1918      * If native accessibility (not script injection) is enabled, and if this is
   1919      * running on JellyBean or later, returns an AccessibilityNodeProvider that
   1920      * implements native accessibility for this view. Returns null otherwise.
   1921      * @return The AccessibilityNodeProvider, if available, or null otherwise.
   1922      */
   1923     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
   1924         return isDestroyed() ? null : mContentViewCore.getAccessibilityNodeProvider();
   1925     }
   1926 
   1927     /**
   1928      * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
   1929      */
   1930     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
   1931         if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityNodeInfo(info);
   1932     }
   1933 
   1934     /**
   1935      * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent)
   1936      */
   1937     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   1938         if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityEvent(event);
   1939     }
   1940 
   1941     public boolean supportsAccessibilityAction(int action) {
   1942         return isDestroyed() ? false : mContentViewCore.supportsAccessibilityAction(action);
   1943     }
   1944 
   1945     /**
   1946      * @see android.webkit.WebView#performAccessibilityAction(int, Bundle)
   1947      */
   1948     public boolean performAccessibilityAction(int action, Bundle arguments) {
   1949         return isDestroyed() ? false
   1950                 : mContentViewCore.performAccessibilityAction(action, arguments);
   1951     }
   1952 
   1953     /**
   1954      * @see android.webkit.WebView#clearFormData()
   1955      */
   1956     public void hideAutofillPopup() {
   1957         if (mAwAutofillClient != null) {
   1958             mAwAutofillClient.hideAutofillPopup();
   1959         }
   1960     }
   1961 
   1962     public void setNetworkAvailable(boolean networkUp) {
   1963         if (!isDestroyed()) nativeSetJsOnlineProperty(mNativeAwContents, networkUp);
   1964     }
   1965 
   1966     //--------------------------------------------------------------------------------------------
   1967     //  Methods called from native via JNI
   1968     //--------------------------------------------------------------------------------------------
   1969 
   1970     @CalledByNative
   1971     private static void onDocumentHasImagesResponse(boolean result, Message message) {
   1972         message.arg1 = result ? 1 : 0;
   1973         message.sendToTarget();
   1974     }
   1975 
   1976     @CalledByNative
   1977     private void onReceivedTouchIconUrl(String url, boolean precomposed) {
   1978         mContentsClient.onReceivedTouchIconUrl(url, precomposed);
   1979     }
   1980 
   1981     @CalledByNative
   1982     private void onReceivedIcon(Bitmap bitmap) {
   1983         mContentsClient.onReceivedIcon(bitmap);
   1984         mFavicon = bitmap;
   1985     }
   1986 
   1987     /** Callback for generateMHTML. */
   1988     @CalledByNative
   1989     private static void generateMHTMLCallback(
   1990             String path, long size, ValueCallback<String> callback) {
   1991         if (callback == null) return;
   1992         callback.onReceiveValue(size < 0 ? null : path);
   1993     }
   1994 
   1995     @CalledByNative
   1996     private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
   1997         mContentsClient.onReceivedHttpAuthRequest(handler, host, realm);
   1998     }
   1999 
   2000     private class AwGeolocationCallback implements GeolocationPermissions.Callback {
   2001 
   2002         @Override
   2003         public void invoke(final String origin, final boolean allow, final boolean retain) {
   2004             ThreadUtils.runOnUiThread(new Runnable() {
   2005                 @Override
   2006                 public void run() {
   2007                     if (retain) {
   2008                         if (allow) {
   2009                             mBrowserContext.getGeolocationPermissions().allow(origin);
   2010                         } else {
   2011                             mBrowserContext.getGeolocationPermissions().deny(origin);
   2012                         }
   2013                     }
   2014                     if (isDestroyed()) return;
   2015                     nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin);
   2016                 }
   2017             });
   2018         }
   2019     }
   2020 
   2021     @CalledByNative
   2022     private void onGeolocationPermissionsShowPrompt(String origin) {
   2023         if (isDestroyed()) return;
   2024         AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions();
   2025         // Reject if geoloaction is disabled, or the origin has a retained deny
   2026         if (!mSettings.getGeolocationEnabled()) {
   2027             nativeInvokeGeolocationCallback(mNativeAwContents, false, origin);
   2028             return;
   2029         }
   2030         // Allow if the origin has a retained allow
   2031         if (permissions.hasOrigin(origin)) {
   2032             nativeInvokeGeolocationCallback(mNativeAwContents, permissions.isOriginAllowed(origin),
   2033                     origin);
   2034             return;
   2035         }
   2036         mContentsClient.onGeolocationPermissionsShowPrompt(
   2037                 origin, new AwGeolocationCallback());
   2038     }
   2039 
   2040     @CalledByNative
   2041     private void onGeolocationPermissionsHidePrompt() {
   2042         mContentsClient.onGeolocationPermissionsHidePrompt();
   2043     }
   2044 
   2045     @CalledByNative
   2046     private void onPermissionRequest(AwPermissionRequest awPermissionRequest) {
   2047         mContentsClient.onPermissionRequest(awPermissionRequest);
   2048     }
   2049 
   2050     @CalledByNative
   2051     private void onPermissionRequestCanceled(AwPermissionRequest awPermissionRequest) {
   2052         mContentsClient.onPermissionRequestCanceled(awPermissionRequest);
   2053     }
   2054 
   2055     @CalledByNative
   2056     public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
   2057             boolean isDoneCounting) {
   2058         mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
   2059     }
   2060 
   2061     @CalledByNative
   2062     public void onNewPicture() {
   2063         // Don't call capturePicture() here but instead defer it until the posted task runs within
   2064         // the callback helper, to avoid doubling back into the renderer compositor in the middle
   2065         // of the notification it is sending up to here.
   2066         mContentsClient.getCallbackHelper().postOnNewPicture(mPictureListenerContentProvider);
   2067     }
   2068 
   2069     // Called as a result of nativeUpdateLastHitTestData.
   2070     @CalledByNative
   2071     private void updateHitTestData(
   2072             int type, String extra, String href, String anchorText, String imgSrc) {
   2073         mPossiblyStaleHitTestData.hitTestResultType = type;
   2074         mPossiblyStaleHitTestData.hitTestResultExtraData = extra;
   2075         mPossiblyStaleHitTestData.href = href;
   2076         mPossiblyStaleHitTestData.anchorText = anchorText;
   2077         mPossiblyStaleHitTestData.imgSrc = imgSrc;
   2078     }
   2079 
   2080     @CalledByNative
   2081     private boolean requestDrawGL(Canvas canvas, boolean waitForCompletion) {
   2082         return mNativeGLDelegate.requestDrawGL(canvas, waitForCompletion, mContainerView);
   2083     }
   2084 
   2085     private static final boolean SUPPORTS_ON_ANIMATION =
   2086             Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
   2087 
   2088     @CalledByNative
   2089     private void postInvalidateOnAnimation() {
   2090         if (SUPPORTS_ON_ANIMATION && !mWindowAndroid.isInsideVSync()) {
   2091             mContainerView.postInvalidateOnAnimation();
   2092         } else {
   2093             mContainerView.invalidate();
   2094         }
   2095     }
   2096 
   2097     // Call postInvalidateOnAnimation for invalidations. This is only used to synchronize
   2098     // draw functor destruction.
   2099     @CalledByNative
   2100     private void invalidateOnFunctorDestroy() {
   2101         mContainerView.invalidate();
   2102     }
   2103 
   2104     @CalledByNative
   2105     private int[] getLocationOnScreen() {
   2106         int[] result = new int[2];
   2107         mContainerView.getLocationOnScreen(result);
   2108         return result;
   2109     }
   2110 
   2111     @CalledByNative
   2112     private void onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor) {
   2113         // This change notification comes from the renderer thread, not from the cc/ impl thread.
   2114         mLayoutSizer.onPageScaleChanged(webLayoutPageScaleFactor);
   2115     }
   2116 
   2117     @CalledByNative
   2118     private void onWebLayoutContentsSizeChanged(int widthCss, int heightCss) {
   2119         // This change notification comes from the renderer thread, not from the cc/ impl thread.
   2120         mLayoutSizer.onContentSizeChanged(widthCss, heightCss);
   2121     }
   2122 
   2123     @CalledByNative
   2124     private void scrollContainerViewTo(int x, int y) {
   2125         mScrollOffsetManager.scrollContainerViewTo(x, y);
   2126     }
   2127 
   2128     @CalledByNative
   2129     private boolean isFlingActive() {
   2130         return mScrollOffsetManager.isFlingActive();
   2131     }
   2132 
   2133     @CalledByNative
   2134     private void updateScrollState(int maxContainerViewScrollOffsetX,
   2135             int maxContainerViewScrollOffsetY, int contentWidthDip, int contentHeightDip,
   2136             float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) {
   2137         mContentWidthDip = contentWidthDip;
   2138         mContentHeightDip = contentHeightDip;
   2139         mScrollOffsetManager.setMaxScrollOffset(maxContainerViewScrollOffsetX,
   2140             maxContainerViewScrollOffsetY);
   2141         setPageScaleFactorAndLimits(pageScaleFactor, minPageScaleFactor, maxPageScaleFactor);
   2142     }
   2143 
   2144     @CalledByNative
   2145     private void setAwAutofillClient(AwAutofillClient client) {
   2146         mAwAutofillClient = client;
   2147         client.init(mContentViewCore);
   2148     }
   2149 
   2150     @CalledByNative
   2151     private void didOverscroll(int deltaX, int deltaY) {
   2152         if (mOverScrollGlow != null) {
   2153             mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
   2154         }
   2155 
   2156         mScrollOffsetManager.overScrollBy(deltaX, deltaY);
   2157 
   2158         if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
   2159             mContainerView.invalidate();
   2160         }
   2161     }
   2162 
   2163     // -------------------------------------------------------------------------------------------
   2164     // Helper methods
   2165     // -------------------------------------------------------------------------------------------
   2166 
   2167     private void setPageScaleFactorAndLimits(
   2168             float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) {
   2169         if (mPageScaleFactor == pageScaleFactor &&
   2170                 mMinPageScaleFactor == minPageScaleFactor &&
   2171                 mMaxPageScaleFactor == maxPageScaleFactor) {
   2172             return;
   2173         }
   2174         mMinPageScaleFactor = minPageScaleFactor;
   2175         mMaxPageScaleFactor = maxPageScaleFactor;
   2176         if (mPageScaleFactor != pageScaleFactor) {
   2177             float oldPageScaleFactor = mPageScaleFactor;
   2178             mPageScaleFactor = pageScaleFactor;
   2179             mContentsClient.getCallbackHelper().postOnScaleChangedScaled(
   2180                     (float) (oldPageScaleFactor * mDIPScale),
   2181                     (float) (mPageScaleFactor * mDIPScale));
   2182         }
   2183     }
   2184 
   2185     private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) {
   2186         if (path == null || isDestroyed()) {
   2187             ThreadUtils.runOnUiThread(new Runnable() {
   2188                 @Override
   2189                 public void run() {
   2190                     callback.onReceiveValue(null);
   2191                 }
   2192             });
   2193         } else {
   2194             nativeGenerateMHTML(mNativeAwContents, path, callback);
   2195         }
   2196     }
   2197 
   2198     /**
   2199      * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's
   2200      * autoname logic.
   2201      */
   2202     private static String generateArchiveAutoNamePath(String originalUrl, String baseName) {
   2203         String name = null;
   2204         if (originalUrl != null && !originalUrl.isEmpty()) {
   2205             try {
   2206                 String path = new URL(originalUrl).getPath();
   2207                 int lastSlash = path.lastIndexOf('/');
   2208                 if (lastSlash > 0) {
   2209                     name = path.substring(lastSlash + 1);
   2210                 } else {
   2211                     name = path;
   2212                 }
   2213             } catch (MalformedURLException e) {
   2214                 // If it fails parsing the URL, we'll just rely on the default name below.
   2215             }
   2216         }
   2217 
   2218         if (TextUtils.isEmpty(name)) name = "index";
   2219 
   2220         String testName = baseName + name + WEB_ARCHIVE_EXTENSION;
   2221         if (!new File(testName).exists()) return testName;
   2222 
   2223         for (int i = 1; i < 100; i++) {
   2224             testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION;
   2225             if (!new File(testName).exists()) return testName;
   2226         }
   2227 
   2228         Log.e(TAG, "Unable to auto generate archive name for path: " + baseName);
   2229         return null;
   2230     }
   2231 
   2232     @Override
   2233     public void extractSmartClipData(int x, int y, int width, int height) {
   2234         if (!isDestroyed()) mContentViewCore.extractSmartClipData(x, y, width, height);
   2235     }
   2236 
   2237     @Override
   2238     public void setSmartClipResultHandler(final Handler resultHandler) {
   2239         if (resultHandler == null) {
   2240             mContentViewCore.setSmartClipDataListener(null);
   2241             return;
   2242         }
   2243         mContentViewCore.setSmartClipDataListener(new ContentViewCore.SmartClipDataListener() {
   2244             public void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
   2245                 Bundle bundle = new Bundle();
   2246                 bundle.putString("url", mContentViewCore.getWebContents().getVisibleUrl());
   2247                 bundle.putString("title", mContentViewCore.getWebContents().getTitle());
   2248                 bundle.putParcelable("rect", clipRect);
   2249                 bundle.putString("text", text);
   2250                 bundle.putString("html", html);
   2251                 try {
   2252                     Message msg = Message.obtain(resultHandler, 0);
   2253                     msg.setData(bundle);
   2254                     msg.sendToTarget();
   2255                 } catch (Exception e) {
   2256                     Log.e(TAG, "Error calling handler for smart clip data: ", e);
   2257                 }
   2258             }
   2259         });
   2260     }
   2261 
   2262     // --------------------------------------------------------------------------------------------
   2263     // This is the AwViewMethods implementation that does real work. The AwViewMethodsImpl is
   2264     // hooked up to the WebView in embedded mode and to the FullScreenView in fullscreen mode,
   2265     // but not to both at the same time.
   2266     private class AwViewMethodsImpl implements AwViewMethods {
   2267         private int mLayerType = View.LAYER_TYPE_NONE;
   2268         private ComponentCallbacks2 mComponentCallbacks;
   2269 
   2270         // Only valid within software onDraw().
   2271         private final Rect mClipBoundsTemporary = new Rect();
   2272 
   2273         @Override
   2274         public void onDraw(Canvas canvas) {
   2275             if (isDestroyed()) {
   2276                 canvas.drawColor(getEffectiveBackgroundColor());
   2277                 return;
   2278             }
   2279 
   2280             // For hardware draws, the clip at onDraw time could be different
   2281             // from the clip during DrawGL.
   2282             if (!canvas.isHardwareAccelerated() && !canvas.getClipBounds(mClipBoundsTemporary)) {
   2283                 return;
   2284             }
   2285 
   2286             mScrollOffsetManager.syncScrollOffsetFromOnDraw();
   2287             Rect globalVisibleRect = getGlobalVisibleRect();
   2288             if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(),
   2289                     mContainerView.getScrollX(), mContainerView.getScrollY(),
   2290                     globalVisibleRect.left, globalVisibleRect.top,
   2291                     globalVisibleRect.right, globalVisibleRect.bottom)) {
   2292                 // Can happen during initialization when compositor is not set
   2293                 // up. Or when clearView
   2294                 // is in effect. Just draw background color instead.
   2295                 canvas.drawColor(getEffectiveBackgroundColor());
   2296             }
   2297 
   2298             if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas,
   2299                     mScrollOffsetManager.computeMaximumHorizontalScrollOffset(),
   2300                     mScrollOffsetManager.computeMaximumVerticalScrollOffset())) {
   2301                 mContainerView.invalidate();
   2302             }
   2303         }
   2304 
   2305         @Override
   2306         public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   2307             mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec);
   2308         }
   2309 
   2310         @Override
   2311         public void requestFocus() {
   2312             if (isDestroyed()) return;
   2313             if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) {
   2314                 nativeFocusFirstNode(mNativeAwContents);
   2315             }
   2316         }
   2317 
   2318         @Override
   2319         public void setLayerType(int layerType, Paint paint) {
   2320             mLayerType = layerType;
   2321             updateHardwareAcceleratedFeaturesToggle();
   2322         }
   2323 
   2324         private void updateHardwareAcceleratedFeaturesToggle() {
   2325             mSettings.setEnableSupportedHardwareAcceleratedFeatures(
   2326                     mIsAttachedToWindow && mContainerView.isHardwareAccelerated() &&
   2327                             (mLayerType == View.LAYER_TYPE_NONE
   2328                             || mLayerType == View.LAYER_TYPE_HARDWARE));
   2329         }
   2330 
   2331         @Override
   2332         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
   2333             return isDestroyed() ? null : mContentViewCore.onCreateInputConnection(outAttrs);
   2334         }
   2335 
   2336         @Override
   2337         public boolean onKeyUp(int keyCode, KeyEvent event) {
   2338             return isDestroyed() ? false : mContentViewCore.onKeyUp(keyCode, event);
   2339         }
   2340 
   2341         @Override
   2342         public boolean dispatchKeyEvent(KeyEvent event) {
   2343             if (isDestroyed()) return false;
   2344             if (isDpadEvent(event)) {
   2345                 mSettings.setSpatialNavigationEnabled(true);
   2346             }
   2347             return mContentViewCore.dispatchKeyEvent(event);
   2348         }
   2349 
   2350         private boolean isDpadEvent(KeyEvent event) {
   2351             if (event.getAction() == KeyEvent.ACTION_DOWN) {
   2352                 switch (event.getKeyCode()) {
   2353                     case KeyEvent.KEYCODE_DPAD_CENTER:
   2354                     case KeyEvent.KEYCODE_DPAD_DOWN:
   2355                     case KeyEvent.KEYCODE_DPAD_UP:
   2356                     case KeyEvent.KEYCODE_DPAD_LEFT:
   2357                     case KeyEvent.KEYCODE_DPAD_RIGHT:
   2358                         return true;
   2359                 }
   2360             }
   2361             return false;
   2362         }
   2363 
   2364         @Override
   2365         public boolean onTouchEvent(MotionEvent event) {
   2366             if (isDestroyed()) return false;
   2367             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
   2368                 mSettings.setSpatialNavigationEnabled(false);
   2369             }
   2370 
   2371             mScrollOffsetManager.setProcessingTouchEvent(true);
   2372             boolean rv = mContentViewCore.onTouchEvent(event);
   2373             mScrollOffsetManager.setProcessingTouchEvent(false);
   2374 
   2375             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
   2376                 int actionIndex = event.getActionIndex();
   2377 
   2378                 // Note this will trigger IPC back to browser even if nothing is
   2379                 // hit.
   2380                 nativeRequestNewHitTestDataAt(mNativeAwContents,
   2381                         (int) Math.round(event.getX(actionIndex) / mDIPScale),
   2382                         (int) Math.round(event.getY(actionIndex) / mDIPScale));
   2383             }
   2384 
   2385             if (mOverScrollGlow != null) {
   2386                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
   2387                     mOverScrollGlow.setShouldPull(true);
   2388                 } else if (event.getActionMasked() == MotionEvent.ACTION_UP ||
   2389                         event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
   2390                     mOverScrollGlow.setShouldPull(false);
   2391                     mOverScrollGlow.releaseAll();
   2392                 }
   2393             }
   2394 
   2395             return rv;
   2396         }
   2397 
   2398         @Override
   2399         public boolean onHoverEvent(MotionEvent event) {
   2400             return isDestroyed() ? false : mContentViewCore.onHoverEvent(event);
   2401         }
   2402 
   2403         @Override
   2404         public boolean onGenericMotionEvent(MotionEvent event) {
   2405             return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event);
   2406         }
   2407 
   2408         @Override
   2409         public void onConfigurationChanged(Configuration newConfig) {
   2410             if (!isDestroyed()) mContentViewCore.onConfigurationChanged(newConfig);
   2411         }
   2412 
   2413         @Override
   2414         public void onAttachedToWindow() {
   2415             if (isDestroyed()) return;
   2416             if (mIsAttachedToWindow) {
   2417                 Log.w(TAG, "onAttachedToWindow called when already attached. Ignoring");
   2418                 return;
   2419             }
   2420             mIsAttachedToWindow = true;
   2421 
   2422             mContentViewCore.onAttachedToWindow();
   2423             nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(),
   2424                     mContainerView.getHeight());
   2425             updateHardwareAcceleratedFeaturesToggle();
   2426 
   2427             if (mComponentCallbacks != null) return;
   2428             mComponentCallbacks = new AwComponentCallbacks();
   2429             mContext.registerComponentCallbacks(mComponentCallbacks);
   2430         }
   2431 
   2432         @Override
   2433         public void onDetachedFromWindow() {
   2434             if (isDestroyed()) return;
   2435             if (!mIsAttachedToWindow) {
   2436                 Log.w(TAG, "onDetachedFromWindow called when already detached. Ignoring");
   2437                 return;
   2438             }
   2439             mIsAttachedToWindow = false;
   2440             hideAutofillPopup();
   2441             nativeOnDetachedFromWindow(mNativeAwContents);
   2442 
   2443             mContentViewCore.onDetachedFromWindow();
   2444             updateHardwareAcceleratedFeaturesToggle();
   2445 
   2446             if (mComponentCallbacks != null) {
   2447                 mContext.unregisterComponentCallbacks(mComponentCallbacks);
   2448                 mComponentCallbacks = null;
   2449             }
   2450 
   2451             mScrollAccessibilityHelper.removePostedCallbacks();
   2452             mNativeGLDelegate.detachGLFunctor();
   2453         }
   2454 
   2455         @Override
   2456         public void onWindowFocusChanged(boolean hasWindowFocus) {
   2457             if (isDestroyed()) return;
   2458             mWindowFocused = hasWindowFocus;
   2459             mContentViewCore.onWindowFocusChanged(hasWindowFocus);
   2460         }
   2461 
   2462         @Override
   2463         public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
   2464             if (isDestroyed()) return;
   2465             mContainerViewFocused = focused;
   2466             mContentViewCore.onFocusChanged(focused);
   2467         }
   2468 
   2469         @Override
   2470         public void onSizeChanged(int w, int h, int ow, int oh) {
   2471             if (isDestroyed()) return;
   2472             mScrollOffsetManager.setContainerViewSize(w, h);
   2473             // The AwLayoutSizer needs to go first so that if we're in
   2474             // fixedLayoutSize mode the update
   2475             // to enter fixedLayoutSize mode is sent before the first resize
   2476             // update.
   2477             mLayoutSizer.onSizeChanged(w, h, ow, oh);
   2478             mContentViewCore.onPhysicalBackingSizeChanged(w, h);
   2479             mContentViewCore.onSizeChanged(w, h, ow, oh);
   2480             nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh);
   2481         }
   2482 
   2483         @Override
   2484         public void onVisibilityChanged(View changedView, int visibility) {
   2485             boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE;
   2486             if (mIsViewVisible == viewVisible) return;
   2487             setViewVisibilityInternal(viewVisible);
   2488         }
   2489 
   2490         @Override
   2491         public void onWindowVisibilityChanged(int visibility) {
   2492             boolean windowVisible = visibility == View.VISIBLE;
   2493             if (mIsWindowVisible == windowVisible) return;
   2494             setWindowVisibilityInternal(windowVisible);
   2495         }
   2496     }
   2497 
   2498     // Return true if the GeolocationPermissionAPI should be used.
   2499     @CalledByNative
   2500     private boolean useLegacyGeolocationPermissionAPI() {
   2501         // Always return true since we are not ready to swap the geolocation yet.
   2502         // TODO: If we decide not to migrate the geolocation, there are some unreachable
   2503         // code need to remove. http://crbug.com/396184.
   2504         return true;
   2505     }
   2506 
   2507     //--------------------------------------------------------------------------------------------
   2508     //  Native methods
   2509     //--------------------------------------------------------------------------------------------
   2510 
   2511     private static native long nativeInit(AwBrowserContext browserContext);
   2512     private static native void nativeDestroy(long nativeAwContents);
   2513     private static native void nativeSetAwDrawSWFunctionTable(long functionTablePointer);
   2514     private static native void nativeSetAwDrawGLFunctionTable(long functionTablePointer);
   2515     private static native long nativeGetAwDrawGLFunction();
   2516     private static native int nativeGetNativeInstanceCount();
   2517     private static native void nativeSetShouldDownloadFavicons();
   2518 
   2519     private native void nativeSetJavaPeers(long nativeAwContents, AwContents awContents,
   2520             AwWebContentsDelegate webViewWebContentsDelegate,
   2521             AwContentsClientBridge contentsClientBridge,
   2522             AwContentsIoThreadClient ioThreadClient,
   2523             InterceptNavigationDelegate navigationInterceptionDelegate);
   2524     private native long nativeGetWebContents(long nativeAwContents);
   2525 
   2526     private native void nativeDocumentHasImages(long nativeAwContents, Message message);
   2527     private native void nativeGenerateMHTML(
   2528             long nativeAwContents, String path, ValueCallback<String> callback);
   2529 
   2530     private native void nativeAddVisitedLinks(long nativeAwContents, String[] visitedLinks);
   2531     private native boolean nativeOnDraw(long nativeAwContents, Canvas canvas,
   2532             boolean isHardwareAccelerated, int scrollX, int scrollY,
   2533             int visibleLeft, int visibleTop, int visibleRight, int visibleBottom);
   2534     private native void nativeFindAllAsync(long nativeAwContents, String searchString);
   2535     private native void nativeFindNext(long nativeAwContents, boolean forward);
   2536     private native void nativeClearMatches(long nativeAwContents);
   2537     private native void nativeClearCache(long nativeAwContents, boolean includeDiskFiles);
   2538     private native byte[] nativeGetCertificate(long nativeAwContents);
   2539 
   2540     // Coordinates in desity independent pixels.
   2541     private native void nativeRequestNewHitTestDataAt(long nativeAwContents, int x, int y);
   2542     private native void nativeUpdateLastHitTestData(long nativeAwContents);
   2543 
   2544     private native void nativeOnSizeChanged(long nativeAwContents, int w, int h, int ow, int oh);
   2545     private native void nativeScrollTo(long nativeAwContents, int x, int y);
   2546     private native void nativeSetViewVisibility(long nativeAwContents, boolean visible);
   2547     private native void nativeSetWindowVisibility(long nativeAwContents, boolean visible);
   2548     private native void nativeSetIsPaused(long nativeAwContents, boolean paused);
   2549     private native void nativeOnAttachedToWindow(long nativeAwContents, int w, int h);
   2550     private static native void nativeOnDetachedFromWindow(long nativeAwContents);
   2551     private native void nativeSetDipScale(long nativeAwContents, float dipScale);
   2552 
   2553     // Returns null if save state fails.
   2554     private native byte[] nativeGetOpaqueState(long nativeAwContents);
   2555 
   2556     // Returns false if restore state fails.
   2557     private native boolean nativeRestoreFromOpaqueState(long nativeAwContents, byte[] state);
   2558 
   2559     private native long nativeReleasePopupAwContents(long nativeAwContents);
   2560     private native void nativeFocusFirstNode(long nativeAwContents);
   2561     private native void nativeSetBackgroundColor(long nativeAwContents, int color);
   2562 
   2563     private native long nativeGetAwDrawGLViewContext(long nativeAwContents);
   2564     private native long nativeCapturePicture(long nativeAwContents, int width, int height);
   2565     private native void nativeEnableOnNewPicture(long nativeAwContents, boolean enabled);
   2566     private native void nativeClearView(long nativeAwContents);
   2567     private native void nativeSetExtraHeadersForUrl(long nativeAwContents,
   2568             String url, String extraHeaders);
   2569 
   2570     private native void nativeInvokeGeolocationCallback(
   2571             long nativeAwContents, boolean value, String requestingFrame);
   2572 
   2573     private native void nativeSetJsOnlineProperty(long nativeAwContents, boolean networkUp);
   2574 
   2575     private native void nativeTrimMemory(long nativeAwContents, int level, boolean visible);
   2576 
   2577     private native void nativeCreatePdfExporter(long nativeAwContents, AwPdfExporter awPdfExporter);
   2578 
   2579     private native void nativePreauthorizePermission(long nativeAwContents, String origin,
   2580             long resources);
   2581 }
   2582