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