AwContents.java revision bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3
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.content.pm.PackageManager;
8import android.content.res.Configuration;
9import android.graphics.Bitmap;
10import android.graphics.Canvas;
11import android.graphics.Color;
12import android.graphics.Picture;
13import android.graphics.Rect;
14import android.net.http.SslCertificate;
15import android.os.AsyncTask;
16import android.os.Build;
17import android.os.Bundle;
18import android.os.Message;
19import android.os.Process;
20import android.text.TextUtils;
21import android.util.Log;
22import android.view.KeyEvent;
23import android.view.MotionEvent;
24import android.view.View;
25import android.view.ViewGroup;
26import android.view.ViewTreeObserver;
27import android.view.accessibility.AccessibilityEvent;
28import android.view.accessibility.AccessibilityNodeInfo;
29import android.view.accessibility.AccessibilityNodeProvider;
30import android.view.inputmethod.EditorInfo;
31import android.view.inputmethod.InputConnection;
32import android.webkit.GeolocationPermissions;
33import android.webkit.ValueCallback;
34
35import com.google.common.annotations.VisibleForTesting;
36
37import org.chromium.base.CalledByNative;
38import org.chromium.base.JNINamespace;
39import org.chromium.base.ThreadUtils;
40import org.chromium.content.browser.ContentSettings;
41import org.chromium.content.browser.ContentVideoView;
42import org.chromium.content.browser.ContentViewClient;
43import org.chromium.content.browser.ContentViewCore;
44import org.chromium.content.browser.ContentViewStatics;
45import org.chromium.content.browser.LoadUrlParams;
46import org.chromium.content.browser.NavigationHistory;
47import org.chromium.content.browser.PageTransitionTypes;
48import org.chromium.content.common.CleanupReference;
49import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
50import org.chromium.components.navigation_interception.NavigationParams;
51import org.chromium.net.GURLUtils;
52import org.chromium.ui.gfx.DeviceDisplayInfo;
53
54import java.io.File;
55import java.lang.annotation.Annotation;
56import java.net.MalformedURLException;
57import java.net.URL;
58import java.util.concurrent.Callable;
59
60/**
61 * Exposes the native AwContents class, and together these classes wrap the ContentViewCore
62 * and Browser components that are required to implement Android WebView API. This is the
63 * primary entry point for the WebViewProvider implementation; it holds a 1:1 object
64 * relationship with application WebView instances.
65 * (We define this class independent of the hidden WebViewProvider interfaces, to allow
66 * continuous build & test in the open source SDK-based tree).
67 */
68@JNINamespace("android_webview")
69public class AwContents {
70    private static final String TAG = "AwContents";
71
72    private static final String WEB_ARCHIVE_EXTENSION = ".mht";
73
74    /**
75     * WebKit hit test related data strcutre. These are used to implement
76     * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView.
77     * All values should be updated together. The native counterpart is
78     * AwHitTestData.
79     */
80    public static class HitTestData {
81        // Used in getHitTestResult.
82        public int hitTestResultType;
83        public String hitTestResultExtraData;
84
85        // Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc).
86        public String href;
87        public String anchorText;
88        public String imgSrc;
89    }
90
91    /**
92     * Interface that consumers of {@link AwContents} must implement to allow the proper
93     * dispatching of view methods through the containing view.
94     */
95    public interface InternalAccessDelegate extends ContentViewCore.InternalAccessDelegate {
96        /**
97         * @see View#onScrollChanged(int, int, int, int)
98         *
99         * TODO(mkosiba): WebViewClassic calls this, AwContents doesn't. Check if there
100         * are any cases we're missing, if not - remove.
101         */
102        void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
103
104        /**
105         * @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean);
106         */
107        void overScrollBy(int deltaX, int deltaY,
108                int scrollX, int scrollY,
109                int scrollRangeX, int scrollRangeY,
110                int maxOverScrollX, int maxOverScrollY,
111                boolean isTouchEvent);
112
113        /**
114         * @see View#scrollTo(int, int)
115         */
116        void super_scrollTo(int scrollX, int scrollY);
117
118        /**
119         * @see View#setMeasuredDimension(int, int)
120         */
121        void setMeasuredDimension(int measuredWidth, int measuredHeight);
122
123        /**
124         * Requests a callback on the native DrawGL method (see getAwDrawGLFunction)
125         * if called from within onDraw, |canvas| will be non-null and hardware accelerated.
126         * otherwise, |canvas| will be null, and the container view itself will be hardware
127         * accelerated.
128         *
129         * @return false indicates the GL draw request was not accepted, and the caller
130         *         should fallback to the SW path.
131         */
132        boolean requestDrawGL(Canvas canvas);
133    }
134
135    private int mNativeAwContents;
136    private final AwBrowserContext mBrowserContext;
137    private final ViewGroup mContainerView;
138    private ContentViewCore mContentViewCore;
139    private final AwContentsClient mContentsClient;
140    private final AwContentsClientBridge mContentsClientBridge;
141    private final AwWebContentsDelegate mWebContentsDelegate;
142    private final AwContentsIoThreadClient mIoThreadClient;
143    private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
144    private final InternalAccessDelegate mInternalAccessAdapter;
145    private final AwLayoutSizer mLayoutSizer;
146    private final AwZoomControls mZoomControls;
147    private final AwScrollOffsetManager mScrollOffsetManager;
148    private OverScrollGlow mOverScrollGlow;
149    // This can be accessed on any thread after construction. See AwContentsIoThreadClient.
150    private final AwSettings mSettings;
151
152    private boolean mIsPaused;
153    private boolean mIsVisible;  // Equivalent to windowVisible && viewVisible && !mIsPaused.
154    private boolean mIsAttachedToWindow;
155    private Bitmap mFavicon;
156    private boolean mHasRequestedVisitedHistoryFromClient;
157    // TODO(boliu): This should be in a global context, not per webview.
158    private final double mDIPScale;
159
160    // The base background color, i.e. not accounting for any CSS body from the current page.
161    private int mBaseBackgroundColor = Color.WHITE;
162
163    // Must call nativeUpdateLastHitTestData first to update this before use.
164    private final HitTestData mPossiblyStaleHitTestData = new HitTestData();
165
166    private DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler;
167
168    // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the
169    // picture listener API has not yet been enabled, or if it is using invalidation-only mode.
170    private Callable<Picture> mPictureListenerContentProvider;
171
172    private int mLastGlobalVisibleWidth;
173    private int mLastGlobalVisibleHeight;
174
175    private boolean mContainerViewFocused;
176    private boolean mWindowFocused;
177
178    private AwAutofillManagerDelegate mAwAutofillManagerDelegate;
179
180    private static final class DestroyRunnable implements Runnable {
181        private int mNativeAwContents;
182        private DestroyRunnable(int nativeAwContents) {
183            mNativeAwContents = nativeAwContents;
184        }
185        @Override
186        public void run() {
187            nativeDestroy(mNativeAwContents);
188        }
189    }
190
191    private CleanupReference mCleanupReference;
192
193    //--------------------------------------------------------------------------------------------
194    private class IoThreadClientImpl implements AwContentsIoThreadClient {
195        // All methods are called on the IO thread.
196
197        @Override
198        public int getCacheMode() {
199            return mSettings.getCacheMode();
200        }
201
202        @Override
203        public InterceptedRequestData shouldInterceptRequest(final String url,
204                boolean isMainFrame) {
205            InterceptedRequestData interceptedRequestData;
206            // Return the response directly if the url is default video poster url.
207            interceptedRequestData = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url);
208            if (interceptedRequestData != null) return interceptedRequestData;
209
210            interceptedRequestData = mContentsClient.shouldInterceptRequest(url);
211
212            if (interceptedRequestData == null) {
213                mContentsClient.getCallbackHelper().postOnLoadResource(url);
214            }
215
216            if (isMainFrame && interceptedRequestData != null &&
217                    interceptedRequestData.getData() == null) {
218                // In this case the intercepted URLRequest job will simulate an empty response
219                // which doesn't trigger the onReceivedError callback. For WebViewClassic
220                // compatibility we synthesize that callback. http://crbug.com/180950
221                mContentsClient.getCallbackHelper().postOnReceivedError(
222                        ErrorCodeConversionHelper.ERROR_UNKNOWN,
223                        null /* filled in by the glue layer */, url);
224            }
225            return interceptedRequestData;
226        }
227
228        @Override
229        public boolean shouldBlockContentUrls() {
230            return !mSettings.getAllowContentAccess();
231        }
232
233        @Override
234        public boolean shouldBlockFileUrls() {
235            return !mSettings.getAllowFileAccess();
236        }
237
238        @Override
239        public boolean shouldBlockNetworkLoads() {
240            return mSettings.getBlockNetworkLoads();
241        }
242
243        @Override
244        public void onDownloadStart(String url,
245                                    String userAgent,
246                                    String contentDisposition,
247                                    String mimeType,
248                                    long contentLength) {
249            mContentsClient.getCallbackHelper().postOnDownloadStart(url, userAgent,
250                    contentDisposition, mimeType, contentLength);
251        }
252
253        @Override
254        public void newLoginRequest(String realm, String account, String args) {
255            mContentsClient.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args);
256        }
257    }
258
259    //--------------------------------------------------------------------------------------------
260    private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
261        private String mLastLoadUrlAddress;
262
263        public void onUrlLoadRequested(String url) {
264            mLastLoadUrlAddress = url;
265        }
266
267        @Override
268        public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
269            final String url = navigationParams.url;
270            final int transitionType = navigationParams.pageTransitionType;
271            final boolean isLoadUrl =
272                    (transitionType & PageTransitionTypes.PAGE_TRANSITION_FROM_API) != 0;
273            final boolean isBackForward =
274                    (transitionType & PageTransitionTypes.PAGE_TRANSITION_FORWARD_BACK) != 0;
275            final boolean isReload =
276                    (transitionType & PageTransitionTypes.PAGE_TRANSITION_CORE_MASK) ==
277                    PageTransitionTypes.PAGE_TRANSITION_RELOAD;
278            final boolean isRedirect = navigationParams.isRedirect;
279
280            boolean ignoreNavigation = false;
281
282            // Any navigation from loadUrl, goBack/Forward, or reload, are considered application
283            // initiated and hence will not yield a shouldOverrideUrlLoading() callback.
284            // TODO(joth): Using PageTransitionTypes should be sufficient to determine all app
285            // initiated navigations, and so mLastLoadUrlAddress should be removed.
286            if ((isLoadUrl && !isRedirect) || isBackForward || isReload ||
287                    mLastLoadUrlAddress != null && mLastLoadUrlAddress.equals(url)) {
288                // Support the case where the user clicks on a link that takes them back to the
289                // same page.
290                mLastLoadUrlAddress = null;
291
292                // If the embedder requested the load of a certain URL via the loadUrl API, then we
293                // do not offer it to AwContentsClient.shouldOverrideUrlLoading.
294                // The embedder is also not allowed to intercept POST requests because of
295                // crbug.com/155250.
296            } else if (!navigationParams.isPost) {
297                ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url);
298            }
299
300            // The existing contract is that shouldOverrideUrlLoading callbacks are delivered before
301            // onPageStarted callbacks; third party apps depend on this behavior.
302            // Using a ResouceThrottle to implement the navigation interception feature results in
303            // the WebContentsObserver.didStartLoading callback happening before the
304            // ResourceThrottle has a chance to run.
305            // To preserve the ordering the onPageStarted callback is synthesized from the
306            // shouldOverrideUrlLoading, and only if the navigation was not ignored (this
307            // balances out with the onPageFinished callback, which is suppressed in the
308            // AwContentsClient if the navigation was ignored).
309            if (!ignoreNavigation) {
310                // The shouldOverrideUrlLoading call might have resulted in posting messages to the
311                // UI thread. Using sendMessage here (instead of calling onPageStarted directly)
312                // will allow those to run in order.
313                mContentsClient.getCallbackHelper().postOnPageStarted(url);
314            }
315
316            return ignoreNavigation;
317        }
318    }
319
320    //--------------------------------------------------------------------------------------------
321    private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate {
322        @Override
323        public void requestLayout() {
324            mContainerView.requestLayout();
325        }
326
327        @Override
328        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
329            mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight);
330        }
331    }
332
333    //--------------------------------------------------------------------------------------------
334    // NOTE: This content size change notification comes from the compositor and reflects the size
335    // of the content on screen (but not neccessarily in the renderer main thread).
336    private class AwContentUpdateFrameInfoListener
337                implements ContentViewCore.UpdateFrameInfoListener {
338        @Override
339        public void onFrameInfoUpdated(float widthCss, float heightCss, float pageScaleFactor) {
340            int widthPix = (int) Math.floor(widthCss * mDIPScale * pageScaleFactor);
341            int heightPix = (int) Math.floor(heightCss * mDIPScale * pageScaleFactor);
342            mScrollOffsetManager.setContentSize(widthPix, heightPix);
343
344            nativeSetDisplayedPageScaleFactor(mNativeAwContents, pageScaleFactor);
345        }
346    }
347
348    //--------------------------------------------------------------------------------------------
349    private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate {
350        @Override
351        public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY,
352                int scrollRangeX, int scrollRangeY) {
353            mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY,
354                    scrollRangeX, scrollRangeY, 0, 0, true);
355        }
356
357        @Override
358        public void scrollContainerViewTo(int x, int y) {
359            mInternalAccessAdapter.super_scrollTo(x, y);
360        }
361
362        @Override
363        public void scrollNativeTo(int x, int y) {
364            nativeScrollTo(mNativeAwContents, x, y);
365        }
366
367        @Override
368        public int getContainerViewScrollX() {
369            return mContainerView.getScrollX();
370        }
371
372        @Override
373        public int getContainerViewScrollY() {
374            return mContainerView.getScrollY();
375        }
376    }
377
378    //--------------------------------------------------------------------------------------------
379    private class AwPinchGestureStateListener implements ContentViewCore.PinchGestureStateListener {
380        @Override
381        public void onPinchGestureStart() {
382            // While it's possible to re-layout the view during a pinch gesture, the effect is very
383            // janky (especially that the page scale update notification comes from the renderer
384            // main thread, not from the impl thread, so it's usually out of sync with what's on
385            // screen). It's also quite expensive to do a re-layout, so we simply postpone
386            // re-layout for the duration of the gesture. This is compatible with what
387            // WebViewClassic does.
388            mLayoutSizer.freezeLayoutRequests();
389        }
390
391        public void onPinchGestureEnd() {
392            mLayoutSizer.unfreezeLayoutRequests();
393        }
394    }
395
396    //--------------------------------------------------------------------------------------------
397    private class ScrollChangeListener implements ViewTreeObserver.OnScrollChangedListener {
398        @Override
399        public void onScrollChanged() {
400            // We do this to cover the case that when the view hierarchy is scrolled,
401            // more of the containing view  becomes visible (i.e. a containing view
402            // with a width/height of "wrap_content" and dimensions greater than
403            // that of the screen).
404            AwContents.this.updatePhysicalBackingSizeIfNeeded();
405         }
406    };
407
408    private ScrollChangeListener mScrollChangeListener;
409
410    /**
411     * @param browserContext the browsing context to associate this view contents with.
412     * @param containerView the view-hierarchy item this object will be bound to.
413     * @param internalAccessAdapter to access private methods on containerView.
414     * @param contentsClient will receive API callbacks from this WebView Contents
415     * @param isAccessFromFileURLsGrantedByDefault passed to AwSettings.
416     *
417     * This constructor uses the default view sizing policy.
418     */
419    public AwContents(AwBrowserContext browserContext, ViewGroup containerView,
420            InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient,
421            boolean isAccessFromFileURLsGrantedByDefault) {
422        this(browserContext, containerView, internalAccessAdapter, contentsClient,
423                isAccessFromFileURLsGrantedByDefault, new AwLayoutSizer());
424    }
425
426    private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView,
427            InternalAccessDelegate internalDispatcher, int nativeWebContents,
428            ContentViewCore.PinchGestureStateListener pinchGestureStateListener,
429            ContentViewClient contentViewClient,
430            ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) {
431      ContentViewCore contentViewCore = new ContentViewCore(containerView.getContext());
432      // Note INPUT_EVENTS_DELIVERED_IMMEDIATELY is passed to avoid triggering vsync in the
433      // compositor, not because input events are delivered immediately.
434      contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, null,
435                ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
436      contentViewCore.setPinchGestureStateListener(pinchGestureStateListener);
437      contentViewCore.setContentViewClient(contentViewClient);
438      contentViewCore.setZoomControlsDelegate(zoomControlsDelegate);
439      return contentViewCore;
440    }
441
442    /**
443     * @param layoutSizer the AwLayoutSizer instance implementing the sizing policy for the view.
444     *
445     * This version of the constructor is used in test code to inject test versions of the above
446     * documented classes
447     */
448    public AwContents(AwBrowserContext browserContext, ViewGroup containerView,
449            InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient,
450            boolean isAccessFromFileURLsGrantedByDefault, AwLayoutSizer layoutSizer) {
451        mBrowserContext = browserContext;
452        mContainerView = containerView;
453        mInternalAccessAdapter = internalAccessAdapter;
454        mContentsClient = contentsClient;
455        mLayoutSizer = layoutSizer;
456        mDIPScale = DeviceDisplayInfo.create(containerView.getContext()).getDIPScale();
457        mLayoutSizer.setDelegate(new AwLayoutSizerDelegate());
458        mLayoutSizer.setDIPScale(mDIPScale);
459        mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient,
460                mLayoutSizer.getPreferredSizeChangedListener());
461        mContentsClientBridge = new AwContentsClientBridge(contentsClient);
462        mZoomControls = new AwZoomControls(this);
463        mIoThreadClient = new IoThreadClientImpl();
464        mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl();
465
466        boolean hasInternetPermission = containerView.getContext().checkPermission(
467                    android.Manifest.permission.INTERNET,
468                    Process.myPid(),
469                    Process.myUid()) == PackageManager.PERMISSION_GRANTED;
470        AwSettings.ZoomSupportChangeListener zoomListener =
471                new AwSettings.ZoomSupportChangeListener() {
472                    @Override
473                    public void onMultiTouchZoomSupportChanged(boolean supportsMultiTouchZoom) {
474                        mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom);
475                    }
476                };
477        mSettings = new AwSettings(hasInternetPermission, zoomListener,
478                isAccessFromFileURLsGrantedByDefault, mDIPScale);
479        mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient);
480        mSettings.setDefaultVideoPosterURL(
481                mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL());
482        mContentsClient.setDIPScale(mDIPScale);
483        mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate());
484
485        setOverScrollMode(mContainerView.getOverScrollMode());
486
487        setNewAwContents(nativeInit(browserContext));
488    }
489
490    /**
491     * Common initialization routine for adopting a native AwContents instance into this
492     * java instance.
493     *
494     * TAKE CARE! This method can get called multiple times per java instance. Code accordingly.
495     * ^^^^^^^^^  See the native class declaration for more details on relative object lifetimes.
496     */
497    private void setNewAwContents(int newAwContentsPtr) {
498        if (mNativeAwContents != 0) {
499            destroy();
500            mContentViewCore = null;
501        }
502
503        assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null;
504
505        mNativeAwContents = newAwContentsPtr;
506        // TODO(joth): when the native and java counterparts of AwBrowserContext are hooked up to
507        // each other, we should update |mBrowserContext| according to the newly received native
508        // WebContent's browser context.
509
510        // The native side object has been bound to this java instance, so now is the time to
511        // bind all the native->java relationships.
512        mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents));
513
514        int nativeWebContents = nativeGetWebContents(mNativeAwContents);
515        mContentViewCore = createAndInitializeContentViewCore(
516                mContainerView, mInternalAccessAdapter, nativeWebContents,
517                new AwPinchGestureStateListener(), mContentsClient.getContentViewClient(),
518                mZoomControls);
519        nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
520                mIoThreadClient, mInterceptNavigationDelegate);
521        mContentsClient.installWebContentsObserver(mContentViewCore);
522        mContentViewCore.setUpdateFrameInfoListener(new AwContentUpdateFrameInfoListener());
523        mSettings.setWebContents(nativeWebContents);
524        nativeSetDipScale(mNativeAwContents, (float) mDIPScale);
525   }
526
527    /**
528     * Called on the "source" AwContents that is opening the popup window to
529     * provide the AwContents to host the pop up content.
530     */
531    public void supplyContentsForPopup(AwContents newContents) {
532        int popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents);
533        if (popupNativeAwContents == 0) {
534            Log.w(TAG, "Popup WebView bind failed: no pending content.");
535            if (newContents != null) newContents.destroy();
536            return;
537        }
538        if (newContents == null) {
539            nativeDestroy(popupNativeAwContents);
540            return;
541        }
542
543        newContents.receivePopupContents(popupNativeAwContents);
544    }
545
546    // Recap: supplyContentsForPopup() is called on the parent window's content, this method is
547    // called on the popup window's content.
548    private void receivePopupContents(int popupNativeAwContents) {
549        // Save existing view state.
550        final boolean wasAttached = mIsAttachedToWindow;
551        final boolean wasVisible = getContainerViewVisible();
552        final boolean wasPaused = mIsPaused;
553        final boolean wasFocused = mWindowFocused;
554
555        // Properly clean up existing mContentViewCore and mNativeAwContents.
556        if (wasFocused) onWindowFocusChanged(false);
557        if (wasVisible) setVisibilityInternal(false);
558        // TODO(boliu): This may destroy GL resources outside of functor.
559        if (wasAttached) onDetachedFromWindow();
560        if (!wasPaused) onPause();
561
562        setNewAwContents(popupNativeAwContents);
563
564        // Finally refresh all view state for mContentViewCore and mNativeAwContents.
565        if (!wasPaused) onResume();
566        if (wasAttached) onAttachedToWindow();
567        mLastGlobalVisibleWidth = 0;
568        mLastGlobalVisibleHeight = 0;
569        onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0);
570        if (wasVisible) setVisibilityInternal(true);
571        if (wasFocused) onWindowFocusChanged(true);
572    }
573
574    public void destroy() {
575        mContentViewCore.destroy();
576        // We explicitly do not null out the mContentViewCore reference here
577        // because ContentViewCore already has code to deal with the case
578        // methods are called on it after it's been destroyed, and other
579        // code relies on AwContents.mContentViewCore to be non-null.
580
581        if (mCleanupReference != null) mCleanupReference.cleanupNow();
582        mNativeAwContents = 0;
583        mCleanupReference = null;
584    }
585
586    @VisibleForTesting
587    public ContentViewCore getContentViewCore() {
588        return mContentViewCore;
589    }
590
591    // Can be called from any thread.
592    public AwSettings getSettings() {
593        return mSettings;
594    }
595
596    public static void setAwDrawSWFunctionTable(int functionTablePointer) {
597        nativeSetAwDrawSWFunctionTable(functionTablePointer);
598    }
599
600    public static void setAwDrawGLFunctionTable(int functionTablePointer) {
601        nativeSetAwDrawGLFunctionTable(functionTablePointer);
602    }
603
604    public static int getAwDrawGLFunction() {
605        return nativeGetAwDrawGLFunction();
606    }
607
608    /**
609     * Intended for test code.
610     * @return the number of native instances of this class.
611     */
612    @VisibleForTesting
613    public static int getNativeInstanceCount() {
614        return nativeGetNativeInstanceCount();
615    }
616
617    public int getAwDrawGLViewContext() {
618        // Using the native pointer as the returned viewContext. This is matched by the
619        // reinterpret_cast back to BrowserViewRenderer pointer in the native DrawGLFunction.
620        return nativeGetAwDrawGLViewContext(mNativeAwContents);
621    }
622
623    // Only valid within updatePhysicalBackingSizeIfNeeded().
624    private final Rect mGlobalVisibleBoundsTemporary = new Rect();
625
626    private void updatePhysicalBackingSizeIfNeeded() {
627        // We musn't let the physical backing size get too big, otherwise we
628        // will try to allocate a SurfaceTexture beyond what the GL driver can
629        // cope with. In most cases, limiting the SurfaceTexture size to that
630        // of the visible bounds of the WebView will be good enough i.e. the maximum
631        // SurfaceTexture dimensions will match the screen dimensions).
632        mContainerView.getGlobalVisibleRect(mGlobalVisibleBoundsTemporary);
633        int width = mGlobalVisibleBoundsTemporary.width();
634        int height = mGlobalVisibleBoundsTemporary.height();
635        if (width != mLastGlobalVisibleWidth || height != mLastGlobalVisibleHeight) {
636            mLastGlobalVisibleWidth = width;
637            mLastGlobalVisibleHeight = height;
638            mContentViewCore.onPhysicalBackingSizeChanged(width, height);
639        }
640    }
641
642    //--------------------------------------------------------------------------------------------
643    //  WebView[Provider] method implementations (where not provided by ContentViewCore)
644    //--------------------------------------------------------------------------------------------
645
646    // Only valid within onDraw().
647    private final Rect mClipBoundsTemporary = new Rect();
648
649    public void onDraw(Canvas canvas) {
650        if (mNativeAwContents == 0) {
651            canvas.drawColor(getEffectiveBackgroundColor());
652            return;
653        }
654
655        mScrollOffsetManager.syncScrollOffsetFromOnDraw();
656
657        canvas.getClipBounds(mClipBoundsTemporary);
658        if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(),
659                    mContainerView.getScrollX(), mContainerView.getScrollY(),
660                    mClipBoundsTemporary.left, mClipBoundsTemporary.top,
661                    mClipBoundsTemporary.right, mClipBoundsTemporary.bottom )) {
662            Log.w(TAG, "nativeOnDraw failed; clearing to background color.");
663            canvas.drawColor(getEffectiveBackgroundColor());
664        }
665
666        if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas,
667                    mScrollOffsetManager.computeMaximumHorizontalScrollOffset(),
668                    mScrollOffsetManager.computeMaximumVerticalScrollOffset())) {
669            mContainerView.invalidate();
670        }
671    }
672
673    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
674        mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec);
675    }
676
677    public int getContentHeightCss() {
678        return (int) Math.ceil(mContentViewCore.getContentHeightCss());
679    }
680
681    public int getContentWidthCss() {
682        return (int) Math.ceil(mContentViewCore.getContentWidthCss());
683    }
684
685    public Picture capturePicture() {
686        return nativeCapturePicture(mNativeAwContents);
687    }
688
689    /**
690     * Enable the onNewPicture callback.
691     * @param enabled Flag to enable the callback.
692     * @param invalidationOnly Flag to call back only on invalidation without providing a picture.
693     */
694    public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) {
695        if (invalidationOnly) {
696            mPictureListenerContentProvider = null;
697        } else if (enabled && mPictureListenerContentProvider == null) {
698            mPictureListenerContentProvider = new Callable<Picture>() {
699                @Override
700                public Picture call() {
701                    return capturePicture();
702                }
703            };
704        }
705        nativeEnableOnNewPicture(mNativeAwContents, enabled);
706    }
707
708    public void findAllAsync(String searchString) {
709        if (mNativeAwContents == 0) return;
710        nativeFindAllAsync(mNativeAwContents, searchString);
711    }
712
713    public void findNext(boolean forward) {
714        if (mNativeAwContents == 0) return;
715        nativeFindNext(mNativeAwContents, forward);
716    }
717
718    public void clearMatches() {
719        if (mNativeAwContents == 0) return;
720        nativeClearMatches(mNativeAwContents);
721    }
722
723    /**
724     * @return load progress of the WebContents.
725     */
726    public int getMostRecentProgress() {
727        // WebContentsDelegateAndroid conveniently caches the most recent notified value for us.
728        return mWebContentsDelegate.getMostRecentProgress();
729    }
730
731    public Bitmap getFavicon() {
732        return mFavicon;
733    }
734
735    private void requestVisitedHistoryFromClient() {
736        ValueCallback<String[]> callback = new ValueCallback<String[]>() {
737            @Override
738            public void onReceiveValue(final String[] value) {
739                ThreadUtils.runOnUiThread(new Runnable() {
740                    @Override
741                    public void run() {
742                        if (mNativeAwContents == 0) return;
743                        nativeAddVisitedLinks(mNativeAwContents, value);
744                    }
745                });
746            }
747        };
748        mContentsClient.getVisitedHistory(callback);
749    }
750
751    /**
752     * Load url without fixing up the url string. Consumers of ContentView are responsible for
753     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
754     * off during user input).
755     *
756     * @param pararms Parameters for this load.
757     */
758    public void loadUrl(LoadUrlParams params) {
759        if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA &&
760            !params.isBaseUrlDataScheme()) {
761            // This allows data URLs with a non-data base URL access to file:///android_asset/ and
762            // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also
763            // allow access to file:// URLs (subject to OS level permission checks).
764            params.setCanLoadLocalResources(true);
765        }
766
767        // If we are reloading the same url, then set transition type as reload.
768        if (params.getUrl() != null &&
769            params.getUrl().equals(mContentViewCore.getUrl()) &&
770            params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) {
771            params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_RELOAD);
772        }
773        params.setTransitionType(
774                params.getTransitionType() | PageTransitionTypes.PAGE_TRANSITION_FROM_API);
775
776        // For WebView, always use the user agent override, which is set
777        // every time the user agent in AwSettings is modified.
778        params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
779
780        mContentViewCore.loadUrl(params);
781
782        suppressInterceptionForThisNavigation();
783
784        // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit.
785        // Chromium does not use this use code path and the best emulation of this behavior to call
786        // request visited links once on the first URL load of the WebView.
787        if (!mHasRequestedVisitedHistoryFromClient) {
788          mHasRequestedVisitedHistoryFromClient = true;
789          requestVisitedHistoryFromClient();
790        }
791    }
792
793    private void suppressInterceptionForThisNavigation() {
794        if (mInterceptNavigationDelegate != null) {
795            // getUrl returns a sanitized address in the same format that will be used for
796            // callbacks, so it's safe to use string comparison as an equality check later on.
797            mInterceptNavigationDelegate.onUrlLoadRequested(mContentViewCore.getUrl());
798        }
799    }
800
801    /**
802     * Get the URL of the current page.
803     *
804     * @return The URL of the current page or null if it's empty.
805     */
806    public String getUrl() {
807        String url =  mContentViewCore.getUrl();
808        if (url == null || url.trim().isEmpty()) return null;
809        return url;
810    }
811
812    public void requestFocus() {
813        if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) {
814            nativeFocusFirstNode(mNativeAwContents);
815        }
816    }
817
818    public void setBackgroundColor(int color) {
819        mBaseBackgroundColor = color;
820        if (mNativeAwContents != 0) nativeSetBackgroundColor(mNativeAwContents, color);
821    }
822
823    private int getEffectiveBackgroundColor() {
824        // Do not ask the ContentViewCore for the background color, as it will always
825        // report white prior to initial navigation or post destruction,  whereas we want
826        // to use the client supplied base value in those cases.
827        if (mNativeAwContents == 0 || !mContentsClient.isCachedRendererBackgroundColorValid()) {
828            return mBaseBackgroundColor;
829        }
830        return mContentsClient.getCachedRendererBackgroundColor();
831    }
832
833    public boolean isMultiTouchZoomSupported() {
834        return mSettings.supportsMultiTouchZoom();
835    }
836
837    public View getZoomControlsForTest() {
838        return mZoomControls.getZoomControlsViewForTest();
839    }
840
841    /**
842     * @see ContentViewCore#getContentSettings()
843     */
844    public ContentSettings getContentSettings() {
845        return mContentViewCore.getContentSettings();
846    }
847
848    /**
849     * @see View#setOverScrollMode(int)
850     */
851    public void setOverScrollMode(int mode) {
852        if (mode != View.OVER_SCROLL_NEVER) {
853            mOverScrollGlow = new OverScrollGlow(mContainerView);
854        } else {
855            mOverScrollGlow = null;
856        }
857    }
858
859    /**
860     * Called by the embedder when the scroll offset of the containing view has changed.
861     * @see View#onScrollChanged(int,int)
862     */
863    public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) {
864        mScrollOffsetManager.onContainerViewScrollChanged(l, t);
865    }
866
867    /**
868     * Called by the embedder when the containing view is to be scrolled or overscrolled.
869     * @see View#onOverScrolled(int,int,int,int)
870     */
871    public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX,
872            boolean clampedY) {
873        int oldX = mContainerView.getScrollX();
874        int oldY = mContainerView.getScrollY();
875
876        mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
877
878        if (mOverScrollGlow != null) {
879            mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(),
880                    oldX, oldY,
881                    mScrollOffsetManager.computeMaximumHorizontalScrollOffset(),
882                    mScrollOffsetManager.computeMaximumVerticalScrollOffset());
883        }
884    }
885
886    /**
887     * @see View#computeHorizontalScrollRange()
888     */
889    public int computeHorizontalScrollRange() {
890        return mScrollOffsetManager.computeHorizontalScrollRange();
891    }
892
893    /**
894     * @see View#computeHorizontalScrollOffset()
895     */
896    public int computeHorizontalScrollOffset() {
897        return mScrollOffsetManager.computeHorizontalScrollOffset();
898    }
899
900    /**
901     * @see View#computeVerticalScrollRange()
902     */
903    public int computeVerticalScrollRange() {
904        return mScrollOffsetManager.computeVerticalScrollRange();
905    }
906
907    /**
908     * @see View#computeVerticalScrollOffset()
909     */
910    public int computeVerticalScrollOffset() {
911        return mScrollOffsetManager.computeVerticalScrollOffset();
912    }
913
914    /**
915     * @see View#computeVerticalScrollExtent()
916     */
917    public int computeVerticalScrollExtent() {
918        return mScrollOffsetManager.computeVerticalScrollExtent();
919    }
920
921    /**
922     * @see android.webkit.WebView#stopLoading()
923     */
924    public void stopLoading() {
925        mContentViewCore.stopLoading();
926    }
927
928    /**
929     * @see android.webkit.WebView#reload()
930     */
931    public void reload() {
932        mContentViewCore.reload();
933    }
934
935    /**
936     * @see android.webkit.WebView#canGoBack()
937     */
938    public boolean canGoBack() {
939        return mContentViewCore.canGoBack();
940    }
941
942    /**
943     * @see android.webkit.WebView#goBack()
944     */
945    public void goBack() {
946        mContentViewCore.goBack();
947
948        suppressInterceptionForThisNavigation();
949    }
950
951    /**
952     * @see android.webkit.WebView#canGoForward()
953     */
954    public boolean canGoForward() {
955        return mContentViewCore.canGoForward();
956    }
957
958    /**
959     * @see android.webkit.WebView#goForward()
960     */
961    public void goForward() {
962        mContentViewCore.goForward();
963
964        suppressInterceptionForThisNavigation();
965    }
966
967    /**
968     * @see android.webkit.WebView#canGoBackOrForward(int)
969     */
970    public boolean canGoBackOrForward(int steps) {
971        return mContentViewCore.canGoToOffset(steps);
972    }
973
974    /**
975     * @see android.webkit.WebView#goBackOrForward(int)
976     */
977    public void goBackOrForward(int steps) {
978        mContentViewCore.goToOffset(steps);
979
980        suppressInterceptionForThisNavigation();
981    }
982
983    /**
984     * @see android.webkit.WebView#pauseTimers()
985     */
986    // TODO(kristianm): Remove
987    public void pauseTimers() {
988        ContentViewStatics.setWebKitSharedTimersSuspended(true);
989    }
990
991    /**
992     * @see android.webkit.WebView#resumeTimers()
993     */
994    // TODO(kristianm): Remove
995    public void resumeTimers() {
996        ContentViewStatics.setWebKitSharedTimersSuspended(false);
997    }
998
999    /**
1000     * @see android.webkit.WebView#onPause()
1001     */
1002    public void onPause() {
1003        mIsPaused = true;
1004        updateVisibilityState();
1005        mContentViewCore.onActivityPause();
1006    }
1007
1008    /**
1009     * @see android.webkit.WebView#onResume()
1010     */
1011    public void onResume() {
1012        mIsPaused = false;
1013        updateVisibilityState();
1014        // Not calling mContentViewCore.onActivityResume because it is the same
1015        // as onShow, but we do not want to call onShow yet, since AwContents
1016        // visibility depends on other things. TODO(boliu): Clean this up.
1017    }
1018
1019    /**
1020     * @see android.webkit.WebView#isPaused()
1021     */
1022    public boolean isPaused() {
1023        return mIsPaused;
1024    }
1025
1026    /**
1027     * @see android.webkit.WebView#onCreateInputConnection(EditorInfo)
1028     */
1029    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1030        return mContentViewCore.onCreateInputConnection(outAttrs);
1031    }
1032
1033    /**
1034     * @see android.webkit.WebView#onKeyUp(int, KeyEvent)
1035     */
1036    public boolean onKeyUp(int keyCode, KeyEvent event) {
1037        return mContentViewCore.onKeyUp(keyCode, event);
1038    }
1039
1040    /**
1041     * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent)
1042     */
1043    public boolean dispatchKeyEvent(KeyEvent event) {
1044        return mContentViewCore.dispatchKeyEvent(event);
1045    }
1046
1047    /**
1048     * Clears the resource cache. Note that the cache is per-application, so this will clear the
1049     * cache for all WebViews used.
1050     *
1051     * @param includeDiskFiles if false, only the RAM cache is cleared
1052     */
1053    public void clearCache(boolean includeDiskFiles) {
1054        if (mNativeAwContents == 0) return;
1055        nativeClearCache(mNativeAwContents, includeDiskFiles);
1056    }
1057
1058    public void documentHasImages(Message message) {
1059        if (mNativeAwContents == 0) return;
1060        nativeDocumentHasImages(mNativeAwContents, message);
1061    }
1062
1063    public void saveWebArchive(
1064            final String basename, boolean autoname, final ValueCallback<String> callback) {
1065        if (!autoname) {
1066            saveWebArchiveInternal(basename, callback);
1067            return;
1068        }
1069        // If auto-generating the file name, handle the name generation on a background thread
1070        // as it will require I/O access for checking whether previous files existed.
1071        new AsyncTask<Void, Void, String>() {
1072            @Override
1073            protected String doInBackground(Void... params) {
1074                return generateArchiveAutoNamePath(getOriginalUrl(), basename);
1075            }
1076
1077            @Override
1078            protected void onPostExecute(String result) {
1079                saveWebArchiveInternal(result, callback);
1080            }
1081        }.execute();
1082    }
1083
1084    public String getOriginalUrl() {
1085        NavigationHistory history = mContentViewCore.getNavigationHistory();
1086        int currentIndex = history.getCurrentEntryIndex();
1087        if (currentIndex >= 0 && currentIndex < history.getEntryCount()) {
1088            return history.getEntryAtIndex(currentIndex).getOriginalUrl();
1089        }
1090        return null;
1091    }
1092
1093    /**
1094     * @see ContentViewCore#getNavigationHistory()
1095     */
1096    public NavigationHistory getNavigationHistory() {
1097        return mContentViewCore.getNavigationHistory();
1098    }
1099
1100    /**
1101     * @see android.webkit.WebView#getTitle()
1102     */
1103    public String getTitle() {
1104        return mContentViewCore.getTitle();
1105    }
1106
1107    /**
1108     * @see android.webkit.WebView#clearHistory()
1109     */
1110    public void clearHistory() {
1111        mContentViewCore.clearHistory();
1112    }
1113
1114    public String[] getHttpAuthUsernamePassword(String host, String realm) {
1115        return mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext())
1116                .getHttpAuthUsernamePassword(host, realm);
1117    }
1118
1119    public void setHttpAuthUsernamePassword(String host, String realm, String username,
1120            String password) {
1121        mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext())
1122                .setHttpAuthUsernamePassword(host, realm, username, password);
1123    }
1124
1125    /**
1126     * @see android.webkit.WebView#getCertificate()
1127     */
1128    public SslCertificate getCertificate() {
1129        if (mNativeAwContents == 0) return null;
1130        return SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents));
1131    }
1132
1133    /**
1134     * @see android.webkit.WebView#clearSslPreferences()
1135     */
1136    public void clearSslPreferences() {
1137        mContentViewCore.clearSslPreferences();
1138    }
1139
1140    /**
1141     * Method to return all hit test values relevant to public WebView API.
1142     * Note that this expose more data than needed for WebView.getHitTestResult.
1143     * Unsafely returning reference to mutable internal object to avoid excessive
1144     * garbage allocation on repeated calls.
1145     */
1146    public HitTestData getLastHitTestResult() {
1147        if (mNativeAwContents == 0) return null;
1148        nativeUpdateLastHitTestData(mNativeAwContents);
1149        return mPossiblyStaleHitTestData;
1150    }
1151
1152    /**
1153     * @see android.webkit.WebView#requestFocusNodeHref()
1154     */
1155    public void requestFocusNodeHref(Message msg) {
1156        if (msg == null || mNativeAwContents == 0) return;
1157
1158        nativeUpdateLastHitTestData(mNativeAwContents);
1159        Bundle data = msg.getData();
1160        data.putString("url", mPossiblyStaleHitTestData.href);
1161        data.putString("title", mPossiblyStaleHitTestData.anchorText);
1162        data.putString("src", mPossiblyStaleHitTestData.imgSrc);
1163        msg.setData(data);
1164        msg.sendToTarget();
1165    }
1166
1167    /**
1168     * @see android.webkit.WebView#requestImageRef()
1169     */
1170    public void requestImageRef(Message msg) {
1171        if (msg == null || mNativeAwContents == 0) return;
1172
1173        nativeUpdateLastHitTestData(mNativeAwContents);
1174        Bundle data = msg.getData();
1175        data.putString("url", mPossiblyStaleHitTestData.imgSrc);
1176        msg.setData(data);
1177        msg.sendToTarget();
1178    }
1179
1180    /**
1181     * @see android.webkit.WebView#getScale()
1182     *
1183     * Please note that the scale returned is the page scale multiplied by
1184     * the screen density factor. See CTS WebViewTest.testSetInitialScale.
1185     */
1186    public float getScale() {
1187        return (float)(mContentViewCore.getScale() * mDIPScale);
1188    }
1189
1190    /**
1191     * @see android.webkit.WebView#flingScroll(int, int)
1192     */
1193    public void flingScroll(int vx, int vy) {
1194        mContentViewCore.flingScroll(vx, vy);
1195    }
1196
1197    /**
1198     * @see android.webkit.WebView#pageUp(boolean)
1199     */
1200    public boolean pageUp(boolean top) {
1201        return mContentViewCore.pageUp(top);
1202    }
1203
1204    /**
1205     * @see android.webkit.WebView#pageDown(boolean)
1206     */
1207    public boolean pageDown(boolean bottom) {
1208        return mContentViewCore.pageDown(bottom);
1209    }
1210
1211    /**
1212     * @see android.webkit.WebView#canZoomIn()
1213     */
1214    public boolean canZoomIn() {
1215        return mContentViewCore.canZoomIn();
1216    }
1217
1218    /**
1219     * @see android.webkit.WebView#canZoomOut()
1220     */
1221    public boolean canZoomOut() {
1222        return mContentViewCore.canZoomOut();
1223    }
1224
1225    /**
1226     * @see android.webkit.WebView#zoomIn()
1227     */
1228    public boolean zoomIn() {
1229        return mContentViewCore.zoomIn();
1230    }
1231
1232    /**
1233     * @see android.webkit.WebView#zoomOut()
1234     */
1235    public boolean zoomOut() {
1236        return mContentViewCore.zoomOut();
1237    }
1238
1239    /**
1240     * @see android.webkit.WebView#invokeZoomPicker()
1241     */
1242    public void invokeZoomPicker() {
1243        mContentViewCore.invokeZoomPicker();
1244    }
1245
1246    /**
1247     * @see ContentViewCore.evaluateJavaScript(String, ContentViewCOre.JavaScriptCallback)
1248     */
1249    public void evaluateJavaScript(String script, final ValueCallback<String> callback) {
1250        ContentViewCore.JavaScriptCallback jsCallback = null;
1251        if (callback != null) {
1252            jsCallback = new ContentViewCore.JavaScriptCallback() {
1253                @Override
1254                public void handleJavaScriptResult(String jsonResult) {
1255                    callback.onReceiveValue(jsonResult);
1256                }
1257            };
1258        }
1259
1260        mContentViewCore.evaluateJavaScript(script, jsCallback);
1261    }
1262
1263    //--------------------------------------------------------------------------------------------
1264    //  View and ViewGroup method implementations
1265    //--------------------------------------------------------------------------------------------
1266
1267    /**
1268     * @see android.webkit.View#onTouchEvent()
1269     */
1270    public boolean onTouchEvent(MotionEvent event) {
1271        if (mNativeAwContents == 0) return false;
1272        boolean rv = mContentViewCore.onTouchEvent(event);
1273
1274        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1275            int actionIndex = event.getActionIndex();
1276
1277            // Note this will trigger IPC back to browser even if nothing is hit.
1278            nativeRequestNewHitTestDataAt(mNativeAwContents,
1279                                          (int)Math.round(event.getX(actionIndex) / mDIPScale),
1280                                          (int)Math.round(event.getY(actionIndex) / mDIPScale));
1281        }
1282
1283        if (mOverScrollGlow != null && event.getActionMasked() == MotionEvent.ACTION_UP) {
1284            mOverScrollGlow.releaseAll();
1285        }
1286
1287        return rv;
1288    }
1289
1290    /**
1291     * @see android.view.View#onHoverEvent()
1292     */
1293    public boolean onHoverEvent(MotionEvent event) {
1294        return mContentViewCore.onHoverEvent(event);
1295    }
1296
1297    /**
1298     * @see android.view.View#onGenericMotionEvent()
1299     */
1300    public boolean onGenericMotionEvent(MotionEvent event) {
1301        return mContentViewCore.onGenericMotionEvent(event);
1302    }
1303
1304    /**
1305     * @see android.view.View#onConfigurationChanged()
1306     */
1307    public void onConfigurationChanged(Configuration newConfig) {
1308        mContentViewCore.onConfigurationChanged(newConfig);
1309    }
1310
1311    /**
1312     * @see android.view.View#onAttachedToWindow()
1313     *
1314     * Note that this is also called from receivePopupContents.
1315     */
1316    public void onAttachedToWindow() {
1317        mIsAttachedToWindow = true;
1318        if (mScrollChangeListener == null) {
1319            mScrollChangeListener = new ScrollChangeListener();
1320        }
1321        mContainerView.getViewTreeObserver().addOnScrollChangedListener(mScrollChangeListener);
1322
1323        mContentViewCore.onAttachedToWindow();
1324        nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(),
1325                mContainerView.getHeight());
1326
1327        // This is for the case where this is created by restoreState, which
1328        // needs to call to NavigationController::LoadIfNecessary to actually
1329        // load the restored page.
1330        if (!mIsPaused) onResume();
1331    }
1332
1333    /**
1334     * @see android.view.View#onDetachedFromWindow()
1335     */
1336    public void onDetachedFromWindow() {
1337        mIsAttachedToWindow = false;
1338        if (mNativeAwContents != 0) {
1339            nativeOnDetachedFromWindow(mNativeAwContents);
1340        }
1341
1342        if (mScrollChangeListener != null) {
1343            mContainerView.getViewTreeObserver().removeOnScrollChangedListener(
1344                    mScrollChangeListener);
1345            mScrollChangeListener = null;
1346        }
1347
1348        mContentViewCore.onDetachedFromWindow();
1349    }
1350
1351    /**
1352     * @see android.view.View#onWindowFocusChanged()
1353     */
1354    public void onWindowFocusChanged(boolean hasWindowFocus) {
1355        mWindowFocused = hasWindowFocus;
1356        mContentViewCore.onFocusChanged(mContainerViewFocused && mWindowFocused);
1357    }
1358
1359    /**
1360     * @see android.view.View#onFocusChanged()
1361     */
1362    public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
1363        mContainerViewFocused = focused;
1364        mContentViewCore.onFocusChanged(mContainerViewFocused && mWindowFocused);
1365    }
1366
1367    /**
1368     * @see android.view.View#onSizeChanged()
1369     */
1370    public void onSizeChanged(int w, int h, int ow, int oh) {
1371        if (mNativeAwContents == 0) return;
1372        mScrollOffsetManager.setContainerViewSize(w, h);
1373        updatePhysicalBackingSizeIfNeeded();
1374        mContentViewCore.onSizeChanged(w, h, ow, oh);
1375        nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh);
1376    }
1377
1378    /**
1379     * @see android.view.View#onVisibilityChanged()
1380     */
1381    public void onVisibilityChanged(View changedView, int visibility) {
1382        updateVisibilityState();
1383    }
1384
1385    /**
1386     * @see android.view.View#onWindowVisibilityChanged()
1387     */
1388    public void onWindowVisibilityChanged(int visibility) {
1389        updateVisibilityState();
1390    }
1391
1392    private void updateVisibilityState() {
1393        boolean visible = getContainerViewVisible();
1394        if (mIsVisible == visible) return;
1395
1396        setVisibilityInternal(visible);
1397    }
1398
1399    private boolean getContainerViewVisible() {
1400        boolean windowVisible = mContainerView.getWindowVisibility() == View.VISIBLE;
1401        boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE;
1402
1403        return windowVisible && viewVisible && !mIsPaused;
1404    }
1405
1406    private void setVisibilityInternal(boolean visible) {
1407        // Note that this skips mIsVisible check and unconditionally sets
1408        // visibility. In general, callers should use updateVisibilityState
1409        // instead.
1410        mIsVisible = visible;
1411        if (mIsVisible) {
1412            mContentViewCore.onShow();
1413        } else {
1414            mContentViewCore.onHide();
1415        }
1416        nativeSetVisibility(mNativeAwContents, mIsVisible);
1417    }
1418
1419    /**
1420     * Key for opaque state in bundle. Note this is only public for tests.
1421     */
1422    public static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE";
1423
1424    /**
1425     * Save the state of this AwContents into provided Bundle.
1426     * @return False if saving state failed.
1427     */
1428    public boolean saveState(Bundle outState) {
1429        if (outState == null) return false;
1430
1431        byte[] state = nativeGetOpaqueState(mNativeAwContents);
1432        if (state == null) return false;
1433
1434        outState.putByteArray(SAVE_RESTORE_STATE_KEY, state);
1435        return true;
1436    }
1437
1438    /**
1439     * Restore the state of this AwContents into provided Bundle.
1440     * @param inState Must be a bundle returned by saveState.
1441     * @return False if restoring state failed.
1442     */
1443    public boolean restoreState(Bundle inState) {
1444        if (inState == null) return false;
1445
1446        byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY);
1447        if (state == null) return false;
1448
1449        boolean result = nativeRestoreFromOpaqueState(mNativeAwContents, state);
1450
1451        // The onUpdateTitle callback normally happens when a page is loaded,
1452        // but is optimized out in the restoreState case because the title is
1453        // already restored. See WebContentsImpl::UpdateTitleForEntry. So we
1454        // call the callback explicitly here.
1455        if (result) mContentsClient.onReceivedTitle(mContentViewCore.getTitle());
1456
1457        return result;
1458    }
1459
1460    /**
1461     * @see ContentViewCore#addPossiblyUnsafeJavascriptInterface(Object, String, Class)
1462     */
1463    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
1464            Class<? extends Annotation> requiredAnnotation) {
1465        mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation);
1466    }
1467
1468    /**
1469     * @see android.webkit.WebView#removeJavascriptInterface(String)
1470     */
1471    public void removeJavascriptInterface(String interfaceName) {
1472        mContentViewCore.removeJavascriptInterface(interfaceName);
1473    }
1474
1475    /**
1476     * If native accessibility (not script injection) is enabled, and if this is
1477     * running on JellyBean or later, returns an AccessibilityNodeProvider that
1478     * implements native accessibility for this view. Returns null otherwise.
1479     * @return The AccessibilityNodeProvider, if available, or null otherwise.
1480     */
1481    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1482        return mContentViewCore.getAccessibilityNodeProvider();
1483    }
1484
1485    /**
1486     * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
1487     */
1488    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1489        mContentViewCore.onInitializeAccessibilityNodeInfo(info);
1490    }
1491
1492    /**
1493     * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent)
1494     */
1495    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1496        mContentViewCore.onInitializeAccessibilityEvent(event);
1497    }
1498
1499    public boolean supportsAccessibilityAction(int action) {
1500        return mContentViewCore.supportsAccessibilityAction(action);
1501    }
1502
1503    /**
1504     * @see android.webkit.WebView#performAccessibilityAction(int, Bundle)
1505     */
1506    public boolean performAccessibilityAction(int action, Bundle arguments) {
1507        return mContentViewCore.performAccessibilityAction(action, arguments);
1508    }
1509
1510    /**
1511     * @see android.webkit.WebView#clearFormData()
1512     */
1513    public void hideAutofillPopup() {
1514        if (mAwAutofillManagerDelegate != null)
1515            mAwAutofillManagerDelegate.hideAutofillPopup();
1516    }
1517
1518    //--------------------------------------------------------------------------------------------
1519    //  Methods called from native via JNI
1520    //--------------------------------------------------------------------------------------------
1521
1522    @CalledByNative
1523    private static void onDocumentHasImagesResponse(boolean result, Message message) {
1524        message.arg1 = result ? 1 : 0;
1525        message.sendToTarget();
1526    }
1527
1528    @CalledByNative
1529    private void onReceivedTouchIconUrl(String url, boolean precomposed) {
1530        mContentsClient.onReceivedTouchIconUrl(url, precomposed);
1531    }
1532
1533    @CalledByNative
1534    private void onReceivedIcon(Bitmap bitmap) {
1535        mContentsClient.onReceivedIcon(bitmap);
1536        mFavicon = bitmap;
1537    }
1538
1539    /** Callback for generateMHTML. */
1540    @CalledByNative
1541    private static void generateMHTMLCallback(
1542            String path, long size, ValueCallback<String> callback) {
1543        if (callback == null) return;
1544        callback.onReceiveValue(size < 0 ? null : path);
1545    }
1546
1547    @CalledByNative
1548    private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
1549        mContentsClient.onReceivedHttpAuthRequest(handler, host, realm);
1550    }
1551
1552    private class AwGeolocationCallback implements GeolocationPermissions.Callback {
1553
1554        @Override
1555        public void invoke(final String origin, final boolean allow, final boolean retain) {
1556            ThreadUtils.runOnUiThread(new Runnable() {
1557                @Override
1558                public void run() {
1559                    if (retain) {
1560                        if (allow) {
1561                            mBrowserContext.getGeolocationPermissions().allow(origin);
1562                        } else {
1563                            mBrowserContext.getGeolocationPermissions().deny(origin);
1564                        }
1565                    }
1566                    nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin);
1567                }
1568            });
1569        }
1570    }
1571
1572    @CalledByNative
1573    private void onGeolocationPermissionsShowPrompt(String origin) {
1574        AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions();
1575        // Reject if geoloaction is disabled, or the origin has a retained deny
1576        if (!mSettings.getGeolocationEnabled()) {
1577            nativeInvokeGeolocationCallback(mNativeAwContents, false, origin);
1578            return;
1579        }
1580        // Allow if the origin has a retained allow
1581        if (permissions.hasOrigin(origin)) {
1582            nativeInvokeGeolocationCallback(mNativeAwContents, permissions.isOriginAllowed(origin),
1583                    origin);
1584            return;
1585        }
1586        mContentsClient.onGeolocationPermissionsShowPrompt(
1587                origin, new AwGeolocationCallback());
1588    }
1589
1590    @CalledByNative
1591    private void onGeolocationPermissionsHidePrompt() {
1592        mContentsClient.onGeolocationPermissionsHidePrompt();
1593    }
1594
1595    @CalledByNative
1596    public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
1597            boolean isDoneCounting) {
1598        mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
1599    }
1600
1601    @CalledByNative
1602    public void onNewPicture() {
1603        // Don't call capturePicture() here but instead defer it until the posted task runs within
1604        // the callback helper, to avoid doubling back into the renderer compositor in the middle
1605        // of the notification it is sending up to here.
1606        mContentsClient.getCallbackHelper().postOnNewPicture(mPictureListenerContentProvider);
1607    }
1608
1609    // Called as a result of nativeUpdateLastHitTestData.
1610    @CalledByNative
1611    private void updateHitTestData(
1612            int type, String extra, String href, String anchorText, String imgSrc) {
1613        mPossiblyStaleHitTestData.hitTestResultType = type;
1614        mPossiblyStaleHitTestData.hitTestResultExtraData = extra;
1615        mPossiblyStaleHitTestData.href = href;
1616        mPossiblyStaleHitTestData.anchorText = anchorText;
1617        mPossiblyStaleHitTestData.imgSrc = imgSrc;
1618    }
1619
1620    @CalledByNative
1621    private boolean requestDrawGL(Canvas canvas) {
1622        return mInternalAccessAdapter.requestDrawGL(canvas);
1623    }
1624
1625    private static final boolean SUPPORTS_ON_ANIMATION =
1626            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
1627
1628    @CalledByNative
1629    private void postInvalidateOnAnimation() {
1630        if (SUPPORTS_ON_ANIMATION) {
1631            mContainerView.postInvalidateOnAnimation();
1632        } else {
1633            mContainerView.postInvalidate();
1634        }
1635    }
1636
1637    @CalledByNative
1638    private boolean performLongClick() {
1639        return mContainerView.performLongClick();
1640    }
1641
1642    @CalledByNative
1643    private int[] getLocationOnScreen() {
1644        int[] result = new int[2];
1645        mContainerView.getLocationOnScreen(result);
1646        return result;
1647    }
1648
1649    @CalledByNative
1650    private void onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor) {
1651        // This change notification comes from the renderer thread, not from the cc/ impl thread.
1652        mLayoutSizer.onPageScaleChanged(webLayoutPageScaleFactor);
1653    }
1654
1655    @CalledByNative
1656    private void scrollContainerViewTo(int x, int y) {
1657        mScrollOffsetManager.scrollContainerViewTo(x, y);
1658    }
1659
1660    @CalledByNative
1661    private void setAwAutofillManagerDelegate(AwAutofillManagerDelegate delegate) {
1662        mAwAutofillManagerDelegate = delegate;
1663        delegate.init(mContentViewCore, mDIPScale);
1664    }
1665
1666    @CalledByNative
1667    private void didOverscroll(int deltaX, int deltaY) {
1668        if (mOverScrollGlow != null) {
1669            mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
1670        }
1671
1672        mScrollOffsetManager.overscrollBy(deltaX, deltaY);
1673
1674        if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
1675            mContainerView.invalidate();
1676        }
1677    }
1678
1679    // -------------------------------------------------------------------------------------------
1680    // Helper methods
1681    // -------------------------------------------------------------------------------------------
1682
1683    private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) {
1684        if (path == null || mNativeAwContents == 0) {
1685            ThreadUtils.runOnUiThread(new Runnable() {
1686                @Override
1687                public void run() {
1688                    callback.onReceiveValue(null);
1689                }
1690            });
1691        } else {
1692            nativeGenerateMHTML(mNativeAwContents, path, callback);
1693        }
1694    }
1695
1696    /**
1697     * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's
1698     * autoname logic.
1699     */
1700    private static String generateArchiveAutoNamePath(String originalUrl, String baseName) {
1701        String name = null;
1702        if (originalUrl != null && !originalUrl.isEmpty()) {
1703            try {
1704                String path = new URL(originalUrl).getPath();
1705                int lastSlash = path.lastIndexOf('/');
1706                if (lastSlash > 0) {
1707                    name = path.substring(lastSlash + 1);
1708                } else {
1709                    name = path;
1710                }
1711            } catch (MalformedURLException e) {
1712                // If it fails parsing the URL, we'll just rely on the default name below.
1713            }
1714        }
1715
1716        if (TextUtils.isEmpty(name)) name = "index";
1717
1718        String testName = baseName + name + WEB_ARCHIVE_EXTENSION;
1719        if (!new File(testName).exists()) return testName;
1720
1721        for (int i = 1; i < 100; i++) {
1722            testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION;
1723            if (!new File(testName).exists()) return testName;
1724        }
1725
1726        Log.e(TAG, "Unable to auto generate archive name for path: " + baseName);
1727        return null;
1728    }
1729
1730    //--------------------------------------------------------------------------------------------
1731    //  Native methods
1732    //--------------------------------------------------------------------------------------------
1733
1734    private static native int nativeInit(AwBrowserContext browserContext);
1735    private static native void nativeDestroy(int nativeAwContents);
1736    private static native void nativeSetAwDrawSWFunctionTable(int functionTablePointer);
1737    private static native void nativeSetAwDrawGLFunctionTable(int functionTablePointer);
1738    private static native int nativeGetAwDrawGLFunction();
1739    private static native int nativeGetNativeInstanceCount();
1740    private native void nativeSetJavaPeers(int nativeAwContents, AwContents awContents,
1741            AwWebContentsDelegate webViewWebContentsDelegate,
1742            AwContentsClientBridge contentsClientBridge,
1743            AwContentsIoThreadClient ioThreadClient,
1744            InterceptNavigationDelegate navigationInterceptionDelegate);
1745    private native int nativeGetWebContents(int nativeAwContents);
1746
1747    private native void nativeDocumentHasImages(int nativeAwContents, Message message);
1748    private native void nativeGenerateMHTML(
1749            int nativeAwContents, String path, ValueCallback<String> callback);
1750
1751    private native void nativeAddVisitedLinks(int nativeAwContents, String[] visitedLinks);
1752    private native boolean nativeOnDraw(int nativeAwContents, Canvas canvas,
1753            boolean isHardwareAccelerated, int scrollX, int ScrollY,
1754            int clipLeft, int clipTop, int clipRight, int clipBottom);
1755    private native void nativeFindAllAsync(int nativeAwContents, String searchString);
1756    private native void nativeFindNext(int nativeAwContents, boolean forward);
1757    private native void nativeClearMatches(int nativeAwContents);
1758    private native void nativeClearCache(int nativeAwContents, boolean includeDiskFiles);
1759    private native byte[] nativeGetCertificate(int nativeAwContents);
1760
1761    // Coordinates in desity independent pixels.
1762    private native void nativeRequestNewHitTestDataAt(int nativeAwContents, int x, int y);
1763    private native void nativeUpdateLastHitTestData(int nativeAwContents);
1764
1765    private native void nativeOnSizeChanged(int nativeAwContents, int w, int h, int ow, int oh);
1766    private native void nativeScrollTo(int nativeAwContents, int x, int y);
1767    private native void nativeSetVisibility(int nativeAwContents, boolean visible);
1768    private native void nativeOnAttachedToWindow(int nativeAwContents, int w, int h);
1769    private native void nativeOnDetachedFromWindow(int nativeAwContents);
1770    private native void nativeSetDipScale(int nativeAwContents, float dipScale);
1771    private native void nativeSetDisplayedPageScaleFactor(int nativeAwContents,
1772            float pageScaleFactor);
1773
1774    // Returns null if save state fails.
1775    private native byte[] nativeGetOpaqueState(int nativeAwContents);
1776
1777    // Returns false if restore state fails.
1778    private native boolean nativeRestoreFromOpaqueState(int nativeAwContents, byte[] state);
1779
1780    private native int nativeReleasePopupAwContents(int nativeAwContents);
1781    private native void nativeFocusFirstNode(int nativeAwContents);
1782    private native void nativeSetBackgroundColor(int nativeAwContents, int color);
1783
1784    private native int nativeGetAwDrawGLViewContext(int nativeAwContents);
1785    private native Picture nativeCapturePicture(int nativeAwContents);
1786    private native void nativeEnableOnNewPicture(int nativeAwContents, boolean enabled);
1787
1788    private native void nativeInvokeGeolocationCallback(
1789            int nativeAwContents, boolean value, String requestingFrame);
1790}
1791