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