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