ContentViewCore.java revision a3f7b4e666c476898878fa745f637129375cd889
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)package org.chromium.content.browser;
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.app.Activity;
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.ContentResolver;
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.Context;
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.pm.ActivityInfo;
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.pm.PackageManager;
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.res.Configuration;
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.database.ContentObserver;
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Bitmap;
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Canvas;
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Color;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Rect;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.net.Uri;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.os.Build;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.os.Bundle;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.os.Handler;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.os.ResultReceiver;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.provider.Settings;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.provider.Settings.Secure;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.text.Editable;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.util.Log;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.util.Pair;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.ActionMode;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.InputDevice;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.KeyEvent;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.MotionEvent;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.Surface;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.View;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.ViewGroup;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.Window;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.WindowManager;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.accessibility.AccessibilityEvent;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.accessibility.AccessibilityManager;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.accessibility.AccessibilityNodeInfo;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.accessibility.AccessibilityNodeProvider;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.inputmethod.EditorInfo;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.inputmethod.InputConnection;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.view.inputmethod.InputMethodManager;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.widget.FrameLayout;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.google.common.annotations.VisibleForTesting;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.base.CalledByNative;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.base.JNINamespace;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.base.WeakContext;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.R;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.accessibility.AccessibilityInjector;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.AdapterInputConnection;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.HandleView;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.ImeAdapter;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.InputMethodManagerWrapper;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.InsertionHandleController;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.SelectPopupDialog;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.browser.input.SelectionHandleController;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.content.common.TraceEvent;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.ui.ViewAndroid;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.ui.ViewAndroidDelegate;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.ui.WindowAndroid;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.chromium.ui.gfx.DeviceDisplayInfo;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.lang.annotation.Annotation;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.lang.reflect.Field;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.Arrays;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.HashMap;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.HashSet;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.Map;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Provides a Java-side 'wrapper' around a WebContent (native) instance.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * being tied to the view system.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@JNINamespace("content")
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public class ContentViewCore implements MotionEventDelegate,
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            NavigationClient,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            AccessibilityStateChangeListener {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Indicates that input events are batched together and delivered just before vsync.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public static final int INPUT_EVENTS_DELIVERED_AT_VSYNC = 1;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Opposite of INPUT_EVENTS_DELIVERED_AT_VSYNC.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public static final int INPUT_EVENTS_DELIVERED_IMMEDIATELY = 0;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private static final String TAG = "ContentViewCore";
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Used to avoid enabling zooming in / out if resulting zooming will
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // produce little visible difference.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Used to represent gestures for long press and long tap.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private static final int IS_LONG_PRESS = 1;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private static final int IS_LONG_TAP = 2;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Length of the delay (in ms) before fading in handles after the last page movement.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the embedder adds a JavaScript interface object that contains an indirect reference to
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the ContentViewCore, then storing a strong ref to the interface object on the native
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // side would prevent garbage collection of the ContentViewCore (as that strong ref would
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // create a new GC root).
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // For that reason, we store only a weak reference to the interface object on the
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // native side. However we still need a strong reference on the Java side to
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // prevent garbage collection if the embedder doesn't maintain their own ref to the
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // interface object - the Java side ref won't create a new GC root.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This map stores those refernces. We put into the map on addJavaScriptInterface()
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // and remove from it in removeJavaScriptInterface().
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Additionally, we keep track of all Java bound JS objects that are in use on the
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // current page to ensure that they are not garbage collected until the page is
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // navigated. This includes interface objects that have been removed
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // via the removeJavaScriptInterface API and transient objects returned from methods
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // on the interface object. Note we use HashSet rather than Set as the native side
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // expects HashSet (no bindings for interfaces).
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * dispatching of view methods through the containing view.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * <p>
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * All methods with the "super_" prefix should be routed to the parent of the
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * implementing container view.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    @SuppressWarnings("javadoc")
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public interface InternalAccessDelegate {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#drawChild(Canvas, View, long)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean drawChild(Canvas canvas, View child, long drawingTime);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#onKeyUp(keyCode, KeyEvent)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean super_onKeyUp(int keyCode, KeyEvent event);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#dispatchKeyEventPreIme(KeyEvent)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean super_dispatchKeyEventPreIme(KeyEvent event);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#dispatchKeyEvent(KeyEvent)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean super_dispatchKeyEvent(KeyEvent event);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#onGenericMotionEvent(MotionEvent)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean super_onGenericMotionEvent(MotionEvent event);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#onConfigurationChanged(Configuration)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void super_onConfigurationChanged(Configuration newConfig);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#awakenScrollBars()
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean awakenScrollBars();
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @see View#awakenScrollBars(int, boolean)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        boolean super_awakenScrollBars(int startDelay, boolean invalidate);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * An interface that allows the embedder to be notified when the pinch gesture starts and
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * stops.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public interface PinchGestureStateListener {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * Called when the pinch gesture starts.
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void onPinchGestureStart();
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * Called when the pinch gesture ends.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void onPinchGestureEnd();
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * An interface for controlling visibility and state of embedder-provided zoom controls.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public interface ZoomControlsDelegate {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * Called when it's reasonable to show zoom controls.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void invokeZoomPicker();
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * Called when zoom controls need to be hidden (e.g. when the view hides).
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void dismissZoomPicker();
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * Called when page scale has been changed, so the controls can update their state.
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void updateZoomControls();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * An interface that allows the embedder to be notified of changes to the parameters of the
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * currently displayed contents.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * These notifications are consistent with respect to the UI thread (the size is the size of
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the contents currently displayed on screen).
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public interface UpdateFrameInfoListener {
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /**
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * Called each time any of the parameters are changed.
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         *
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @param widthCss The content width in logical (CSS) pixels.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @param heightCss The content height in logical (CSS) pixels.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         * @param pageScaleFactor The page scale.
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         */
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        void onFrameInfoUpdated(float widthCss, float heightCss, float pageScaleFactor);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private VSyncManager.Provider mVSyncProvider;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private VSyncManager.Listener mVSyncListener;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private int mVSyncSubscriberCount;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private boolean mVSyncListenerRegistered;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // When we do this, we also need to avoid sending the real vsync signal for the current
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    private boolean mDidSignalVSyncUsingInputEvent;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (mVSyncProvider != null && mVSyncListenerRegistered) {
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            mVSyncProvider.unregisterVSyncListener(mVSyncListener);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            mVSyncListenerRegistered = false;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mVSyncProvider = vsyncProvider;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mVSyncListener = new VSyncManager.Listener() {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            @Override
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            public void updateVSync(long tickTimeMicros, long intervalMicros) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                if (mNativeContentViewCore != 0) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            intervalMicros);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            @Override
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            public void onVSync(long frameTimeMicros) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                animateIfNecessary(frameTimeMicros);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                if (mDidSignalVSyncUsingInputEvent) {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    TraceEvent.instant("ContentViewCore::onVSync ignored");
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    mDidSignalVSyncUsingInputEvent = false;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    return;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                if (mNativeContentViewCore != 0) {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        };
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (mVSyncSubscriberCount > 0) {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // setVSyncNotificationEnabled(true) is called before getVSyncListener.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            vsyncProvider.registerVSyncListener(mVSyncListener);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            mVSyncListenerRegistered = true;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return mVSyncListener;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    @CalledByNative
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void setVSyncNotificationEnabled(boolean enabled) {
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!isVSyncNotificationEnabled() && enabled) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            mDidSignalVSyncUsingInputEvent = false;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (mVSyncProvider != null) {
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            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    @CalledByNative
887    private void onBackgroundColorChanged(int color) {
888        getContentViewClient().onBackgroundColorChanged(color);
889    }
890
891    /**
892     * Load url without fixing up the url string. Consumers of ContentView are responsible for
893     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
894     * off during user input).
895     *
896     * @param params Parameters for this load.
897     */
898    public void loadUrl(LoadUrlParams params) {
899        if (mNativeContentViewCore == 0) return;
900
901        nativeLoadUrl(mNativeContentViewCore,
902                params.mUrl,
903                params.mLoadUrlType,
904                params.mTransitionType,
905                params.mUaOverrideOption,
906                params.getExtraHeadersString(),
907                params.mPostData,
908                params.mBaseUrlForDataUrl,
909                params.mVirtualUrlForDataUrl,
910                params.mCanLoadLocalResources);
911    }
912
913    /**
914     * Stops loading the current web contents.
915     */
916    public void stopLoading() {
917        if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
918    }
919
920    /**
921     * Get the URL of the current page.
922     *
923     * @return The URL of the current page.
924     */
925    public String getUrl() {
926        if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
927        return null;
928    }
929
930    /**
931     * Get the title of the current page.
932     *
933     * @return The title of the current page.
934     */
935    public String getTitle() {
936        if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
937        return null;
938    }
939
940    /**
941     * Shows an interstitial page driven by the passed in delegate.
942     *
943     * @param url The URL being blocked by the interstitial.
944     * @param delegate The delegate handling the interstitial.
945     */
946    @VisibleForTesting
947    public void showInterstitialPage(
948            String url, InterstitialPageDelegateAndroid delegate) {
949        if (mNativeContentViewCore == 0) return;
950        nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
951    }
952
953    /**
954     * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
955     */
956    public boolean isShowingInterstitialPage() {
957        return mNativeContentViewCore == 0 ?
958                false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
959    }
960
961    /**
962     * Mark any new frames that have arrived since this function was last called as non-pending.
963     *
964     * @return Whether there was a pending frame from the renderer.
965     */
966    public boolean consumePendingRendererFrame() {
967        boolean hadPendingFrame = mPendingRendererFrame;
968        mPendingRendererFrame = false;
969        return hadPendingFrame;
970    }
971
972    /**
973     * @return Viewport width in physical pixels as set from onSizeChanged.
974     */
975    @CalledByNative
976    public int getViewportWidthPix() { return mViewportWidthPix; }
977
978    /**
979     * @return Viewport height in physical pixels as set from onSizeChanged.
980     */
981    @CalledByNative
982    public int getViewportHeightPix() { return mViewportHeightPix; }
983
984    /**
985     * @return Width of underlying physical surface.
986     */
987    @CalledByNative
988    public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
989
990    /**
991     * @return Height of underlying physical surface.
992     */
993    @CalledByNative
994    public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
995
996    /**
997     * @return Amount the output surface extends past the bottom of the window viewport.
998     */
999    @CalledByNative
1000    public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
1001
1002    /**
1003     * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
1004     */
1005    @CalledByNative
1006    public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
1007
1008    /**
1009     * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
1010     */
1011    @CalledByNative
1012    public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
1013
1014    /**
1015     * @see android.webkit.WebView#getContentHeight()
1016     */
1017    public float getContentHeightCss() {
1018        return mRenderCoordinates.getContentHeightCss();
1019    }
1020
1021    /**
1022     * @see android.webkit.WebView#getContentWidth()
1023     */
1024    public float getContentWidthCss() {
1025        return mRenderCoordinates.getContentWidthCss();
1026    }
1027
1028    public Bitmap getBitmap() {
1029        return getBitmap(getViewportWidthPix(), getViewportHeightPix());
1030    }
1031
1032    public Bitmap getBitmap(int width, int height) {
1033        if (width == 0 || height == 0
1034                || getViewportWidthPix() == 0 || getViewportHeightPix() == 0) {
1035            return null;
1036        }
1037
1038        Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1039
1040        if (mNativeContentViewCore != 0 &&
1041                nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) {
1042            // If we successfully grabbed a bitmap, check if we have to draw the Android overlay
1043            // components as well.
1044            if (mContainerView.getChildCount() > 0) {
1045                Canvas c = new Canvas(b);
1046                c.scale(width / (float) getViewportWidthPix(),
1047                        height / (float) getViewportHeightPix());
1048                mContainerView.draw(c);
1049            }
1050            return b;
1051        }
1052
1053        return null;
1054    }
1055
1056    /**
1057     * Generates a bitmap of the content that is performance optimized based on capture time.
1058     *
1059     * <p>
1060     * To have a consistent capture time across devices, we will scale down the captured bitmap
1061     * where necessary to reduce the time to generate the bitmap.
1062     *
1063     * @param width The width of the content to be captured.
1064     * @param height The height of the content to be captured.
1065     * @return A pair of the generated bitmap, and the scale that needs to be applied to return the
1066     *         bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this
1067     *         will be 2).
1068     */
1069    public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
1070        float scale = 1f;
1071        // On tablets, always scale down to MDPI for performance reasons.
1072        if (DeviceUtils.isTablet(getContext())) {
1073            scale = getContext().getResources().getDisplayMetrics().density;
1074        }
1075        return Pair.create(
1076                getBitmap((int) (width / scale), (int) (height / scale)),
1077                scale);
1078    }
1079
1080    /**
1081     * @return Whether the current WebContents has a previous navigation entry.
1082     */
1083    public boolean canGoBack() {
1084        return mNativeContentViewCore != 0 && nativeCanGoBack(mNativeContentViewCore);
1085    }
1086
1087    /**
1088     * @return Whether the current WebContents has a navigation entry after the current one.
1089     */
1090    public boolean canGoForward() {
1091        return mNativeContentViewCore != 0 && nativeCanGoForward(mNativeContentViewCore);
1092    }
1093
1094    /**
1095     * @param offset The offset into the navigation history.
1096     * @return Whether we can move in history by given offset
1097     */
1098    public boolean canGoToOffset(int offset) {
1099        return mNativeContentViewCore != 0 && nativeCanGoToOffset(mNativeContentViewCore, offset);
1100    }
1101
1102    /**
1103     * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1104     * of bounds.
1105     * @param offset The offset into the navigation history.
1106     */
1107    public void goToOffset(int offset) {
1108        if (mNativeContentViewCore != 0) nativeGoToOffset(mNativeContentViewCore, offset);
1109    }
1110
1111    @Override
1112    public void goToNavigationIndex(int index) {
1113        if (mNativeContentViewCore != 0) nativeGoToNavigationIndex(mNativeContentViewCore, index);
1114    }
1115
1116    /**
1117     * Goes to the navigation entry before the current one.
1118     */
1119    public void goBack() {
1120        if (mNativeContentViewCore != 0) nativeGoBack(mNativeContentViewCore);
1121    }
1122
1123    /**
1124     * Goes to the navigation entry following the current one.
1125     */
1126    public void goForward() {
1127        if (mNativeContentViewCore != 0) nativeGoForward(mNativeContentViewCore);
1128    }
1129
1130    /**
1131     * Reload the current page.
1132     */
1133    public void reload() {
1134        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1135        if (mNativeContentViewCore != 0) nativeReload(mNativeContentViewCore);
1136    }
1137
1138    /**
1139     * Cancel the pending reload.
1140     */
1141    public void cancelPendingReload() {
1142        if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1143    }
1144
1145    /**
1146     * Continue the pending reload.
1147     */
1148    public void continuePendingReload() {
1149        if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1150    }
1151
1152    /**
1153     * Clears the ContentViewCore's page history in both the backwards and
1154     * forwards directions.
1155     */
1156    public void clearHistory() {
1157        if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1158    }
1159
1160    String getSelectedText() {
1161        return mHasSelection ? mLastSelectedText : "";
1162    }
1163
1164    // End FrameLayout overrides.
1165
1166    /**
1167     * @see {@link android.webkit.WebView#flingScroll(int, int)}
1168     */
1169    public void flingScroll(int vx, int vy) {
1170        // Notes:
1171        //   (1) Use large negative values for the x/y parameters so we don't accidentally scroll a
1172        //       nested frame.
1173        //   (2) vx and vy are inverted to match WebView behavior.
1174        mContentViewGestureHandler.fling(
1175                System.currentTimeMillis(), -Integer.MAX_VALUE, -Integer.MIN_VALUE, -vx, -vy);
1176    }
1177
1178    /**
1179     * @see View#onTouchEvent(MotionEvent)
1180     */
1181    public boolean onTouchEvent(MotionEvent event) {
1182        undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1183        return mContentViewGestureHandler.onTouchEvent(event);
1184    }
1185
1186    /**
1187     * @return ContentViewGestureHandler for all MotionEvent and gesture related calls.
1188     */
1189    ContentViewGestureHandler getContentViewGestureHandler() {
1190        return mContentViewGestureHandler;
1191    }
1192
1193    @Override
1194    public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
1195        if (mNativeContentViewCore != 0) {
1196            return nativeSendTouchEvent(mNativeContentViewCore, timeMs, action, pts);
1197        }
1198        return false;
1199    }
1200
1201    @SuppressWarnings("unused")
1202    @CalledByNative
1203    private void hasTouchEventHandlers(boolean hasTouchHandlers) {
1204        mContentViewGestureHandler.hasTouchEventHandlers(hasTouchHandlers);
1205    }
1206
1207    @SuppressWarnings("unused")
1208    @CalledByNative
1209    private void confirmTouchEvent(int ackResult) {
1210        mContentViewGestureHandler.confirmTouchEvent(ackResult);
1211    }
1212
1213    @Override
1214    public boolean sendGesture(int type, long timeMs, int x, int y, boolean lastInputEventForVSync,
1215                               Bundle b) {
1216        if (offerGestureToEmbedder(type)) return false;
1217        if (mNativeContentViewCore == 0) return false;
1218        updateTextHandlesForGesture(type);
1219        updatePinchGestureStateListener(type);
1220        if (lastInputEventForVSync && isVSyncNotificationEnabled()) {
1221            assert type == ContentViewGestureHandler.GESTURE_SCROLL_BY ||
1222                    type == ContentViewGestureHandler.GESTURE_PINCH_BY;
1223            mDidSignalVSyncUsingInputEvent = true;
1224        }
1225        switch (type) {
1226            case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE:
1227                nativeShowPressState(mNativeContentViewCore, timeMs, x, y);
1228                return true;
1229            case ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL:
1230                nativeShowPressCancel(mNativeContentViewCore, timeMs, x, y);
1231                return true;
1232            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
1233                nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1234                return true;
1235            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP:
1236                nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1237                return true;
1238            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED:
1239                handleTapOrPress(timeMs, x, y, 0,
1240                        b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false));
1241                return true;
1242            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED:
1243                nativeSingleTapUnconfirmed(mNativeContentViewCore, timeMs, x, y);
1244                return true;
1245            case ContentViewGestureHandler.GESTURE_LONG_PRESS:
1246                handleTapOrPress(timeMs, x, y, IS_LONG_PRESS, false);
1247                return true;
1248            case ContentViewGestureHandler.GESTURE_LONG_TAP:
1249                handleTapOrPress(timeMs, x, y, IS_LONG_TAP, false);
1250                return true;
1251            case ContentViewGestureHandler.GESTURE_SCROLL_START:
1252                nativeScrollBegin(mNativeContentViewCore, timeMs, x, y);
1253                return true;
1254            case ContentViewGestureHandler.GESTURE_SCROLL_BY: {
1255                int dx = b.getInt(ContentViewGestureHandler.DISTANCE_X);
1256                int dy = b.getInt(ContentViewGestureHandler.DISTANCE_Y);
1257                nativeScrollBy(mNativeContentViewCore, timeMs, x, y, dx, dy,
1258                        lastInputEventForVSync);
1259                return true;
1260            }
1261            case ContentViewGestureHandler.GESTURE_SCROLL_END:
1262                nativeScrollEnd(mNativeContentViewCore, timeMs);
1263                return true;
1264            case ContentViewGestureHandler.GESTURE_FLING_START:
1265                nativeFlingStart(mNativeContentViewCore, timeMs, x, y,
1266                        b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
1267                        b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
1268                return true;
1269            case ContentViewGestureHandler.GESTURE_FLING_CANCEL:
1270                nativeFlingCancel(mNativeContentViewCore, timeMs);
1271                return true;
1272            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
1273                nativePinchBegin(mNativeContentViewCore, timeMs, x, y);
1274                return true;
1275            case ContentViewGestureHandler.GESTURE_PINCH_BY:
1276                nativePinchBy(mNativeContentViewCore, timeMs, x, y,
1277                        b.getFloat(ContentViewGestureHandler.DELTA, 0),
1278                        lastInputEventForVSync);
1279                return true;
1280            case ContentViewGestureHandler.GESTURE_PINCH_END:
1281                nativePinchEnd(mNativeContentViewCore, timeMs);
1282                return true;
1283            default:
1284                return false;
1285        }
1286    }
1287
1288    public void setPinchGestureStateListener(PinchGestureStateListener pinchGestureStateListener) {
1289        mPinchGestureStateListener = pinchGestureStateListener;
1290    }
1291
1292    void updatePinchGestureStateListener(int gestureType) {
1293        if (mPinchGestureStateListener == null) return;
1294
1295        switch (gestureType) {
1296            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
1297                mPinchGestureStateListener.onPinchGestureStart();
1298                break;
1299            case ContentViewGestureHandler.GESTURE_PINCH_END:
1300                mPinchGestureStateListener.onPinchGestureEnd();
1301                break;
1302            default:
1303                break;
1304        }
1305    }
1306
1307    public interface JavaScriptCallback {
1308        void handleJavaScriptResult(String jsonResult);
1309    }
1310
1311    /**
1312     * Injects the passed Javascript code in the current page and evaluates it.
1313     * If a result is required, pass in a callback.
1314     * Used in automation tests.
1315     *
1316     * @param script The Javascript to execute.
1317     * @param callback The callback to be fired off when a result is ready. The script's
1318     *                 result will be json encoded and passed as the parameter, and the call
1319     *                 will be made on the main thread.
1320     *                 If no result is required, pass null.
1321     * @throws IllegalStateException If the ContentView has been destroyed.
1322     */
1323    public void evaluateJavaScript(
1324            String script, JavaScriptCallback callback) throws IllegalStateException {
1325        checkIsAlive();
1326        nativeEvaluateJavaScript(mNativeContentViewCore, script, callback);
1327    }
1328
1329    /**
1330     * This method should be called when the containing activity is paused.
1331     */
1332    public void onActivityPause() {
1333        TraceEvent.begin();
1334        hidePopupDialog();
1335        nativeOnHide(mNativeContentViewCore);
1336        TraceEvent.end();
1337    }
1338
1339    /**
1340     * This method should be called when the containing activity is resumed.
1341     */
1342    public void onActivityResume() {
1343        nativeOnShow(mNativeContentViewCore);
1344        setAccessibilityState(mAccessibilityManager.isEnabled());
1345    }
1346
1347    /**
1348     * To be called when the ContentView is shown.
1349     */
1350    public void onShow() {
1351        nativeOnShow(mNativeContentViewCore);
1352        setAccessibilityState(mAccessibilityManager.isEnabled());
1353    }
1354
1355    /**
1356     * To be called when the ContentView is hidden.
1357     */
1358    public void onHide() {
1359        hidePopupDialog();
1360        setInjectedAccessibility(false);
1361        nativeOnHide(mNativeContentViewCore);
1362    }
1363
1364    /**
1365     * Return the ContentSettings object used to retrieve the settings for this
1366     * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1367     * @return A ContentSettings object that can be used to retrieve this
1368     *         ContentViewCore's settings.
1369     */
1370    public ContentSettings getContentSettings() {
1371        return mContentSettings;
1372    }
1373
1374    @Override
1375    public boolean didUIStealScroll(float x, float y) {
1376        return getContentViewClient().shouldOverrideScroll(
1377                x, y, computeHorizontalScrollOffset(), computeVerticalScrollOffset());
1378    }
1379
1380    @Override
1381    public boolean hasFixedPageScale() {
1382        return mRenderCoordinates.hasFixedPageScale();
1383    }
1384
1385    private void hidePopupDialog() {
1386        SelectPopupDialog.hide(this);
1387        hideHandles();
1388        hideSelectActionBar();
1389    }
1390
1391    void hideSelectActionBar() {
1392        if (mActionMode != null) {
1393            mActionMode.finish();
1394            mActionMode = null;
1395        }
1396    }
1397
1398    private void resetGestureDetectors() {
1399        mContentViewGestureHandler.resetGestureHandlers();
1400    }
1401
1402    /**
1403     * @see View#onAttachedToWindow()
1404     */
1405    @SuppressWarnings("javadoc")
1406    public void onAttachedToWindow() {
1407        mAttachedToWindow = true;
1408        if (mNativeContentViewCore != 0) {
1409            int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1410            ChildProcessLauncher.bindAsHighPriority(pid);
1411            // Normally the initial binding is removed in onRenderProcessSwap(), but it is
1412            // possible to construct WebContents and spawn the renderer before passing it to
1413            // ContentViewCore. In this case there will be no onRendererSwap() call and the
1414            // initial binding will be removed here.
1415            ChildProcessLauncher.removeInitialBinding(pid);
1416        }
1417        setAccessibilityState(mAccessibilityManager.isEnabled());
1418    }
1419
1420    /**
1421     * @see View#onDetachedFromWindow()
1422     */
1423    @SuppressWarnings("javadoc")
1424    public void onDetachedFromWindow() {
1425        mAttachedToWindow = false;
1426        if (mNativeContentViewCore != 0) {
1427            int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1428            ChildProcessLauncher.unbindAsHighPriority(pid);
1429        }
1430        setInjectedAccessibility(false);
1431        hidePopupDialog();
1432        mZoomControlsDelegate.dismissZoomPicker();
1433    }
1434
1435    /**
1436     * @see View#onVisibilityChanged(android.view.View, int)
1437     */
1438    public void onVisibilityChanged(View changedView, int visibility) {
1439      if (visibility != View.VISIBLE) {
1440          mZoomControlsDelegate.dismissZoomPicker();
1441      }
1442    }
1443
1444    /**
1445     * @see View#onCreateInputConnection(EditorInfo)
1446     */
1447    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1448        if (!mImeAdapter.hasTextInputType()) {
1449            // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1450            // is still used by the InputMethodService. Need to make sure the IME doesn't
1451            // enter fullscreen mode.
1452            outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1453        }
1454        mInputConnection =
1455                mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
1456        return mInputConnection;
1457    }
1458
1459    public Editable getEditableForTest() {
1460        return mInputConnection.getEditable();
1461    }
1462
1463    /**
1464     * @see View#onCheckIsTextEditor()
1465     */
1466    public boolean onCheckIsTextEditor() {
1467        return mImeAdapter.hasTextInputType();
1468    }
1469
1470    /**
1471     * @see View#onConfigurationChanged(Configuration)
1472     */
1473    @SuppressWarnings("javadoc")
1474    public void onConfigurationChanged(Configuration newConfig) {
1475        TraceEvent.begin();
1476
1477        if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1478            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1479                    ImeAdapter.getTextInputTypeNone(),
1480                    AdapterInputConnection.INVALID_SELECTION,
1481                    AdapterInputConnection.INVALID_SELECTION);
1482            InputMethodManager manager = (InputMethodManager)
1483                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1484            manager.restartInput(mContainerView);
1485        }
1486        mContainerViewInternals.super_onConfigurationChanged(newConfig);
1487        mNeedUpdateOrientationChanged = true;
1488        TraceEvent.end();
1489    }
1490
1491    /**
1492     * @see View#onSizeChanged(int, int, int, int)
1493     */
1494    @SuppressWarnings("javadoc")
1495    public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1496        if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1497
1498        mViewportWidthPix = wPix;
1499        mViewportHeightPix = hPix;
1500        if (mNativeContentViewCore != 0) {
1501            nativeWasResized(mNativeContentViewCore);
1502        }
1503
1504        updateAfterSizeChanged();
1505    }
1506
1507    /**
1508     * Called when the underlying surface the compositor draws to changes size.
1509     * This may be larger than the viewport size.
1510     */
1511    public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1512        if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1513
1514        mPhysicalBackingWidthPix = wPix;
1515        mPhysicalBackingHeightPix = hPix;
1516
1517        if (mNativeContentViewCore != 0) {
1518            nativeWasResized(mNativeContentViewCore);
1519        }
1520    }
1521
1522    /**
1523     * Called when the amount the surface is overdrawing off the bottom has changed.
1524     * @param overdrawHeightPix The overdraw height.
1525     */
1526    public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1527        if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1528
1529        mOverdrawBottomHeightPix = overdrawHeightPix;
1530
1531        if (mNativeContentViewCore != 0) {
1532            nativeWasResized(mNativeContentViewCore);
1533        }
1534    }
1535
1536    private void updateAfterSizeChanged() {
1537        mPopupZoomer.hide(false);
1538
1539        // Execute a delayed form focus operation because the OSK was brought
1540        // up earlier.
1541        if (!mFocusPreOSKViewportRect.isEmpty()) {
1542            Rect rect = new Rect();
1543            getContainerView().getWindowVisibleDisplayFrame(rect);
1544            if (!rect.equals(mFocusPreOSKViewportRect)) {
1545                scrollFocusedEditableNodeIntoView();
1546                mFocusPreOSKViewportRect.setEmpty();
1547            }
1548        } else if (mUnfocusOnNextSizeChanged) {
1549            undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1550            mUnfocusOnNextSizeChanged = false;
1551        }
1552
1553        if (mNeedUpdateOrientationChanged) {
1554            sendOrientationChangeEvent();
1555            mNeedUpdateOrientationChanged = false;
1556        }
1557    }
1558
1559    private void scrollFocusedEditableNodeIntoView() {
1560        if (mNativeContentViewCore != 0) {
1561            Runnable scrollTask = new Runnable() {
1562                @Override
1563                public void run() {
1564                    if (mNativeContentViewCore != 0) {
1565                        nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1566                    }
1567                }
1568            };
1569
1570            scrollTask.run();
1571
1572            // The native side keeps track of whether the zoom and scroll actually occurred. It is
1573            // more efficient to do it this way and sometimes fire an unnecessary message rather
1574            // than synchronize with the renderer and always have an additional message.
1575            mScrolledAndZoomedFocusedEditableNode = true;
1576        }
1577    }
1578
1579    private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) {
1580        // The only call to this function that matters is the first call after the
1581        // scrollFocusedEditableNodeIntoView function call.
1582        // If the first call to this function is a result of a back button press we want to undo the
1583        // preceding scroll. If the call is a result of some other action we don't want to perform
1584        // an undo.
1585        // All subsequent calls are ignored since only the scroll function sets
1586        // mScrolledAndZoomedFocusedEditableNode to true.
1587        if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed &&
1588                mNativeContentViewCore != 0) {
1589            Runnable scrollTask = new Runnable() {
1590                @Override
1591                public void run() {
1592                    if (mNativeContentViewCore != 0) {
1593                        nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1594                    }
1595                }
1596            };
1597
1598            scrollTask.run();
1599        }
1600        mScrolledAndZoomedFocusedEditableNode = false;
1601    }
1602
1603    public void onFocusChanged(boolean gainFocus) {
1604        if (!gainFocus) getContentViewClient().onImeStateChangeRequested(false);
1605        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1606    }
1607
1608    /**
1609     * @see View#onKeyUp(int, KeyEvent)
1610     */
1611    public boolean onKeyUp(int keyCode, KeyEvent event) {
1612        if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1613            mPopupZoomer.hide(true);
1614            return true;
1615        }
1616        return mContainerViewInternals.super_onKeyUp(keyCode, event);
1617    }
1618
1619    /**
1620     * @see View#dispatchKeyEventPreIme(KeyEvent)
1621     */
1622    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1623        try {
1624            TraceEvent.begin();
1625            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1626                mUnfocusOnNextSizeChanged = true;
1627            } else {
1628                undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1629            }
1630            return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1631        } finally {
1632            TraceEvent.end();
1633        }
1634    }
1635
1636    /**
1637     * @see View#dispatchKeyEvent(KeyEvent)
1638     */
1639    public boolean dispatchKeyEvent(KeyEvent event) {
1640        if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1641            return mContainerViewInternals.super_dispatchKeyEvent(event);
1642        }
1643
1644        if (mImeAdapter.dispatchKeyEvent(event)) return true;
1645
1646        return mContainerViewInternals.super_dispatchKeyEvent(event);
1647    }
1648
1649    /**
1650     * @see View#onHoverEvent(MotionEvent)
1651     * Mouse move events are sent on hover enter, hover move and hover exit.
1652     * They are sent on hover exit because sometimes it acts as both a hover
1653     * move and hover exit.
1654     */
1655    public boolean onHoverEvent(MotionEvent event) {
1656        TraceEvent.begin("onHoverEvent");
1657        mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1658        if (mBrowserAccessibilityManager != null) {
1659            return mBrowserAccessibilityManager.onHoverEvent(event);
1660        }
1661        if (mNativeContentViewCore != 0) {
1662            nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1663                    event.getX(), event.getY());
1664        }
1665        TraceEvent.end("onHoverEvent");
1666        return true;
1667    }
1668
1669    /**
1670     * @see View#onGenericMotionEvent(MotionEvent)
1671     */
1672    public boolean onGenericMotionEvent(MotionEvent event) {
1673        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1674            switch (event.getAction()) {
1675                case MotionEvent.ACTION_SCROLL:
1676                    nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1677                            event.getX(), event.getY(),
1678                            event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1679
1680                    mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1681                    // Send a delayed onMouseMove event so that we end
1682                    // up hovering over the right position after the scroll.
1683                    final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1684                    mFakeMouseMoveRunnable = new Runnable() {
1685                          @Override
1686                          public void run() {
1687                              onHoverEvent(eventFakeMouseMove);
1688                          }
1689                    };
1690                    mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1691                    return true;
1692            }
1693        }
1694        return mContainerViewInternals.super_onGenericMotionEvent(event);
1695    }
1696
1697    /**
1698     * @see View#scrollBy(int, int)
1699     * Currently the ContentView scrolling happens in the native side. In
1700     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1701     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1702     * (0, 0). This is critical for drawing ContentView correctly.
1703     */
1704    public void scrollBy(int xPix, int yPix) {
1705        if (mNativeContentViewCore != 0) {
1706            nativeScrollBy(mNativeContentViewCore,
1707                    System.currentTimeMillis(), 0, 0, xPix, yPix, false);
1708        }
1709    }
1710
1711    /**
1712     * @see View#scrollTo(int, int)
1713     */
1714    public void scrollTo(int xPix, int yPix) {
1715        if (mNativeContentViewCore == 0) return;
1716        final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1717        final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1718        final float dxPix = xPix - xCurrentPix;
1719        final float dyPix = yPix - yCurrentPix;
1720        if (dxPix != 0 || dyPix != 0) {
1721            long time = System.currentTimeMillis();
1722            nativeScrollBegin(mNativeContentViewCore, time, xCurrentPix, yCurrentPix);
1723            nativeScrollBy(mNativeContentViewCore,
1724                    time, xCurrentPix, yCurrentPix, dxPix, dyPix, false);
1725            nativeScrollEnd(mNativeContentViewCore, time);
1726        }
1727    }
1728
1729    // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1730    //       see: b/6029133
1731    public int getNativeScrollXForTest() {
1732        return mRenderCoordinates.getScrollXPixInt();
1733    }
1734
1735    // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1736    //       see: b/6029133
1737    public int getNativeScrollYForTest() {
1738        return mRenderCoordinates.getScrollYPixInt();
1739    }
1740
1741    /**
1742     * @see View#computeHorizontalScrollExtent()
1743     */
1744    @SuppressWarnings("javadoc")
1745    public int computeHorizontalScrollExtent() {
1746        return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1747    }
1748
1749    /**
1750     * @see View#computeHorizontalScrollOffset()
1751     */
1752    @SuppressWarnings("javadoc")
1753    public int computeHorizontalScrollOffset() {
1754        return mRenderCoordinates.getScrollXPixInt();
1755    }
1756
1757    /**
1758     * @see View#computeHorizontalScrollRange()
1759     */
1760    @SuppressWarnings("javadoc")
1761    public int computeHorizontalScrollRange() {
1762        return mRenderCoordinates.getContentWidthPixInt();
1763    }
1764
1765    /**
1766     * @see View#computeVerticalScrollExtent()
1767     */
1768    @SuppressWarnings("javadoc")
1769    public int computeVerticalScrollExtent() {
1770        return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1771    }
1772
1773    /**
1774     * @see View#computeVerticalScrollOffset()
1775     */
1776    @SuppressWarnings("javadoc")
1777    public int computeVerticalScrollOffset() {
1778        return mRenderCoordinates.getScrollYPixInt();
1779    }
1780
1781    /**
1782     * @see View#computeVerticalScrollRange()
1783     */
1784    @SuppressWarnings("javadoc")
1785    public int computeVerticalScrollRange() {
1786        return mRenderCoordinates.getContentHeightPixInt();
1787    }
1788
1789    // End FrameLayout overrides.
1790
1791    /**
1792     * @see View#awakenScrollBars(int, boolean)
1793     */
1794    @SuppressWarnings("javadoc")
1795    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1796        // For the default implementation of ContentView which draws the scrollBars on the native
1797        // side, calling this function may get us into a bad state where we keep drawing the
1798        // scrollBars, so disable it by always returning false.
1799        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1800            return false;
1801        } else {
1802            return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1803        }
1804    }
1805
1806    @SuppressWarnings("unused")
1807    @CalledByNative
1808    private void onTabCrash() {
1809        getContentViewClient().onTabCrash();
1810    }
1811
1812    private void handleTapOrPress(
1813            long timeMs, float xPix, float yPix, int isLongPressOrTap, boolean showPress) {
1814        if (!mContainerView.isFocused()) mContainerView.requestFocus();
1815
1816        if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1817
1818        if (isLongPressOrTap == IS_LONG_PRESS) {
1819            getInsertionHandleController().allowAutomaticShowing();
1820            getSelectionHandleController().allowAutomaticShowing();
1821            if (mNativeContentViewCore != 0) {
1822                nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false);
1823            }
1824        } else if (isLongPressOrTap == IS_LONG_TAP) {
1825            getInsertionHandleController().allowAutomaticShowing();
1826            getSelectionHandleController().allowAutomaticShowing();
1827            if (mNativeContentViewCore != 0) {
1828                nativeLongTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1829            }
1830        } else {
1831            if (!showPress && mNativeContentViewCore != 0) {
1832                nativeShowPressState(mNativeContentViewCore, timeMs, xPix, yPix);
1833            }
1834            if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1835            if (mNativeContentViewCore != 0) {
1836                nativeSingleTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1837            }
1838        }
1839    }
1840
1841    public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1842        mZoomControlsDelegate = zoomControlsDelegate;
1843    }
1844
1845    public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1846        mZoomManager.updateMultiTouchSupport(supportsMultiTouchZoom);
1847    }
1848
1849    public void selectPopupMenuItems(int[] indices) {
1850        if (mNativeContentViewCore != 0) {
1851            nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1852        }
1853    }
1854
1855    /**
1856     * Get the screen orientation from the OS and push it to WebKit.
1857     *
1858     * TODO(husky): Add a hook for mock orientations.
1859     *
1860     * TODO(husky): Currently each new tab starts with an orientation of 0 until you actually
1861     * rotate the device. This is wrong if you actually started in landscape mode. To fix this, we
1862     * need to push the correct orientation, but only after WebKit's Frame object has been fully
1863     * initialized. Need to find a good time to do that. onPageFinished() would probably work but
1864     * it isn't implemented yet.
1865     */
1866    private void sendOrientationChangeEvent() {
1867        if (mNativeContentViewCore == 0) return;
1868
1869        WindowManager windowManager =
1870                (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
1871        switch (windowManager.getDefaultDisplay().getRotation()) {
1872            case Surface.ROTATION_90:
1873                nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
1874                break;
1875            case Surface.ROTATION_180:
1876                nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
1877                break;
1878            case Surface.ROTATION_270:
1879                nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
1880                break;
1881            case Surface.ROTATION_0:
1882                nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
1883                break;
1884            default:
1885                Log.w(TAG, "Unknown rotation!");
1886                break;
1887        }
1888    }
1889
1890    /**
1891     * Register the delegate to be used when content can not be handled by
1892     * the rendering engine, and should be downloaded instead. This will replace
1893     * the current delegate, if any.
1894     * @param delegate An implementation of ContentViewDownloadDelegate.
1895     */
1896    public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1897        mDownloadDelegate = delegate;
1898    }
1899
1900    // Called by DownloadController.
1901    ContentViewDownloadDelegate getDownloadDelegate() {
1902        return mDownloadDelegate;
1903    }
1904
1905    private SelectionHandleController getSelectionHandleController() {
1906        if (mSelectionHandleController == null) {
1907            mSelectionHandleController = new SelectionHandleController(getContainerView()) {
1908                @Override
1909                public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
1910                    if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
1911                        nativeSelectBetweenCoordinates(mNativeContentViewCore,
1912                                x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
1913                                x2, y2 - mRenderCoordinates.getContentOffsetYPix());
1914                    }
1915                }
1916
1917                @Override
1918                public void showHandles(int startDir, int endDir) {
1919                    super.showHandles(startDir, endDir);
1920                    showSelectActionBar();
1921                }
1922
1923            };
1924
1925            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1926        }
1927
1928        return mSelectionHandleController;
1929    }
1930
1931    private InsertionHandleController getInsertionHandleController() {
1932        if (mInsertionHandleController == null) {
1933            mInsertionHandleController = new InsertionHandleController(getContainerView()) {
1934                private static final int AVERAGE_LINE_HEIGHT = 14;
1935
1936                @Override
1937                public void setCursorPosition(int x, int y) {
1938                    if (mNativeContentViewCore != 0) {
1939                        nativeMoveCaret(mNativeContentViewCore,
1940                                x, y - mRenderCoordinates.getContentOffsetYPix());
1941                    }
1942                }
1943
1944                @Override
1945                public void paste() {
1946                    mImeAdapter.paste();
1947                    hideHandles();
1948                }
1949
1950                @Override
1951                public int getLineHeight() {
1952                    return (int) Math.ceil(
1953                            mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
1954                }
1955
1956                @Override
1957                public void showHandle() {
1958                    super.showHandle();
1959                }
1960            };
1961
1962            mInsertionHandleController.hideAndDisallowAutomaticShowing();
1963        }
1964
1965        return mInsertionHandleController;
1966    }
1967
1968    public InsertionHandleController getInsertionHandleControllerForTest() {
1969        return mInsertionHandleController;
1970    }
1971
1972    private void updateHandleScreenPositions() {
1973        if (isSelectionHandleShowing()) {
1974            mSelectionHandleController.setStartHandlePosition(
1975                    mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
1976            mSelectionHandleController.setEndHandlePosition(
1977                    mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
1978        }
1979
1980        if (isInsertionHandleShowing()) {
1981            mInsertionHandleController.setHandlePosition(
1982                    mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
1983        }
1984    }
1985
1986    private void hideHandles() {
1987        if (mSelectionHandleController != null) {
1988            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1989        }
1990        if (mInsertionHandleController != null) {
1991            mInsertionHandleController.hideAndDisallowAutomaticShowing();
1992        }
1993    }
1994
1995    private void showSelectActionBar() {
1996        if (mActionMode != null) {
1997            mActionMode.invalidate();
1998            return;
1999        }
2000
2001        // Start a new action mode with a SelectActionModeCallback.
2002        SelectActionModeCallback.ActionHandler actionHandler =
2003                new SelectActionModeCallback.ActionHandler() {
2004            @Override
2005            public boolean selectAll() {
2006                return mImeAdapter.selectAll();
2007            }
2008
2009            @Override
2010            public boolean cut() {
2011                return mImeAdapter.cut();
2012            }
2013
2014            @Override
2015            public boolean copy() {
2016                return mImeAdapter.copy();
2017            }
2018
2019            @Override
2020            public boolean paste() {
2021                return mImeAdapter.paste();
2022            }
2023
2024            @Override
2025            public boolean isSelectionEditable() {
2026                return mSelectionEditable;
2027            }
2028
2029            @Override
2030            public String getSelectedText() {
2031                return ContentViewCore.this.getSelectedText();
2032            }
2033
2034            @Override
2035            public void onDestroyActionMode() {
2036                mActionMode = null;
2037                if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2038                getContentViewClient().onContextualActionBarHidden();
2039            }
2040        };
2041        mActionMode = null;
2042        // On ICS, startActionMode throws an NPE when getParent() is null.
2043        if (mContainerView.getParent() != null) {
2044            mActionMode = mContainerView.startActionMode(
2045                    getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2046                            nativeIsIncognito(mNativeContentViewCore)));
2047        }
2048        mUnselectAllOnActionModeDismiss = true;
2049        if (mActionMode == null) {
2050            // There is no ActionMode, so remove the selection.
2051            mImeAdapter.unselect();
2052        } else {
2053            getContentViewClient().onContextualActionBarShown();
2054        }
2055    }
2056
2057    public boolean getUseDesktopUserAgent() {
2058        if (mNativeContentViewCore != 0) {
2059            return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2060        }
2061        return false;
2062    }
2063
2064    /**
2065     * Set whether or not we're using a desktop user agent for the currently loaded page.
2066     * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
2067     * @param reloadOnChange Reload the page if the UA has changed.
2068     */
2069    public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2070        if (mNativeContentViewCore != 0) {
2071            nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2072        }
2073    }
2074
2075    public void clearSslPreferences() {
2076        nativeClearSslPreferences(mNativeContentViewCore);
2077    }
2078
2079    /**
2080     * @return Whether the native ContentView has crashed.
2081     */
2082    public boolean isCrashed() {
2083        if (mNativeContentViewCore == 0) return false;
2084        return nativeCrashed(mNativeContentViewCore);
2085    }
2086
2087    private boolean isSelectionHandleShowing() {
2088        return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2089    }
2090
2091    private boolean isInsertionHandleShowing() {
2092        return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2093    }
2094
2095    private void updateTextHandlesForGesture(int type) {
2096        switch(type) {
2097            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
2098            case ContentViewGestureHandler.GESTURE_SCROLL_START:
2099            case ContentViewGestureHandler.GESTURE_FLING_START:
2100            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
2101                temporarilyHideTextHandles();
2102                break;
2103
2104            default:
2105                break;
2106        }
2107    }
2108
2109    // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2110    // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2111    private void temporarilyHideTextHandles() {
2112        if (isSelectionHandleShowing()) {
2113            mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2114        }
2115        if (isInsertionHandleShowing()) {
2116            mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2117        }
2118        scheduleTextHandleFadeIn();
2119    }
2120
2121    // Cancels any pending fade in and schedules a new one.
2122    private void scheduleTextHandleFadeIn() {
2123        if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2124
2125        if (mDeferredHandleFadeInRunnable == null) {
2126            mDeferredHandleFadeInRunnable = new Runnable() {
2127                @Override
2128                public void run() {
2129                    if (mContentViewGestureHandler.isNativeScrolling() ||
2130                            mContentViewGestureHandler.isNativePinching()) {
2131                        // Delay fade in until no longer scrolling or pinching.
2132                        scheduleTextHandleFadeIn();
2133                    } else {
2134                        if (isSelectionHandleShowing()) {
2135                            mSelectionHandleController.beginHandleFadeIn();
2136                        }
2137                        if (isInsertionHandleShowing()) {
2138                            mInsertionHandleController.beginHandleFadeIn();
2139                        }
2140                    }
2141                }
2142            };
2143        }
2144
2145        mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2146        mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2147    }
2148
2149    /**
2150     * Shows the IME if the focused widget could accept text input.
2151     */
2152    public void showImeIfNeeded() {
2153        if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2154    }
2155
2156    public void setUpdateFrameInfoListener(UpdateFrameInfoListener updateFrameInfoListener) {
2157        mUpdateFrameInfoListener = updateFrameInfoListener;
2158    }
2159
2160    @SuppressWarnings("unused")
2161    @CalledByNative
2162    private void updateFrameInfo(
2163            float scrollOffsetX, float scrollOffsetY,
2164            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2165            float contentWidth, float contentHeight,
2166            float viewportWidth, float viewportHeight,
2167            float controlsOffsetYCss, float contentOffsetYCss,
2168            float overdrawBottomHeightCss) {
2169        TraceEvent.instant("ContentViewCore:updateFrameInfo");
2170        // Adjust contentWidth/Height to be always at least as big as
2171        // the actual viewport (as set by onSizeChanged).
2172        contentWidth = Math.max(contentWidth,
2173                mRenderCoordinates.fromPixToLocalCss(mViewportWidthPix));
2174        contentHeight = Math.max(contentHeight,
2175                mRenderCoordinates.fromPixToLocalCss(mViewportHeightPix));
2176
2177        final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2178
2179        final boolean contentSizeChanged =
2180                contentWidth != mRenderCoordinates.getContentWidthCss()
2181                || contentHeight != mRenderCoordinates.getContentHeightCss();
2182        final boolean scaleLimitsChanged =
2183                minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2184                || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2185        final boolean pageScaleChanged =
2186                pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2187        final boolean scrollChanged =
2188                pageScaleChanged
2189                || scrollOffsetX != mRenderCoordinates.getScrollX()
2190                || scrollOffsetY != mRenderCoordinates.getScrollY();
2191        final boolean contentOffsetChanged =
2192                contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2193
2194        final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2195        final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2196        final boolean needTemporarilyHideHandles = scrollChanged;
2197
2198        if (needHidePopupZoomer) mPopupZoomer.hide(true);
2199
2200        if (pageScaleChanged) {
2201            // This function should be called back from native as soon
2202            // as the scroll is applied to the backbuffer.  We should only
2203            // update mNativeScrollX/Y here for consistency.
2204            getContentViewClient().onScaleChanged(
2205                    mRenderCoordinates.getPageScaleFactor(), pageScaleFactor);
2206        }
2207
2208        mRenderCoordinates.updateFrameInfo(
2209                scrollOffsetX, scrollOffsetY,
2210                contentWidth, contentHeight,
2211                viewportWidth, viewportHeight,
2212                pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2213                contentOffsetYPix);
2214
2215        if ((contentSizeChanged || pageScaleChanged) && mUpdateFrameInfoListener != null) {
2216            mUpdateFrameInfoListener.onFrameInfoUpdated(
2217                    contentWidth, contentHeight, pageScaleFactor);
2218        }
2219
2220        if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2221        if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2222        if (contentOffsetChanged) updateHandleScreenPositions();
2223
2224        // Update offsets for fullscreen.
2225        final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2226        final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2227        final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2228        getContentViewClient().onOffsetsForFullscreenChanged(
2229                controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2230
2231        mPendingRendererFrame = true;
2232        if (mBrowserAccessibilityManager != null) {
2233            mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2234        }
2235    }
2236
2237    @SuppressWarnings("unused")
2238    @CalledByNative
2239    private void updateImeAdapter(int nativeImeAdapterAndroid, int textInputType,
2240            String text, int selectionStart, int selectionEnd,
2241            int compositionStart, int compositionEnd, boolean showImeIfNeeded) {
2242        TraceEvent.begin();
2243        mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2244
2245        if (mActionMode != null) mActionMode.invalidate();
2246
2247        mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
2248                selectionStart, selectionEnd, showImeIfNeeded);
2249
2250        if (mInputConnection != null) {
2251            mInputConnection.setEditableText(text, selectionStart, selectionEnd,
2252                    compositionStart, compositionEnd);
2253        }
2254        TraceEvent.end();
2255    }
2256
2257    @SuppressWarnings("unused")
2258    @CalledByNative
2259    private void processImeBatchStateAck(boolean isBegin) {
2260        if (mInputConnection == null) return;
2261        mInputConnection.setIgnoreTextInputStateUpdates(isBegin);
2262    }
2263
2264    @SuppressWarnings("unused")
2265    @CalledByNative
2266    private void setTitle(String title) {
2267        getContentViewClient().onUpdateTitle(title);
2268    }
2269
2270    /**
2271     * Called (from native) when the <select> popup needs to be shown.
2272     * @param items           Items to show.
2273     * @param enabled         POPUP_ITEM_TYPEs for items.
2274     * @param multiple        Whether the popup menu should support multi-select.
2275     * @param selectedIndices Indices of selected items.
2276     */
2277    @SuppressWarnings("unused")
2278    @CalledByNative
2279    private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
2280            int[] selectedIndices) {
2281        SelectPopupDialog.show(this, items, enabled, multiple, selectedIndices);
2282    }
2283
2284    @SuppressWarnings("unused")
2285    @CalledByNative
2286    private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2287        mPopupZoomer.setBitmap(zoomedBitmap);
2288        mPopupZoomer.show(targetRect);
2289    }
2290
2291    @SuppressWarnings("unused")
2292    @CalledByNative
2293    private SmoothScroller createSmoothScroller(boolean scrollDown, int mouseEventX,
2294            int mouseEventY) {
2295        return new SmoothScroller(this, scrollDown, mouseEventX, mouseEventY);
2296    }
2297
2298    @SuppressWarnings("unused")
2299    @CalledByNative
2300    private void onSelectionChanged(String text) {
2301        mLastSelectedText = text;
2302    }
2303
2304    @SuppressWarnings("unused")
2305    @CalledByNative
2306    private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2307            int focusDir, boolean isAnchorFirst) {
2308        // All coordinates are in DIP.
2309        int x1 = anchorRectDip.left;
2310        int y1 = anchorRectDip.bottom;
2311        int x2 = focusRectDip.left;
2312        int y2 = focusRectDip.bottom;
2313
2314        if (x1 != x2 || y1 != y2 ||
2315                (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2316            if (mInsertionHandleController != null) {
2317                mInsertionHandleController.hide();
2318            }
2319            if (isAnchorFirst) {
2320                mStartHandlePoint.setLocalDip(x1, y1);
2321                mEndHandlePoint.setLocalDip(x2, y2);
2322            } else {
2323                mStartHandlePoint.setLocalDip(x2, y2);
2324                mEndHandlePoint.setLocalDip(x1, y1);
2325            }
2326
2327            getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2328            updateHandleScreenPositions();
2329            mHasSelection = true;
2330        } else {
2331            mUnselectAllOnActionModeDismiss = false;
2332            hideSelectActionBar();
2333            if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2334                // Selection is a caret, and a text field is focused.
2335                if (mSelectionHandleController != null) {
2336                    mSelectionHandleController.hide();
2337                }
2338                mInsertionHandlePoint.setLocalDip(x1, y1);
2339
2340                getInsertionHandleController().onCursorPositionChanged();
2341                updateHandleScreenPositions();
2342                InputMethodManager manager = (InputMethodManager)
2343                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
2344                if (manager.isWatchingCursor(mContainerView)) {
2345                    final int xPix = (int) mInsertionHandlePoint.getXPix();
2346                    final int yPix = (int) mInsertionHandlePoint.getYPix();
2347                    manager.updateCursor(mContainerView, xPix, yPix, xPix, yPix);
2348                }
2349            } else {
2350                // Deselection
2351                if (mSelectionHandleController != null) {
2352                    mSelectionHandleController.hideAndDisallowAutomaticShowing();
2353                }
2354                if (mInsertionHandleController != null) {
2355                    mInsertionHandleController.hideAndDisallowAutomaticShowing();
2356                }
2357            }
2358            mHasSelection = false;
2359        }
2360    }
2361
2362    @SuppressWarnings("unused")
2363    @CalledByNative
2364    private static void onEvaluateJavaScriptResult(
2365            String jsonResult, JavaScriptCallback callback) {
2366        callback.handleJavaScriptResult(jsonResult);
2367    }
2368
2369    @SuppressWarnings("unused")
2370    @CalledByNative
2371    private void showPastePopup(int xDip, int yDip) {
2372        mInsertionHandlePoint.setLocalDip(xDip, yDip);
2373        getInsertionHandleController().showHandle();
2374        updateHandleScreenPositions();
2375        getInsertionHandleController().showHandleWithPastePopup();
2376    }
2377
2378    @SuppressWarnings("unused")
2379    @CalledByNative
2380    private void onRenderProcessSwap(int oldPid, int newPid) {
2381        if (mAttachedToWindow && oldPid != newPid) {
2382            ChildProcessLauncher.unbindAsHighPriority(oldPid);
2383            ChildProcessLauncher.bindAsHighPriority(newPid);
2384        }
2385
2386        // We want to remove the initial binding even if the ContentView is not attached, so that
2387        // renderers for ContentViews loading in background do not retain the high priority.
2388        ChildProcessLauncher.removeInitialBinding(newPid);
2389    }
2390
2391    @SuppressWarnings("unused")
2392    @CalledByNative
2393    private void onWebContentsConnected() {
2394        if (mImeAdapter != null &&
2395                !mImeAdapter.isNativeImeAdapterAttached() && mNativeContentViewCore != 0) {
2396            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2397        }
2398    }
2399
2400    @SuppressWarnings("unused")
2401    @CalledByNative
2402    private void onWebContentsSwapped() {
2403        if (mImeAdapter != null && mNativeContentViewCore != 0) {
2404            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2405        }
2406    }
2407
2408    /**
2409     * @return Whether a reload happens when this ContentView is activated.
2410     */
2411    public boolean needsReload() {
2412        return mNativeContentViewCore != 0 && nativeNeedsReload(mNativeContentViewCore);
2413    }
2414
2415    /**
2416     * @see View#hasFocus()
2417     */
2418    @CalledByNative
2419    public boolean hasFocus() {
2420        return mContainerView.hasFocus();
2421    }
2422
2423    /**
2424     * Checks whether the ContentViewCore can be zoomed in.
2425     *
2426     * @return True if the ContentViewCore can be zoomed in.
2427     */
2428    // This method uses the term 'zoom' for legacy reasons, but relates
2429    // to what chrome calls the 'page scale factor'.
2430    public boolean canZoomIn() {
2431        final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2432                - mRenderCoordinates.getPageScaleFactor();
2433        return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2434    }
2435
2436    /**
2437     * Checks whether the ContentViewCore can be zoomed out.
2438     *
2439     * @return True if the ContentViewCore can be zoomed out.
2440     */
2441    // This method uses the term 'zoom' for legacy reasons, but relates
2442    // to what chrome calls the 'page scale factor'.
2443    public boolean canZoomOut() {
2444        final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2445                - mRenderCoordinates.getMinPageScaleFactor();
2446        return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2447    }
2448
2449    /**
2450     * Zooms in the ContentViewCore by 25% (or less if that would result in
2451     * zooming in more than possible).
2452     *
2453     * @return True if there was a zoom change, false otherwise.
2454     */
2455    // This method uses the term 'zoom' for legacy reasons, but relates
2456    // to what chrome calls the 'page scale factor'.
2457    public boolean zoomIn() {
2458        if (!canZoomIn()) {
2459            return false;
2460        }
2461        return zoomByDelta(1.25f);
2462    }
2463
2464    /**
2465     * Zooms out the ContentViewCore by 20% (or less if that would result in
2466     * zooming out more than possible).
2467     *
2468     * @return True if there was a zoom change, false otherwise.
2469     */
2470    // This method uses the term 'zoom' for legacy reasons, but relates
2471    // to what chrome calls the 'page scale factor'.
2472    public boolean zoomOut() {
2473        if (!canZoomOut()) {
2474            return false;
2475        }
2476        return zoomByDelta(0.8f);
2477    }
2478
2479    /**
2480     * Resets the zoom factor of the ContentViewCore.
2481     *
2482     * @return True if there was a zoom change, false otherwise.
2483     */
2484    // This method uses the term 'zoom' for legacy reasons, but relates
2485    // to what chrome calls the 'page scale factor'.
2486    public boolean zoomReset() {
2487        // The page scale factor is initialized to mNativeMinimumScale when
2488        // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2489        if (!canZoomOut()) return false;
2490        return zoomByDelta(
2491                mRenderCoordinates.getMinPageScaleFactor()
2492                        / mRenderCoordinates.getPageScaleFactor());
2493    }
2494
2495    private boolean zoomByDelta(float delta) {
2496        if (mNativeContentViewCore == 0) {
2497            return false;
2498        }
2499
2500        long timeMs = System.currentTimeMillis();
2501        int xPix = getViewportWidthPix() / 2;
2502        int yPix = getViewportHeightPix() / 2;
2503
2504        getContentViewGestureHandler().pinchBegin(timeMs, xPix, yPix);
2505        getContentViewGestureHandler().pinchBy(timeMs, xPix, yPix, delta);
2506        getContentViewGestureHandler().pinchEnd(timeMs);
2507
2508        return true;
2509    }
2510
2511    /**
2512     * Invokes the graphical zoom picker widget for this ContentView.
2513     */
2514    @Override
2515    public void invokeZoomPicker() {
2516        mZoomControlsDelegate.invokeZoomPicker();
2517    }
2518
2519    /**
2520     * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2521     * and automatically pass in {@link JavascriptInterface} as the required annotation.
2522     *
2523     * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2524     *               values are ignored.
2525     * @param name   The name used to expose the instance in JavaScript.
2526     */
2527    public void addJavascriptInterface(Object object, String name) {
2528        addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2529    }
2530
2531    /**
2532     * This method injects the supplied Java object into the ContentViewCore.
2533     * The object is injected into the JavaScript context of the main frame,
2534     * using the supplied name. This allows the Java object to be accessed from
2535     * JavaScript. Note that that injected objects will not appear in
2536     * JavaScript until the page is next (re)loaded. For example:
2537     * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2538     * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2539     * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2540     * <p><strong>IMPORTANT:</strong>
2541     * <ul>
2542     * <li> addJavascriptInterface() can be used to allow JavaScript to control
2543     * the host application. This is a powerful feature, but also presents a
2544     * security risk. Use of this method in a ContentViewCore containing
2545     * untrusted content could allow an attacker to manipulate the host
2546     * application in unintended ways, executing Java code with the permissions
2547     * of the host application. Use extreme care when using this method in a
2548     * ContentViewCore which could contain untrusted content. Particular care
2549     * should be taken to avoid unintentional access to inherited methods, such
2550     * as {@link Object#getClass()}. To prevent access to inherited methods,
2551     * pass an annotation for {@code requiredAnnotation}.  This will ensure
2552     * that only methods with {@code requiredAnnotation} are exposed to the
2553     * Javascript layer.  {@code requiredAnnotation} will be passed to all
2554     * subsequently injected Java objects if any methods return an object.  This
2555     * means the same restrictions (or lack thereof) will apply.  Alternatively,
2556     * {@link #addJavascriptInterface(Object, String)} can be called, which
2557     * automatically uses the {@link JavascriptInterface} annotation.
2558     * <li> JavaScript interacts with Java objects on a private, background
2559     * thread of the ContentViewCore. Care is therefore required to maintain
2560     * thread safety.</li>
2561     * </ul></p>
2562     *
2563     * @param object             The Java object to inject into the
2564     *                           ContentViewCore's JavaScript context. Null
2565     *                           values are ignored.
2566     * @param name               The name used to expose the instance in
2567     *                           JavaScript.
2568     * @param requiredAnnotation Restrict exposed methods to ones with this
2569     *                           annotation.  If {@code null} all methods are
2570     *                           exposed.
2571     *
2572     */
2573    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2574            Class<? extends Annotation> requiredAnnotation) {
2575        if (mNativeContentViewCore != 0 && object != null) {
2576            mJavaScriptInterfaces.put(name, object);
2577            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
2578                    mRetainedJavaScriptObjects);
2579        }
2580    }
2581
2582    /**
2583     * Removes a previously added JavaScript interface with the given name.
2584     *
2585     * @param name The name of the interface to remove.
2586     */
2587    public void removeJavascriptInterface(String name) {
2588        mJavaScriptInterfaces.remove(name);
2589        if (mNativeContentViewCore != 0) {
2590            nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2591        }
2592    }
2593
2594    /**
2595     * Return the current scale of the ContentView.
2596     * @return The current page scale factor.
2597     */
2598    public float getScale() {
2599        return mRenderCoordinates.getPageScaleFactor();
2600    }
2601
2602    /**
2603     * If the view is ready to draw contents to the screen. In hardware mode,
2604     * the initialization of the surface texture may not occur until after the
2605     * view has been added to the layout. This method will return {@code true}
2606     * once the texture is actually ready.
2607     */
2608    public boolean isReady() {
2609        return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2610    }
2611
2612    @CalledByNative
2613    private void startContentIntent(String contentUrl) {
2614        getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2615    }
2616
2617    @Override
2618    public void onAccessibilityStateChanged(boolean enabled) {
2619        setAccessibilityState(enabled);
2620    }
2621
2622    /**
2623     * Determines whether or not this ContentViewCore can handle this accessibility action.
2624     * @param action The action to perform.
2625     * @return Whether or not this action is supported.
2626     */
2627    public boolean supportsAccessibilityAction(int action) {
2628        return mAccessibilityInjector.supportsAccessibilityAction(action);
2629    }
2630
2631    /**
2632     * Attempts to perform an accessibility action on the web content.  If the accessibility action
2633     * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2634     * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2635     * Otherwise the return value from this method should be used.
2636     * @param action The action to perform.
2637     * @param arguments Optional action arguments.
2638     * @return Whether the action was performed or {@code null} if the call should be delegated to
2639     *         the super {@link View} class.
2640     */
2641    public boolean performAccessibilityAction(int action, Bundle arguments) {
2642        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2643            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2644        }
2645
2646        return false;
2647    }
2648
2649    /**
2650     * Set the BrowserAccessibilityManager, used for native accessibility
2651     * (not script injection). This is only set when system accessibility
2652     * has been enabled.
2653     * @param manager The new BrowserAccessibilityManager.
2654     */
2655    public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2656        mBrowserAccessibilityManager = manager;
2657    }
2658
2659    /**
2660     * Get the BrowserAccessibilityManager, used for native accessibility
2661     * (not script injection). This will return null when system accessibility
2662     * is not enabled.
2663     * @return This view's BrowserAccessibilityManager.
2664     */
2665    public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2666        return mBrowserAccessibilityManager;
2667    }
2668
2669    /**
2670     * If native accessibility (not script injection) is enabled, and if this is
2671     * running on JellyBean or later, returns an AccessibilityNodeProvider that
2672     * implements native accessibility for this view. Returns null otherwise.
2673     * @return The AccessibilityNodeProvider, if available, or null otherwise.
2674     */
2675    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2676        if (mBrowserAccessibilityManager != null) {
2677            return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2678        } else {
2679            return null;
2680        }
2681    }
2682
2683    /**
2684     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2685     */
2686    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2687        // Note: this is only used by the script-injecting accessibility code.
2688        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2689    }
2690
2691    /**
2692     * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2693     */
2694    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2695        // Note: this is only used by the script-injecting accessibility code.
2696        event.setClassName(this.getClass().getName());
2697
2698        // Identify where the top-left of the screen currently points to.
2699        event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2700        event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2701
2702        // The maximum scroll values are determined by taking the content dimensions and
2703        // subtracting off the actual dimensions of the ChromeView.
2704        int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2705        int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2706        event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2707
2708        // Setting the maximum scroll values requires API level 15 or higher.
2709        final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2710        if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2711            event.setMaxScrollX(maxScrollXPix);
2712            event.setMaxScrollY(maxScrollYPix);
2713        }
2714    }
2715
2716    /**
2717     * Returns whether accessibility script injection is enabled on the device
2718     */
2719    public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2720        try {
2721            if (!mContentSettings.getJavaScriptEnabled()) {
2722                return false;
2723            }
2724
2725            int result = getContext().checkCallingOrSelfPermission(
2726                    android.Manifest.permission.INTERNET);
2727            if (result != PackageManager.PERMISSION_GRANTED) {
2728                return false;
2729            }
2730
2731            Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2732            field.setAccessible(true);
2733            String accessibilityScriptInjection = (String) field.get(null);
2734            ContentResolver contentResolver = getContext().getContentResolver();
2735
2736            if (mAccessibilityScriptInjectionObserver == null) {
2737                ContentObserver contentObserver = new ContentObserver(new Handler()) {
2738                    public void onChange(boolean selfChange, Uri uri) {
2739                        setAccessibilityState(mAccessibilityManager.isEnabled());
2740                    }
2741                };
2742                contentResolver.registerContentObserver(
2743                    Settings.Secure.getUriFor(accessibilityScriptInjection),
2744                    false,
2745                    contentObserver);
2746                mAccessibilityScriptInjectionObserver = contentObserver;
2747            }
2748
2749            return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2750        } catch (NoSuchFieldException e) {
2751        } catch (IllegalAccessException e) {
2752        }
2753        return false;
2754    }
2755
2756    /**
2757     * Returns whether or not accessibility injection is being used.
2758     */
2759    public boolean isInjectingAccessibilityScript() {
2760        return mAccessibilityInjector.accessibilityIsAvailable();
2761    }
2762
2763    /**
2764     * Turns browser accessibility on or off.
2765     * If |state| is |false|, this turns off both native and injected accessibility.
2766     * Otherwise, if accessibility script injection is enabled, this will enable the injected
2767     * accessibility scripts, and if it is disabled this will enable the native accessibility.
2768     */
2769    public void setAccessibilityState(boolean state) {
2770        boolean injectedAccessibility = false;
2771        boolean nativeAccessibility = false;
2772        if (state) {
2773            if (isDeviceAccessibilityScriptInjectionEnabled()) {
2774                injectedAccessibility = true;
2775            } else {
2776                nativeAccessibility = true;
2777            }
2778        }
2779        setInjectedAccessibility(injectedAccessibility);
2780        setNativeAccessibilityState(nativeAccessibility);
2781    }
2782
2783    /**
2784     * Enable or disable native accessibility features.
2785     */
2786    public void setNativeAccessibilityState(boolean enabled) {
2787        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2788            nativeSetAccessibilityEnabled(mNativeContentViewCore, enabled);
2789        }
2790    }
2791
2792    /**
2793     * Enable or disable injected accessibility features
2794     */
2795    public void setInjectedAccessibility(boolean enabled) {
2796        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2797        mAccessibilityInjector.setScriptEnabled(enabled);
2798    }
2799
2800    /**
2801     * Stop any TTS notifications that are currently going on.
2802     */
2803    public void stopCurrentAccessibilityNotifications() {
2804        mAccessibilityInjector.onPageLostFocus();
2805    }
2806
2807    /**
2808     * Inform WebKit that Fullscreen mode has been exited by the user.
2809     */
2810    public void exitFullscreen() {
2811        nativeExitFullscreen(mNativeContentViewCore);
2812    }
2813
2814    /**
2815     * Changes whether hiding the top controls is enabled.
2816     *
2817     * @param enableHiding Whether hiding the top controls should be enabled or not.
2818     * @param enableShowing Whether showing the top controls should be enabled or not.
2819     * @param animate Whether the transition should be animated or not.
2820     */
2821    public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2822            boolean animate) {
2823        nativeUpdateTopControlsState(mNativeContentViewCore, enableHiding, enableShowing, animate);
2824    }
2825
2826    /**
2827     * @See android.webkit.WebView#pageDown(boolean)
2828     */
2829    public boolean pageDown(boolean bottom) {
2830        final int maxVerticalScrollPix = mRenderCoordinates.getMaxVerticalScrollPixInt();
2831        if (computeVerticalScrollOffset() >= maxVerticalScrollPix) {
2832            // We seem to already be at the bottom of the page, so no scrolling will occur.
2833            return false;
2834        }
2835
2836        if (bottom) {
2837            scrollTo(computeHorizontalScrollOffset(), maxVerticalScrollPix);
2838        } else {
2839            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN));
2840            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_DOWN));
2841        }
2842        return true;
2843    }
2844
2845    /**
2846     * @See android.webkit.WebView#pageUp(boolean)
2847     */
2848    public boolean pageUp(boolean top) {
2849        if (computeVerticalScrollOffset() == 0) {
2850            // We seem to already be at the top of the page, so no scrolling will occur.
2851            return false;
2852        }
2853
2854        if (top) {
2855            scrollTo(computeHorizontalScrollOffset(), 0);
2856        } else {
2857            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP));
2858            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_UP));
2859        }
2860        return true;
2861    }
2862
2863    /**
2864     * Callback factory method for nativeGetNavigationHistory().
2865     */
2866    @CalledByNative
2867    private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2868            String originalUrl, String title, Bitmap favicon) {
2869        NavigationEntry entry = new NavigationEntry(
2870                index, url, virtualUrl, originalUrl, title, favicon);
2871        ((NavigationHistory) history).addEntry(entry);
2872    }
2873
2874    /**
2875     * Get a copy of the navigation history of the view.
2876     */
2877    public NavigationHistory getNavigationHistory() {
2878        NavigationHistory history = new NavigationHistory();
2879        int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2880        history.setCurrentEntryIndex(currentIndex);
2881        return history;
2882    }
2883
2884    @Override
2885    public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2886        NavigationHistory history = new NavigationHistory();
2887        nativeGetDirectedNavigationHistory(mNativeContentViewCore, history, isForward, itemLimit);
2888        return history;
2889    }
2890
2891    /**
2892     * @return The original request URL for the current navigation entry, or null if there is no
2893     *         current entry.
2894     */
2895    public String getOriginalUrlForActiveNavigationEntry() {
2896        return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2897    }
2898
2899    /**
2900     * @return The cached copy of render positions and scales.
2901     */
2902    public RenderCoordinates getRenderCoordinates() {
2903        return mRenderCoordinates;
2904    }
2905
2906    @CalledByNative
2907    private static Rect createRect(int x, int y, int right, int bottom) {
2908        return new Rect(x, y, right, bottom);
2909    }
2910
2911    public void attachExternalVideoSurface(int playerId, Surface surface) {
2912        if (mNativeContentViewCore != 0) {
2913            nativeAttachExternalVideoSurface(mNativeContentViewCore, playerId, surface);
2914        }
2915    }
2916
2917    public void detachExternalVideoSurface(int playerId) {
2918        if (mNativeContentViewCore != 0) {
2919            nativeDetachExternalVideoSurface(mNativeContentViewCore, playerId);
2920        }
2921    }
2922
2923    private boolean onAnimate(long frameTimeMicros) {
2924        if (mNativeContentViewCore == 0) return false;
2925        return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
2926    }
2927
2928    private void animateIfNecessary(long frameTimeMicros) {
2929        if (mNeedAnimate) {
2930            mNeedAnimate = onAnimate(frameTimeMicros);
2931            if (!mNeedAnimate) setVSyncNotificationEnabled(false);
2932        }
2933    }
2934
2935    @CalledByNative
2936    private void notifyExternalSurface(
2937            int playerId, boolean isRequest, float x, float y, float width, float height) {
2938        RenderCoordinates.NormalizedPoint topLeft = mRenderCoordinates.createNormalizedPoint();
2939        RenderCoordinates.NormalizedPoint bottomRight = mRenderCoordinates.createNormalizedPoint();
2940        topLeft.setLocalDip(x * getScale(), y * getScale());
2941        bottomRight.setLocalDip((x + width) * getScale(), (y + height) * getScale());
2942
2943        if (isRequest) getContentViewClient().onExternalVideoSurfaceRequested(playerId);
2944        getContentViewClient().onGeometryChanged(
2945                playerId,
2946                topLeft.getXPix(),
2947                topLeft.getYPix(),
2948                bottomRight.getXPix() - topLeft.getXPix(),
2949                bottomRight.getYPix() - topLeft.getYPix());
2950    }
2951
2952    /**
2953     * Offer a subset of gesture events to the embedding View,
2954     * primarily for WebView compatibility.
2955     *
2956     * @param type The type of the event.
2957     *
2958     * @return true if the embedder handled the event.
2959     */
2960    private boolean offerGestureToEmbedder(int type) {
2961        if (type == ContentViewGestureHandler.GESTURE_LONG_PRESS) {
2962            return mContainerView.performLongClick();
2963        }
2964        return false;
2965    }
2966
2967    private native int nativeInit(boolean hardwareAccelerated, int webContentsPtr,
2968            int viewAndroidPtr, int windowAndroidPtr);
2969
2970    @CalledByNative
2971    private ContentVideoViewClient getContentVideoViewClient() {
2972        return mContentViewClient.getContentVideoViewClient();
2973    }
2974
2975    private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl);
2976
2977    private native void nativeLoadUrl(
2978            int nativeContentViewCoreImpl,
2979            String url,
2980            int loadUrlType,
2981            int transitionType,
2982            int uaOverrideOption,
2983            String extraHeaders,
2984            byte[] postData,
2985            String baseUrlForDataUrl,
2986            String virtualUrlForDataUrl,
2987            boolean canLoadLocalResources);
2988
2989    private native String nativeGetURL(int nativeContentViewCoreImpl);
2990
2991    private native String nativeGetTitle(int nativeContentViewCoreImpl);
2992
2993    private native void nativeShowInterstitialPage(
2994            int nativeContentViewCoreImpl, String url, int nativeInterstitialPageDelegateAndroid);
2995    private native boolean nativeIsShowingInterstitialPage(int nativeContentViewCoreImpl);
2996
2997    private native boolean nativeIsIncognito(int nativeContentViewCoreImpl);
2998
2999    // Returns true if the native side crashed so that java side can draw a sad tab.
3000    private native boolean nativeCrashed(int nativeContentViewCoreImpl);
3001
3002    private native void nativeSetFocus(int nativeContentViewCoreImpl, boolean focused);
3003
3004    private native void nativeSendOrientationChangeEvent(
3005            int nativeContentViewCoreImpl, int orientation);
3006
3007    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3008    private native boolean nativeSendTouchEvent(
3009            int nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts);
3010
3011    private native int nativeSendMouseMoveEvent(
3012            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3013
3014    private native int nativeSendMouseWheelEvent(
3015            int nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3016
3017    private native void nativeScrollBegin(
3018            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3019
3020    private native void nativeScrollEnd(int nativeContentViewCoreImpl, long timeMs);
3021
3022    private native void nativeScrollBy(
3023            int nativeContentViewCoreImpl, long timeMs, float x, float y,
3024            float deltaX, float deltaY, boolean lastInputEventForVSync);
3025
3026    private native void nativeFlingStart(
3027            int nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3028
3029    private native void nativeFlingCancel(int nativeContentViewCoreImpl, long timeMs);
3030
3031    private native void nativeSingleTap(
3032            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3033
3034    private native void nativeSingleTapUnconfirmed(
3035            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3036
3037    private native void nativeShowPressState(
3038            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3039
3040    private native void nativeShowPressCancel(
3041            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3042
3043    private native void nativeDoubleTap(
3044            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3045
3046    private native void nativeLongPress(
3047            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3048
3049    private native void nativeLongTap(
3050            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3051
3052    private native void nativePinchBegin(
3053            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3054
3055    private native void nativePinchEnd(int nativeContentViewCoreImpl, long timeMs);
3056
3057    private native void nativePinchBy(int nativeContentViewCoreImpl, long timeMs,
3058            float anchorX, float anchorY, float deltaScale, boolean lastInputEventForVSync);
3059
3060    private native void nativeSelectBetweenCoordinates(
3061            int nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3062
3063    private native void nativeMoveCaret(int nativeContentViewCoreImpl, float x, float y);
3064
3065    private native boolean nativeCanGoBack(int nativeContentViewCoreImpl);
3066    private native boolean nativeCanGoForward(int nativeContentViewCoreImpl);
3067    private native boolean nativeCanGoToOffset(int nativeContentViewCoreImpl, int offset);
3068    private native void nativeGoBack(int nativeContentViewCoreImpl);
3069    private native void nativeGoForward(int nativeContentViewCoreImpl);
3070    private native void nativeGoToOffset(int nativeContentViewCoreImpl, int offset);
3071    private native void nativeGoToNavigationIndex(int nativeContentViewCoreImpl, int index);
3072
3073    private native void nativeStopLoading(int nativeContentViewCoreImpl);
3074
3075    private native void nativeReload(int nativeContentViewCoreImpl);
3076
3077    private native void nativeCancelPendingReload(int nativeContentViewCoreImpl);
3078
3079    private native void nativeContinuePendingReload(int nativeContentViewCoreImpl);
3080
3081    private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices);
3082
3083    private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
3084    private native void nativeUndoScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
3085    private native boolean nativeNeedsReload(int nativeContentViewCoreImpl);
3086
3087    private native void nativeClearHistory(int nativeContentViewCoreImpl);
3088
3089    private native void nativeEvaluateJavaScript(int nativeContentViewCoreImpl,
3090            String script, JavaScriptCallback callback);
3091
3092    private native int nativeGetNativeImeAdapter(int nativeContentViewCoreImpl);
3093
3094    private native int nativeGetCurrentRenderProcessId(int nativeContentViewCoreImpl);
3095
3096    private native int nativeGetBackgroundColor(int nativeContentViewCoreImpl);
3097
3098    private native void nativeOnShow(int nativeContentViewCoreImpl);
3099    private native void nativeOnHide(int nativeContentViewCoreImpl);
3100
3101    private native void nativeSetUseDesktopUserAgent(int nativeContentViewCoreImpl,
3102            boolean enabled, boolean reloadOnChange);
3103    private native boolean nativeGetUseDesktopUserAgent(int nativeContentViewCoreImpl);
3104
3105    private native void nativeClearSslPreferences(int nativeContentViewCoreImpl);
3106
3107    private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object,
3108            String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
3109
3110    private native void nativeRemoveJavascriptInterface(int nativeContentViewCoreImpl, String name);
3111
3112    private native int nativeGetNavigationHistory(int nativeContentViewCoreImpl, Object context);
3113    private native void nativeGetDirectedNavigationHistory(int nativeContentViewCoreImpl,
3114            Object context, boolean isForward, int maxEntries);
3115    private native String nativeGetOriginalUrlForActiveNavigationEntry(
3116            int nativeContentViewCoreImpl);
3117
3118    private native void nativeUpdateVSyncParameters(int nativeContentViewCoreImpl,
3119            long timebaseMicros, long intervalMicros);
3120
3121    private native void nativeOnVSync(int nativeContentViewCoreImpl, long frameTimeMicros);
3122
3123    private native boolean nativeOnAnimate(int nativeContentViewCoreImpl, long frameTimeMicros);
3124
3125    private native boolean nativePopulateBitmapFromCompositor(int nativeContentViewCoreImpl,
3126            Bitmap bitmap);
3127
3128    private native void nativeWasResized(int nativeContentViewCoreImpl);
3129
3130    private native boolean nativeIsRenderWidgetHostViewReady(int nativeContentViewCoreImpl);
3131
3132    private native void nativeExitFullscreen(int nativeContentViewCoreImpl);
3133    private native void nativeUpdateTopControlsState(int nativeContentViewCoreImpl,
3134            boolean enableHiding, boolean enableShowing, boolean animate);
3135
3136    private native void nativeShowImeIfNeeded(int nativeContentViewCoreImpl);
3137
3138    private native void nativeAttachExternalVideoSurface(
3139            int nativeContentViewCoreImpl, int playerId, Surface surface);
3140
3141    private native void nativeDetachExternalVideoSurface(
3142            int nativeContentViewCoreImpl, int playerId);
3143
3144    private native void nativeSetAccessibilityEnabled(
3145            int nativeContentViewCoreImpl, boolean enabled);
3146}
3147