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