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