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