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