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