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