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