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