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