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