• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)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          */
super_scrollTo(int scrollX, int scrollY)129         void super_scrollTo(int scrollX, int scrollY);
130 
131         /**
132          * @see View#setMeasuredDimension(int, int)
133          */
setMeasuredDimension(int measuredWidth, int measuredHeight)134         void setMeasuredDimension(int measuredWidth, int measuredHeight);
135 
136         /**
137          * @see View#getScrollBarStyle()
138          */
super_getScrollBarStyle()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          */
requestDrawGL(Canvas canvas, boolean waitForCompletion, View containerView)159         boolean requestDrawGL(Canvas canvas, boolean waitForCompletion, View containerView);
160 
161         /**
162          * Detaches the GLFunctor from the view tree.
163          */
detachGLFunctor()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 {
createLayoutSizer()172         public AwLayoutSizer createLayoutSizer() {
173             return new AwLayoutSizer();
174         }
175 
createScrollOffsetManager( AwScrollOffsetManager.Delegate delegate, OverScroller overScroller)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;
DestroyRunnable(long nativeAwContents)262         private DestroyRunnable(long nativeAwContents) {
263             mNativeAwContents = nativeAwContents;
264         }
265         @Override
run()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 
FullScreenTransitionsState(ViewGroup initialContainerView, InternalAccessDelegate initialInternalAccessAdapter, AwViewMethods initialAwViewMethods)280         private FullScreenTransitionsState(ViewGroup initialContainerView,
281                 InternalAccessDelegate initialInternalAccessAdapter,
282                 AwViewMethods initialAwViewMethods) {
283             mInitialContainerView = initialContainerView;
284             mInitialInternalAccessAdapter = initialInternalAccessAdapter;
285             mInitialAwViewMethods = initialAwViewMethods;
286         }
287 
enterFullScreen(FullScreenView fullScreenView)288         private void enterFullScreen(FullScreenView fullScreenView) {
289             mFullScreenView = fullScreenView;
290         }
291 
exitFullScreen()292         private void exitFullScreen() {
293             mFullScreenView = null;
294         }
295 
isFullScreen()296         private boolean isFullScreen() {
297             return mFullScreenView != null;
298         }
299 
getInitialContainerView()300         private ViewGroup getInitialContainerView() {
301             return mInitialContainerView;
302         }
303 
getInitialInternalAccessDelegate()304         private InternalAccessDelegate getInitialInternalAccessDelegate() {
305             return mInitialInternalAccessAdapter;
306         }
307 
getInitialAwViewMethods()308         private AwViewMethods getInitialAwViewMethods() {
309             return mInitialAwViewMethods;
310         }
311 
getFullScreenView()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
getCacheMode()326         public int getCacheMode() {
327             return mSettings.getCacheMode();
328         }
329 
330         @Override
shouldInterceptRequest( AwContentsClient.ShouldInterceptRequestParams params)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
shouldBlockContentUrls()358         public boolean shouldBlockContentUrls() {
359             return !mSettings.getAllowContentAccess();
360         }
361 
362         @Override
shouldBlockFileUrls()363         public boolean shouldBlockFileUrls() {
364             return !mSettings.getAllowFileAccess();
365         }
366 
367         @Override
shouldBlockNetworkLoads()368         public boolean shouldBlockNetworkLoads() {
369             return mSettings.getBlockNetworkLoads();
370         }
371 
372         @Override
shouldAcceptThirdPartyCookies()373         public boolean shouldAcceptThirdPartyCookies() {
374             return mSettings.getAcceptThirdPartyCookies();
375         }
376 
377         @Override
onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength)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
newLoginRequest(String realm, String account, String args)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
shouldIgnoreNavigation(NavigationParams navigationParams)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
requestLayout()425         public void requestLayout() {
426             mContainerView.requestLayout();
427         }
428 
429         @Override
setMeasuredDimension(int measuredWidth, int measuredHeight)430         public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
431             mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight);
432         }
433 
434         @Override
isLayoutParamsHeightWrapContent()435         public boolean isLayoutParamsHeightWrapContent() {
436             return mContainerView.getLayoutParams() != null &&
437                     mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
438         }
439 
440         @Override
setForceZeroLayoutHeight(boolean forceZeroHeight)441         public void setForceZeroLayoutHeight(boolean forceZeroHeight) {
442             getSettings().setForceZeroLayoutHeight(forceZeroHeight);
443         }
444     }
445 
446     //--------------------------------------------------------------------------------------------
447     private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate {
448         @Override
overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, boolean isTouchEvent)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
scrollContainerViewTo(int x, int y)456         public void scrollContainerViewTo(int x, int y) {
457             mInternalAccessAdapter.super_scrollTo(x, y);
458         }
459 
460         @Override
scrollNativeTo(int x, int y)461         public void scrollNativeTo(int x, int y) {
462             if (!isDestroyed()) nativeScrollTo(mNativeAwContents, x, y);
463         }
464 
465         @Override
getContainerViewScrollX()466         public int getContainerViewScrollX() {
467             return mContainerView.getScrollX();
468         }
469 
470         @Override
getContainerViewScrollY()471         public int getContainerViewScrollY() {
472             return mContainerView.getScrollY();
473         }
474 
475         @Override
invalidate()476         public void invalidate() {
477             mContainerView.invalidate();
478         }
479     }
480 
481     //--------------------------------------------------------------------------------------------
482     private class AwGestureStateListener extends GestureStateListener {
483         @Override
onPinchStarted()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
onPinchEnded()495         public void onPinchEnded() {
496             mLayoutSizer.unfreezeLayoutRequests();
497         }
498 
499         @Override
onFlingCancelGesture()500         public void onFlingCancelGesture() {
501             mScrollOffsetManager.onFlingCancelGesture();
502         }
503 
504         @Override
onUnhandledFlingStartEvent(int velocityX, int velocityY)505         public void onUnhandledFlingStartEvent(int velocityX, int velocityY) {
506             mScrollOffsetManager.onUnhandledFlingStartEvent(velocityX, velocityY);
507         }
508 
509         @Override
onScrollUpdateGestureConsumed()510         public void onScrollUpdateGestureConsumed() {
511             mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback();
512         }
513     }
514 
515     //--------------------------------------------------------------------------------------------
516     private class AwComponentCallbacks implements ComponentCallbacks2 {
517         @Override
onTrimMemory(final int level)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
onLowMemory()526         public void onLowMemory() {}
527 
528         @Override
onConfigurationChanged(Configuration configuration)529         public void onConfigurationChanged(Configuration configuration) {}
530     };
531 
532     //--------------------------------------------------------------------------------------------
533     private class AwLayoutChangeListener implements View.OnLayoutChangeListener {
534         @Override
onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)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      */
AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate, AwContentsClient contentsClient, AwSettings awSettings)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      */
AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate, AwContentsClient contentsClient, AwSettings settings, DependencyFactory dependencyFactory)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 
createAndInitializeContentViewCore(ViewGroup containerView, Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents, GestureStateListener gestureStateListener, ContentViewClient contentViewClient, ContentViewCore.ZoomControlsDelegate zoomControlsDelegate, WindowAndroid windowAndroid)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 
isFullScreen()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      */
enterFullScreen()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      */
exitFullScreen()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 
setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter)707     private void setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter) {
708         mInternalAccessAdapter = internalAccessAdapter;
709         mContentViewCore.setContainerViewInternals(mInternalAccessAdapter);
710     }
711 
setContainerView(ViewGroup newContainerView)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      */
onContainerViewChanged()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      */
setNewAwContents(long newAwContentsPtr)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 
installWebContentsObserver()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      */
supplyContentsForPopup(AwContents newContents)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.
receivePopupContents(long popupNativeAwContents)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      */
destroy()874     public void destroy() {
875         mIsDestroyed = true;
876         destroyNatives();
877     }
878 
879     /**
880      * Deletes the native counterpart of this object.
881      */
destroyNatives()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 
isDestroyed()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
getContentViewCore()925     public ContentViewCore getContentViewCore() {
926         return mContentViewCore;
927     }
928 
929     @VisibleForTesting
getWebContents()930     public WebContents getWebContents() {
931         return mWebContents;
932     }
933 
934     @VisibleForTesting
getNavigationController()935     public NavigationController getNavigationController() {
936         return mNavigationController;
937     }
938 
939     // Can be called from any thread.
getSettings()940     public AwSettings getSettings() {
941         return mSettings;
942     }
943 
getPdfExporter()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 
setAwDrawSWFunctionTable(long functionTablePointer)953     public static void setAwDrawSWFunctionTable(long functionTablePointer) {
954         nativeSetAwDrawSWFunctionTable(functionTablePointer);
955     }
956 
setAwDrawGLFunctionTable(long functionTablePointer)957     public static void setAwDrawGLFunctionTable(long functionTablePointer) {
958         nativeSetAwDrawGLFunctionTable(functionTablePointer);
959     }
960 
getAwDrawGLFunction()961     public static long getAwDrawGLFunction() {
962         return nativeGetAwDrawGLFunction();
963     }
964 
setShouldDownloadFavicons()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      */
disableJavascriptInterfacesInspection()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
getNativeInstanceCount()984     public static int getNativeInstanceCount() {
985         return nativeGetNativeInstanceCount();
986     }
987 
getAwDrawGLViewContext()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 
getGlobalVisibleRect()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 
onDraw(Canvas canvas)1013     public void onDraw(Canvas canvas) {
1014         mAwViewMethods.onDraw(canvas);
1015     }
1016 
onMeasure(int widthMeasureSpec, int heightMeasureSpec)1017     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1018         mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec);
1019     }
1020 
getContentHeightCss()1021     public int getContentHeightCss() {
1022         return (int) Math.ceil(mContentHeightDip);
1023     }
1024 
getContentWidthCss()1025     public int getContentWidthCss() {
1026         return (int) Math.ceil(mContentWidthDip);
1027     }
1028 
capturePicture()1029     public Picture capturePicture() {
1030         if (isDestroyed()) return null;
1031         return new AwPicture(nativeCapturePicture(mNativeAwContents,
1032                 mScrollOffsetManager.computeHorizontalScrollRange(),
1033                 mScrollOffsetManager.computeVerticalScrollRange()));
1034     }
1035 
clearView()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      */
enableOnNewPicture(boolean enabled, boolean invalidationOnly)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 
findAllAsync(String searchString)1060     public void findAllAsync(String searchString) {
1061         if (!isDestroyed()) nativeFindAllAsync(mNativeAwContents, searchString);
1062     }
1063 
findNext(boolean forward)1064     public void findNext(boolean forward) {
1065         if (!isDestroyed()) nativeFindNext(mNativeAwContents, forward);
1066     }
1067 
clearMatches()1068     public void clearMatches() {
1069         if (!isDestroyed()) nativeClearMatches(mNativeAwContents);
1070     }
1071 
1072     /**
1073      * @return load progress of the WebContents.
1074      */
getMostRecentProgress()1075     public int getMostRecentProgress() {
1076         // WebContentsDelegateAndroid conveniently caches the most recent notified value for us.
1077         return mWebContentsDelegate.getMostRecentProgress();
1078     }
1079 
getFavicon()1080     public Bitmap getFavicon() {
1081         return mFavicon;
1082     }
1083 
requestVisitedHistoryFromClient()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      */
loadUrl(LoadUrlParams params)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      */
getUrl()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 
requestFocus()1181     public void requestFocus() {
1182         mAwViewMethods.requestFocus();
1183     }
1184 
setBackgroundColor(int color)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      */
setLayerType(int layerType, Paint paint)1193     public void setLayerType(int layerType, Paint paint) {
1194         mAwViewMethods.setLayerType(layerType, paint);
1195     }
1196 
getEffectiveBackgroundColor()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 
isMultiTouchZoomSupported()1207     public boolean isMultiTouchZoomSupported() {
1208         return mSettings.supportsMultiTouchZoom();
1209     }
1210 
getZoomControlsForTest()1211     public View getZoomControlsForTest() {
1212         return mZoomControls.getZoomControlsViewForTest();
1213     }
1214 
1215     /**
1216      * @see ContentViewCore#getContentSettings()
1217      */
getContentSettings()1218     public ContentSettings getContentSettings() {
1219         return isDestroyed() ? null : mContentViewCore.getContentSettings();
1220     }
1221 
1222     /**
1223      * @see View#setOverScrollMode(int)
1224      */
setOverScrollMode(int mode)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      */
setScrollBarStyle(int style)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      */
setHorizontalScrollbarOverlay(boolean overlay)1256     public void setHorizontalScrollbarOverlay(boolean overlay) {
1257         mOverlayHorizontalScrollbar = overlay;
1258     }
1259 
1260     /**
1261      * @see View#setVerticalScrollbarOverlay(boolean)
1262      */
setVerticalScrollbarOverlay(boolean overlay)1263     public void setVerticalScrollbarOverlay(boolean overlay) {
1264         mOverlayVerticalScrollbar = overlay;
1265     }
1266 
1267     /**
1268      * @see View#overlayHorizontalScrollbar()
1269      */
overlayHorizontalScrollbar()1270     public boolean overlayHorizontalScrollbar() {
1271         return mOverlayHorizontalScrollbar;
1272     }
1273 
1274     /**
1275      * @see View#overlayVerticalScrollbar()
1276      */
overlayVerticalScrollbar()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      */
onContainerViewScrollChanged(int l, int t, int oldl, int oldt)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      */
onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)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      */
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)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      */
computeScroll()1324     public void computeScroll() {
1325         mScrollOffsetManager.computeScrollAndAbsorbGlow(mOverScrollGlow);
1326     }
1327 
1328     /**
1329      * @see View#computeHorizontalScrollRange()
1330      */
computeHorizontalScrollRange()1331     public int computeHorizontalScrollRange() {
1332         return mScrollOffsetManager.computeHorizontalScrollRange();
1333     }
1334 
1335     /**
1336      * @see View#computeHorizontalScrollOffset()
1337      */
computeHorizontalScrollOffset()1338     public int computeHorizontalScrollOffset() {
1339         return mScrollOffsetManager.computeHorizontalScrollOffset();
1340     }
1341 
1342     /**
1343      * @see View#computeVerticalScrollRange()
1344      */
computeVerticalScrollRange()1345     public int computeVerticalScrollRange() {
1346         return mScrollOffsetManager.computeVerticalScrollRange();
1347     }
1348 
1349     /**
1350      * @see View#computeVerticalScrollOffset()
1351      */
computeVerticalScrollOffset()1352     public int computeVerticalScrollOffset() {
1353         return mScrollOffsetManager.computeVerticalScrollOffset();
1354     }
1355 
1356     /**
1357      * @see View#computeVerticalScrollExtent()
1358      */
computeVerticalScrollExtent()1359     public int computeVerticalScrollExtent() {
1360         return mScrollOffsetManager.computeVerticalScrollExtent();
1361     }
1362 
1363     /**
1364      * @see android.webkit.WebView#stopLoading()
1365      */
stopLoading()1366     public void stopLoading() {
1367         if (!isDestroyed()) mWebContents.stop();
1368     }
1369 
1370     /**
1371      * @see android.webkit.WebView#reload()
1372      */
reload()1373     public void reload() {
1374         if (!isDestroyed()) mNavigationController.reload(true);
1375     }
1376 
1377     /**
1378      * @see android.webkit.WebView#canGoBack()
1379      */
canGoBack()1380     public boolean canGoBack() {
1381         return isDestroyed() ? false : mNavigationController.canGoBack();
1382     }
1383 
1384     /**
1385      * @see android.webkit.WebView#goBack()
1386      */
goBack()1387     public void goBack() {
1388         if (!isDestroyed()) mNavigationController.goBack();
1389     }
1390 
1391     /**
1392      * @see android.webkit.WebView#canGoForward()
1393      */
canGoForward()1394     public boolean canGoForward() {
1395         return isDestroyed() ? false : mNavigationController.canGoForward();
1396     }
1397 
1398     /**
1399      * @see android.webkit.WebView#goForward()
1400      */
goForward()1401     public void goForward() {
1402         if (!isDestroyed()) mNavigationController.goForward();
1403     }
1404 
1405     /**
1406      * @see android.webkit.WebView#canGoBackOrForward(int)
1407      */
canGoBackOrForward(int steps)1408     public boolean canGoBackOrForward(int steps) {
1409         return isDestroyed() ? false : mNavigationController.canGoToOffset(steps);
1410     }
1411 
1412     /**
1413      * @see android.webkit.WebView#goBackOrForward(int)
1414      */
goBackOrForward(int steps)1415     public void goBackOrForward(int steps) {
1416         if (!isDestroyed()) mNavigationController.goToOffset(steps);
1417     }
1418 
1419     /**
1420      * @see android.webkit.WebView#pauseTimers()
1421      */
pauseTimers()1422     public void pauseTimers() {
1423         if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(true);
1424     }
1425 
1426     /**
1427      * @see android.webkit.WebView#resumeTimers()
1428      */
resumeTimers()1429     public void resumeTimers() {
1430         if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(false);
1431     }
1432 
1433     /**
1434      * @see android.webkit.WebView#onPause()
1435      */
onPause()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      */
onResume()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      */
isPaused()1456     public boolean isPaused() {
1457         return mIsPaused;
1458     }
1459 
1460     /**
1461      * @see android.webkit.WebView#onCreateInputConnection(EditorInfo)
1462      */
onCreateInputConnection(EditorInfo outAttrs)1463     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1464         return mAwViewMethods.onCreateInputConnection(outAttrs);
1465     }
1466 
1467     /**
1468      * @see android.webkit.WebView#onKeyUp(int, KeyEvent)
1469      */
onKeyUp(int keyCode, KeyEvent event)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      */
dispatchKeyEvent(KeyEvent event)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      */
clearCache(boolean includeDiskFiles)1487     public void clearCache(boolean includeDiskFiles) {
1488         if (!isDestroyed()) nativeClearCache(mNativeAwContents, includeDiskFiles);
1489     }
1490 
documentHasImages(Message message)1491     public void documentHasImages(Message message) {
1492         if (!isDestroyed()) nativeDocumentHasImages(mNativeAwContents, message);
1493     }
1494 
saveWebArchive( final String basename, boolean autoname, final ValueCallback<String> callback)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 
getOriginalUrl()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      */
getNavigationHistory()1529     public NavigationHistory getNavigationHistory() {
1530         return isDestroyed() ? null : mNavigationController.getNavigationHistory();
1531     }
1532 
1533     /**
1534      * @see android.webkit.WebView#getTitle()
1535      */
getTitle()1536     public String getTitle() {
1537         return isDestroyed() ? null : mWebContents.getTitle();
1538     }
1539 
1540     /**
1541      * @see android.webkit.WebView#clearHistory()
1542      */
clearHistory()1543     public void clearHistory() {
1544         if (!isDestroyed()) mNavigationController.clearHistory();
1545     }
1546 
getHttpAuthUsernamePassword(String host, String realm)1547     public String[] getHttpAuthUsernamePassword(String host, String realm) {
1548         return mBrowserContext.getHttpAuthDatabase(mContext)
1549                 .getHttpAuthUsernamePassword(host, realm);
1550     }
1551 
setHttpAuthUsernamePassword(String host, String realm, String username, String password)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      */
getCertificate()1561     public SslCertificate getCertificate() {
1562         return isDestroyed() ? null
1563                 : SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents));
1564     }
1565 
1566     /**
1567      * @see android.webkit.WebView#clearSslPreferences()
1568      */
clearSslPreferences()1569     public void clearSslPreferences() {
1570         if (!isDestroyed()) mNavigationController.clearSslPreferences();
1571     }
1572 
1573     // TODO(sgurun) remove after this rolls in. To keep internal tree happy.
clearClientCertPreferences()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      */
getLastHitTestResult()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      */
requestFocusNodeHref(Message msg)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      */
requestImageRef(Message msg)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
getPageScaleFactor()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      */
getScale()1631     public float getScale() {
1632         return (float) (mPageScaleFactor * mDIPScale);
1633     }
1634 
1635     /**
1636      * @see android.webkit.WebView#flingScroll(int, int)
1637      */
flingScroll(int velocityX, int velocityY)1638     public void flingScroll(int velocityX, int velocityY) {
1639         mScrollOffsetManager.flingScroll(velocityX, velocityY);
1640     }
1641 
1642     /**
1643      * @see android.webkit.WebView#pageUp(boolean)
1644      */
pageUp(boolean top)1645     public boolean pageUp(boolean top) {
1646         return mScrollOffsetManager.pageUp(top);
1647     }
1648 
1649     /**
1650      * @see android.webkit.WebView#pageDown(boolean)
1651      */
pageDown(boolean bottom)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'.
canZoomIn()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'.
canZoomOut()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'.
zoomIn()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'.
zoomOut()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'.
zoomBy(float delta)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      */
invokeZoomPicker()1716     public void invokeZoomPicker() {
1717         if (!isDestroyed()) mContentViewCore.invokeZoomPicker();
1718     }
1719 
1720     /**
1721      * @see android.webkit.WebView#preauthorizePermission(Uri, long)
1722      */
preauthorizePermission(Uri origin, long resources)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      */
evaluateJavaScript(String script, final ValueCallback<String> callback)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.
evaluateJavaScriptEvenIfNotYetNavigated(String script)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      */
onTouchEvent(MotionEvent event)1758     public boolean onTouchEvent(MotionEvent event) {
1759         return mAwViewMethods.onTouchEvent(event);
1760     }
1761 
1762     /**
1763      * @see android.view.View#onHoverEvent()
1764      */
onHoverEvent(MotionEvent event)1765     public boolean onHoverEvent(MotionEvent event) {
1766         return mAwViewMethods.onHoverEvent(event);
1767     }
1768 
1769     /**
1770      * @see android.view.View#onGenericMotionEvent()
1771      */
onGenericMotionEvent(MotionEvent event)1772     public boolean onGenericMotionEvent(MotionEvent event) {
1773         return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event);
1774     }
1775 
1776     /**
1777      * @see android.view.View#onConfigurationChanged()
1778      */
onConfigurationChanged(Configuration newConfig)1779     public void onConfigurationChanged(Configuration newConfig) {
1780         mAwViewMethods.onConfigurationChanged(newConfig);
1781     }
1782 
1783     /**
1784      * @see android.view.View#onAttachedToWindow()
1785      */
onAttachedToWindow()1786     public void onAttachedToWindow() {
1787         mTemporarilyDetached = false;
1788         mAwViewMethods.onAttachedToWindow();
1789     }
1790 
1791     /**
1792      * @see android.view.View#onDetachedFromWindow()
1793      */
1794     @SuppressLint("MissingSuperCall")
onDetachedFromWindow()1795     public void onDetachedFromWindow() {
1796         mAwViewMethods.onDetachedFromWindow();
1797     }
1798 
1799     /**
1800      * @see android.view.View#onWindowFocusChanged()
1801      */
onWindowFocusChanged(boolean hasWindowFocus)1802     public void onWindowFocusChanged(boolean hasWindowFocus) {
1803         mAwViewMethods.onWindowFocusChanged(hasWindowFocus);
1804     }
1805 
1806     /**
1807      * @see android.view.View#onFocusChanged()
1808      */
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)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      */
onStartTemporaryDetach()1818     public void onStartTemporaryDetach() {
1819         mTemporarilyDetached = true;
1820     }
1821 
1822     /**
1823      * @see android.view.View#onFinishTemporaryDetach()
1824      */
onFinishTemporaryDetach()1825     public void onFinishTemporaryDetach() {
1826         mTemporarilyDetached = false;
1827     }
1828 
1829     /**
1830      * @see android.view.View#onSizeChanged()
1831      */
onSizeChanged(int w, int h, int ow, int oh)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      */
onVisibilityChanged(View changedView, int visibility)1839     public void onVisibilityChanged(View changedView, int visibility) {
1840         mAwViewMethods.onVisibilityChanged(changedView, visibility);
1841     }
1842 
1843     /**
1844      * @see android.view.View#onWindowVisibilityChanged()
1845      */
onWindowVisibilityChanged(int visibility)1846     public void onWindowVisibilityChanged(int visibility) {
1847         mAwViewMethods.onWindowVisibilityChanged(visibility);
1848     }
1849 
setViewVisibilityInternal(boolean visible)1850     private void setViewVisibilityInternal(boolean visible) {
1851         mIsViewVisible = visible;
1852         if (!isDestroyed()) nativeSetViewVisibility(mNativeAwContents, mIsViewVisible);
1853     }
1854 
setWindowVisibilityInternal(boolean visible)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      */
saveState(Bundle outState)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      */
restoreState(Bundle inState)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      */
addPossiblyUnsafeJavascriptInterface(Object object, String name, Class<? extends Annotation> requiredAnnotation)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      */
removeJavascriptInterface(String interfaceName)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      */
getAccessibilityNodeProvider()1923     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1924         return isDestroyed() ? null : mContentViewCore.getAccessibilityNodeProvider();
1925     }
1926 
1927     /**
1928      * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
1929      */
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1930     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1931         if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityNodeInfo(info);
1932     }
1933 
1934     /**
1935      * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent)
1936      */
onInitializeAccessibilityEvent(AccessibilityEvent event)1937     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1938         if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityEvent(event);
1939     }
1940 
supportsAccessibilityAction(int action)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      */
performAccessibilityAction(int action, Bundle arguments)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      */
hideAutofillPopup()1956     public void hideAutofillPopup() {
1957         if (mAwAutofillClient != null) {
1958             mAwAutofillClient.hideAutofillPopup();
1959         }
1960     }
1961 
setNetworkAvailable(boolean networkUp)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
onDocumentHasImagesResponse(boolean result, Message message)1971     private static void onDocumentHasImagesResponse(boolean result, Message message) {
1972         message.arg1 = result ? 1 : 0;
1973         message.sendToTarget();
1974     }
1975 
1976     @CalledByNative
onReceivedTouchIconUrl(String url, boolean precomposed)1977     private void onReceivedTouchIconUrl(String url, boolean precomposed) {
1978         mContentsClient.onReceivedTouchIconUrl(url, precomposed);
1979     }
1980 
1981     @CalledByNative
onReceivedIcon(Bitmap bitmap)1982     private void onReceivedIcon(Bitmap bitmap) {
1983         mContentsClient.onReceivedIcon(bitmap);
1984         mFavicon = bitmap;
1985     }
1986 
1987     /** Callback for generateMHTML. */
1988     @CalledByNative
generateMHTMLCallback( String path, long size, ValueCallback<String> callback)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