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