ContentViewCore.java revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.app.Activity;
8import android.content.Context;
9import android.content.pm.ActivityInfo;
10import android.content.pm.PackageManager;
11import android.content.res.Configuration;
12import android.graphics.Bitmap;
13import android.graphics.Canvas;
14import android.graphics.Color;
15import android.graphics.Rect;
16import android.os.Build;
17import android.os.Bundle;
18import android.os.Handler;
19import android.os.ResultReceiver;
20import android.text.Editable;
21import android.util.Log;
22import android.util.Pair;
23import android.view.ActionMode;
24import android.view.InputDevice;
25import android.view.KeyEvent;
26import android.view.MotionEvent;
27import android.view.Surface;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.Window;
31import android.view.WindowManager;
32import android.view.accessibility.AccessibilityEvent;
33import android.view.accessibility.AccessibilityNodeInfo;
34import android.view.inputmethod.EditorInfo;
35import android.view.inputmethod.InputConnection;
36import android.view.inputmethod.InputMethodManager;
37import android.widget.FrameLayout;
38
39import com.google.common.annotations.VisibleForTesting;
40
41import org.chromium.base.CalledByNative;
42import org.chromium.base.JNINamespace;
43import org.chromium.base.WeakContext;
44import org.chromium.content.R;
45import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
46import org.chromium.content.browser.accessibility.AccessibilityInjector;
47import org.chromium.content.browser.input.AdapterInputConnection;
48import org.chromium.content.browser.input.HandleView;
49import org.chromium.content.browser.input.ImeAdapter;
50import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
51import org.chromium.content.browser.input.InsertionHandleController;
52import org.chromium.content.browser.input.SelectPopupDialog;
53import org.chromium.content.browser.input.SelectionHandleController;
54import org.chromium.content.common.TraceEvent;
55import org.chromium.ui.ViewAndroid;
56import org.chromium.ui.ViewAndroidDelegate;
57import org.chromium.ui.WindowAndroid;
58import org.chromium.ui.gfx.DeviceDisplayInfo;
59
60import java.lang.annotation.Annotation;
61import java.util.HashMap;
62import java.util.HashSet;
63import java.util.Map;
64
65/**
66 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
67 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
68 * being tied to the view system.
69 */
70@JNINamespace("content")
71public class ContentViewCore implements MotionEventDelegate, NavigationClient {
72    /**
73     * Indicates that input events are batched together and delivered just before vsync.
74     */
75    public static final int INPUT_EVENTS_DELIVERED_AT_VSYNC = 1;
76
77    /**
78     * Opposite of INPUT_EVENTS_DELIVERED_AT_VSYNC.
79     */
80    public static final int INPUT_EVENTS_DELIVERED_IMMEDIATELY = 0;
81
82    private static final String TAG = ContentViewCore.class.getName();
83
84    // Used to avoid enabling zooming in / out if resulting zooming will
85    // produce little visible difference.
86    private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
87
88    // Used to represent gestures for long press and long tap.
89    private static final int IS_LONG_PRESS = 1;
90    private static final int IS_LONG_TAP = 2;
91
92    // Length of the delay (in ms) before fading in handles after the last page movement.
93    private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
94
95    // If the embedder adds a JavaScript interface object that contains an indirect reference to
96    // the ContentViewCore, then storing a strong ref to the interface object on the native
97    // side would prevent garbage collection of the ContentViewCore (as that strong ref would
98    // create a new GC root).
99    // For that reason, we store only a weak reference to the interface object on the
100    // native side. However we still need a strong reference on the Java side to
101    // prevent garbage collection if the embedder doesn't maintain their own ref to the
102    // interface object - the Java side ref won't create a new GC root.
103    // This map stores those refernces. We put into the map on addJavaScriptInterface()
104    // and remove from it in removeJavaScriptInterface().
105    private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
106
107    // Additionally, we keep track of all Java bound JS objects that are in use on the
108    // current page to ensure that they are not garbage collected until the page is
109    // navigated. This includes interface objects that have been removed
110    // via the removeJavaScriptInterface API and transient objects returned from methods
111    // on the interface object. Note we use HashSet rather than Set as the native side
112    // expects HashSet (no bindings for interfaces).
113    private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
114
115    /**
116     * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
117     * dispatching of view methods through the containing view.
118     *
119     * <p>
120     * All methods with the "super_" prefix should be routed to the parent of the
121     * implementing container view.
122     */
123    @SuppressWarnings("javadoc")
124    public interface InternalAccessDelegate {
125        /**
126         * @see View#drawChild(Canvas, View, long)
127         */
128        boolean drawChild(Canvas canvas, View child, long drawingTime);
129
130        /**
131         * @see View#onKeyUp(keyCode, KeyEvent)
132         */
133        boolean super_onKeyUp(int keyCode, KeyEvent event);
134
135        /**
136         * @see View#dispatchKeyEventPreIme(KeyEvent)
137         */
138        boolean super_dispatchKeyEventPreIme(KeyEvent event);
139
140        /**
141         * @see View#dispatchKeyEvent(KeyEvent)
142         */
143        boolean super_dispatchKeyEvent(KeyEvent event);
144
145        /**
146         * @see View#onGenericMotionEvent(MotionEvent)
147         */
148        boolean super_onGenericMotionEvent(MotionEvent event);
149
150        /**
151         * @see View#onConfigurationChanged(Configuration)
152         */
153        void super_onConfigurationChanged(Configuration newConfig);
154
155        /**
156         * @see View#onScrollChanged(int, int, int, int)
157         */
158        void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
159
160        /**
161         * @see View#awakenScrollBars()
162         */
163        boolean awakenScrollBars();
164
165        /**
166         * @see View#awakenScrollBars(int, boolean)
167         */
168        boolean super_awakenScrollBars(int startDelay, boolean invalidate);
169    }
170
171    /**
172     * An interface that allows the embedder to be notified when the pinch gesture starts and
173     * stops.
174     */
175    public interface PinchGestureStateListener {
176        /**
177         * Called when the pinch gesture starts.
178         */
179        void onPinchGestureStart();
180        /**
181         * Called when the pinch gesture ends.
182         */
183        void onPinchGestureEnd();
184    }
185
186    /**
187     * An interface for controlling visibility and state of embedder-provided zoom controls.
188     */
189    public interface ZoomControlsDelegate {
190        /**
191         * Called when it's reasonable to show zoom controls.
192         */
193        void invokeZoomPicker();
194        /**
195         * Called when zoom controls need to be hidden (e.g. when the view hides).
196         */
197        void dismissZoomPicker();
198        /**
199         * Called when page scale has been changed, so the controls can update their state.
200         */
201        void updateZoomControls();
202    }
203
204    private VSyncManager.Provider mVSyncProvider;
205    private VSyncManager.Listener mVSyncListener;
206    private int mVSyncSubscriberCount;
207    private boolean mVSyncListenerRegistered;
208
209    // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer.
210    // When we do this, we also need to avoid sending the real vsync signal for the current
211    // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification.
212    private boolean mDidSignalVSyncUsingInputEvent;
213
214    public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
215        if (mVSyncProvider != null && mVSyncListenerRegistered) {
216            mVSyncProvider.unregisterVSyncListener(mVSyncListener);
217            mVSyncListenerRegistered = false;
218        }
219
220        mVSyncProvider = vsyncProvider;
221        mVSyncListener = new VSyncManager.Listener() {
222            @Override
223            public void updateVSync(long tickTimeMicros, long intervalMicros) {
224                if (mNativeContentViewCore != 0) {
225                    nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
226                            intervalMicros);
227                }
228            }
229
230            @Override
231            public void onVSync(long frameTimeMicros) {
232                animateIfNecessary(frameTimeMicros);
233
234                if (mDidSignalVSyncUsingInputEvent) {
235                    TraceEvent.instant("ContentViewCore::onVSync ignored");
236                    mDidSignalVSyncUsingInputEvent = false;
237                    return;
238                }
239                if (mNativeContentViewCore != 0) {
240                    nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
241                }
242            }
243        };
244
245        if (mVSyncSubscriberCount > 0) {
246            // setVSyncNotificationEnabled(true) is called before getVSyncListener.
247            vsyncProvider.registerVSyncListener(mVSyncListener);
248            mVSyncListenerRegistered = true;
249        }
250
251        return mVSyncListener;
252    }
253
254    @CalledByNative
255    void setVSyncNotificationEnabled(boolean enabled) {
256        if (!isVSyncNotificationEnabled() && enabled) {
257            mDidSignalVSyncUsingInputEvent = false;
258        }
259        if (mVSyncProvider != null) {
260            if (!mVSyncListenerRegistered && enabled) {
261                mVSyncProvider.registerVSyncListener(mVSyncListener);
262                mVSyncListenerRegistered = true;
263            } else if (mVSyncSubscriberCount == 1 && !enabled) {
264                assert mVSyncListenerRegistered;
265                mVSyncProvider.unregisterVSyncListener(mVSyncListener);
266                mVSyncListenerRegistered = false;
267            }
268        }
269        mVSyncSubscriberCount += enabled ? 1 : -1;
270        assert mVSyncSubscriberCount >= 0;
271    }
272
273    @CalledByNative
274    private void resetVSyncNotification() {
275        while (isVSyncNotificationEnabled()) setVSyncNotificationEnabled(false);
276        mVSyncSubscriberCount = 0;
277        mVSyncListenerRegistered = false;
278        mNeedAnimate = false;
279    }
280
281    private boolean isVSyncNotificationEnabled() {
282        return mVSyncProvider != null && mVSyncListenerRegistered;
283    }
284
285    @CalledByNative
286    private void setNeedsAnimate() {
287        if (!mNeedAnimate) {
288            mNeedAnimate = true;
289            setVSyncNotificationEnabled(true);
290        }
291    }
292
293    private final Context mContext;
294    private ViewGroup mContainerView;
295    private InternalAccessDelegate mContainerViewInternals;
296    private WebContentsObserverAndroid mWebContentsObserver;
297
298    private ContentViewClient mContentViewClient;
299
300    private ContentSettings mContentSettings;
301
302    // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
303    private int mNativeContentViewCore = 0;
304
305    private boolean mAttachedToWindow = false;
306
307    private ContentViewGestureHandler mContentViewGestureHandler;
308    private PinchGestureStateListener mPinchGestureStateListener;
309    private ZoomManager mZoomManager;
310    private ZoomControlsDelegate mZoomControlsDelegate;
311
312    private PopupZoomer mPopupZoomer;
313
314    private Runnable mFakeMouseMoveRunnable = null;
315
316    // Only valid when focused on a text / password field.
317    private ImeAdapter mImeAdapter;
318    private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
319    private AdapterInputConnection mInputConnection;
320
321    private SelectionHandleController mSelectionHandleController;
322    private InsertionHandleController mInsertionHandleController;
323
324    private Runnable mDeferredHandleFadeInRunnable;
325
326    // Size of the viewport in physical pixels as set from onSizeChanged or setInitialViewportSize.
327    private int mViewportWidthPix;
328    private int mViewportHeightPix;
329    private int mPhysicalBackingWidthPix;
330    private int mPhysicalBackingHeightPix;
331    private int mOverdrawBottomHeightPix;
332    private int mViewportSizeOffsetWidthPix;
333    private int mViewportSizeOffsetHeightPix;
334
335    // Cached copy of all positions and scales as reported by the renderer.
336    private final RenderCoordinates mRenderCoordinates;
337
338    private final RenderCoordinates.NormalizedPoint mStartHandlePoint;
339    private final RenderCoordinates.NormalizedPoint mEndHandlePoint;
340    private final RenderCoordinates.NormalizedPoint mInsertionHandlePoint;
341
342    // Tracks whether a selection is currently active.  When applied to selected text, indicates
343    // whether the last selected text is still highlighted.
344    private boolean mHasSelection;
345    private String mLastSelectedText;
346    private boolean mSelectionEditable;
347    private ActionMode mActionMode;
348    private boolean mUnselectAllOnActionModeDismiss;
349
350    // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
351    private ContentViewDownloadDelegate mDownloadDelegate;
352
353    // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
354    private AccessibilityInjector mAccessibilityInjector;
355
356    // Temporary notification to tell onSizeChanged to focus a form element,
357    // because the OSK was just brought up.
358    private boolean mUnfocusOnNextSizeChanged = false;
359    private final Rect mFocusPreOSKViewportRect = new Rect();
360
361    private boolean mNeedUpdateOrientationChanged;
362
363    // Used to keep track of whether we should try to undo the last zoom-to-textfield operation.
364    private boolean mScrolledAndZoomedFocusedEditableNode = false;
365
366    // Whether we use hardware-accelerated drawing.
367    private boolean mHardwareAccelerated = false;
368
369    // Whether we received a new frame since consumePendingRendererFrame() was last called.
370    private boolean mPendingRendererFrame = false;
371
372    // Whether we should animate at the next vsync tick.
373    private boolean mNeedAnimate = false;
374
375    private ViewAndroid mViewAndroid;
376
377    /**
378     * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
379     * a ContentViewCore and before using it.
380     *
381     * @param context The context used to create this.
382     */
383    public ContentViewCore(Context context) {
384        mContext = context;
385
386        WeakContext.initializeWeakContext(context);
387        HeapStatsLogger.init(mContext.getApplicationContext());
388        mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
389
390        mRenderCoordinates = new RenderCoordinates();
391        mRenderCoordinates.setDeviceScaleFactor(
392                getContext().getResources().getDisplayMetrics().density);
393        mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
394        mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
395        mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
396    }
397
398    /**
399     * @return The context used for creating this ContentViewCore.
400     */
401    public Context getContext() {
402        return mContext;
403    }
404
405    /**
406     * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
407     */
408    public ViewGroup getContainerView() {
409        return mContainerView;
410    }
411
412    /**
413     * Set initial viewport size parameters, so that the web page can have a reasonable
414     * size to start before ContentView becomes visible.
415     * This is useful for a background view that loads the web page before it is shown
416     * and gets the first onSizeChanged().
417     */
418    public void setInitialViewportSize(int widthPix, int heightPix,
419            int offsetXPix, int offsetYPix) {
420        assert mViewportWidthPix == 0 && mViewportHeightPix == 0 &&
421                mViewportSizeOffsetWidthPix == 0 && mViewportSizeOffsetHeightPix == 0;
422        mViewportWidthPix = widthPix;
423        mViewportHeightPix = heightPix;
424        mViewportSizeOffsetWidthPix = offsetXPix;
425        mViewportSizeOffsetHeightPix = offsetYPix;
426        if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
427    }
428
429    /**
430     * Specifies how much smaller the WebKit layout size should be relative to the size of this
431     * view.
432     * @param offsetXPix The X amount in pixels to shrink the viewport by.
433     * @param offsetYPix The Y amount in pixels to shrink the viewport by.
434     */
435    public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
436        if (offsetXPix != mViewportSizeOffsetWidthPix ||
437                offsetYPix != mViewportSizeOffsetHeightPix) {
438            mViewportSizeOffsetWidthPix = offsetXPix;
439            mViewportSizeOffsetHeightPix = offsetYPix;
440            if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
441        }
442    }
443
444    /**
445     * Returns a delegate that can be used to add and remove views from the ContainerView.
446     *
447     * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
448     * way. In particular, the Android WebView has limitations on what implementation details can
449     * be provided via a child view, as they are visible in the API and could introduce
450     * compatibility breaks with existing applications. If in doubt, contact the
451     * android_webview/OWNERS
452     *
453     * @return A ViewAndroidDelegate that can be used to add and remove views.
454     */
455    @VisibleForTesting
456    public ViewAndroidDelegate getViewAndroidDelegate() {
457        return new ViewAndroidDelegate() {
458            @Override
459            public View acquireAnchorView() {
460                View anchorView = new View(getContext());
461                mContainerView.addView(anchorView);
462                return anchorView;
463            }
464
465            @Override
466            public void setAnchorViewPosition(
467                    View view, float x, float y, float width, float height) {
468                assert(view.getParent() == mContainerView);
469                float scale = (float) DeviceDisplayInfo.create(getContext()).getDIPScale();
470
471                // The anchor view should not go outside the bounds of the ContainerView.
472                int scaledX = Math.round(x * scale);
473                int scaledWidth = Math.round(width * scale);
474                if (scaledWidth + scaledX > mContainerView.getWidth()) {
475                    scaledWidth = mContainerView.getWidth() - scaledX;
476                }
477
478                FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
479                        scaledWidth, Math.round(height * scale));
480                lp.leftMargin = scaledX;
481                lp.topMargin = (int) mRenderCoordinates.getContentOffsetYPix() +
482                        Math.round(y * scale);
483                view.setLayoutParams(lp);
484            }
485
486            @Override
487            public void releaseAnchorView(View anchorView) {
488                mContainerView.removeView(anchorView);
489            }
490        };
491    }
492
493    @VisibleForTesting
494    public ImeAdapter getImeAdapterForTest() {
495        return mImeAdapter;
496    }
497
498    @VisibleForTesting
499    public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
500        mAdapterInputConnectionFactory = factory;
501    }
502
503    @VisibleForTesting
504    public AdapterInputConnection getInputConnectionForTest() {
505        return mInputConnection;
506    }
507
508    private ImeAdapter createImeAdapter(Context context) {
509        return new ImeAdapter(context, getSelectionHandleController(),
510                getInsertionHandleController(),
511                new ImeAdapter.ViewEmbedder() {
512                    @Override
513                    public void onImeEvent(boolean isFinish) {
514                        getContentViewClient().onImeEvent();
515                        if (!isFinish) {
516                            undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
517                        }
518                    }
519
520                    @Override
521                    public void onSetFieldValue() {
522                        scrollFocusedEditableNodeIntoView();
523                    }
524
525                    @Override
526                    public void onDismissInput() {
527                        getContentViewClient().onImeStateChangeRequested(false);
528                    }
529
530                    @Override
531                    public View getAttachedView() {
532                        return mContainerView;
533                    }
534
535                    @Override
536                    public ResultReceiver getNewShowKeyboardReceiver() {
537                        return new ResultReceiver(new Handler()) {
538                            @Override
539                            public void onReceiveResult(int resultCode, Bundle resultData) {
540                                getContentViewClient().onImeStateChangeRequested(
541                                        resultCode == InputMethodManager.RESULT_SHOWN ||
542                                        resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
543                                if (resultCode == InputMethodManager.RESULT_SHOWN) {
544                                    // If OSK is newly shown, delay the form focus until
545                                    // the onSizeChanged (in order to adjust relative to the
546                                    // new size).
547                                    getContainerView().getWindowVisibleDisplayFrame(
548                                            mFocusPreOSKViewportRect);
549                                } else if (resultCode ==
550                                        InputMethodManager.RESULT_UNCHANGED_SHOWN) {
551                                    // If the OSK was already there, focus the form immediately.
552                                    scrollFocusedEditableNodeIntoView();
553                                } else {
554                                    undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
555                                }
556                            }
557                        };
558                    }
559                }
560        );
561    }
562
563    /**
564     * Returns true if the given Activity has hardware acceleration enabled
565     * in its manifest, or in its foreground window.
566     *
567     * TODO(husky): Remove when initialize() is refactored (see TODO there)
568     * TODO(dtrainor) This is still used by other classes.  Make sure to pull some version of this
569     * out before removing it.
570     */
571    public static boolean hasHardwareAcceleration(Activity activity) {
572        // Has HW acceleration been enabled manually in the current window?
573        Window window = activity.getWindow();
574        if (window != null) {
575            if ((window.getAttributes().flags
576                    & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
577                return true;
578            }
579        }
580
581        // Has HW acceleration been enabled in the manifest?
582        try {
583            ActivityInfo info = activity.getPackageManager().getActivityInfo(
584                    activity.getComponentName(), 0);
585            if ((info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
586                return true;
587            }
588        } catch (PackageManager.NameNotFoundException e) {
589            Log.e("Chrome", "getActivityInfo(self) should not fail");
590        }
591
592        return false;
593    }
594
595    /**
596     * Returns true if the given Context is a HW-accelerated Activity.
597     *
598     * TODO(husky): Remove when initialize() is refactored (see TODO there)
599     */
600    private static boolean hasHardwareAcceleration(Context context) {
601        if (context instanceof Activity) {
602            return hasHardwareAcceleration((Activity) context);
603        }
604        return false;
605    }
606
607    /**
608     *
609     * @param containerView The view that will act as a container for all views created by this.
610     * @param internalDispatcher Handles dispatching all hidden or super methods to the
611     *                           containerView.
612     * @param nativeWebContents A pointer to the native web contents.
613     * @param windowAndroid An instance of the WindowAndroid.
614     */
615    // Perform important post-construction set up of the ContentViewCore.
616    // We do not require the containing view in the constructor to allow embedders to create a
617    // ContentViewCore without having fully created its containing view. The containing view
618    // is a vital component of the ContentViewCore, so embedders must exercise caution in what
619    // they do with the ContentViewCore before calling initialize().
620    // We supply the nativeWebContents pointer here rather than in the constructor to allow us
621    // to set the private browsing mode at a later point for the WebView implementation.
622    // Note that the caller remains the owner of the nativeWebContents and is responsible for
623    // deleting it after destroying the ContentViewCore.
624    public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
625            int nativeWebContents, WindowAndroid windowAndroid,
626            int inputEventDeliveryMode) {
627        // Check whether to use hardware acceleration. This is a bit hacky, and
628        // only works if the Context is actually an Activity (as it is in the
629        // Chrome application).
630        //
631        // What we're doing here is checking whether the app has *requested*
632        // hardware acceleration by setting the appropriate flags. This does not
633        // necessarily mean we're going to *get* hardware acceleration -- that's
634        // up to the Android framework.
635        //
636        // TODO(husky): Once the native code has been updated so that the
637        // HW acceleration flag can be set dynamically (Grace is doing this),
638        // move this check into onAttachedToWindow(), where we can test for
639        // HW support directly.
640        mHardwareAccelerated = hasHardwareAcceleration(mContext);
641
642        mContainerView = containerView;
643
644        int windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
645
646        int viewAndroidNativePointer = 0;
647        if (windowNativePointer != 0) {
648            mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
649            viewAndroidNativePointer = mViewAndroid.getNativePointer();
650        }
651
652        mNativeContentViewCore = nativeInit(mHardwareAccelerated,
653                nativeWebContents, viewAndroidNativePointer, windowNativePointer);
654        mContentSettings = new ContentSettings(this, mNativeContentViewCore);
655        initializeContainerView(internalDispatcher, inputEventDeliveryMode);
656
657        mAccessibilityInjector = AccessibilityInjector.newInstance(this);
658        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
659
660        String contentDescription = "Web View";
661        if (R.string.accessibility_content_view == 0) {
662            Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
663        } else {
664            contentDescription = mContext.getResources().getString(
665                    R.string.accessibility_content_view);
666        }
667        mContainerView.setContentDescription(contentDescription);
668        mWebContentsObserver = new WebContentsObserverAndroid(this) {
669            @Override
670            public void didStartLoading(String url) {
671                hidePopupDialog();
672                resetGestureDetectors();
673            }
674        };
675    }
676
677    @CalledByNative
678    void onNativeContentViewCoreDestroyed(int nativeContentViewCore) {
679        assert nativeContentViewCore == mNativeContentViewCore;
680        mNativeContentViewCore = 0;
681    }
682
683    /**
684     * Initializes the View that will contain all Views created by the ContentViewCore.
685     *
686     * @param internalDispatcher Handles dispatching all hidden or super methods to the
687     *                           containerView.
688     */
689    private void initializeContainerView(InternalAccessDelegate internalDispatcher,
690            int inputEventDeliveryMode) {
691        TraceEvent.begin();
692        mContainerViewInternals = internalDispatcher;
693
694        mContainerView.setWillNotDraw(false);
695        mContainerView.setFocusable(true);
696        mContainerView.setFocusableInTouchMode(true);
697        mContainerView.setClickable(true);
698
699        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
700            mContainerView.setHorizontalScrollBarEnabled(false);
701            mContainerView.setVerticalScrollBarEnabled(false);
702        }
703
704        mZoomManager = new ZoomManager(mContext, this);
705        mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this, mZoomManager,
706                inputEventDeliveryMode);
707        mZoomControlsDelegate = new ZoomControlsDelegate() {
708            @Override
709            public void invokeZoomPicker() {}
710            @Override
711            public void dismissZoomPicker() {}
712            @Override
713            public void updateZoomControls() {}
714        };
715
716        mRenderCoordinates.reset();
717
718        initPopupZoomer(mContext);
719        mImeAdapter = createImeAdapter(mContext);
720        TraceEvent.end();
721    }
722
723    private void initPopupZoomer(Context context){
724        mPopupZoomer = new PopupZoomer(context);
725        mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
726            @Override
727            public void onPopupZoomerShown(final PopupZoomer zoomer) {
728                mContainerView.post(new Runnable() {
729                    @Override
730                    public void run() {
731                        if (mContainerView.indexOfChild(zoomer) == -1) {
732                            mContainerView.addView(zoomer);
733                        } else {
734                            assert false : "PopupZoomer should never be shown without being hidden";
735                        }
736                    }
737                });
738            }
739
740            @Override
741            public void onPopupZoomerHidden(final PopupZoomer zoomer) {
742                mContainerView.post(new Runnable() {
743                    @Override
744                    public void run() {
745                        if (mContainerView.indexOfChild(zoomer) != -1) {
746                            mContainerView.removeView(zoomer);
747                            mContainerView.invalidate();
748                        } else {
749                            assert false : "PopupZoomer should never be hidden without being shown";
750                        }
751                    }
752                });
753            }
754        });
755        // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
756        // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
757        PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
758            @Override
759            public boolean onSingleTap(View v, MotionEvent e) {
760                mContainerView.requestFocus();
761                if (mNativeContentViewCore != 0) {
762                    nativeSingleTap(mNativeContentViewCore, e.getEventTime(),
763                            e.getX(), e.getY(), true);
764                }
765                return true;
766            }
767
768            @Override
769            public boolean onLongPress(View v, MotionEvent e) {
770                if (mNativeContentViewCore != 0) {
771                    nativeLongPress(mNativeContentViewCore, e.getEventTime(),
772                            e.getX(), e.getY(), true);
773                }
774                return true;
775            }
776        };
777        mPopupZoomer.setOnTapListener(listener);
778    }
779
780    /**
781     * Destroy the internal state of the ContentView. This method may only be
782     * called after the ContentView has been removed from the view system. No
783     * other methods may be called on this ContentView after this method has
784     * been called.
785     */
786    public void destroy() {
787        if (mNativeContentViewCore != 0) {
788            nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
789        }
790        resetVSyncNotification();
791        mVSyncProvider = null;
792        if (mViewAndroid != null) mViewAndroid.destroy();
793        mNativeContentViewCore = 0;
794        mContentSettings = null;
795        mJavaScriptInterfaces.clear();
796        mRetainedJavaScriptObjects.clear();
797    }
798
799    /**
800     * Returns true initially, false after destroy() has been called.
801     * It is illegal to call any other public method after destroy().
802     */
803    public boolean isAlive() {
804        return mNativeContentViewCore != 0;
805    }
806
807    /**
808     * This is only useful for passing over JNI to native code that requires ContentViewCore*.
809     * @return native ContentViewCore pointer.
810     */
811    @CalledByNative
812    public int getNativeContentViewCore() {
813        return mNativeContentViewCore;
814    }
815
816    /**
817     * For internal use. Throws IllegalStateException if mNativeContentView is 0.
818     * Use this to ensure we get a useful Java stack trace, rather than a native
819     * crash dump, from use-after-destroy bugs in Java code.
820     */
821    void checkIsAlive() throws IllegalStateException {
822        if (!isAlive()) {
823            throw new IllegalStateException("ContentView used after destroy() was called");
824        }
825    }
826
827    public void setContentViewClient(ContentViewClient client) {
828        if (client == null) {
829            throw new IllegalArgumentException("The client can't be null.");
830        }
831        mContentViewClient = client;
832    }
833
834    ContentViewClient getContentViewClient() {
835        if (mContentViewClient == null) {
836            // We use the Null Object pattern to avoid having to perform a null check in this class.
837            // We create it lazily because most of the time a client will be set almost immediately
838            // after ContentView is created.
839            mContentViewClient = new ContentViewClient();
840            // We don't set the native ContentViewClient pointer here on purpose. The native
841            // implementation doesn't mind a null delegate and using one is better than passing a
842            // Null Object, since we cut down on the number of JNI calls.
843        }
844        return mContentViewClient;
845    }
846
847    public int getBackgroundColor() {
848        if (mNativeContentViewCore != 0) {
849            return nativeGetBackgroundColor(mNativeContentViewCore);
850        }
851        return Color.WHITE;
852    }
853
854    public void setBackgroundColor(int color) {
855        if (mNativeContentViewCore != 0 && getBackgroundColor() != color) {
856            nativeSetBackgroundColor(mNativeContentViewCore, color);
857        }
858    }
859
860    @CalledByNative
861    private void onBackgroundColorChanged(int color) {
862        getContentViewClient().onBackgroundColorChanged(color);
863    }
864
865    /**
866     * Load url without fixing up the url string. Consumers of ContentView are responsible for
867     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
868     * off during user input).
869     *
870     * @param params Parameters for this load.
871     */
872    public void loadUrl(LoadUrlParams params) {
873        if (mNativeContentViewCore == 0) return;
874
875        nativeLoadUrl(mNativeContentViewCore,
876                params.mUrl,
877                params.mLoadUrlType,
878                params.mTransitionType,
879                params.mUaOverrideOption,
880                params.getExtraHeadersString(),
881                params.mPostData,
882                params.mBaseUrlForDataUrl,
883                params.mVirtualUrlForDataUrl,
884                params.mCanLoadLocalResources);
885    }
886
887    /**
888     * Stops loading the current web contents.
889     */
890    public void stopLoading() {
891        if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
892    }
893
894    /**
895     * Get the URL of the current page.
896     *
897     * @return The URL of the current page.
898     */
899    public String getUrl() {
900        if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
901        return null;
902    }
903
904    /**
905     * Get the title of the current page.
906     *
907     * @return The title of the current page.
908     */
909    public String getTitle() {
910        if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
911        return null;
912    }
913
914    /**
915     * Shows an interstitial page driven by the passed in delegate.
916     *
917     * @param url The URL being blocked by the interstitial.
918     * @param delegate The delegate handling the interstitial.
919     */
920    @VisibleForTesting
921    public void showInterstitialPage(
922            String url, InterstitialPageDelegateAndroid delegate) {
923        if (mNativeContentViewCore == 0) return;
924        nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
925    }
926
927    /**
928     * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
929     */
930    public boolean isShowingInterstitialPage() {
931        return mNativeContentViewCore == 0 ?
932                false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
933    }
934
935    /**
936     * Mark any new frames that have arrived since this function was last called as non-pending.
937     *
938     * @return Whether there was a pending frame from the renderer.
939     */
940    public boolean consumePendingRendererFrame() {
941        boolean hadPendingFrame = mPendingRendererFrame;
942        mPendingRendererFrame = false;
943        return hadPendingFrame;
944    }
945
946    /**
947     * @return Viewport width in physical pixels as set from onSizeChanged or
948     * setInitialViewportSize.
949     */
950    @CalledByNative
951    public int getViewportWidthPix() { return mViewportWidthPix; }
952
953    /**
954     * @return Viewport height in physical pixels as set from onSizeChanged or
955     * setInitialViewportSize.
956     */
957    @CalledByNative
958    public int getViewportHeightPix() { return mViewportHeightPix; }
959
960    /**
961     * @return Width of underlying physical surface.
962     */
963    @CalledByNative
964    public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
965
966    /**
967     * @return Height of underlying physical surface.
968     */
969    @CalledByNative
970    public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
971
972    /**
973     * @return Amount the output surface extends past the bottom of the window viewport.
974     */
975    @CalledByNative
976    public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
977
978    /**
979     * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
980     */
981    @CalledByNative
982    public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
983
984    /**
985     * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
986     */
987    @CalledByNative
988    public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
989
990    /**
991     * @see android.webkit.WebView#getContentHeight()
992     */
993    public float getContentHeightCss() {
994        return mRenderCoordinates.getContentHeightCss();
995    }
996
997    /**
998     * @see android.webkit.WebView#getContentWidth()
999     */
1000    public float getContentWidthCss() {
1001        return mRenderCoordinates.getContentWidthCss();
1002    }
1003
1004    public Bitmap getBitmap() {
1005        return getBitmap(getViewportWidthPix(), getViewportHeightPix());
1006    }
1007
1008    public Bitmap getBitmap(int width, int height) {
1009        if (width == 0 || height == 0
1010                || getViewportWidthPix() == 0 || getViewportHeightPix() == 0) {
1011            return null;
1012        }
1013
1014        Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1015
1016        if (mNativeContentViewCore != 0 &&
1017                nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) {
1018            // If we successfully grabbed a bitmap, check if we have to draw the Android overlay
1019            // components as well.
1020            if (mContainerView.getChildCount() > 0) {
1021                Canvas c = new Canvas(b);
1022                c.scale(width / (float) getViewportWidthPix(),
1023                        height / (float) getViewportHeightPix());
1024                mContainerView.draw(c);
1025            }
1026            return b;
1027        }
1028
1029        return null;
1030    }
1031
1032    /**
1033     * Generates a bitmap of the content that is performance optimized based on capture time.
1034     *
1035     * <p>
1036     * To have a consistent capture time across devices, we will scale down the captured bitmap
1037     * where necessary to reduce the time to generate the bitmap.
1038     *
1039     * @param width The width of the content to be captured.
1040     * @param height The height of the content to be captured.
1041     * @return A pair of the generated bitmap, and the scale that needs to be applied to return the
1042     *         bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this
1043     *         will be 2).
1044     */
1045    public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
1046        float scale = 1f;
1047        // On tablets, always scale down to MDPI for performance reasons.
1048        if (DeviceUtils.isTablet(getContext())) {
1049            scale = getContext().getResources().getDisplayMetrics().density;
1050        }
1051        return Pair.create(
1052                getBitmap((int) (width / scale), (int) (height / scale)),
1053                scale);
1054    }
1055
1056    /**
1057     * @return Whether the current WebContents has a previous navigation entry.
1058     */
1059    public boolean canGoBack() {
1060        return mNativeContentViewCore != 0 && nativeCanGoBack(mNativeContentViewCore);
1061    }
1062
1063    /**
1064     * @return Whether the current WebContents has a navigation entry after the current one.
1065     */
1066    public boolean canGoForward() {
1067        return mNativeContentViewCore != 0 && nativeCanGoForward(mNativeContentViewCore);
1068    }
1069
1070    /**
1071     * @param offset The offset into the navigation history.
1072     * @return Whether we can move in history by given offset
1073     */
1074    public boolean canGoToOffset(int offset) {
1075        return mNativeContentViewCore != 0 && nativeCanGoToOffset(mNativeContentViewCore, offset);
1076    }
1077
1078    /**
1079     * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1080     * of bounds.
1081     * @param offset The offset into the navigation history.
1082     */
1083    public void goToOffset(int offset) {
1084        if (mNativeContentViewCore != 0) nativeGoToOffset(mNativeContentViewCore, offset);
1085    }
1086
1087    @Override
1088    public void goToNavigationIndex(int index) {
1089        if (mNativeContentViewCore != 0) nativeGoToNavigationIndex(mNativeContentViewCore, index);
1090    }
1091
1092    /**
1093     * Goes to the navigation entry before the current one.
1094     */
1095    public void goBack() {
1096        if (mNativeContentViewCore != 0) nativeGoBack(mNativeContentViewCore);
1097    }
1098
1099    /**
1100     * Goes to the navigation entry following the current one.
1101     */
1102    public void goForward() {
1103        if (mNativeContentViewCore != 0) nativeGoForward(mNativeContentViewCore);
1104    }
1105
1106    /**
1107     * Reload the current page.
1108     */
1109    public void reload() {
1110        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1111        if (mNativeContentViewCore != 0) nativeReload(mNativeContentViewCore);
1112    }
1113
1114    /**
1115     * Cancel the pending reload.
1116     */
1117    public void cancelPendingReload() {
1118        if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1119    }
1120
1121    /**
1122     * Continue the pending reload.
1123     */
1124    public void continuePendingReload() {
1125        if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1126    }
1127
1128    /**
1129     * Clears the ContentViewCore's page history in both the backwards and
1130     * forwards directions.
1131     */
1132    public void clearHistory() {
1133        if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1134    }
1135
1136    String getSelectedText() {
1137        return mHasSelection ? mLastSelectedText : "";
1138    }
1139
1140    // End FrameLayout overrides.
1141
1142    /**
1143     * @see {@link android.webkit.WebView#flingScroll(int, int)}
1144     */
1145    public void flingScroll(int vx, int vy) {
1146        // Notes:
1147        //   (1) Use large negative values for the x/y parameters so we don't accidentally scroll a
1148        //       nested frame.
1149        //   (2) vx and vy are inverted to match WebView behavior.
1150        mContentViewGestureHandler.fling(
1151                System.currentTimeMillis(), -Integer.MAX_VALUE, -Integer.MIN_VALUE, -vx, -vy);
1152    }
1153
1154    /**
1155     * @see View#onTouchEvent(MotionEvent)
1156     */
1157    public boolean onTouchEvent(MotionEvent event) {
1158        undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1159        return mContentViewGestureHandler.onTouchEvent(event);
1160    }
1161
1162    /**
1163     * @return ContentViewGestureHandler for all MotionEvent and gesture related calls.
1164     */
1165    ContentViewGestureHandler getContentViewGestureHandler() {
1166        return mContentViewGestureHandler;
1167    }
1168
1169    @Override
1170    public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
1171        if (mNativeContentViewCore != 0) {
1172            return nativeSendTouchEvent(mNativeContentViewCore, timeMs, action, pts);
1173        }
1174        return false;
1175    }
1176
1177    @SuppressWarnings("unused")
1178    @CalledByNative
1179    private void hasTouchEventHandlers(boolean hasTouchHandlers) {
1180        mContentViewGestureHandler.hasTouchEventHandlers(hasTouchHandlers);
1181    }
1182
1183    @SuppressWarnings("unused")
1184    @CalledByNative
1185    private void confirmTouchEvent(int ackResult) {
1186        mContentViewGestureHandler.confirmTouchEvent(ackResult);
1187    }
1188
1189    @Override
1190    public boolean sendGesture(int type, long timeMs, int x, int y, boolean lastInputEventForVSync,
1191                               Bundle b) {
1192        if (mNativeContentViewCore == 0) return false;
1193        updateTextHandlesForGesture(type);
1194        updatePinchGestureStateListener(type);
1195        if (lastInputEventForVSync && isVSyncNotificationEnabled()) {
1196            assert type == ContentViewGestureHandler.GESTURE_SCROLL_BY ||
1197                    type == ContentViewGestureHandler.GESTURE_PINCH_BY;
1198            mDidSignalVSyncUsingInputEvent = true;
1199        }
1200        switch (type) {
1201            case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE:
1202                nativeShowPressState(mNativeContentViewCore, timeMs, x, y);
1203                return true;
1204            case ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL:
1205                nativeShowPressCancel(mNativeContentViewCore, timeMs, x, y);
1206                return true;
1207            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
1208                nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1209                return true;
1210            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP:
1211                nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1212                return true;
1213            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED:
1214                handleTapOrPress(timeMs, x, y, 0,
1215                        b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false));
1216                return true;
1217            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED:
1218                nativeSingleTapUnconfirmed(mNativeContentViewCore, timeMs, x, y);
1219                return true;
1220            case ContentViewGestureHandler.GESTURE_LONG_PRESS:
1221                handleTapOrPress(timeMs, x, y, IS_LONG_PRESS, false);
1222                return true;
1223            case ContentViewGestureHandler.GESTURE_LONG_TAP:
1224                handleTapOrPress(timeMs, x, y, IS_LONG_TAP, false);
1225                return true;
1226            case ContentViewGestureHandler.GESTURE_SCROLL_START:
1227                nativeScrollBegin(mNativeContentViewCore, timeMs, x, y);
1228                return true;
1229            case ContentViewGestureHandler.GESTURE_SCROLL_BY: {
1230                int dx = b.getInt(ContentViewGestureHandler.DISTANCE_X);
1231                int dy = b.getInt(ContentViewGestureHandler.DISTANCE_Y);
1232                nativeScrollBy(mNativeContentViewCore, timeMs, x, y, dx, dy,
1233                        lastInputEventForVSync);
1234                return true;
1235            }
1236            case ContentViewGestureHandler.GESTURE_SCROLL_END:
1237                nativeScrollEnd(mNativeContentViewCore, timeMs);
1238                return true;
1239            case ContentViewGestureHandler.GESTURE_FLING_START:
1240                nativeFlingStart(mNativeContentViewCore, timeMs, x, y,
1241                        b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
1242                        b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
1243                return true;
1244            case ContentViewGestureHandler.GESTURE_FLING_CANCEL:
1245                nativeFlingCancel(mNativeContentViewCore, timeMs);
1246                return true;
1247            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
1248                nativePinchBegin(mNativeContentViewCore, timeMs, x, y);
1249                return true;
1250            case ContentViewGestureHandler.GESTURE_PINCH_BY:
1251                nativePinchBy(mNativeContentViewCore, timeMs, x, y,
1252                        b.getFloat(ContentViewGestureHandler.DELTA, 0),
1253                        lastInputEventForVSync);
1254                return true;
1255            case ContentViewGestureHandler.GESTURE_PINCH_END:
1256                nativePinchEnd(mNativeContentViewCore, timeMs);
1257                return true;
1258            default:
1259                return false;
1260        }
1261    }
1262
1263    public void setPinchGestureStateListener(PinchGestureStateListener pinchGestureStateListener) {
1264        mPinchGestureStateListener = pinchGestureStateListener;
1265    }
1266
1267    void updatePinchGestureStateListener(int gestureType) {
1268        if (mPinchGestureStateListener == null) return;
1269
1270        switch (gestureType) {
1271            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
1272                mPinchGestureStateListener.onPinchGestureStart();
1273                break;
1274            case ContentViewGestureHandler.GESTURE_PINCH_END:
1275                mPinchGestureStateListener.onPinchGestureEnd();
1276                break;
1277            default:
1278                break;
1279        }
1280    }
1281
1282    public interface JavaScriptCallback {
1283        void handleJavaScriptResult(String jsonResult);
1284    }
1285
1286    /**
1287     * Injects the passed Javascript code in the current page and evaluates it.
1288     * If a result is required, pass in a callback.
1289     * Used in automation tests.
1290     *
1291     * @param script The Javascript to execute.
1292     * @param callback The callback to be fired off when a result is ready. The script's
1293     *                 result will be json encoded and passed as the parameter, and the call
1294     *                 will be made on the main thread.
1295     *                 If no result is required, pass null.
1296     * @throws IllegalStateException If the ContentView has been destroyed.
1297     */
1298    public void evaluateJavaScript(
1299            String script, JavaScriptCallback callback) throws IllegalStateException {
1300        checkIsAlive();
1301        nativeEvaluateJavaScript(mNativeContentViewCore, script, callback);
1302    }
1303
1304    /**
1305     * This method should be called when the containing activity is paused.
1306     */
1307    public void onActivityPause() {
1308        TraceEvent.begin();
1309        hidePopupDialog();
1310        nativeOnHide(mNativeContentViewCore);
1311        setAccessibilityState(false);
1312        TraceEvent.end();
1313    }
1314
1315    /**
1316     * This method should be called when the containing activity is resumed.
1317     */
1318    public void onActivityResume() {
1319        nativeOnShow(mNativeContentViewCore);
1320        setAccessibilityState(true);
1321    }
1322
1323    /**
1324     * To be called when the ContentView is shown.
1325     */
1326    public void onShow() {
1327        nativeOnShow(mNativeContentViewCore);
1328        setAccessibilityState(true);
1329    }
1330
1331    /**
1332     * To be called when the ContentView is hidden.
1333     */
1334    public void onHide() {
1335        hidePopupDialog();
1336        setAccessibilityState(false);
1337        nativeOnHide(mNativeContentViewCore);
1338    }
1339
1340    /**
1341     * Return the ContentSettings object used to retrieve the settings for this
1342     * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1343     * @return A ContentSettings object that can be used to retrieve this
1344     *         ContentViewCore's settings.
1345     */
1346    public ContentSettings getContentSettings() {
1347        return mContentSettings;
1348    }
1349
1350    @Override
1351    public boolean didUIStealScroll(float x, float y) {
1352        return getContentViewClient().shouldOverrideScroll(
1353                x, y, computeHorizontalScrollOffset(), computeVerticalScrollOffset());
1354    }
1355
1356    @Override
1357    public boolean hasFixedPageScale() {
1358        return mRenderCoordinates.hasFixedPageScale();
1359    }
1360
1361    private void hidePopupDialog() {
1362        SelectPopupDialog.hide(this);
1363        hideHandles();
1364        hideSelectActionBar();
1365    }
1366
1367    void hideSelectActionBar() {
1368        if (mActionMode != null) {
1369            mActionMode.finish();
1370        }
1371    }
1372
1373    private void resetGestureDetectors() {
1374        mContentViewGestureHandler.resetGestureHandlers();
1375    }
1376
1377    /**
1378     * @see View#onAttachedToWindow()
1379     */
1380    @SuppressWarnings("javadoc")
1381    public void onAttachedToWindow() {
1382        mAttachedToWindow = true;
1383        if (mNativeContentViewCore != 0) {
1384            int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1385            if (pid > 0) {
1386                ChildProcessLauncher.bindAsHighPriority(pid);
1387            }
1388        }
1389        setAccessibilityState(true);
1390    }
1391
1392    /**
1393     * @see View#onDetachedFromWindow()
1394     */
1395    @SuppressWarnings("javadoc")
1396    public void onDetachedFromWindow() {
1397        mAttachedToWindow = false;
1398        if (mNativeContentViewCore != 0) {
1399            int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1400            if (pid > 0) {
1401                ChildProcessLauncher.unbindAsHighPriority(pid);
1402            }
1403        }
1404        setAccessibilityState(false);
1405        hidePopupDialog();
1406        mZoomControlsDelegate.dismissZoomPicker();
1407    }
1408
1409    /**
1410     * @see View#onVisibilityChanged(android.view.View, int)
1411     */
1412    public void onVisibilityChanged(View changedView, int visibility) {
1413      if (visibility != View.VISIBLE) {
1414          mZoomControlsDelegate.dismissZoomPicker();
1415      }
1416    }
1417
1418    /**
1419     * @see View#onCreateInputConnection(EditorInfo)
1420     */
1421    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1422        if (!mImeAdapter.hasTextInputType()) {
1423            // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1424            // is still used by the InputMethodService. Need to make sure the IME doesn't
1425            // enter fullscreen mode.
1426            outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1427        }
1428        mInputConnection =
1429                mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
1430        return mInputConnection;
1431    }
1432
1433    public Editable getEditableForTest() {
1434        return mInputConnection.getEditable();
1435    }
1436
1437    /**
1438     * @see View#onCheckIsTextEditor()
1439     */
1440    public boolean onCheckIsTextEditor() {
1441        return mImeAdapter.hasTextInputType();
1442    }
1443
1444    /**
1445     * @see View#onConfigurationChanged(Configuration)
1446     */
1447    @SuppressWarnings("javadoc")
1448    public void onConfigurationChanged(Configuration newConfig) {
1449        TraceEvent.begin();
1450
1451        if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1452            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1453                    ImeAdapter.getTextInputTypeNone(),
1454                    AdapterInputConnection.INVALID_SELECTION,
1455                    AdapterInputConnection.INVALID_SELECTION);
1456            InputMethodManager manager = (InputMethodManager)
1457                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1458            manager.restartInput(mContainerView);
1459        }
1460        mContainerViewInternals.super_onConfigurationChanged(newConfig);
1461        mNeedUpdateOrientationChanged = true;
1462        TraceEvent.end();
1463    }
1464
1465    /**
1466     * @see View#onSizeChanged(int, int, int, int)
1467     */
1468    @SuppressWarnings("javadoc")
1469    public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1470        if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1471
1472        mViewportWidthPix = wPix;
1473        mViewportHeightPix = hPix;
1474        if (mNativeContentViewCore != 0) {
1475            nativeWasResized(mNativeContentViewCore);
1476        }
1477
1478        updateAfterSizeChanged();
1479    }
1480
1481    /**
1482     * Called when the underlying surface the compositor draws to changes size.
1483     * This may be larger than the viewport size.
1484     */
1485    public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1486        if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1487
1488        mPhysicalBackingWidthPix = wPix;
1489        mPhysicalBackingHeightPix = hPix;
1490
1491        if (mNativeContentViewCore != 0) {
1492            nativeWasResized(mNativeContentViewCore);
1493        }
1494    }
1495
1496    /**
1497     * Called when the amount the surface is overdrawing off the bottom has changed.
1498     * @param overdrawHeightPix The overdraw height.
1499     */
1500    public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1501        if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1502
1503        mOverdrawBottomHeightPix = overdrawHeightPix;
1504
1505        if (mNativeContentViewCore != 0) {
1506            nativeWasResized(mNativeContentViewCore);
1507        }
1508    }
1509
1510    private void updateAfterSizeChanged() {
1511        mPopupZoomer.hide(false);
1512
1513        // Execute a delayed form focus operation because the OSK was brought
1514        // up earlier.
1515        if (!mFocusPreOSKViewportRect.isEmpty()) {
1516            Rect rect = new Rect();
1517            getContainerView().getWindowVisibleDisplayFrame(rect);
1518            if (!rect.equals(mFocusPreOSKViewportRect)) {
1519                scrollFocusedEditableNodeIntoView();
1520                mFocusPreOSKViewportRect.setEmpty();
1521            }
1522        } else if (mUnfocusOnNextSizeChanged) {
1523            undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1524            mUnfocusOnNextSizeChanged = false;
1525        }
1526
1527        if (mNeedUpdateOrientationChanged) {
1528            sendOrientationChangeEvent();
1529            mNeedUpdateOrientationChanged = false;
1530        }
1531    }
1532
1533    private void scrollFocusedEditableNodeIntoView() {
1534        if (mNativeContentViewCore != 0) {
1535            Runnable scrollTask = new Runnable() {
1536                @Override
1537                public void run() {
1538                    if (mNativeContentViewCore != 0) {
1539                        nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1540                    }
1541                }
1542            };
1543
1544            scrollTask.run();
1545
1546            // The native side keeps track of whether the zoom and scroll actually occurred. It is
1547            // more efficient to do it this way and sometimes fire an unnecessary message rather
1548            // than synchronize with the renderer and always have an additional message.
1549            mScrolledAndZoomedFocusedEditableNode = true;
1550        }
1551    }
1552
1553    private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) {
1554        // The only call to this function that matters is the first call after the
1555        // scrollFocusedEditableNodeIntoView function call.
1556        // If the first call to this function is a result of a back button press we want to undo the
1557        // preceding scroll. If the call is a result of some other action we don't want to perform
1558        // an undo.
1559        // All subsequent calls are ignored since only the scroll function sets
1560        // mScrolledAndZoomedFocusedEditableNode to true.
1561        if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed &&
1562                mNativeContentViewCore != 0) {
1563            Runnable scrollTask = new Runnable() {
1564                @Override
1565                public void run() {
1566                    if (mNativeContentViewCore != 0) {
1567                        nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1568                    }
1569                }
1570            };
1571
1572            scrollTask.run();
1573        }
1574        mScrolledAndZoomedFocusedEditableNode = false;
1575    }
1576
1577    public void onFocusChanged(boolean gainFocus) {
1578        if (!gainFocus) getContentViewClient().onImeStateChangeRequested(false);
1579        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1580    }
1581
1582    /**
1583     * @see View#onKeyUp(int, KeyEvent)
1584     */
1585    public boolean onKeyUp(int keyCode, KeyEvent event) {
1586        if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1587            mPopupZoomer.hide(true);
1588            return true;
1589        }
1590        return mContainerViewInternals.super_onKeyUp(keyCode, event);
1591    }
1592
1593    /**
1594     * @see View#dispatchKeyEventPreIme(KeyEvent)
1595     */
1596    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1597        try {
1598            TraceEvent.begin();
1599            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1600                mUnfocusOnNextSizeChanged = true;
1601            } else {
1602                undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1603            }
1604            return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1605        } finally {
1606            TraceEvent.end();
1607        }
1608    }
1609
1610    /**
1611     * @see View#dispatchKeyEvent(KeyEvent)
1612     */
1613    public boolean dispatchKeyEvent(KeyEvent event) {
1614        if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1615            return mContainerViewInternals.super_dispatchKeyEvent(event);
1616        }
1617
1618        if (mImeAdapter.dispatchKeyEvent(event)) return true;
1619
1620        return mContainerViewInternals.super_dispatchKeyEvent(event);
1621    }
1622
1623    /**
1624     * @see View#onHoverEvent(MotionEvent)
1625     * Mouse move events are sent on hover enter, hover move and hover exit.
1626     * They are sent on hover exit because sometimes it acts as both a hover
1627     * move and hover exit.
1628     */
1629    public boolean onHoverEvent(MotionEvent event) {
1630        TraceEvent.begin("onHoverEvent");
1631        mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1632        if (mNativeContentViewCore != 0) {
1633            nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1634                    event.getX(), event.getY());
1635        }
1636        TraceEvent.end("onHoverEvent");
1637        return true;
1638    }
1639
1640    /**
1641     * @see View#onGenericMotionEvent(MotionEvent)
1642     */
1643    public boolean onGenericMotionEvent(MotionEvent event) {
1644        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1645            switch (event.getAction()) {
1646                case MotionEvent.ACTION_SCROLL:
1647                    nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1648                            event.getX(), event.getY(),
1649                            event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1650
1651                    mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1652                    // Send a delayed onMouseMove event so that we end
1653                    // up hovering over the right position after the scroll.
1654                    final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1655                    mFakeMouseMoveRunnable = new Runnable() {
1656                          @Override
1657                          public void run() {
1658                              onHoverEvent(eventFakeMouseMove);
1659                          }
1660                    };
1661                    mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1662                    return true;
1663            }
1664        }
1665        return mContainerViewInternals.super_onGenericMotionEvent(event);
1666    }
1667
1668    /**
1669     * @see View#scrollBy(int, int)
1670     * Currently the ContentView scrolling happens in the native side. In
1671     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1672     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1673     * (0, 0). This is critical for drawing ContentView correctly.
1674     */
1675    public void scrollBy(int xPix, int yPix) {
1676        if (mNativeContentViewCore != 0) {
1677            nativeScrollBy(mNativeContentViewCore,
1678                    System.currentTimeMillis(), 0, 0, xPix, yPix, false);
1679        }
1680    }
1681
1682    /**
1683     * @see View#scrollTo(int, int)
1684     */
1685    public void scrollTo(int xPix, int yPix) {
1686        if (mNativeContentViewCore == 0) return;
1687        final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1688        final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1689        final float dxPix = xPix - xCurrentPix;
1690        final float dyPix = yPix - yCurrentPix;
1691        if (dxPix != 0 || dyPix != 0) {
1692            long time = System.currentTimeMillis();
1693            nativeScrollBegin(mNativeContentViewCore, time, xCurrentPix, yCurrentPix);
1694            nativeScrollBy(mNativeContentViewCore,
1695                    time, xCurrentPix, yCurrentPix, dxPix, dyPix, false);
1696            nativeScrollEnd(mNativeContentViewCore, time);
1697        }
1698    }
1699
1700    // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1701    //       see: b/6029133
1702    public int getNativeScrollXForTest() {
1703        return mRenderCoordinates.getScrollXPixInt();
1704    }
1705
1706    // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1707    //       see: b/6029133
1708    public int getNativeScrollYForTest() {
1709        return mRenderCoordinates.getScrollYPixInt();
1710    }
1711
1712    /**
1713     * @see View#computeHorizontalScrollExtent()
1714     */
1715    @SuppressWarnings("javadoc")
1716    public int computeHorizontalScrollExtent() {
1717        return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1718    }
1719
1720    /**
1721     * @see View#computeHorizontalScrollOffset()
1722     */
1723    @SuppressWarnings("javadoc")
1724    public int computeHorizontalScrollOffset() {
1725        return mRenderCoordinates.getScrollXPixInt();
1726    }
1727
1728    /**
1729     * @see View#computeHorizontalScrollRange()
1730     */
1731    @SuppressWarnings("javadoc")
1732    public int computeHorizontalScrollRange() {
1733        return mRenderCoordinates.getContentWidthPixInt();
1734    }
1735
1736    /**
1737     * @see View#computeVerticalScrollExtent()
1738     */
1739    @SuppressWarnings("javadoc")
1740    public int computeVerticalScrollExtent() {
1741        return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1742    }
1743
1744    /**
1745     * @see View#computeVerticalScrollOffset()
1746     */
1747    @SuppressWarnings("javadoc")
1748    public int computeVerticalScrollOffset() {
1749        return mRenderCoordinates.getScrollYPixInt();
1750    }
1751
1752    /**
1753     * @see View#computeVerticalScrollRange()
1754     */
1755    @SuppressWarnings("javadoc")
1756    public int computeVerticalScrollRange() {
1757        return mRenderCoordinates.getContentHeightPixInt();
1758    }
1759
1760    // End FrameLayout overrides.
1761
1762    /**
1763     * @see View#awakenScrollBars(int, boolean)
1764     */
1765    @SuppressWarnings("javadoc")
1766    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1767        // For the default implementation of ContentView which draws the scrollBars on the native
1768        // side, calling this function may get us into a bad state where we keep drawing the
1769        // scrollBars, so disable it by always returning false.
1770        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1771            return false;
1772        } else {
1773            return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1774        }
1775    }
1776
1777    @SuppressWarnings("unused")
1778    @CalledByNative
1779    private void onTabCrash() {
1780        getContentViewClient().onTabCrash();
1781    }
1782
1783    private void handleTapOrPress(
1784            long timeMs, float xPix, float yPix, int isLongPressOrTap, boolean showPress) {
1785        if (!mContainerView.isFocused()) mContainerView.requestFocus();
1786
1787        if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1788
1789        if (isLongPressOrTap == IS_LONG_PRESS) {
1790            getInsertionHandleController().allowAutomaticShowing();
1791            getSelectionHandleController().allowAutomaticShowing();
1792            if (mNativeContentViewCore != 0) {
1793                nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false);
1794            }
1795        } else if (isLongPressOrTap == IS_LONG_TAP) {
1796            getInsertionHandleController().allowAutomaticShowing();
1797            getSelectionHandleController().allowAutomaticShowing();
1798            if (mNativeContentViewCore != 0) {
1799                nativeLongTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1800            }
1801        } else {
1802            if (!showPress && mNativeContentViewCore != 0) {
1803                nativeShowPressState(mNativeContentViewCore, timeMs, xPix, yPix);
1804            }
1805            if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1806            if (mNativeContentViewCore != 0) {
1807                nativeSingleTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1808            }
1809        }
1810    }
1811
1812    public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1813        mZoomControlsDelegate = zoomControlsDelegate;
1814    }
1815
1816    public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1817        mZoomManager.updateMultiTouchSupport(supportsMultiTouchZoom);
1818    }
1819
1820    public void selectPopupMenuItems(int[] indices) {
1821        if (mNativeContentViewCore != 0) {
1822            nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1823        }
1824    }
1825
1826    /**
1827     * Get the screen orientation from the OS and push it to WebKit.
1828     *
1829     * TODO(husky): Add a hook for mock orientations.
1830     *
1831     * TODO(husky): Currently each new tab starts with an orientation of 0 until you actually
1832     * rotate the device. This is wrong if you actually started in landscape mode. To fix this, we
1833     * need to push the correct orientation, but only after WebKit's Frame object has been fully
1834     * initialized. Need to find a good time to do that. onPageFinished() would probably work but
1835     * it isn't implemented yet.
1836     */
1837    private void sendOrientationChangeEvent() {
1838        if (mNativeContentViewCore == 0) return;
1839
1840        WindowManager windowManager =
1841                (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
1842        switch (windowManager.getDefaultDisplay().getRotation()) {
1843            case Surface.ROTATION_90:
1844                nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
1845                break;
1846            case Surface.ROTATION_180:
1847                nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
1848                break;
1849            case Surface.ROTATION_270:
1850                nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
1851                break;
1852            case Surface.ROTATION_0:
1853                nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
1854                break;
1855            default:
1856                Log.w(TAG, "Unknown rotation!");
1857                break;
1858        }
1859    }
1860
1861    /**
1862     * Register the delegate to be used when content can not be handled by
1863     * the rendering engine, and should be downloaded instead. This will replace
1864     * the current delegate, if any.
1865     * @param delegate An implementation of ContentViewDownloadDelegate.
1866     */
1867    public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1868        mDownloadDelegate = delegate;
1869    }
1870
1871    // Called by DownloadController.
1872    ContentViewDownloadDelegate getDownloadDelegate() {
1873        return mDownloadDelegate;
1874    }
1875
1876    private SelectionHandleController getSelectionHandleController() {
1877        if (mSelectionHandleController == null) {
1878            mSelectionHandleController = new SelectionHandleController(getContainerView()) {
1879                @Override
1880                public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
1881                    if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
1882                        nativeSelectBetweenCoordinates(mNativeContentViewCore,
1883                                x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
1884                                x2, y2 - mRenderCoordinates.getContentOffsetYPix());
1885                    }
1886                }
1887
1888                @Override
1889                public void showHandles(int startDir, int endDir) {
1890                    super.showHandles(startDir, endDir);
1891                    showSelectActionBar();
1892                }
1893
1894            };
1895
1896            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1897        }
1898
1899        return mSelectionHandleController;
1900    }
1901
1902    private InsertionHandleController getInsertionHandleController() {
1903        if (mInsertionHandleController == null) {
1904            mInsertionHandleController = new InsertionHandleController(getContainerView()) {
1905                private static final int AVERAGE_LINE_HEIGHT = 14;
1906
1907                @Override
1908                public void setCursorPosition(int x, int y) {
1909                    if (mNativeContentViewCore != 0) {
1910                        nativeMoveCaret(mNativeContentViewCore,
1911                                x, y - mRenderCoordinates.getContentOffsetYPix());
1912                    }
1913                }
1914
1915                @Override
1916                public void paste() {
1917                    mImeAdapter.paste();
1918                    hideHandles();
1919                }
1920
1921                @Override
1922                public int getLineHeight() {
1923                    return (int) Math.ceil(
1924                            mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
1925                }
1926
1927                @Override
1928                public void showHandle() {
1929                    super.showHandle();
1930                }
1931            };
1932
1933            mInsertionHandleController.hideAndDisallowAutomaticShowing();
1934        }
1935
1936        return mInsertionHandleController;
1937    }
1938
1939    public InsertionHandleController getInsertionHandleControllerForTest() {
1940        return mInsertionHandleController;
1941    }
1942
1943    private void updateHandleScreenPositions() {
1944        if (isSelectionHandleShowing()) {
1945            mSelectionHandleController.setStartHandlePosition(
1946                    mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
1947            mSelectionHandleController.setEndHandlePosition(
1948                    mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
1949        }
1950
1951        if (isInsertionHandleShowing()) {
1952            mInsertionHandleController.setHandlePosition(
1953                    mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
1954        }
1955    }
1956
1957    private void hideHandles() {
1958        if (mSelectionHandleController != null) {
1959            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1960        }
1961        if (mInsertionHandleController != null) {
1962            mInsertionHandleController.hideAndDisallowAutomaticShowing();
1963        }
1964    }
1965
1966    private void showSelectActionBar() {
1967        if (mActionMode != null) {
1968            mActionMode.invalidate();
1969            return;
1970        }
1971
1972        // Start a new action mode with a SelectActionModeCallback.
1973        SelectActionModeCallback.ActionHandler actionHandler =
1974                new SelectActionModeCallback.ActionHandler() {
1975            @Override
1976            public boolean selectAll() {
1977                return mImeAdapter.selectAll();
1978            }
1979
1980            @Override
1981            public boolean cut() {
1982                return mImeAdapter.cut();
1983            }
1984
1985            @Override
1986            public boolean copy() {
1987                return mImeAdapter.copy();
1988            }
1989
1990            @Override
1991            public boolean paste() {
1992                return mImeAdapter.paste();
1993            }
1994
1995            @Override
1996            public boolean isSelectionEditable() {
1997                return mSelectionEditable;
1998            }
1999
2000            @Override
2001            public String getSelectedText() {
2002                return ContentViewCore.this.getSelectedText();
2003            }
2004
2005            @Override
2006            public void onDestroyActionMode() {
2007                mActionMode = null;
2008                if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2009                getContentViewClient().onContextualActionBarHidden();
2010            }
2011        };
2012        mActionMode = null;
2013        // On ICS, startActionMode throws an NPE when getParent() is null.
2014        if (mContainerView.getParent() != null) {
2015            mActionMode = mContainerView.startActionMode(
2016                    getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2017                            nativeIsIncognito(mNativeContentViewCore)));
2018        }
2019        mUnselectAllOnActionModeDismiss = true;
2020        if (mActionMode == null) {
2021            // There is no ActionMode, so remove the selection.
2022            mImeAdapter.unselect();
2023        } else {
2024            getContentViewClient().onContextualActionBarShown();
2025        }
2026    }
2027
2028    public boolean getUseDesktopUserAgent() {
2029        if (mNativeContentViewCore != 0) {
2030            return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2031        }
2032        return false;
2033    }
2034
2035    /**
2036     * Set whether or not we're using a desktop user agent for the currently loaded page.
2037     * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
2038     * @param reloadOnChange Reload the page if the UA has changed.
2039     */
2040    public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2041        if (mNativeContentViewCore != 0) {
2042            nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2043        }
2044    }
2045
2046    public void clearSslPreferences() {
2047        nativeClearSslPreferences(mNativeContentViewCore);
2048    }
2049
2050    /**
2051     * @return Whether the native ContentView has crashed.
2052     */
2053    public boolean isCrashed() {
2054        if (mNativeContentViewCore == 0) return false;
2055        return nativeCrashed(mNativeContentViewCore);
2056    }
2057
2058    private boolean isSelectionHandleShowing() {
2059        return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2060    }
2061
2062    private boolean isInsertionHandleShowing() {
2063        return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2064    }
2065
2066    private void updateTextHandlesForGesture(int type) {
2067        switch(type) {
2068            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
2069            case ContentViewGestureHandler.GESTURE_SCROLL_START:
2070            case ContentViewGestureHandler.GESTURE_FLING_START:
2071            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
2072                temporarilyHideTextHandles();
2073                break;
2074
2075            default:
2076                break;
2077        }
2078    }
2079
2080    // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2081    // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2082    private void temporarilyHideTextHandles() {
2083        if (isSelectionHandleShowing()) {
2084            mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2085        }
2086        if (isInsertionHandleShowing()) {
2087            mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2088        }
2089        scheduleTextHandleFadeIn();
2090    }
2091
2092    // Cancels any pending fade in and schedules a new one.
2093    private void scheduleTextHandleFadeIn() {
2094        if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2095
2096        if (mDeferredHandleFadeInRunnable == null) {
2097            mDeferredHandleFadeInRunnable = new Runnable() {
2098                @Override
2099                public void run() {
2100                    if (mContentViewGestureHandler.isNativeScrolling() ||
2101                            mContentViewGestureHandler.isNativePinching()) {
2102                        // Delay fade in until no longer scrolling or pinching.
2103                        scheduleTextHandleFadeIn();
2104                    } else {
2105                        if (isSelectionHandleShowing()) {
2106                            mSelectionHandleController.beginHandleFadeIn();
2107                        }
2108                        if (isInsertionHandleShowing()) {
2109                            mInsertionHandleController.beginHandleFadeIn();
2110                        }
2111                    }
2112                }
2113            };
2114        }
2115
2116        mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2117        mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2118    }
2119
2120    /**
2121     * Shows the IME if the focused widget could accept text input.
2122     */
2123    public void showImeIfNeeded() {
2124        if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2125    }
2126
2127    @SuppressWarnings("unused")
2128    @CalledByNative
2129    private void updateFrameInfo(
2130            float scrollOffsetX, float scrollOffsetY,
2131            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2132            float contentWidth, float contentHeight,
2133            float viewportWidth, float viewportHeight,
2134            float controlsOffsetYCss, float contentOffsetYCss,
2135            float overdrawBottomHeightCss) {
2136        TraceEvent.instant("ContentViewCore:updateFrameInfo");
2137        // Adjust contentWidth/Height to be always at least as big as the actual viewport
2138        // (as set by onSizeChanged or setInitialViewportSize).
2139        contentWidth = Math.max(contentWidth,
2140                mRenderCoordinates.fromPixToLocalCss(mViewportWidthPix));
2141        contentHeight = Math.max(contentHeight,
2142                mRenderCoordinates.fromPixToLocalCss(mViewportHeightPix));
2143
2144        final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2145
2146        final boolean contentSizeChanged =
2147                contentWidth != mRenderCoordinates.getContentWidthCss()
2148                || contentHeight != mRenderCoordinates.getContentHeightCss();
2149        final boolean scaleLimitsChanged =
2150                minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2151                || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2152        final boolean pageScaleChanged =
2153                pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2154        final boolean scrollChanged =
2155                pageScaleChanged
2156                || scrollOffsetX != mRenderCoordinates.getScrollX()
2157                || scrollOffsetY != mRenderCoordinates.getScrollY();
2158        final boolean contentOffsetChanged =
2159                contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2160
2161        final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2162        final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2163        final boolean needTemporarilyHideHandles = scrollChanged;
2164
2165        if (needHidePopupZoomer) mPopupZoomer.hide(true);
2166
2167        if (scrollChanged) {
2168            mContainerViewInternals.onScrollChanged(
2169                    (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2170                    (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2171                    (int) mRenderCoordinates.getScrollXPix(),
2172                    (int) mRenderCoordinates.getScrollYPix());
2173        }
2174
2175        if (pageScaleChanged) {
2176            // This function should be called back from native as soon
2177            // as the scroll is applied to the backbuffer.  We should only
2178            // update mNativeScrollX/Y here for consistency.
2179            getContentViewClient().onScaleChanged(
2180                    mRenderCoordinates.getPageScaleFactor(), pageScaleFactor);
2181        }
2182
2183        mRenderCoordinates.updateFrameInfo(
2184                scrollOffsetX, scrollOffsetY,
2185                contentWidth, contentHeight,
2186                viewportWidth, viewportHeight,
2187                pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2188                contentOffsetYPix);
2189
2190        if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2191        if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2192        if (contentOffsetChanged) updateHandleScreenPositions();
2193
2194        // Update offsets for fullscreen.
2195        final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2196        final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2197        final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2198        getContentViewClient().onOffsetsForFullscreenChanged(
2199                controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2200
2201        mPendingRendererFrame = true;
2202    }
2203
2204    @SuppressWarnings("unused")
2205    @CalledByNative
2206    private void updateImeAdapter(int nativeImeAdapterAndroid, int textInputType,
2207            String text, int selectionStart, int selectionEnd,
2208            int compositionStart, int compositionEnd, boolean showImeIfNeeded) {
2209        TraceEvent.begin();
2210        mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2211
2212        if (mActionMode != null) mActionMode.invalidate();
2213
2214        mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
2215                selectionStart, selectionEnd, showImeIfNeeded);
2216
2217        if (mInputConnection != null) {
2218            mInputConnection.setEditableText(text, selectionStart, selectionEnd,
2219                    compositionStart, compositionEnd);
2220        }
2221        TraceEvent.end();
2222    }
2223
2224    @SuppressWarnings("unused")
2225    @CalledByNative
2226    private void processImeBatchStateAck(boolean isBegin) {
2227        if (mInputConnection == null) return;
2228        mInputConnection.setIgnoreTextInputStateUpdates(isBegin);
2229    }
2230
2231    @SuppressWarnings("unused")
2232    @CalledByNative
2233    private void setTitle(String title) {
2234        getContentViewClient().onUpdateTitle(title);
2235    }
2236
2237    /**
2238     * Called (from native) when the <select> popup needs to be shown.
2239     * @param items           Items to show.
2240     * @param enabled         POPUP_ITEM_TYPEs for items.
2241     * @param multiple        Whether the popup menu should support multi-select.
2242     * @param selectedIndices Indices of selected items.
2243     */
2244    @SuppressWarnings("unused")
2245    @CalledByNative
2246    private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
2247            int[] selectedIndices) {
2248        SelectPopupDialog.show(this, items, enabled, multiple, selectedIndices);
2249    }
2250
2251    @SuppressWarnings("unused")
2252    @CalledByNative
2253    private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2254        mPopupZoomer.setBitmap(zoomedBitmap);
2255        mPopupZoomer.show(targetRect);
2256    }
2257
2258    @SuppressWarnings("unused")
2259    @CalledByNative
2260    private SmoothScroller createSmoothScroller(boolean scrollDown, int mouseEventX,
2261            int mouseEventY) {
2262        return new SmoothScroller(this, scrollDown, mouseEventX, mouseEventY);
2263    }
2264
2265    @SuppressWarnings("unused")
2266    @CalledByNative
2267    private void onSelectionChanged(String text) {
2268        mLastSelectedText = text;
2269    }
2270
2271    @SuppressWarnings("unused")
2272    @CalledByNative
2273    private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2274            int focusDir, boolean isAnchorFirst) {
2275        // All coordinates are in DIP.
2276        int x1 = anchorRectDip.left;
2277        int y1 = anchorRectDip.bottom;
2278        int x2 = focusRectDip.left;
2279        int y2 = focusRectDip.bottom;
2280
2281        if (x1 != x2 || y1 != y2 ||
2282                (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2283            if (mInsertionHandleController != null) {
2284                mInsertionHandleController.hide();
2285            }
2286            if (isAnchorFirst) {
2287                mStartHandlePoint.setLocalDip(x1, y1);
2288                mEndHandlePoint.setLocalDip(x2, y2);
2289            } else {
2290                mStartHandlePoint.setLocalDip(x2, y2);
2291                mEndHandlePoint.setLocalDip(x1, y1);
2292            }
2293
2294            getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2295            updateHandleScreenPositions();
2296            mHasSelection = true;
2297        } else {
2298            mUnselectAllOnActionModeDismiss = false;
2299            hideSelectActionBar();
2300            if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2301                // Selection is a caret, and a text field is focused.
2302                if (mSelectionHandleController != null) {
2303                    mSelectionHandleController.hide();
2304                }
2305                mInsertionHandlePoint.setLocalDip(x1, y1);
2306
2307                getInsertionHandleController().onCursorPositionChanged();
2308                updateHandleScreenPositions();
2309                InputMethodManager manager = (InputMethodManager)
2310                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
2311                if (manager.isWatchingCursor(mContainerView)) {
2312                    final int xPix = (int) mInsertionHandlePoint.getXPix();
2313                    final int yPix = (int) mInsertionHandlePoint.getYPix();
2314                    manager.updateCursor(mContainerView, xPix, yPix, xPix, yPix);
2315                }
2316            } else {
2317                // Deselection
2318                if (mSelectionHandleController != null) {
2319                    mSelectionHandleController.hideAndDisallowAutomaticShowing();
2320                }
2321                if (mInsertionHandleController != null) {
2322                    mInsertionHandleController.hideAndDisallowAutomaticShowing();
2323                }
2324            }
2325            mHasSelection = false;
2326        }
2327    }
2328
2329    @SuppressWarnings("unused")
2330    @CalledByNative
2331    private static void onEvaluateJavaScriptResult(
2332            String jsonResult, JavaScriptCallback callback) {
2333        callback.handleJavaScriptResult(jsonResult);
2334    }
2335
2336    @SuppressWarnings("unused")
2337    @CalledByNative
2338    private void showPastePopup(int xDip, int yDip) {
2339        mInsertionHandlePoint.setLocalDip(xDip, yDip);
2340        getInsertionHandleController().showHandle();
2341        updateHandleScreenPositions();
2342        getInsertionHandleController().showHandleWithPastePopup();
2343    }
2344
2345    @SuppressWarnings("unused")
2346    @CalledByNative
2347    private void onRenderProcessSwap(int oldPid, int newPid) {
2348        if (mAttachedToWindow && oldPid != newPid) {
2349            if (oldPid > 0) {
2350                ChildProcessLauncher.unbindAsHighPriority(oldPid);
2351            }
2352            if (newPid > 0) {
2353                ChildProcessLauncher.bindAsHighPriority(newPid);
2354            }
2355        }
2356    }
2357
2358    @SuppressWarnings("unused")
2359    @CalledByNative
2360    private void onWebContentsConnected() {
2361        if (mImeAdapter != null &&
2362                !mImeAdapter.isNativeImeAdapterAttached() && mNativeContentViewCore != 0) {
2363            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2364        }
2365    }
2366
2367    @SuppressWarnings("unused")
2368    @CalledByNative
2369    private void onWebContentsSwapped() {
2370        if (mImeAdapter != null && mNativeContentViewCore != 0) {
2371            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2372        }
2373    }
2374
2375    /**
2376     * @return Whether a reload happens when this ContentView is activated.
2377     */
2378    public boolean needsReload() {
2379        return mNativeContentViewCore != 0 && nativeNeedsReload(mNativeContentViewCore);
2380    }
2381
2382    /**
2383     * @see View#hasFocus()
2384     */
2385    @CalledByNative
2386    public boolean hasFocus() {
2387        return mContainerView.hasFocus();
2388    }
2389
2390    /**
2391     * Checks whether the ContentViewCore can be zoomed in.
2392     *
2393     * @return True if the ContentViewCore can be zoomed in.
2394     */
2395    // This method uses the term 'zoom' for legacy reasons, but relates
2396    // to what chrome calls the 'page scale factor'.
2397    public boolean canZoomIn() {
2398        final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2399                - mRenderCoordinates.getPageScaleFactor();
2400        return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2401    }
2402
2403    /**
2404     * Checks whether the ContentViewCore can be zoomed out.
2405     *
2406     * @return True if the ContentViewCore can be zoomed out.
2407     */
2408    // This method uses the term 'zoom' for legacy reasons, but relates
2409    // to what chrome calls the 'page scale factor'.
2410    public boolean canZoomOut() {
2411        final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2412                - mRenderCoordinates.getMinPageScaleFactor();
2413        return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2414    }
2415
2416    /**
2417     * Zooms in the ContentViewCore by 25% (or less if that would result in
2418     * zooming in more than possible).
2419     *
2420     * @return True if there was a zoom change, false otherwise.
2421     */
2422    // This method uses the term 'zoom' for legacy reasons, but relates
2423    // to what chrome calls the 'page scale factor'.
2424    public boolean zoomIn() {
2425        if (!canZoomIn()) {
2426            return false;
2427        }
2428        return zoomByDelta(1.25f);
2429    }
2430
2431    /**
2432     * Zooms out the ContentViewCore by 20% (or less if that would result in
2433     * zooming out more than possible).
2434     *
2435     * @return True if there was a zoom change, false otherwise.
2436     */
2437    // This method uses the term 'zoom' for legacy reasons, but relates
2438    // to what chrome calls the 'page scale factor'.
2439    public boolean zoomOut() {
2440        if (!canZoomOut()) {
2441            return false;
2442        }
2443        return zoomByDelta(0.8f);
2444    }
2445
2446    /**
2447     * Resets the zoom factor of the ContentViewCore.
2448     *
2449     * @return True if there was a zoom change, false otherwise.
2450     */
2451    // This method uses the term 'zoom' for legacy reasons, but relates
2452    // to what chrome calls the 'page scale factor'.
2453    public boolean zoomReset() {
2454        // The page scale factor is initialized to mNativeMinimumScale when
2455        // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2456        if (!canZoomOut()) return false;
2457        return zoomByDelta(
2458                mRenderCoordinates.getMinPageScaleFactor()
2459                        / mRenderCoordinates.getPageScaleFactor());
2460    }
2461
2462    private boolean zoomByDelta(float delta) {
2463        if (mNativeContentViewCore == 0) {
2464            return false;
2465        }
2466
2467        long timeMs = System.currentTimeMillis();
2468        int xPix = getViewportWidthPix() / 2;
2469        int yPix = getViewportHeightPix() / 2;
2470
2471        getContentViewGestureHandler().pinchBegin(timeMs, xPix, yPix);
2472        getContentViewGestureHandler().pinchBy(timeMs, xPix, yPix, delta);
2473        getContentViewGestureHandler().pinchEnd(timeMs);
2474
2475        return true;
2476    }
2477
2478    /**
2479     * Invokes the graphical zoom picker widget for this ContentView.
2480     */
2481    @Override
2482    public void invokeZoomPicker() {
2483        mZoomControlsDelegate.invokeZoomPicker();
2484    }
2485
2486    /**
2487     * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2488     * and automatically pass in {@link JavascriptInterface} as the required annotation.
2489     *
2490     * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2491     *               values are ignored.
2492     * @param name   The name used to expose the instance in JavaScript.
2493     */
2494    public void addJavascriptInterface(Object object, String name) {
2495        addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2496    }
2497
2498    /**
2499     * This method injects the supplied Java object into the ContentViewCore.
2500     * The object is injected into the JavaScript context of the main frame,
2501     * using the supplied name. This allows the Java object to be accessed from
2502     * JavaScript. Note that that injected objects will not appear in
2503     * JavaScript until the page is next (re)loaded. For example:
2504     * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2505     * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2506     * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2507     * <p><strong>IMPORTANT:</strong>
2508     * <ul>
2509     * <li> addJavascriptInterface() can be used to allow JavaScript to control
2510     * the host application. This is a powerful feature, but also presents a
2511     * security risk. Use of this method in a ContentViewCore containing
2512     * untrusted content could allow an attacker to manipulate the host
2513     * application in unintended ways, executing Java code with the permissions
2514     * of the host application. Use extreme care when using this method in a
2515     * ContentViewCore which could contain untrusted content. Particular care
2516     * should be taken to avoid unintentional access to inherited methods, such
2517     * as {@link Object#getClass()}. To prevent access to inherited methods,
2518     * pass an annotation for {@code requiredAnnotation}.  This will ensure
2519     * that only methods with {@code requiredAnnotation} are exposed to the
2520     * Javascript layer.  {@code requiredAnnotation} will be passed to all
2521     * subsequently injected Java objects if any methods return an object.  This
2522     * means the same restrictions (or lack thereof) will apply.  Alternatively,
2523     * {@link #addJavascriptInterface(Object, String)} can be called, which
2524     * automatically uses the {@link JavascriptInterface} annotation.
2525     * <li> JavaScript interacts with Java objects on a private, background
2526     * thread of the ContentViewCore. Care is therefore required to maintain
2527     * thread safety.</li>
2528     * </ul></p>
2529     *
2530     * @param object             The Java object to inject into the
2531     *                           ContentViewCore's JavaScript context. Null
2532     *                           values are ignored.
2533     * @param name               The name used to expose the instance in
2534     *                           JavaScript.
2535     * @param requiredAnnotation Restrict exposed methods to ones with this
2536     *                           annotation.  If {@code null} all methods are
2537     *                           exposed.
2538     *
2539     */
2540    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2541            Class<? extends Annotation> requiredAnnotation) {
2542        if (mNativeContentViewCore != 0 && object != null) {
2543            mJavaScriptInterfaces.put(name, object);
2544            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
2545                    mRetainedJavaScriptObjects);
2546        }
2547    }
2548
2549    /**
2550     * Removes a previously added JavaScript interface with the given name.
2551     *
2552     * @param name The name of the interface to remove.
2553     */
2554    public void removeJavascriptInterface(String name) {
2555        mJavaScriptInterfaces.remove(name);
2556        if (mNativeContentViewCore != 0) {
2557            nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2558        }
2559    }
2560
2561    /**
2562     * Return the current scale of the ContentView.
2563     * @return The current page scale factor.
2564     */
2565    public float getScale() {
2566        return mRenderCoordinates.getPageScaleFactor();
2567    }
2568
2569    /**
2570     * If the view is ready to draw contents to the screen. In hardware mode,
2571     * the initialization of the surface texture may not occur until after the
2572     * view has been added to the layout. This method will return {@code true}
2573     * once the texture is actually ready.
2574     */
2575    public boolean isReady() {
2576        return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2577    }
2578
2579    @CalledByNative
2580    private void startContentIntent(String contentUrl) {
2581        getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2582    }
2583
2584    /**
2585     * Determines whether or not this ContentViewCore can handle this accessibility action.
2586     * @param action The action to perform.
2587     * @return Whether or not this action is supported.
2588     */
2589    public boolean supportsAccessibilityAction(int action) {
2590        return mAccessibilityInjector.supportsAccessibilityAction(action);
2591    }
2592
2593    /**
2594     * Attempts to perform an accessibility action on the web content.  If the accessibility action
2595     * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2596     * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2597     * Otherwise the return value from this method should be used.
2598     * @param action The action to perform.
2599     * @param arguments Optional action arguments.
2600     * @return Whether the action was performed or {@code null} if the call should be delegated to
2601     *         the super {@link View} class.
2602     */
2603    public boolean performAccessibilityAction(int action, Bundle arguments) {
2604        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2605            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2606        }
2607
2608        return false;
2609    }
2610
2611    /**
2612     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2613     */
2614    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2615        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2616    }
2617
2618    /**
2619     * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2620     */
2621    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2622        event.setClassName(this.getClass().getName());
2623
2624        // Identify where the top-left of the screen currently points to.
2625        event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2626        event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2627
2628        // The maximum scroll values are determined by taking the content dimensions and
2629        // subtracting off the actual dimensions of the ChromeView.
2630        int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2631        int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2632        event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2633
2634        // Setting the maximum scroll values requires API level 15 or higher.
2635        final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2636        if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2637            event.setMaxScrollX(maxScrollXPix);
2638            event.setMaxScrollY(maxScrollYPix);
2639        }
2640    }
2641
2642    /**
2643     * Returns whether or not accessibility injection is being used.
2644     */
2645    public boolean isInjectingAccessibilityScript() {
2646        return mAccessibilityInjector.accessibilityIsAvailable();
2647    }
2648
2649    /**
2650     * Enable or disable accessibility features.
2651     */
2652    public void setAccessibilityState(boolean state) {
2653        mAccessibilityInjector.setScriptEnabled(state);
2654    }
2655
2656    /**
2657     * Stop any TTS notifications that are currently going on.
2658     */
2659    public void stopCurrentAccessibilityNotifications() {
2660        mAccessibilityInjector.onPageLostFocus();
2661    }
2662
2663    /**
2664     * Inform WebKit that Fullscreen mode has been exited by the user.
2665     */
2666    public void exitFullscreen() {
2667        nativeExitFullscreen(mNativeContentViewCore);
2668    }
2669
2670    /**
2671     * Changes whether hiding the top controls is enabled.
2672     *
2673     * @param enableHiding Whether hiding the top controls should be enabled or not.
2674     * @param enableShowing Whether showing the top controls should be enabled or not.
2675     * @param animate Whether the transition should be animated or not.
2676     */
2677    public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2678            boolean animate) {
2679        nativeUpdateTopControlsState(mNativeContentViewCore, enableHiding, enableShowing, animate);
2680    }
2681
2682    /**
2683     * @See android.webkit.WebView#pageDown(boolean)
2684     */
2685    public boolean pageDown(boolean bottom) {
2686        final int maxVerticalScrollPix = mRenderCoordinates.getMaxVerticalScrollPixInt();
2687        if (computeVerticalScrollOffset() >= maxVerticalScrollPix) {
2688            // We seem to already be at the bottom of the page, so no scrolling will occur.
2689            return false;
2690        }
2691
2692        if (bottom) {
2693            scrollTo(computeHorizontalScrollOffset(), maxVerticalScrollPix);
2694        } else {
2695            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN));
2696            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_DOWN));
2697        }
2698        return true;
2699    }
2700
2701    /**
2702     * @See android.webkit.WebView#pageUp(boolean)
2703     */
2704    public boolean pageUp(boolean top) {
2705        if (computeVerticalScrollOffset() == 0) {
2706            // We seem to already be at the top of the page, so no scrolling will occur.
2707            return false;
2708        }
2709
2710        if (top) {
2711            scrollTo(computeHorizontalScrollOffset(), 0);
2712        } else {
2713            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP));
2714            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_UP));
2715        }
2716        return true;
2717    }
2718
2719    /**
2720     * Callback factory method for nativeGetNavigationHistory().
2721     */
2722    @CalledByNative
2723    private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2724            String originalUrl, String title, Bitmap favicon) {
2725        NavigationEntry entry = new NavigationEntry(
2726                index, url, virtualUrl, originalUrl, title, favicon);
2727        ((NavigationHistory) history).addEntry(entry);
2728    }
2729
2730    /**
2731     * Get a copy of the navigation history of the view.
2732     */
2733    public NavigationHistory getNavigationHistory() {
2734        NavigationHistory history = new NavigationHistory();
2735        int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2736        history.setCurrentEntryIndex(currentIndex);
2737        return history;
2738    }
2739
2740    @Override
2741    public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2742        NavigationHistory history = new NavigationHistory();
2743        nativeGetDirectedNavigationHistory(mNativeContentViewCore, history, isForward, itemLimit);
2744        return history;
2745    }
2746
2747    /**
2748     * @return The original request URL for the current navigation entry, or null if there is no
2749     *         current entry.
2750     */
2751    public String getOriginalUrlForActiveNavigationEntry() {
2752        return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2753    }
2754
2755    /**
2756     * @return The cached copy of render positions and scales.
2757     */
2758    public RenderCoordinates getRenderCoordinates() {
2759        return mRenderCoordinates;
2760    }
2761
2762    @CalledByNative
2763    private static Rect createRect(int x, int y, int right, int bottom) {
2764        return new Rect(x, y, right, bottom);
2765    }
2766
2767    public void attachExternalVideoSurface(int playerId, Surface surface) {
2768        if (mNativeContentViewCore != 0) {
2769            nativeAttachExternalVideoSurface(mNativeContentViewCore, playerId, surface);
2770        }
2771    }
2772
2773    public void detachExternalVideoSurface(int playerId) {
2774        if (mNativeContentViewCore != 0) {
2775            nativeDetachExternalVideoSurface(mNativeContentViewCore, playerId);
2776        }
2777    }
2778
2779    private boolean onAnimate(long frameTimeMicros) {
2780        if (mNativeContentViewCore == 0) return false;
2781        return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
2782    }
2783
2784    private void animateIfNecessary(long frameTimeMicros) {
2785        if (mNeedAnimate) {
2786            mNeedAnimate = onAnimate(frameTimeMicros);
2787            if (!mNeedAnimate) setVSyncNotificationEnabled(false);
2788        }
2789    }
2790
2791    @CalledByNative
2792    private void notifyExternalSurface(
2793            int playerId, boolean isRequest, float x, float y, float width, float height) {
2794        RenderCoordinates.NormalizedPoint topLeft = mRenderCoordinates.createNormalizedPoint();
2795        RenderCoordinates.NormalizedPoint bottomRight = mRenderCoordinates.createNormalizedPoint();
2796        topLeft.setLocalDip(x * getScale(), y * getScale());
2797        bottomRight.setLocalDip((x + width) * getScale(), (y + height) * getScale());
2798
2799        if (isRequest) getContentViewClient().onExternalVideoSurfaceRequested(playerId);
2800        getContentViewClient().onGeometryChanged(
2801                playerId,
2802                topLeft.getXPix(),
2803                topLeft.getYPix(),
2804                bottomRight.getXPix() - topLeft.getXPix(),
2805                bottomRight.getYPix() - topLeft.getYPix());
2806    }
2807
2808    private native int nativeInit(boolean hardwareAccelerated, int webContentsPtr,
2809            int viewAndroidPtr, int windowAndroidPtr);
2810
2811    private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl);
2812
2813    private native void nativeLoadUrl(
2814            int nativeContentViewCoreImpl,
2815            String url,
2816            int loadUrlType,
2817            int transitionType,
2818            int uaOverrideOption,
2819            String extraHeaders,
2820            byte[] postData,
2821            String baseUrlForDataUrl,
2822            String virtualUrlForDataUrl,
2823            boolean canLoadLocalResources);
2824
2825    private native String nativeGetURL(int nativeContentViewCoreImpl);
2826
2827    private native String nativeGetTitle(int nativeContentViewCoreImpl);
2828
2829    private native void nativeShowInterstitialPage(
2830            int nativeContentViewCoreImpl, String url, int nativeInterstitialPageDelegateAndroid);
2831    private native boolean nativeIsShowingInterstitialPage(int nativeContentViewCoreImpl);
2832
2833    private native boolean nativeIsIncognito(int nativeContentViewCoreImpl);
2834
2835    // Returns true if the native side crashed so that java side can draw a sad tab.
2836    private native boolean nativeCrashed(int nativeContentViewCoreImpl);
2837
2838    private native void nativeSetFocus(int nativeContentViewCoreImpl, boolean focused);
2839
2840    private native void nativeSendOrientationChangeEvent(
2841            int nativeContentViewCoreImpl, int orientation);
2842
2843    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
2844    private native boolean nativeSendTouchEvent(
2845            int nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts);
2846
2847    private native int nativeSendMouseMoveEvent(
2848            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2849
2850    private native int nativeSendMouseWheelEvent(
2851            int nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
2852
2853    private native void nativeScrollBegin(
2854            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2855
2856    private native void nativeScrollEnd(int nativeContentViewCoreImpl, long timeMs);
2857
2858    private native void nativeScrollBy(
2859            int nativeContentViewCoreImpl, long timeMs, float x, float y,
2860            float deltaX, float deltaY, boolean lastInputEventForVSync);
2861
2862    private native void nativeFlingStart(
2863            int nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
2864
2865    private native void nativeFlingCancel(int nativeContentViewCoreImpl, long timeMs);
2866
2867    private native void nativeSingleTap(
2868            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
2869
2870    private native void nativeSingleTapUnconfirmed(
2871            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2872
2873    private native void nativeShowPressState(
2874            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2875
2876    private native void nativeShowPressCancel(
2877            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2878
2879    private native void nativeDoubleTap(
2880            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2881
2882    private native void nativeLongPress(
2883            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
2884
2885    private native void nativeLongTap(
2886            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
2887
2888    private native void nativePinchBegin(
2889            int nativeContentViewCoreImpl, long timeMs, float x, float y);
2890
2891    private native void nativePinchEnd(int nativeContentViewCoreImpl, long timeMs);
2892
2893    private native void nativePinchBy(int nativeContentViewCoreImpl, long timeMs,
2894            float anchorX, float anchorY, float deltaScale, boolean lastInputEventForVSync);
2895
2896    private native void nativeSelectBetweenCoordinates(
2897            int nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
2898
2899    private native void nativeMoveCaret(int nativeContentViewCoreImpl, float x, float y);
2900
2901    private native boolean nativeCanGoBack(int nativeContentViewCoreImpl);
2902    private native boolean nativeCanGoForward(int nativeContentViewCoreImpl);
2903    private native boolean nativeCanGoToOffset(int nativeContentViewCoreImpl, int offset);
2904    private native void nativeGoBack(int nativeContentViewCoreImpl);
2905    private native void nativeGoForward(int nativeContentViewCoreImpl);
2906    private native void nativeGoToOffset(int nativeContentViewCoreImpl, int offset);
2907    private native void nativeGoToNavigationIndex(int nativeContentViewCoreImpl, int index);
2908
2909    private native void nativeStopLoading(int nativeContentViewCoreImpl);
2910
2911    private native void nativeReload(int nativeContentViewCoreImpl);
2912
2913    private native void nativeCancelPendingReload(int nativeContentViewCoreImpl);
2914
2915    private native void nativeContinuePendingReload(int nativeContentViewCoreImpl);
2916
2917    private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices);
2918
2919    private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
2920    private native void nativeUndoScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
2921    private native boolean nativeNeedsReload(int nativeContentViewCoreImpl);
2922
2923    private native void nativeClearHistory(int nativeContentViewCoreImpl);
2924
2925    private native void nativeEvaluateJavaScript(int nativeContentViewCoreImpl,
2926            String script, JavaScriptCallback callback);
2927
2928    private native int nativeGetNativeImeAdapter(int nativeContentViewCoreImpl);
2929
2930    private native int nativeGetCurrentRenderProcessId(int nativeContentViewCoreImpl);
2931
2932    private native int nativeGetBackgroundColor(int nativeContentViewCoreImpl);
2933
2934    private native void nativeSetBackgroundColor(int nativeContentViewCoreImpl, int color);
2935
2936    private native void nativeOnShow(int nativeContentViewCoreImpl);
2937    private native void nativeOnHide(int nativeContentViewCoreImpl);
2938
2939    private native void nativeSetUseDesktopUserAgent(int nativeContentViewCoreImpl,
2940            boolean enabled, boolean reloadOnChange);
2941    private native boolean nativeGetUseDesktopUserAgent(int nativeContentViewCoreImpl);
2942
2943    private native void nativeClearSslPreferences(int nativeContentViewCoreImpl);
2944
2945    private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object,
2946            String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
2947
2948    private native void nativeRemoveJavascriptInterface(int nativeContentViewCoreImpl, String name);
2949
2950    private native int nativeGetNavigationHistory(int nativeContentViewCoreImpl, Object context);
2951    private native void nativeGetDirectedNavigationHistory(int nativeContentViewCoreImpl,
2952            Object context, boolean isForward, int maxEntries);
2953    private native String nativeGetOriginalUrlForActiveNavigationEntry(
2954            int nativeContentViewCoreImpl);
2955
2956    private native void nativeUpdateVSyncParameters(int nativeContentViewCoreImpl,
2957            long timebaseMicros, long intervalMicros);
2958
2959    private native void nativeOnVSync(int nativeContentViewCoreImpl, long frameTimeMicros);
2960
2961    private native boolean nativeOnAnimate(int nativeContentViewCoreImpl, long frameTimeMicros);
2962
2963    private native boolean nativePopulateBitmapFromCompositor(int nativeContentViewCoreImpl,
2964            Bitmap bitmap);
2965
2966    private native void nativeWasResized(int nativeContentViewCoreImpl);
2967
2968    private native boolean nativeIsRenderWidgetHostViewReady(int nativeContentViewCoreImpl);
2969
2970    private native void nativeExitFullscreen(int nativeContentViewCoreImpl);
2971    private native void nativeUpdateTopControlsState(int nativeContentViewCoreImpl,
2972            boolean enableHiding, boolean enableShowing, boolean animate);
2973
2974    private native void nativeShowImeIfNeeded(int nativeContentViewCoreImpl);
2975
2976    private native void nativeAttachExternalVideoSurface(
2977            int nativeContentViewCoreImpl, int playerId, Surface surface);
2978
2979    private native void nativeDetachExternalVideoSurface(
2980            int nativeContentViewCoreImpl, int playerId);
2981}
2982