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