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