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