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