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