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