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