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