Upstream version 10.38.208.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         assert mWebContents != null;
877         mWebContents.stop();
878     }
879
880     /**
881      * Get the URL of the current page.
882      *
883      * @return The URL of the current page.
884      */
885     public String getUrl() {
886         assert mWebContents != null;
887         return mWebContents.getUrl();
888     }
889
890     /**
891      * Get the title of the current page.
892      *
893      * @return The title of the current page.
894      */
895     public String getTitle() {
896         assert mWebContents != null;
897         return mWebContents.getTitle();
898     }
899
900     /**
901      * Shows an interstitial page driven by the passed in delegate.
902      *
903      * @param url The URL being blocked by the interstitial.
904      * @param delegate The delegate handling the interstitial.
905      */
906     @VisibleForTesting
907     public void showInterstitialPage(
908             String url, InterstitialPageDelegateAndroid delegate) {
909         assert mWebContents != null;
910         mWebContents.showInterstitialPage(url, delegate.getNative());
911     }
912
913     /**
914      * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
915      */
916     public boolean isShowingInterstitialPage() {
917         assert mWebContents != null;
918         return mWebContents.isShowingInterstitialPage();
919     }
920
921     /**
922      * @return Viewport width in physical pixels as set from onSizeChanged.
923      */
924     @CalledByNative
925     public int getViewportWidthPix() { return mViewportWidthPix; }
926
927     /**
928      * @return Viewport height in physical pixels as set from onSizeChanged.
929      */
930     @CalledByNative
931     public int getViewportHeightPix() { return mViewportHeightPix; }
932
933     /**
934      * @return Width of underlying physical surface.
935      */
936     @CalledByNative
937     public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
938
939     /**
940      * @return Height of underlying physical surface.
941      */
942     @CalledByNative
943     public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
944
945     /**
946      * @return Amount the output surface extends past the bottom of the window viewport.
947      */
948     @CalledByNative
949     public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
950
951     /**
952      * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
953      */
954     @CalledByNative
955     public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
956
957     /**
958      * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
959      */
960     @CalledByNative
961     public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
962
963     /**
964      * @see android.webkit.WebView#getContentHeight()
965      */
966     public float getContentHeightCss() {
967         return mRenderCoordinates.getContentHeightCss();
968     }
969
970     /**
971      * @see android.webkit.WebView#getContentWidth()
972      */
973     public float getContentWidthCss() {
974         return mRenderCoordinates.getContentWidthCss();
975     }
976
977     // TODO(teddchoc): Remove all these navigation controller methods from here and have the
978     //                 embedders manage it.
979     /**
980      * @return Whether the current WebContents has a previous navigation entry.
981      */
982     public boolean canGoBack() {
983         return mWebContents != null && mWebContents.getNavigationController().canGoBack();
984     }
985
986     /**
987      * @return Whether the current WebContents has a navigation entry after the current one.
988      */
989     public boolean canGoForward() {
990         return mWebContents != null && mWebContents.getNavigationController().canGoForward();
991     }
992
993     /**
994      * @param offset The offset into the navigation history.
995      * @return Whether we can move in history by given offset
996      */
997     public boolean canGoToOffset(int offset) {
998         return mWebContents != null &&
999                 mWebContents.getNavigationController().canGoToOffset(offset);
1000     }
1001
1002     /**
1003      * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1004      * of bounds.
1005      * @param offset The offset into the navigation history.
1006      */
1007     public void goToOffset(int offset) {
1008         if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1009     }
1010
1011     @Override
1012     public void goToNavigationIndex(int index) {
1013         if (mWebContents != null) {
1014             mWebContents.getNavigationController().goToNavigationIndex(index);
1015         }
1016     }
1017
1018     /**
1019      * Goes to the navigation entry before the current one.
1020      */
1021     public void goBack() {
1022         if (mWebContents != null) mWebContents.getNavigationController().goBack();
1023     }
1024
1025     /**
1026      * Goes to the navigation entry following the current one.
1027      */
1028     public void goForward() {
1029         if (mWebContents != null) mWebContents.getNavigationController().goForward();
1030     }
1031
1032     /**
1033      * Loads the current navigation if there is a pending lazy load (after tab restore).
1034      */
1035     public void loadIfNecessary() {
1036         if (mWebContents != null) mWebContents.getNavigationController().loadIfNecessary();
1037     }
1038
1039     /**
1040      * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1041      */
1042     public void requestRestoreLoad() {
1043         if (mWebContents != null) mWebContents.getNavigationController().requestRestoreLoad();
1044     }
1045
1046     /**
1047      * Reload the current page.
1048      */
1049     public void reload(boolean checkForRepost) {
1050         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1051         if (mWebContents != null) mWebContents.getNavigationController().reload(checkForRepost);
1052     }
1053
1054     /**
1055      * Reload the current page, ignoring the contents of the cache.
1056      */
1057     public void reloadIgnoringCache(boolean checkForRepost) {
1058         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1059         if (mWebContents != null) mWebContents.getNavigationController().reloadIgnoringCache(
1060                 checkForRepost);
1061     }
1062
1063     /**
1064      * Cancel the pending reload.
1065      */
1066     public void cancelPendingReload() {
1067         if (mWebContents != null) mWebContents.getNavigationController().cancelPendingReload();
1068     }
1069
1070     /**
1071      * Continue the pending reload.
1072      */
1073     public void continuePendingReload() {
1074         if (mWebContents != null) mWebContents.getNavigationController().continuePendingReload();
1075     }
1076
1077     /**
1078      * Clears the ContentViewCore's page history in both the backwards and
1079      * forwards directions.
1080      */
1081     public void clearHistory() {
1082         if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1083     }
1084
1085     /**
1086      * @return The selected text (empty if no text selected).
1087      */
1088     public String getSelectedText() {
1089         return mHasSelection ? mLastSelectedText : "";
1090     }
1091
1092     /**
1093      * @return Whether the current selection is editable (false if no text selected).
1094      */
1095     public boolean isSelectionEditable() {
1096         return mHasSelection ? mFocusedNodeEditable : false;
1097     }
1098
1099     /**
1100      * @return Whether the current focused node is editable.
1101      */
1102     public boolean isFocusedNodeEditable() {
1103         return mFocusedNodeEditable;
1104     }
1105
1106     // End FrameLayout overrides.
1107
1108     /**
1109      * @see View#onTouchEvent(MotionEvent)
1110      */
1111     public boolean onTouchEvent(MotionEvent event) {
1112         final boolean isTouchHandleEvent = false;
1113         return onTouchEventImpl(event, isTouchHandleEvent);
1114     }
1115
1116     private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
1117         TraceEvent.begin("onTouchEvent");
1118         try {
1119             int eventAction = event.getActionMasked();
1120
1121             if (eventAction == MotionEvent.ACTION_DOWN) {
1122                 cancelRequestToScrollFocusedEditableNodeIntoView();
1123             }
1124
1125             if (SPenSupport.isSPenSupported(mContext))
1126                 eventAction = SPenSupport.convertSPenEventAction(eventAction);
1127             if (!isValidTouchEventActionForNative(eventAction)) return false;
1128
1129             if (mNativeContentViewCore == 0) return false;
1130
1131             // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1132             MotionEvent offset = null;
1133             if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1134                 offset = createOffsetMotionEvent(event);
1135                 event = offset;
1136             }
1137
1138             final int pointerCount = event.getPointerCount();
1139             final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1140                     event.getEventTime(), eventAction,
1141                     pointerCount, event.getHistorySize(), event.getActionIndex(),
1142                     event.getX(), event.getY(),
1143                     pointerCount > 1 ? event.getX(1) : 0,
1144                     pointerCount > 1 ? event.getY(1) : 0,
1145                     event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1146                     event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1147                     event.getRawX(), event.getRawY(),
1148                     event.getToolType(0),
1149                     pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1150                     event.getButtonState(),
1151                     isTouchHandleEvent);
1152
1153             if (offset != null) offset.recycle();
1154             return consumed;
1155         } finally {
1156             TraceEvent.end("onTouchEvent");
1157         }
1158     }
1159
1160     private static boolean isValidTouchEventActionForNative(int eventAction) {
1161         // Only these actions have any effect on gesture detection.  Other
1162         // actions have no corresponding WebTouchEvent type and may confuse the
1163         // touch pipline, so we ignore them entirely.
1164         return eventAction == MotionEvent.ACTION_DOWN
1165                 || eventAction == MotionEvent.ACTION_UP
1166                 || eventAction == MotionEvent.ACTION_CANCEL
1167                 || eventAction == MotionEvent.ACTION_MOVE
1168                 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1169                 || eventAction == MotionEvent.ACTION_POINTER_UP;
1170     }
1171
1172     public void setIgnoreRemainingTouchEvents() {
1173         resetGestureDetection();
1174     }
1175
1176     public boolean isScrollInProgress() {
1177         return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1178     }
1179
1180     @SuppressWarnings("unused")
1181     @CalledByNative
1182     private void onFlingStartEventConsumed(int vx, int vy) {
1183         mTouchScrollInProgress = false;
1184         mPotentiallyActiveFlingCount++;
1185         for (mGestureStateListenersIterator.rewind();
1186                     mGestureStateListenersIterator.hasNext();) {
1187             mGestureStateListenersIterator.next().onFlingStartGesture(
1188                     vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1189         }
1190     }
1191
1192     @SuppressWarnings("unused")
1193     @CalledByNative
1194     private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1195         mTouchScrollInProgress = false;
1196         for (mGestureStateListenersIterator.rewind();
1197                     mGestureStateListenersIterator.hasNext();) {
1198             mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1199         }
1200     }
1201
1202     @SuppressWarnings("unused")
1203     @CalledByNative
1204     private void onFlingCancelEventAck() {
1205         updateGestureStateListener(GestureEventType.FLING_CANCEL);
1206     }
1207
1208     @SuppressWarnings("unused")
1209     @CalledByNative
1210     private void onScrollBeginEventAck() {
1211         mTouchScrollInProgress = true;
1212         hidePastePopup();
1213         mZoomControlsDelegate.invokeZoomPicker();
1214         updateGestureStateListener(GestureEventType.SCROLL_START);
1215     }
1216
1217     @SuppressWarnings("unused")
1218     @CalledByNative
1219     private void onScrollUpdateGestureConsumed() {
1220         mZoomControlsDelegate.invokeZoomPicker();
1221         for (mGestureStateListenersIterator.rewind();
1222                 mGestureStateListenersIterator.hasNext();) {
1223             mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1224         }
1225     }
1226
1227     @SuppressWarnings("unused")
1228     @CalledByNative
1229     private void onScrollEndEventAck() {
1230         if (!mTouchScrollInProgress) return;
1231         mTouchScrollInProgress = false;
1232         updateGestureStateListener(GestureEventType.SCROLL_END);
1233     }
1234
1235     @SuppressWarnings("unused")
1236     @CalledByNative
1237     private void onPinchBeginEventAck() {
1238         updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1239     }
1240
1241     @SuppressWarnings("unused")
1242     @CalledByNative
1243     private void onPinchEndEventAck() {
1244         updateGestureStateListener(GestureEventType.PINCH_END);
1245     }
1246
1247     @SuppressWarnings("unused")
1248     @CalledByNative
1249     private void onSingleTapEventAck(boolean consumed, int x, int y) {
1250         for (mGestureStateListenersIterator.rewind();
1251                 mGestureStateListenersIterator.hasNext();) {
1252             mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1253         }
1254     }
1255
1256     /**
1257      * Called just prior to a tap or press gesture being forwarded to the renderer.
1258      */
1259     @SuppressWarnings("unused")
1260     @CalledByNative
1261     private boolean filterTapOrPressEvent(int type, int x, int y) {
1262         if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1263             return true;
1264         }
1265         updateForTapOrPress(type, x, y);
1266         return false;
1267     }
1268
1269     @VisibleForTesting
1270     public void sendDoubleTapForTest(long timeMs, int x, int y) {
1271         if (mNativeContentViewCore == 0) return;
1272         nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1273     }
1274
1275     @VisibleForTesting
1276     public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1277         if (mNativeContentViewCore == 0) return;
1278         nativeFlingCancel(mNativeContentViewCore, timeMs);
1279         nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1280         nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1281     }
1282
1283     /**
1284      * Cancel any fling gestures active.
1285      * @param timeMs Current time (in milliseconds).
1286      */
1287     public void cancelFling(long timeMs) {
1288         if (mNativeContentViewCore == 0) return;
1289         nativeFlingCancel(mNativeContentViewCore, timeMs);
1290     }
1291
1292     /**
1293      * Add a listener that gets alerted on gesture state changes.
1294      * @param listener Listener to add.
1295      */
1296     public void addGestureStateListener(GestureStateListener listener) {
1297         mGestureStateListeners.addObserver(listener);
1298     }
1299
1300     /**
1301      * Removes a listener that was added to watch for gesture state changes.
1302      * @param listener Listener to remove.
1303      */
1304     public void removeGestureStateListener(GestureStateListener listener) {
1305         mGestureStateListeners.removeObserver(listener);
1306     }
1307
1308     void updateGestureStateListener(int gestureType) {
1309         for (mGestureStateListenersIterator.rewind();
1310                 mGestureStateListenersIterator.hasNext();) {
1311             GestureStateListener listener = mGestureStateListenersIterator.next();
1312             switch (gestureType) {
1313                 case GestureEventType.PINCH_BEGIN:
1314                     listener.onPinchStarted();
1315                     break;
1316                 case GestureEventType.PINCH_END:
1317                     listener.onPinchEnded();
1318                     break;
1319                 case GestureEventType.FLING_END:
1320                     listener.onFlingEndGesture(
1321                             computeVerticalScrollOffset(),
1322                             computeVerticalScrollExtent());
1323                     break;
1324                 case GestureEventType.FLING_CANCEL:
1325                     listener.onFlingCancelGesture();
1326                     break;
1327                 case GestureEventType.SCROLL_START:
1328                     listener.onScrollStarted(
1329                             computeVerticalScrollOffset(),
1330                             computeVerticalScrollExtent());
1331                     break;
1332                 case GestureEventType.SCROLL_END:
1333                     listener.onScrollEnded(
1334                             computeVerticalScrollOffset(),
1335                             computeVerticalScrollExtent());
1336                     break;
1337                 default:
1338                     break;
1339             }
1340         }
1341     }
1342
1343     /**
1344      * Inserts the provided markup sandboxed into the frame.
1345      */
1346     public void setupTransitionView(String markup) {
1347         assert mWebContents != null;
1348         mWebContents.setupTransitionView(markup);
1349     }
1350
1351     /**
1352      * Hides transition elements specified by the selector, and activates any
1353      * exiting-transition stylesheets.
1354      */
1355     public void beginExitTransition(String cssSelector) {
1356         assert mWebContents != null;
1357         mWebContents.beginExitTransition(cssSelector);
1358     }
1359
1360     /**
1361      * Requests the renderer insert a link to the specified stylesheet in the
1362      * main frame's document.
1363      */
1364     public void addStyleSheetByURL(String url) {
1365         assert mWebContents != null;
1366         mWebContents.addStyleSheetByURL(url);
1367     }
1368
1369     /**
1370      * Injects the passed Javascript code in the current page and evaluates it.
1371      * If a result is required, pass in a callback.
1372      * Used in automation tests.
1373      *
1374      * @param script The Javascript to execute.
1375      * @param callback The callback to be fired off when a result is ready. The script's
1376      *                 result will be json encoded and passed as the parameter, and the call
1377      *                 will be made on the main thread.
1378      *                 If no result is required, pass null.
1379      */
1380     public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1381         assert mWebContents != null;
1382         mWebContents.evaluateJavaScript(script, callback, false);
1383     }
1384
1385     /**
1386      * Injects the passed Javascript code in the current page and evaluates it.
1387      * If there is no page existing, a new one will be created.
1388      *
1389      * @param script The Javascript to execute.
1390      */
1391     public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1392         assert mWebContents != null;
1393         mWebContents.evaluateJavaScript(script, null, true);
1394     }
1395
1396     /**
1397      * Post a message to a frame.
1398      * TODO(sgurun) also add support for transferring a message channel port.
1399      *
1400      * @param frameName The name of the frame. If the name is null the message is posted
1401      *                  to the main frame.
1402      * @param message   The message
1403      * @param sourceOrigin  The source origin
1404      * @param targetOrigin  The target origin
1405      */
1406     public void postMessageToFrame(String frameName, String message,
1407             String sourceOrigin, String targetOrigin) {
1408         if (mNativeContentViewCore == 0) return;
1409         nativePostMessageToFrame(mNativeContentViewCore, frameName, message, sourceOrigin,
1410             targetOrigin);
1411     }
1412
1413     /**
1414      * To be called when the ContentView is shown.
1415      */
1416     public void onShow() {
1417         assert mWebContents != null;
1418         mWebContents.onShow();
1419         setAccessibilityState(mAccessibilityManager.isEnabled());
1420     }
1421
1422     /**
1423      * @return The ID of the renderer process that backs this tab or
1424      *         {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1425      */
1426     public int getCurrentRenderProcessId() {
1427         return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1428     }
1429
1430     /**
1431      * To be called when the ContentView is hidden.
1432      */
1433     public void onHide() {
1434         assert mWebContents != null;
1435         hidePopups();
1436         setInjectedAccessibility(false);
1437         mWebContents.onHide();
1438     }
1439
1440     /**
1441      * Return the ContentSettings object used to retrieve the settings for this
1442      * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1443      * @return A ContentSettings object that can be used to retrieve this
1444      *         ContentViewCore's settings.
1445      */
1446     public ContentSettings getContentSettings() {
1447         return mContentSettings;
1448     }
1449
1450     private void hidePopups() {
1451         mUnselectAllOnActionModeDismiss = true;
1452         hideSelectActionBar();
1453         hidePastePopup();
1454         hideSelectPopup();
1455         hideTextHandles();
1456     }
1457
1458     public void hideSelectActionBar() {
1459         if (mActionMode != null) {
1460             mActionMode.finish();
1461             mActionMode = null;
1462         }
1463     }
1464
1465     public boolean isSelectActionBarShowing() {
1466         return mActionMode != null;
1467     }
1468
1469     private void resetGestureDetection() {
1470         if (mNativeContentViewCore == 0) return;
1471         nativeResetGestureDetection(mNativeContentViewCore);
1472     }
1473
1474     /**
1475      * @see View#onAttachedToWindow()
1476      */
1477     @SuppressWarnings("javadoc")
1478     public void onAttachedToWindow() {
1479         setAccessibilityState(mAccessibilityManager.isEnabled());
1480
1481         ScreenOrientationListener.getInstance().addObserver(this, mContext);
1482         GamepadList.onAttachedToWindow(mContext);
1483     }
1484
1485     /**
1486      * @see View#onDetachedFromWindow()
1487      */
1488     @SuppressWarnings("javadoc")
1489     @SuppressLint("MissingSuperCall")
1490     public void onDetachedFromWindow() {
1491         setInjectedAccessibility(false);
1492         hidePopups();
1493         mZoomControlsDelegate.dismissZoomPicker();
1494         unregisterAccessibilityContentObserver();
1495
1496         ScreenOrientationListener.getInstance().removeObserver(this);
1497         GamepadList.onDetachedFromWindow();
1498     }
1499
1500     /**
1501      * @see View#onVisibilityChanged(android.view.View, int)
1502      */
1503     public void onVisibilityChanged(View changedView, int visibility) {
1504         if (visibility != View.VISIBLE) {
1505             mZoomControlsDelegate.dismissZoomPicker();
1506         }
1507     }
1508
1509     /**
1510      * @see View#onCreateInputConnection(EditorInfo)
1511      */
1512     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1513         if (!mImeAdapter.hasTextInputType()) {
1514             // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1515             // is still used by the InputMethodService. Need to make sure the IME doesn't
1516             // enter fullscreen mode.
1517             outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1518         }
1519         mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1520                 mEditable, outAttrs);
1521         return mInputConnection;
1522     }
1523
1524     @VisibleForTesting
1525     public AdapterInputConnection getAdapterInputConnectionForTest() {
1526         return mInputConnection;
1527     }
1528
1529     @VisibleForTesting
1530     public Editable getEditableForTest() {
1531         return mEditable;
1532     }
1533
1534     /**
1535      * @see View#onCheckIsTextEditor()
1536      */
1537     public boolean onCheckIsTextEditor() {
1538         return mImeAdapter.hasTextInputType();
1539     }
1540
1541     /**
1542      * @see View#onConfigurationChanged(Configuration)
1543      */
1544     @SuppressWarnings("javadoc")
1545     public void onConfigurationChanged(Configuration newConfig) {
1546         TraceEvent.begin();
1547
1548         if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1549             if (mNativeContentViewCore != 0) {
1550                 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1551                         ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1552             }
1553             mInputMethodManagerWrapper.restartInput(mContainerView);
1554         }
1555         mContainerViewInternals.super_onConfigurationChanged(newConfig);
1556
1557         // To request layout has side effect, but it seems OK as it only happen in
1558         // onConfigurationChange and layout has to be changed in most case.
1559         mContainerView.requestLayout();
1560         TraceEvent.end();
1561     }
1562
1563     /**
1564      * @see View#onSizeChanged(int, int, int, int)
1565      */
1566     @SuppressWarnings("javadoc")
1567     public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1568         if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1569
1570         mViewportWidthPix = wPix;
1571         mViewportHeightPix = hPix;
1572         if (mNativeContentViewCore != 0) {
1573             nativeWasResized(mNativeContentViewCore);
1574         }
1575
1576         updateAfterSizeChanged();
1577     }
1578
1579     /**
1580      * Called when the underlying surface the compositor draws to changes size.
1581      * This may be larger than the viewport size.
1582      */
1583     public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1584         if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1585
1586         mPhysicalBackingWidthPix = wPix;
1587         mPhysicalBackingHeightPix = hPix;
1588
1589         if (mNativeContentViewCore != 0) {
1590             nativeWasResized(mNativeContentViewCore);
1591         }
1592     }
1593
1594     /**
1595      * Called when the amount the surface is overdrawing off the bottom has changed.
1596      * @param overdrawHeightPix The overdraw height.
1597      */
1598     public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1599         if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1600
1601         mOverdrawBottomHeightPix = overdrawHeightPix;
1602
1603         if (mNativeContentViewCore != 0) {
1604             nativeWasResized(mNativeContentViewCore);
1605         }
1606     }
1607
1608     private void updateAfterSizeChanged() {
1609         mPopupZoomer.hide(false);
1610
1611         // Execute a delayed form focus operation because the OSK was brought
1612         // up earlier.
1613         if (!mFocusPreOSKViewportRect.isEmpty()) {
1614             Rect rect = new Rect();
1615             getContainerView().getWindowVisibleDisplayFrame(rect);
1616             if (!rect.equals(mFocusPreOSKViewportRect)) {
1617                 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1618                 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1619                     scrollFocusedEditableNodeIntoView();
1620                 }
1621                 cancelRequestToScrollFocusedEditableNodeIntoView();
1622             }
1623         }
1624     }
1625
1626     private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1627         // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1628         // issuing the delayed form focus event.
1629         mFocusPreOSKViewportRect.setEmpty();
1630     }
1631
1632     private void scrollFocusedEditableNodeIntoView() {
1633         assert mWebContents != null;
1634         mWebContents.scrollFocusedEditableNodeIntoView();
1635     }
1636
1637     /**
1638      * Selects the word around the caret, if any.
1639      * The caller can check if selection actually occurred by listening to OnSelectionChanged.
1640      */
1641     public void selectWordAroundCaret() {
1642         assert mWebContents != null;
1643         mWebContents.selectWordAroundCaret();
1644     }
1645
1646     /**
1647      * @see View#onWindowFocusChanged(boolean)
1648      */
1649     public void onWindowFocusChanged(boolean hasWindowFocus) {
1650         if (!hasWindowFocus) resetGestureDetection();
1651     }
1652
1653     public void onFocusChanged(boolean gainFocus) {
1654         if (!gainFocus) {
1655             hideImeIfNeeded();
1656             cancelRequestToScrollFocusedEditableNodeIntoView();
1657             hidePastePopup();
1658             hideTextHandles();
1659         }
1660         if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1661     }
1662
1663     /**
1664      * @see View#onKeyUp(int, KeyEvent)
1665      */
1666     public boolean onKeyUp(int keyCode, KeyEvent event) {
1667         if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1668             mPopupZoomer.hide(true);
1669             return true;
1670         }
1671         return mContainerViewInternals.super_onKeyUp(keyCode, event);
1672     }
1673
1674     /**
1675      * @see View#dispatchKeyEventPreIme(KeyEvent)
1676      */
1677     public boolean dispatchKeyEventPreIme(KeyEvent event) {
1678         try {
1679             TraceEvent.begin();
1680             return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1681         } finally {
1682             TraceEvent.end();
1683         }
1684     }
1685
1686     /**
1687      * @see View#dispatchKeyEvent(KeyEvent)
1688      */
1689     public boolean dispatchKeyEvent(KeyEvent event) {
1690         if (GamepadList.dispatchKeyEvent(event)) return true;
1691         if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1692             return mContainerViewInternals.super_dispatchKeyEvent(event);
1693         }
1694
1695         if (mImeAdapter.dispatchKeyEvent(event)) return true;
1696
1697         return mContainerViewInternals.super_dispatchKeyEvent(event);
1698     }
1699
1700     /**
1701      * @see View#onHoverEvent(MotionEvent)
1702      * Mouse move events are sent on hover enter, hover move and hover exit.
1703      * They are sent on hover exit because sometimes it acts as both a hover
1704      * move and hover exit.
1705      */
1706     public boolean onHoverEvent(MotionEvent event) {
1707         TraceEvent.begin("onHoverEvent");
1708         MotionEvent offset = createOffsetMotionEvent(event);
1709         try {
1710             if (mBrowserAccessibilityManager != null) {
1711                 return mBrowserAccessibilityManager.onHoverEvent(offset);
1712             }
1713
1714             // Work around Android bug where the x, y coordinates of a hover exit
1715             // event are incorrect when touch exploration is on.
1716             if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1717                 return true;
1718             }
1719
1720             mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1721             if (mNativeContentViewCore != 0) {
1722                 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1723                         offset.getX(), offset.getY());
1724             }
1725             return true;
1726         } finally {
1727             offset.recycle();
1728             TraceEvent.end("onHoverEvent");
1729         }
1730     }
1731
1732     /**
1733      * @see View#onGenericMotionEvent(MotionEvent)
1734      */
1735     public boolean onGenericMotionEvent(MotionEvent event) {
1736         if (GamepadList.onGenericMotionEvent(event)) return true;
1737         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1738             switch (event.getAction()) {
1739                 case MotionEvent.ACTION_SCROLL:
1740                     if (mNativeContentViewCore == 0) return false;
1741
1742                     nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1743                             event.getX(), event.getY(),
1744                             event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1745
1746                     mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1747                     // Send a delayed onMouseMove event so that we end
1748                     // up hovering over the right position after the scroll.
1749                     final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1750                     mFakeMouseMoveRunnable = new Runnable() {
1751                         @Override
1752                         public void run() {
1753                             onHoverEvent(eventFakeMouseMove);
1754                             eventFakeMouseMove.recycle();
1755                         }
1756                     };
1757                     mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1758                     return true;
1759             }
1760         }
1761         return mContainerViewInternals.super_onGenericMotionEvent(event);
1762     }
1763
1764     /**
1765      * Sets the current amount to offset incoming touch events by.  This is used to handle content
1766      * moving and not lining up properly with the android input system.
1767      * @param dx The X offset in pixels to shift touch events.
1768      * @param dy The Y offset in pixels to shift touch events.
1769      */
1770     public void setCurrentMotionEventOffsets(float dx, float dy) {
1771         mCurrentTouchOffsetX = dx;
1772         mCurrentTouchOffsetY = dy;
1773     }
1774
1775     private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1776         MotionEvent dst = MotionEvent.obtain(src);
1777         dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1778         return dst;
1779     }
1780
1781     /**
1782      * @see View#scrollBy(int, int)
1783      * Currently the ContentView scrolling happens in the native side. In
1784      * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1785      * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1786      * (0, 0). This is critical for drawing ContentView correctly.
1787      */
1788     public void scrollBy(int xPix, int yPix) {
1789         if (mNativeContentViewCore != 0) {
1790             nativeScrollBy(mNativeContentViewCore,
1791                     SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1792         }
1793     }
1794
1795     /**
1796      * @see View#scrollTo(int, int)
1797      */
1798     public void scrollTo(int xPix, int yPix) {
1799         if (mNativeContentViewCore == 0) return;
1800         final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1801         final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1802         final float dxPix = xPix - xCurrentPix;
1803         final float dyPix = yPix - yCurrentPix;
1804         if (dxPix != 0 || dyPix != 0) {
1805             long time = SystemClock.uptimeMillis();
1806             nativeScrollBegin(mNativeContentViewCore, time,
1807                     xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1808             nativeScrollBy(mNativeContentViewCore,
1809                     time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1810             nativeScrollEnd(mNativeContentViewCore, time);
1811         }
1812     }
1813
1814     // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1815     //       see: b/6029133
1816     public int getNativeScrollXForTest() {
1817         return mRenderCoordinates.getScrollXPixInt();
1818     }
1819
1820     // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1821     //       see: b/6029133
1822     public int getNativeScrollYForTest() {
1823         return mRenderCoordinates.getScrollYPixInt();
1824     }
1825
1826     /**
1827      * @see View#computeHorizontalScrollExtent()
1828      */
1829     @SuppressWarnings("javadoc")
1830     public int computeHorizontalScrollExtent() {
1831         return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1832     }
1833
1834     /**
1835      * @see View#computeHorizontalScrollOffset()
1836      */
1837     @SuppressWarnings("javadoc")
1838     public int computeHorizontalScrollOffset() {
1839         return mRenderCoordinates.getScrollXPixInt();
1840     }
1841
1842     /**
1843      * @see View#computeHorizontalScrollRange()
1844      */
1845     @SuppressWarnings("javadoc")
1846     public int computeHorizontalScrollRange() {
1847         return mRenderCoordinates.getContentWidthPixInt();
1848     }
1849
1850     /**
1851      * @see View#computeVerticalScrollExtent()
1852      */
1853     @SuppressWarnings("javadoc")
1854     public int computeVerticalScrollExtent() {
1855         return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1856     }
1857
1858     /**
1859      * @see View#computeVerticalScrollOffset()
1860      */
1861     @SuppressWarnings("javadoc")
1862     public int computeVerticalScrollOffset() {
1863         return mRenderCoordinates.getScrollYPixInt();
1864     }
1865
1866     /**
1867      * @see View#computeVerticalScrollRange()
1868      */
1869     @SuppressWarnings("javadoc")
1870     public int computeVerticalScrollRange() {
1871         return mRenderCoordinates.getContentHeightPixInt();
1872     }
1873
1874     // End FrameLayout overrides.
1875
1876     /**
1877      * @see View#awakenScrollBars(int, boolean)
1878      */
1879     @SuppressWarnings("javadoc")
1880     public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1881         // For the default implementation of ContentView which draws the scrollBars on the native
1882         // side, calling this function may get us into a bad state where we keep drawing the
1883         // scrollBars, so disable it by always returning false.
1884         if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1885             return false;
1886         } else {
1887             return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1888         }
1889     }
1890
1891     private void updateForTapOrPress(int type, float xPix, float yPix) {
1892         if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1893                 && type != GestureEventType.SINGLE_TAP_UP
1894                 && type != GestureEventType.LONG_PRESS
1895                 && type != GestureEventType.LONG_TAP) {
1896             return;
1897         }
1898
1899         if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1900                 && !mContainerView.isFocused())  {
1901             mContainerView.requestFocus();
1902         }
1903
1904         if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1905
1906         mLastTapX = (int) xPix;
1907         mLastTapY = (int) yPix;
1908     }
1909
1910     /**
1911      * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1912      */
1913     public int getLastTapX()  {
1914         return mLastTapX;
1915     }
1916
1917     /**
1918      * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1919      */
1920     public int getLastTapY()  {
1921         return mLastTapY;
1922     }
1923
1924     public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1925         mZoomControlsDelegate = zoomControlsDelegate;
1926     }
1927
1928     public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1929         if (mNativeContentViewCore == 0) return;
1930         nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1931     }
1932
1933     public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1934         if (mNativeContentViewCore == 0) return;
1935         nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1936     }
1937
1938     public void selectPopupMenuItems(int[] indices) {
1939         if (mNativeContentViewCore != 0) {
1940             nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1941         }
1942         mSelectPopup = null;
1943     }
1944
1945     /**
1946      * Send the screen orientation value to the renderer.
1947      */
1948     @VisibleForTesting
1949     void sendOrientationChangeEvent(int orientation) {
1950         if (mNativeContentViewCore == 0) return;
1951
1952         nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1953     }
1954
1955     /**
1956      * Register the delegate to be used when content can not be handled by
1957      * the rendering engine, and should be downloaded instead. This will replace
1958      * the current delegate, if any.
1959      * @param delegate An implementation of ContentViewDownloadDelegate.
1960      */
1961     public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1962         mDownloadDelegate = delegate;
1963     }
1964
1965     // Called by DownloadController.
1966     ContentViewDownloadDelegate getDownloadDelegate() {
1967         return mDownloadDelegate;
1968     }
1969
1970     private void showSelectActionBar() {
1971         if (mActionMode != null) {
1972             mActionMode.invalidate();
1973             return;
1974         }
1975
1976         // Start a new action mode with a SelectActionModeCallback.
1977         SelectActionModeCallback.ActionHandler actionHandler =
1978                 new SelectActionModeCallback.ActionHandler() {
1979             @Override
1980             public void selectAll() {
1981                 mImeAdapter.selectAll();
1982             }
1983
1984             @Override
1985             public void cut() {
1986                 mImeAdapter.cut();
1987             }
1988
1989             @Override
1990             public void copy() {
1991                 mImeAdapter.copy();
1992             }
1993
1994             @Override
1995             public void paste() {
1996                 mImeAdapter.paste();
1997             }
1998
1999             @Override
2000             public void share() {
2001                 final String query = getSelectedText();
2002                 if (TextUtils.isEmpty(query)) return;
2003
2004                 Intent send = new Intent(Intent.ACTION_SEND);
2005                 send.setType("text/plain");
2006                 send.putExtra(Intent.EXTRA_TEXT, query);
2007                 try {
2008                     Intent i = Intent.createChooser(send, getContext().getString(
2009                             R.string.actionbar_share));
2010                     i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2011                     getContext().startActivity(i);
2012                 } catch (android.content.ActivityNotFoundException ex) {
2013                     // If no app handles it, do nothing.
2014                 }
2015             }
2016
2017             @Override
2018             public void search() {
2019                 final String query = getSelectedText();
2020                 if (TextUtils.isEmpty(query)) return;
2021
2022                 // See if ContentViewClient wants to override
2023                 if (getContentViewClient().doesPerformWebSearch()) {
2024                     getContentViewClient().performWebSearch(query);
2025                     return;
2026                 }
2027
2028                 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2029                 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2030                 i.putExtra(SearchManager.QUERY, query);
2031                 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2032                 if (!(getContext() instanceof Activity)) {
2033                     i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2034                 }
2035                 try {
2036                     getContext().startActivity(i);
2037                 } catch (android.content.ActivityNotFoundException ex) {
2038                     // If no app handles it, do nothing.
2039                 }
2040             }
2041
2042             @Override
2043             public boolean isSelectionPassword() {
2044                 return mImeAdapter.isSelectionPassword();
2045             }
2046
2047             @Override
2048             public boolean isSelectionEditable() {
2049                 return mFocusedNodeEditable;
2050             }
2051
2052             @Override
2053             public void onDestroyActionMode() {
2054                 mActionMode = null;
2055                 if (mUnselectAllOnActionModeDismiss) {
2056                     hideTextHandles();
2057                     if (isSelectionEditable()) {
2058                         int selectionEnd = Selection.getSelectionEnd(mEditable);
2059                         mInputConnection.setSelection(selectionEnd, selectionEnd);
2060                     } else {
2061                         mImeAdapter.unselect();
2062                     }
2063                 }
2064                 getContentViewClient().onContextualActionBarHidden();
2065             }
2066
2067             @Override
2068             public boolean isShareAvailable() {
2069                 Intent intent = new Intent(Intent.ACTION_SEND);
2070                 intent.setType("text/plain");
2071                 return getContext().getPackageManager().queryIntentActivities(intent,
2072                         PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2073             }
2074
2075             @Override
2076             public boolean isWebSearchAvailable() {
2077                 if (getContentViewClient().doesPerformWebSearch()) return true;
2078                 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2079                 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2080                 return getContext().getPackageManager().queryIntentActivities(intent,
2081                         PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2082             }
2083         };
2084         mActionMode = null;
2085         // On ICS, startActionMode throws an NPE when getParent() is null.
2086         if (mContainerView.getParent() != null) {
2087             assert mWebContents != null;
2088             mActionMode = mContainerView.startActionMode(
2089                     getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2090                             mWebContents.isIncognito()));
2091         }
2092         mUnselectAllOnActionModeDismiss = true;
2093         if (mActionMode == null) {
2094             // There is no ActionMode, so remove the selection.
2095             mImeAdapter.unselect();
2096         } else {
2097             getContentViewClient().onContextualActionBarShown();
2098         }
2099     }
2100
2101     /**
2102      * Clears the current text selection.
2103      */
2104     public void clearSelection() {
2105         mImeAdapter.unselect();
2106     }
2107
2108     private void hidePastePopup() {
2109         if (mPastePopupMenu == null) return;
2110         mPastePopupMenu.hide();
2111     }
2112
2113     @CalledByNative
2114     private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
2115         switch (eventType) {
2116             case SelectionEventType.SELECTION_SHOWN:
2117                 mHasSelection = true;
2118                 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2119                 // a selection. See http://crbug.com/150151.
2120                 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2121                 showSelectActionBar();
2122                 break;
2123
2124             case SelectionEventType.SELECTION_CLEARED:
2125                 mHasSelection = false;
2126                 mUnselectAllOnActionModeDismiss = false;
2127                 hideSelectActionBar();
2128                 break;
2129
2130             case SelectionEventType.SELECTION_DRAG_STARTED:
2131                 break;
2132
2133             case SelectionEventType.SELECTION_DRAG_STOPPED:
2134                 break;
2135
2136             case SelectionEventType.INSERTION_SHOWN:
2137                 mHasInsertion = true;
2138                 break;
2139
2140             case SelectionEventType.INSERTION_MOVED:
2141                 if (mPastePopupMenu == null) break;
2142                 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2143                     showPastePopup((int) posXDip, (int) posYDip);
2144                 } else {
2145                     hidePastePopup();
2146                 }
2147                 break;
2148
2149             case SelectionEventType.INSERTION_TAPPED:
2150                 if (mWasPastePopupShowingOnInsertionDragStart)
2151                     hidePastePopup();
2152                 else
2153                     showPastePopup((int) posXDip, (int) posYDip);
2154                 break;
2155
2156             case SelectionEventType.INSERTION_CLEARED:
2157                 mHasInsertion = false;
2158                 hidePastePopup();
2159                 break;
2160
2161             case SelectionEventType.INSERTION_DRAG_STARTED:
2162                 mWasPastePopupShowingOnInsertionDragStart =
2163                         mPastePopupMenu != null && mPastePopupMenu.isShowing();
2164                 hidePastePopup();
2165                 break;
2166
2167             default:
2168                 assert false : "Invalid selection event type.";
2169         }
2170         getContentViewClient().onSelectionEvent(eventType);
2171     }
2172
2173     public boolean getUseDesktopUserAgent() {
2174         if (mNativeContentViewCore != 0) {
2175             return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2176         }
2177         return false;
2178     }
2179
2180     /**
2181      * Set whether or not we're using a desktop user agent for the currently loaded page.
2182      * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
2183      * @param reloadOnChange Reload the page if the UA has changed.
2184      */
2185     public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2186         if (mNativeContentViewCore != 0) {
2187             nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2188         }
2189     }
2190
2191     public void clearSslPreferences() {
2192         if (mNativeContentViewCore != 0) nativeClearSslPreferences(mNativeContentViewCore);
2193     }
2194
2195     private void hideTextHandles() {
2196         mHasSelection = false;
2197         mHasInsertion = false;
2198         if (mNativeContentViewCore != 0) nativeHideTextHandles(mNativeContentViewCore);
2199     }
2200
2201     /**
2202      * Shows the IME if the focused widget could accept text input.
2203      */
2204     public void showImeIfNeeded() {
2205         assert mWebContents != null;
2206         mWebContents.showImeIfNeeded();
2207     }
2208
2209     /**
2210      * Hides the IME if the containerView is the active view for IME.
2211      */
2212     public void hideImeIfNeeded() {
2213         // Hide input method window from the current view synchronously
2214         // because ImeAdapter does so asynchronouly with a delay, and
2215         // by the time when ImeAdapter dismisses the input, the
2216         // containerView may have lost focus.
2217         // We cannot trust ContentViewClient#onImeStateChangeRequested to
2218         // hide the input window because it has an empty default implementation.
2219         // So we need to explicitly hide the input method window here.
2220         if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2221             mInputMethodManagerWrapper.hideSoftInputFromWindow(
2222                     mContainerView.getWindowToken(), 0, null);
2223         }
2224         getContentViewClient().onImeStateChangeRequested(false);
2225     }
2226
2227     @SuppressWarnings("unused")
2228     @CalledByNative
2229     private void updateFrameInfo(
2230             float scrollOffsetX, float scrollOffsetY,
2231             float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2232             float contentWidth, float contentHeight,
2233             float viewportWidth, float viewportHeight,
2234             float controlsOffsetYCss, float contentOffsetYCss,
2235             float overdrawBottomHeightCss) {
2236         TraceEvent.begin("ContentViewCore:updateFrameInfo");
2237         // Adjust contentWidth/Height to be always at least as big as
2238         // the actual viewport (as set by onSizeChanged).
2239         final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2240         contentWidth = Math.max(contentWidth,
2241                 mViewportWidthPix / (deviceScale * pageScaleFactor));
2242         contentHeight = Math.max(contentHeight,
2243                 mViewportHeightPix / (deviceScale * pageScaleFactor));
2244         final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2245
2246         final boolean contentSizeChanged =
2247                 contentWidth != mRenderCoordinates.getContentWidthCss()
2248                 || contentHeight != mRenderCoordinates.getContentHeightCss();
2249         final boolean scaleLimitsChanged =
2250                 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2251                 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2252         final boolean pageScaleChanged =
2253                 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2254         final boolean scrollChanged =
2255                 pageScaleChanged
2256                 || scrollOffsetX != mRenderCoordinates.getScrollX()
2257                 || scrollOffsetY != mRenderCoordinates.getScrollY();
2258         final boolean contentOffsetChanged =
2259                 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2260
2261         final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2262         final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2263
2264         if (needHidePopupZoomer) mPopupZoomer.hide(true);
2265
2266         if (scrollChanged) {
2267             mContainerViewInternals.onScrollChanged(
2268                     (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2269                     (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2270                     (int) mRenderCoordinates.getScrollXPix(),
2271                     (int) mRenderCoordinates.getScrollYPix());
2272         }
2273
2274         mRenderCoordinates.updateFrameInfo(
2275                 scrollOffsetX, scrollOffsetY,
2276                 contentWidth, contentHeight,
2277                 viewportWidth, viewportHeight,
2278                 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2279                 contentOffsetYPix);
2280
2281         if (scrollChanged || contentOffsetChanged) {
2282             for (mGestureStateListenersIterator.rewind();
2283                     mGestureStateListenersIterator.hasNext();) {
2284                 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2285                         computeVerticalScrollOffset(),
2286                         computeVerticalScrollExtent());
2287             }
2288         }
2289
2290         if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2291
2292         // Update offsets for fullscreen.
2293         final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2294         final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2295         getContentViewClient().onOffsetsForFullscreenChanged(
2296                 controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2297
2298         if (mBrowserAccessibilityManager != null) {
2299             mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2300         }
2301         TraceEvent.end("ContentViewCore:updateFrameInfo");
2302     }
2303
2304     @CalledByNative
2305     private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2306             int textInputFlags, String text, int selectionStart, int selectionEnd,
2307             int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2308             boolean isNonImeChange) {
2309         TraceEvent.begin();
2310         mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2311         if (!mFocusedNodeEditable) hidePastePopup();
2312
2313         mImeAdapter.updateKeyboardVisibility(
2314                 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2315
2316         if (mInputConnection != null) {
2317             mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2318                     compositionEnd, isNonImeChange);
2319         }
2320
2321         if (mActionMode != null) mActionMode.invalidate();
2322         TraceEvent.end();
2323     }
2324
2325     @SuppressWarnings("unused")
2326     @CalledByNative
2327     private void setTitle(String title) {
2328         getContentViewClient().onUpdateTitle(title);
2329     }
2330
2331     /**
2332      * Called (from native) when the <select> popup needs to be shown.
2333      * @param items           Items to show.
2334      * @param enabled         POPUP_ITEM_TYPEs for items.
2335      * @param multiple        Whether the popup menu should support multi-select.
2336      * @param selectedIndices Indices of selected items.
2337      */
2338     @SuppressWarnings("unused")
2339     @CalledByNative
2340     private void showSelectPopup(Rect bounds, String[] items, int[] enabled, boolean multiple,
2341             int[] selectedIndices) {
2342         if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2343             selectPopupMenuItems(null);
2344             return;
2345         }
2346
2347         assert items.length == enabled.length;
2348         List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2349         for (int i = 0; i < items.length; i++) {
2350             popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2351         }
2352         hidePopups();
2353         if (DeviceFormFactor.isTablet(mContext) && !multiple) {
2354             mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2355         } else {
2356             mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2357         }
2358         mSelectPopup.show();
2359     }
2360
2361     /**
2362      * Called when the <select> popup needs to be hidden.
2363      */
2364     @CalledByNative
2365     private void hideSelectPopup() {
2366         if (mSelectPopup != null) mSelectPopup.hide();
2367     }
2368
2369     /**
2370      * @return The visible select popup being shown.
2371      */
2372     public SelectPopup getSelectPopupForTest() {
2373         return mSelectPopup;
2374     }
2375
2376     @SuppressWarnings("unused")
2377     @CalledByNative
2378     private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2379         mPopupZoomer.setBitmap(zoomedBitmap);
2380         mPopupZoomer.show(targetRect);
2381     }
2382
2383     @SuppressWarnings("unused")
2384     @CalledByNative
2385     private TouchEventSynthesizer createTouchEventSynthesizer() {
2386         return new TouchEventSynthesizer(this);
2387     }
2388
2389     @SuppressWarnings("unused")
2390     @CalledByNative
2391     private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2392         if (mTouchHandleDelegate == null) {
2393             mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2394                 public View getParent() {
2395                     return getContainerView();
2396                 }
2397
2398                 public PositionObserver getParentPositionObserver() {
2399                     return mPositionObserver;
2400                 }
2401
2402                 public boolean onTouchHandleEvent(MotionEvent event) {
2403                     final boolean isTouchHandleEvent = true;
2404                     return onTouchEventImpl(event, isTouchHandleEvent);
2405                 }
2406             };
2407         }
2408         return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2409     }
2410
2411     @SuppressWarnings("unused")
2412     @CalledByNative
2413     private void onSelectionChanged(String text) {
2414         mLastSelectedText = text;
2415         getContentViewClient().onSelectionChanged(text);
2416     }
2417
2418     @SuppressWarnings("unused")
2419     @CalledByNative
2420     private void showPastePopupWithFeedback(int xDip, int yDip) {
2421         // TODO(jdduke): Remove this when there is a better signal that long press caused
2422         // showing of the paste popup. See http://crbug.com/150151.
2423         if (showPastePopup(xDip, yDip)) {
2424             mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2425         }
2426     }
2427
2428     private boolean showPastePopup(int xDip, int yDip) {
2429         if (!mHasInsertion || !canPaste()) return false;
2430         final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2431         getPastePopup().showAt(
2432             (int) mRenderCoordinates.fromDipToPix(xDip),
2433             (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2434         return true;
2435     }
2436
2437     private PastePopupMenu getPastePopup() {
2438         if (mPastePopupMenu == null) {
2439             mPastePopupMenu = new PastePopupMenu(getContainerView(),
2440                 new PastePopupMenuDelegate() {
2441                     public void paste() {
2442                         mImeAdapter.paste();
2443                         hideTextHandles();
2444                     }
2445                 });
2446         }
2447         return mPastePopupMenu;
2448     }
2449
2450     private boolean canPaste() {
2451         if (!mFocusedNodeEditable) return false;
2452         return ((ClipboardManager) mContext.getSystemService(
2453                 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2454     }
2455
2456     @SuppressWarnings("unused")
2457     @CalledByNative
2458     private void onRenderProcessChange() {
2459         attachImeAdapter();
2460     }
2461
2462     /**
2463      * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2464      */
2465     public void attachImeAdapter() {
2466         if (mImeAdapter != null && mNativeContentViewCore != 0) {
2467             mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2468         }
2469     }
2470
2471     /**
2472      * @see View#hasFocus()
2473      */
2474     @CalledByNative
2475     public boolean hasFocus() {
2476         return mContainerView.hasFocus();
2477     }
2478
2479     /**
2480      * Checks whether the ContentViewCore can be zoomed in.
2481      *
2482      * @return True if the ContentViewCore can be zoomed in.
2483      */
2484     // This method uses the term 'zoom' for legacy reasons, but relates
2485     // to what chrome calls the 'page scale factor'.
2486     public boolean canZoomIn() {
2487         final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2488                 - mRenderCoordinates.getPageScaleFactor();
2489         return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2490     }
2491
2492     /**
2493      * Checks whether the ContentViewCore can be zoomed out.
2494      *
2495      * @return True if the ContentViewCore can be zoomed out.
2496      */
2497     // This method uses the term 'zoom' for legacy reasons, but relates
2498     // to what chrome calls the 'page scale factor'.
2499     public boolean canZoomOut() {
2500         final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2501                 - mRenderCoordinates.getMinPageScaleFactor();
2502         return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2503     }
2504
2505     /**
2506      * Zooms in the ContentViewCore by 25% (or less if that would result in
2507      * zooming in more than possible).
2508      *
2509      * @return True if there was a zoom change, false otherwise.
2510      */
2511     // This method uses the term 'zoom' for legacy reasons, but relates
2512     // to what chrome calls the 'page scale factor'.
2513     public boolean zoomIn() {
2514         if (!canZoomIn()) {
2515             return false;
2516         }
2517         return pinchByDelta(1.25f);
2518     }
2519
2520     /**
2521      * Zooms out the ContentViewCore by 20% (or less if that would result in
2522      * zooming out more than possible).
2523      *
2524      * @return True if there was a zoom change, false otherwise.
2525      */
2526     // This method uses the term 'zoom' for legacy reasons, but relates
2527     // to what chrome calls the 'page scale factor'.
2528     public boolean zoomOut() {
2529         if (!canZoomOut()) {
2530             return false;
2531         }
2532         return pinchByDelta(0.8f);
2533     }
2534
2535     /**
2536      * Resets the zoom factor of the ContentViewCore.
2537      *
2538      * @return True if there was a zoom change, false otherwise.
2539      */
2540     // This method uses the term 'zoom' for legacy reasons, but relates
2541     // to what chrome calls the 'page scale factor'.
2542     public boolean zoomReset() {
2543         // The page scale factor is initialized to mNativeMinimumScale when
2544         // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2545         if (!canZoomOut()) return false;
2546         return pinchByDelta(
2547                 mRenderCoordinates.getMinPageScaleFactor()
2548                         / mRenderCoordinates.getPageScaleFactor());
2549     }
2550
2551     /**
2552      * Simulate a pinch zoom gesture.
2553      *
2554      * @param delta the factor by which the current page scale should be multiplied by.
2555      * @return whether the gesture was sent.
2556      */
2557     public boolean pinchByDelta(float delta) {
2558         if (mNativeContentViewCore == 0) return false;
2559
2560         long timeMs = SystemClock.uptimeMillis();
2561         int xPix = getViewportWidthPix() / 2;
2562         int yPix = getViewportHeightPix() / 2;
2563
2564         nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2565         nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2566         nativePinchEnd(mNativeContentViewCore, timeMs);
2567
2568         return true;
2569     }
2570
2571     /**
2572      * Invokes the graphical zoom picker widget for this ContentView.
2573      */
2574     public void invokeZoomPicker() {
2575         mZoomControlsDelegate.invokeZoomPicker();
2576     }
2577
2578     /**
2579      * Enables or disables inspection of JavaScript objects added via
2580      * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2581      * &quot;for .. in&quot; loop. Being able to inspect JavaScript objects is useful
2582      * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2583      * to compatibility risks.
2584      *
2585      * @param allow Whether to allow JavaScript objects inspection.
2586      */
2587     public void setAllowJavascriptInterfacesInspection(boolean allow) {
2588         nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2589     }
2590
2591     /**
2592      * Returns JavaScript interface objects previously injected via
2593      * {@link #addJavascriptInterface(Object, String)}.
2594      *
2595      * @return the mapping of names to interface objects and corresponding annotation classes
2596      */
2597     public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2598         return mJavaScriptInterfaces;
2599     }
2600
2601     /**
2602      * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2603      * and automatically pass in {@link JavascriptInterface} as the required annotation.
2604      *
2605      * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2606      *               values are ignored.
2607      * @param name   The name used to expose the instance in JavaScript.
2608      */
2609     public void addJavascriptInterface(Object object, String name) {
2610         addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2611     }
2612
2613     /**
2614      * This method injects the supplied Java object into the ContentViewCore.
2615      * The object is injected into the JavaScript context of the main frame,
2616      * using the supplied name. This allows the Java object to be accessed from
2617      * JavaScript. Note that that injected objects will not appear in
2618      * JavaScript until the page is next (re)loaded. For example:
2619      * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2620      * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2621      * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2622      * <p><strong>IMPORTANT:</strong>
2623      * <ul>
2624      * <li> addJavascriptInterface() can be used to allow JavaScript to control
2625      * the host application. This is a powerful feature, but also presents a
2626      * security risk. Use of this method in a ContentViewCore containing
2627      * untrusted content could allow an attacker to manipulate the host
2628      * application in unintended ways, executing Java code with the permissions
2629      * of the host application. Use extreme care when using this method in a
2630      * ContentViewCore which could contain untrusted content. Particular care
2631      * should be taken to avoid unintentional access to inherited methods, such
2632      * as {@link Object#getClass()}. To prevent access to inherited methods,
2633      * pass an annotation for {@code requiredAnnotation}.  This will ensure
2634      * that only methods with {@code requiredAnnotation} are exposed to the
2635      * Javascript layer.  {@code requiredAnnotation} will be passed to all
2636      * subsequently injected Java objects if any methods return an object.  This
2637      * means the same restrictions (or lack thereof) will apply.  Alternatively,
2638      * {@link #addJavascriptInterface(Object, String)} can be called, which
2639      * automatically uses the {@link JavascriptInterface} annotation.
2640      * <li> JavaScript interacts with Java objects on a private, background
2641      * thread of the ContentViewCore. Care is therefore required to maintain
2642      * thread safety.</li>
2643      * </ul></p>
2644      *
2645      * @param object             The Java object to inject into the
2646      *                           ContentViewCore's JavaScript context. Null
2647      *                           values are ignored.
2648      * @param name               The name used to expose the instance in
2649      *                           JavaScript.
2650      * @param requiredAnnotation Restrict exposed methods to ones with this
2651      *                           annotation.  If {@code null} all methods are
2652      *                           exposed.
2653      *
2654      */
2655     public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2656             Class<? extends Annotation> requiredAnnotation) {
2657         if (mNativeContentViewCore != 0 && object != null) {
2658             mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2659             nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2660         }
2661     }
2662
2663     /**
2664      * Removes a previously added JavaScript interface with the given name.
2665      *
2666      * @param name The name of the interface to remove.
2667      */
2668     public void removeJavascriptInterface(String name) {
2669         mJavaScriptInterfaces.remove(name);
2670         if (mNativeContentViewCore != 0) {
2671             nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2672         }
2673     }
2674
2675     /**
2676      * Return the current scale of the ContentView.
2677      * @return The current page scale factor.
2678      */
2679     public float getScale() {
2680         return mRenderCoordinates.getPageScaleFactor();
2681     }
2682
2683     /**
2684      * If the view is ready to draw contents to the screen. In hardware mode,
2685      * the initialization of the surface texture may not occur until after the
2686      * view has been added to the layout. This method will return {@code true}
2687      * once the texture is actually ready.
2688      */
2689     public boolean isReady() {
2690         assert mWebContents != null;
2691         return mWebContents.isReady();
2692     }
2693
2694     @CalledByNative
2695     private void startContentIntent(String contentUrl) {
2696         getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2697     }
2698
2699     @Override
2700     public void onAccessibilityStateChanged(boolean enabled) {
2701         setAccessibilityState(enabled);
2702     }
2703
2704     /**
2705      * Determines whether or not this ContentViewCore can handle this accessibility action.
2706      * @param action The action to perform.
2707      * @return Whether or not this action is supported.
2708      */
2709     public boolean supportsAccessibilityAction(int action) {
2710         return mAccessibilityInjector.supportsAccessibilityAction(action);
2711     }
2712
2713     /**
2714      * Attempts to perform an accessibility action on the web content.  If the accessibility action
2715      * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2716      * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2717      * Otherwise the return value from this method should be used.
2718      * @param action The action to perform.
2719      * @param arguments Optional action arguments.
2720      * @return Whether the action was performed or {@code null} if the call should be delegated to
2721      *         the super {@link View} class.
2722      */
2723     public boolean performAccessibilityAction(int action, Bundle arguments) {
2724         if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2725             return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2726         }
2727
2728         return false;
2729     }
2730
2731     /**
2732      * Set the BrowserAccessibilityManager, used for native accessibility
2733      * (not script injection). This is only set when system accessibility
2734      * has been enabled.
2735      * @param manager The new BrowserAccessibilityManager.
2736      */
2737     public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2738         mBrowserAccessibilityManager = manager;
2739     }
2740
2741     /**
2742      * Get the BrowserAccessibilityManager, used for native accessibility
2743      * (not script injection). This will return null when system accessibility
2744      * is not enabled.
2745      * @return This view's BrowserAccessibilityManager.
2746      */
2747     public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2748         return mBrowserAccessibilityManager;
2749     }
2750
2751     /**
2752      * If native accessibility (not script injection) is enabled, and if this is
2753      * running on JellyBean or later, returns an AccessibilityNodeProvider that
2754      * implements native accessibility for this view. Returns null otherwise.
2755      * Lazily initializes native accessibility here if it's allowed.
2756      * @return The AccessibilityNodeProvider, if available, or null otherwise.
2757      */
2758     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2759         if (mBrowserAccessibilityManager != null) {
2760             return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2761         }
2762
2763         if (mNativeAccessibilityAllowed &&
2764                 !mNativeAccessibilityEnabled &&
2765                 mNativeContentViewCore != 0 &&
2766                 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2767             mNativeAccessibilityEnabled = true;
2768             nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2769         }
2770
2771         return null;
2772     }
2773
2774     /**
2775      * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2776      */
2777     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2778         // Note: this is only used by the script-injecting accessibility code.
2779         mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2780     }
2781
2782     /**
2783      * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2784      */
2785     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2786         // Note: this is only used by the script-injecting accessibility code.
2787         event.setClassName(this.getClass().getName());
2788
2789         // Identify where the top-left of the screen currently points to.
2790         event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2791         event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2792
2793         // The maximum scroll values are determined by taking the content dimensions and
2794         // subtracting off the actual dimensions of the ChromeView.
2795         int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2796         int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2797         event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2798
2799         // Setting the maximum scroll values requires API level 15 or higher.
2800         final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2801         if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2802             event.setMaxScrollX(maxScrollXPix);
2803             event.setMaxScrollY(maxScrollYPix);
2804         }
2805     }
2806
2807     /**
2808      * Returns whether accessibility script injection is enabled on the device
2809      */
2810     public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2811         try {
2812             // On JellyBean and higher, native accessibility is the default so script
2813             // injection is only allowed if enabled via a flag.
2814             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
2815                     !CommandLine.getInstance().hasSwitch(
2816                             ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2817                 return false;
2818             }
2819
2820             if (!mContentSettings.getJavaScriptEnabled()) {
2821                 return false;
2822             }
2823
2824             int result = getContext().checkCallingOrSelfPermission(
2825                     android.Manifest.permission.INTERNET);
2826             if (result != PackageManager.PERMISSION_GRANTED) {
2827                 return false;
2828             }
2829
2830             Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2831             field.setAccessible(true);
2832             String accessibilityScriptInjection = (String) field.get(null);
2833             ContentResolver contentResolver = getContext().getContentResolver();
2834
2835             if (mAccessibilityScriptInjectionObserver == null) {
2836                 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2837                     @Override
2838                     public void onChange(boolean selfChange, Uri uri) {
2839                         setAccessibilityState(mAccessibilityManager.isEnabled());
2840                     }
2841                 };
2842                 contentResolver.registerContentObserver(
2843                     Settings.Secure.getUriFor(accessibilityScriptInjection),
2844                     false,
2845                     contentObserver);
2846                 mAccessibilityScriptInjectionObserver = contentObserver;
2847             }
2848
2849             return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2850         } catch (NoSuchFieldException e) {
2851             // Do nothing, default to false.
2852         } catch (IllegalAccessException e) {
2853             // Do nothing, default to false.
2854         }
2855         return false;
2856     }
2857
2858     /**
2859      * Returns whether or not accessibility injection is being used.
2860      */
2861     public boolean isInjectingAccessibilityScript() {
2862         return mAccessibilityInjector.accessibilityIsAvailable();
2863     }
2864
2865     /**
2866      * Returns true if accessibility is on and touch exploration is enabled.
2867      */
2868     public boolean isTouchExplorationEnabled() {
2869         return mTouchExplorationEnabled;
2870     }
2871
2872     /**
2873      * Turns browser accessibility on or off.
2874      * If |state| is |false|, this turns off both native and injected accessibility.
2875      * Otherwise, if accessibility script injection is enabled, this will enable the injected
2876      * accessibility scripts. Native accessibility is enabled on demand.
2877      */
2878     public void setAccessibilityState(boolean state) {
2879         if (!state) {
2880             setInjectedAccessibility(false);
2881             mNativeAccessibilityAllowed = false;
2882             mTouchExplorationEnabled = false;
2883         } else {
2884             boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2885             setInjectedAccessibility(useScriptInjection);
2886             mNativeAccessibilityAllowed = !useScriptInjection;
2887             mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2888         }
2889     }
2890
2891     /**
2892      * Enable or disable injected accessibility features
2893      */
2894     public void setInjectedAccessibility(boolean enabled) {
2895         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2896         mAccessibilityInjector.setScriptEnabled(enabled);
2897     }
2898
2899     /**
2900      * Stop any TTS notifications that are currently going on.
2901      */
2902     public void stopCurrentAccessibilityNotifications() {
2903         mAccessibilityInjector.onPageLostFocus();
2904     }
2905
2906     /**
2907      * Return whether or not we should set accessibility focus on page load.
2908      */
2909     public boolean shouldSetAccessibilityFocusOnPageLoad() {
2910         return mShouldSetAccessibilityFocusOnPageLoad;
2911     }
2912
2913     /**
2914      * Return whether or not we should set accessibility focus on page load.
2915      * This only applies if an accessibility service like TalkBack is running.
2916      * This is desirable behavior for a browser window, but not for an embedded
2917      * WebView.
2918      */
2919     public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
2920         mShouldSetAccessibilityFocusOnPageLoad = on;
2921     }
2922
2923     /**
2924      * Inform WebKit that Fullscreen mode has been exited by the user.
2925      */
2926     public void exitFullscreen() {
2927         assert mWebContents != null;
2928         mWebContents.exitFullscreen();
2929     }
2930
2931     /**
2932      * Changes whether hiding the top controls is enabled.
2933      *
2934      * @param enableHiding Whether hiding the top controls should be enabled or not.
2935      * @param enableShowing Whether showing the top controls should be enabled or not.
2936      * @param animate Whether the transition should be animated or not.
2937      */
2938     public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2939             boolean animate) {
2940         assert mWebContents != null;
2941         mWebContents.updateTopControlsState(
2942                 enableHiding, enableShowing, animate);
2943     }
2944
2945     /**
2946      * Callback factory method for nativeGetNavigationHistory().
2947      */
2948     @CalledByNative
2949     private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2950             String originalUrl, String title, Bitmap favicon) {
2951         NavigationEntry entry = new NavigationEntry(
2952                 index, url, virtualUrl, originalUrl, title, favicon);
2953         ((NavigationHistory) history).addEntry(entry);
2954     }
2955
2956     /**
2957      * Get a copy of the navigation history of the view.
2958      */
2959     public NavigationHistory getNavigationHistory() {
2960         NavigationHistory history = new NavigationHistory();
2961         if (mNativeContentViewCore != 0) {
2962             int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2963             history.setCurrentEntryIndex(currentIndex);
2964         }
2965         return history;
2966     }
2967
2968     @Override
2969     public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2970         NavigationHistory history = new NavigationHistory();
2971         if (mNativeContentViewCore != 0) {
2972             nativeGetDirectedNavigationHistory(
2973                 mNativeContentViewCore, history, isForward, itemLimit);
2974         }
2975         return history;
2976     }
2977
2978     /**
2979      * @return The original request URL for the current navigation entry, or null if there is no
2980      *         current entry.
2981      */
2982     public String getOriginalUrlForActiveNavigationEntry() {
2983         if (mNativeContentViewCore != 0) {
2984             return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2985         }
2986         return "";
2987     }
2988
2989     /**
2990      * @return The cached copy of render positions and scales.
2991      */
2992     public RenderCoordinates getRenderCoordinates() {
2993         return mRenderCoordinates;
2994     }
2995
2996     @CalledByNative
2997     private static Rect createRect(int x, int y, int right, int bottom) {
2998         return new Rect(x, y, right, bottom);
2999     }
3000
3001     public void extractSmartClipData(int x, int y, int width, int height) {
3002         if (mNativeContentViewCore != 0) {
3003             x += mSmartClipOffsetX;
3004             y += mSmartClipOffsetY;
3005             nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3006         }
3007     }
3008
3009     /**
3010      * Set offsets for smart clip.
3011      *
3012      * <p>This should be called if there is a viewport change introduced by,
3013      * e.g., show and hide of a location bar.
3014      *
3015      * @param offsetX Offset for X position.
3016      * @param offsetY Offset for Y position.
3017      */
3018     public void setSmartClipOffsets(int offsetX, int offsetY) {
3019         mSmartClipOffsetX = offsetX;
3020         mSmartClipOffsetY = offsetY;
3021     }
3022
3023     @CalledByNative
3024     private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
3025         if (mSmartClipDataListener != null ) {
3026             mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
3027         }
3028     }
3029
3030     public void setSmartClipDataListener(SmartClipDataListener listener) {
3031         mSmartClipDataListener = listener;
3032     }
3033
3034     public void setBackgroundOpaque(boolean opaque) {
3035         if (mNativeContentViewCore != 0) {
3036             nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
3037         }
3038     }
3039
3040     /**
3041      * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3042      *
3043      * @return true if the embedder handled the event.
3044      */
3045     private boolean offerLongPressToEmbedder() {
3046         return mContainerView.performLongClick();
3047     }
3048
3049     /**
3050      * Reset scroll and fling accounting, notifying listeners as appropriate.
3051      * This is useful as a failsafe when the input stream may have been interruped.
3052      */
3053     private void resetScrollInProgress() {
3054         if (!isScrollInProgress()) return;
3055
3056         final boolean touchScrollInProgress = mTouchScrollInProgress;
3057         final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
3058
3059         mTouchScrollInProgress = false;
3060         mPotentiallyActiveFlingCount = 0;
3061
3062         if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
3063         if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
3064     }
3065
3066     private native long nativeInit(long webContentsPtr,
3067             long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
3068
3069     @CalledByNative
3070     private ContentVideoViewClient getContentVideoViewClient() {
3071         return getContentViewClient().getContentVideoViewClient();
3072     }
3073
3074     @CalledByNative
3075     private boolean shouldBlockMediaRequest(String url) {
3076         return getContentViewClient().shouldBlockMediaRequest(url);
3077     }
3078
3079     @CalledByNative
3080     private void onNativeFlingStopped() {
3081         // Note that mTouchScrollInProgress should normally be false at this
3082         // point, but we reset it anyway as another failsafe.
3083         mTouchScrollInProgress = false;
3084         if (mPotentiallyActiveFlingCount <= 0) return;
3085         mPotentiallyActiveFlingCount--;
3086         updateGestureStateListener(GestureEventType.FLING_END);
3087     }
3088
3089     @Override
3090     public void onScreenOrientationChanged(int orientation) {
3091         sendOrientationChangeEvent(orientation);
3092     }
3093
3094     public void resumeResponseDeferredAtStart() {
3095         assert mWebContents != null;
3096         mWebContents.resumeResponseDeferredAtStart();
3097     }
3098
3099     /**
3100      * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
3101      * the screen orientation.
3102      */
3103     public void setFullscreenRequiredForOrientationLock(boolean value) {
3104         mFullscreenRequiredForOrientationLock = value;
3105     }
3106
3107     @CalledByNative
3108     private boolean isFullscreenRequiredForOrientationLock() {
3109         return mFullscreenRequiredForOrientationLock;
3110     }
3111
3112     private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3113
3114     private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3115
3116     private native void nativeLoadUrl(
3117             long nativeContentViewCoreImpl,
3118             String url,
3119             int loadUrlType,
3120             int transitionType,
3121             String referrerUrl,
3122             int referrerPolicy,
3123             int uaOverrideOption,
3124             String extraHeaders,
3125             byte[] postData,
3126             String baseUrlForDataUrl,
3127             String virtualUrlForDataUrl,
3128             boolean canLoadLocalResources,
3129             boolean isRendererInitiated);
3130
3131     private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3132
3133     private native void nativeSendOrientationChangeEvent(
3134             long nativeContentViewCoreImpl, int orientation);
3135
3136     // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3137     private native boolean nativeOnTouchEvent(
3138             long nativeContentViewCoreImpl, MotionEvent event,
3139             long timeMs, int action, int pointerCount, int historySize, int actionIndex,
3140             float x0, float y0, float x1, float y1,
3141             int pointerId0, int pointerId1,
3142             float touchMajor0, float touchMajor1,
3143             float rawX, float rawY,
3144             int androidToolType0, int androidToolType1, int androidButtonState,
3145             boolean isTouchHandleEvent);
3146
3147     private native int nativeSendMouseMoveEvent(
3148             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3149
3150     private native int nativeSendMouseWheelEvent(
3151             long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3152
3153     private native void nativeScrollBegin(
3154             long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3155             float hintY);
3156
3157     private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3158
3159     private native void nativeScrollBy(
3160             long nativeContentViewCoreImpl, long timeMs, float x, float y,
3161             float deltaX, float deltaY);
3162
3163     private native void nativeFlingStart(
3164             long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3165
3166     private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3167
3168     private native void nativeSingleTap(
3169             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3170
3171     private native void nativeDoubleTap(
3172             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3173
3174     private native void nativeLongPress(
3175             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3176
3177     private native void nativePinchBegin(
3178             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3179
3180     private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3181
3182     private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3183             float anchorX, float anchorY, float deltaScale);
3184
3185     private native void nativeSelectBetweenCoordinates(
3186             long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3187
3188     private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3189
3190     private native void nativeHideTextHandles(long nativeContentViewCoreImpl);
3191
3192     private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3193
3194     private native void nativeSetDoubleTapSupportEnabled(
3195             long nativeContentViewCoreImpl, boolean enabled);
3196
3197     private native void nativeSetMultiTouchZoomSupportEnabled(
3198             long nativeContentViewCoreImpl, boolean enabled);
3199
3200     private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3201
3202     private native void nativeClearHistory(long nativeContentViewCoreImpl);
3203
3204     private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId,
3205             String message, String sourceOrigin, String targetOrigin);
3206
3207     private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3208
3209     private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3210
3211     private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3212             boolean enabled, boolean reloadOnChange);
3213
3214     private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3215
3216     private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3217
3218     private native void nativeSetAllowJavascriptInterfacesInspection(
3219             long nativeContentViewCoreImpl, boolean allow);
3220
3221     private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3222             String name, Class requiredAnnotation);
3223
3224     private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3225             String name);
3226
3227     private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3228
3229     private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3230             Object context, boolean isForward, int maxEntries);
3231
3232     private native String nativeGetOriginalUrlForActiveNavigationEntry(
3233             long nativeContentViewCoreImpl);
3234
3235     private native void nativeWasResized(long nativeContentViewCoreImpl);
3236
3237     private native void nativeSetAccessibilityEnabled(
3238             long nativeContentViewCoreImpl, boolean enabled);
3239
3240     private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3241             int x, int y, int w, int h);
3242
3243     private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);
3244 }