ContentViewCore.java revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
1a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org// Copyright 2012 The Chromium Authors. All rights reserved.
2a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org// Use of this source code is governed by a BSD-style license that can be
3a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org// found in the LICENSE file.
4a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
5a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgpackage org.chromium.content.browser;
6a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
7a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.app.Activity;
8a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.app.SearchManager;
9a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.content.ContentResolver;
10a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.content.Context;
11a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.content.Intent;
12a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.content.pm.ActivityInfo;
13a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.content.pm.PackageManager;
14a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.content.res.Configuration;
157e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.database.ContentObserver;
167e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.graphics.Bitmap;
17a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.graphics.Canvas;
1874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport android.graphics.Color;
19a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.graphics.Rect;
207e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.graphics.RectF;
217e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.net.Uri;
227e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.os.Build;
237e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.os.Bundle;
247e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.os.Handler;
257e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.os.ResultReceiver;
26a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.provider.Browser;
27a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.provider.Settings;
28a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.provider.Settings.Secure;
2901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.orgimport android.text.Editable;
3001d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.orgimport android.text.TextUtils;
317e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.orgimport android.util.Log;
3201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.orgimport android.util.Pair;
3374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport android.view.ActionMode;
3474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport android.view.HapticFeedbackConstants;
3574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport android.view.InputDevice;
36a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.view.KeyEvent;
37c4af4cf7814bb773d838aefa476c196e5f1bc360asapersson@webrtc.orgimport android.view.MotionEvent;
3872cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.Surface;
3972cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.View;
4072cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.ViewGroup;
4172cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.Window;
4272cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.WindowManager;
4372cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.accessibility.AccessibilityEvent;
4472cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.accessibility.AccessibilityManager;
45c4af4cf7814bb773d838aefa476c196e5f1bc360asapersson@webrtc.orgimport android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
46c4af4cf7814bb773d838aefa476c196e5f1bc360asapersson@webrtc.orgimport android.view.accessibility.AccessibilityNodeInfo;
47c4af4cf7814bb773d838aefa476c196e5f1bc360asapersson@webrtc.orgimport android.view.accessibility.AccessibilityNodeProvider;
4872cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.inputmethod.EditorInfo;
4972cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.inputmethod.InputConnection;
5072cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.orgimport android.view.inputmethod.InputMethodManager;
51a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.widget.AbsoluteLayout;
52a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport android.widget.FrameLayout;
53a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
5474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport com.google.common.annotations.VisibleForTesting;
5574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
5674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.base.CalledByNative;
5774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.base.JNINamespace;
5874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.base.WeakContext;
5974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.content.R;
60a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
6101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.orgimport org.chromium.content.browser.accessibility.AccessibilityInjector;
6201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.orgimport org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
63a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport org.chromium.content.browser.input.AdapterInputConnection;
6401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.orgimport org.chromium.content.browser.input.HandleView;
65a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport org.chromium.content.browser.input.ImeAdapter;
66a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
67a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport org.chromium.content.browser.input.InputMethodManagerWrapper;
6874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.content.browser.input.InsertionHandleController;
6974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.content.browser.input.SelectPopupDialog;
7074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.content.browser.input.SelectionHandleController;
7174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.content.common.TraceEvent;
7274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.ui.ViewAndroid;
7374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.ui.ViewAndroidDelegate;
7474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.ui.WindowAndroid;
7574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.orgimport org.chromium.ui.gfx.DeviceDisplayInfo;
7674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
77a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport java.lang.annotation.Annotation;
78a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport java.lang.reflect.Field;
79a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport java.util.HashMap;
80a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport java.util.HashSet;
81a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.orgimport java.util.Map;
8201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
8301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org/**
84a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org * Provides a Java-side 'wrapper' around a WebContent (native) instance.
85a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
86a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org * being tied to the view system.
8701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org */
88a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org@JNINamespace("content")
8901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    public class ContentViewCore implements MotionEventDelegate,
9074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org                                            NavigationClient,
9174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org                                            AccessibilityStateChangeListener {
9201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    /**
9301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     * Indicates that input events are batched together and delivered just before vsync.
9401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     */
9501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    public static final int INPUT_EVENTS_DELIVERED_AT_VSYNC = 1;
9601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
9701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    /**
9801d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     * Opposite of INPUT_EVENTS_DELIVERED_AT_VSYNC.
9901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     */
10001d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    public static final int INPUT_EVENTS_DELIVERED_IMMEDIATELY = 0;
10101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
10201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    private static final String TAG = "ContentViewCore";
10301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
10401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // Used to avoid enabling zooming in / out if resulting zooming will
10501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // produce little visible difference.
10601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
10701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
10801d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // Used to represent gestures for long press and long tap.
10901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    private static final int IS_LONG_PRESS = 1;
1104a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    private static final int IS_LONG_TAP = 2;
1114a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org
1124a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // Length of the delay (in ms) before fading in handles after the last page movement.
11301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
11401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
11501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // If the embedder adds a JavaScript interface object that contains an indirect reference to
11601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // the ContentViewCore, then storing a strong ref to the interface object on the native
1174a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // side would prevent garbage collection of the ContentViewCore (as that strong ref would
1184a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // create a new GC root).
1194a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // For that reason, we store only a weak reference to the interface object on the
1204a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // native side. However we still need a strong reference on the Java side to
1214a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // prevent garbage collection if the embedder doesn't maintain their own ref to the
1224a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // interface object - the Java side ref won't create a new GC root.
1234a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // This map stores those refernces. We put into the map on addJavaScriptInterface()
1244a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    // and remove from it in removeJavaScriptInterface().
1254a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
12674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
12774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    // Additionally, we keep track of all Java bound JS objects that are in use on the
12874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    // current page to ensure that they are not garbage collected until the page is
12974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    // navigated. This includes interface objects that have been removed
13074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    // via the removeJavaScriptInterface API and transient objects returned from methods
13174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    // on the interface object. Note we use HashSet rather than Set as the native side
132a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    // expects HashSet (no bindings for interfaces).
133a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
134a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
135a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    /**
136a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org     * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
137a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org     * dispatching of view methods through the containing view.
138a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org     *
13901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     * <p>
14001d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     * All methods with the "super_" prefix should be routed to the parent of the
14101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     * implementing container view.
14201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org     */
14374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    @SuppressWarnings("javadoc")
144903e746cc9a73da17bfa3f6110293582fa6ee3bestefan@webrtc.org    public interface InternalAccessDelegate {
14501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        /**
14674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * @see View#drawChild(Canvas, View, long)
14774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
14874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        boolean drawChild(Canvas canvas, View child, long drawingTime);
14974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
15074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
15174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * @see View#onKeyUp(keyCode, KeyEvent)
15274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
15374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        boolean super_onKeyUp(int keyCode, KeyEvent event);
15474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
15574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
15601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org         * @see View#dispatchKeyEventPreIme(KeyEvent)
15701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org         */
15874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        boolean super_dispatchKeyEventPreIme(KeyEvent event);
159903e746cc9a73da17bfa3f6110293582fa6ee3bestefan@webrtc.org
16001d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        /**
16101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org         * @see View#dispatchKeyEvent(KeyEvent)
16201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org         */
16301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        boolean super_dispatchKeyEvent(KeyEvent event);
164a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
165a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org        /**
16601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org         * @see View#onGenericMotionEvent(MotionEvent)
16701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org         */
16874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        boolean super_onGenericMotionEvent(MotionEvent event);
16974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
17074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
17174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * @see View#onConfigurationChanged(Configuration)
17274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
17374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void super_onConfigurationChanged(Configuration newConfig);
17474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
17574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
17674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * @see View#awakenScrollBars()
17774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
17874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        boolean awakenScrollBars();
17974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
18074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
18174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * @see View#awakenScrollBars(int, boolean)
18274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
18374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        boolean super_awakenScrollBars(int startDelay, boolean invalidate);
18474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    }
18574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
18674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    /**
18774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * An interface that allows the embedder to be notified when the pinch gesture starts and
18874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * stops.
18974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     */
19074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    public interface GestureStateListener {
19174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
19274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when the pinch gesture starts.
19374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
19474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void onPinchGestureStart();
19574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
19674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
19774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when the pinch gesture ends.
19874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
199a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org        void onPinchGestureEnd();
200a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
20174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
20274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when the fling gesture is sent.
20374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
20474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void onFlingStartGesture(int vx, int vy);
20574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
20674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
20774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when the fling cancel gesture is sent.
20874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
20974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void onFlingCancelGesture();
21074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
21174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
21274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when a fling event was not handled by the renderer.
21374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
21474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void onUnhandledFlingStartEvent();
21574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    }
21674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
21774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    /**
21874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * An interface for controlling visibility and state of embedder-provided zoom controls.
21974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     */
22074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    public interface ZoomControlsDelegate {
22174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
22274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when it's reasonable to show zoom controls.
22374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
22474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void invokeZoomPicker();
22574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
22674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
22774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when zoom controls need to be hidden (e.g. when the view hides).
22874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
22974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void dismissZoomPicker();
23074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
23174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
23274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called when page scale has been changed, so the controls can update their state.
23374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
23474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void updateZoomControls();
23574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    }
23674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
23774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    /**
23874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * An interface that allows the embedder to be notified of changes to the parameters of the
23974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * currently displayed contents.
24074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * These notifications are consistent with respect to the UI thread (the size is the size of
24174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     * the contents currently displayed on screen).
24274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org     */
24374257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    public interface UpdateFrameInfoListener {
24474257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        /**
24574257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * Called each time any of the parameters are changed.
24674257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         *
24774257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         * @param pageScaleFactor The page scale.
24874257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org         */
24974257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org        void onFrameInfoUpdated(float pageScaleFactor);
25074257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    }
25174257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org
25274257108b4a17f3aeff1756b1f5bf45f971b6caeasapersson@webrtc.org    private VSyncManager.Provider mVSyncProvider;
253a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    private VSyncManager.Listener mVSyncListener;
254fec6b6e5999edec8c90efae54357f1aae6a4c7ddsolenberg@webrtc.org    private int mVSyncSubscriberCount;
255a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    private boolean mVSyncListenerRegistered;
256a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
25701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer.
258a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    // When we do this, we also need to avoid sending the real vsync signal for the current
25901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification.
260a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org    private boolean mDidSignalVSyncUsingInputEvent;
26101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
26201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
263a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org        if (mVSyncProvider != null && mVSyncListenerRegistered) {
26401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org            mVSyncProvider.unregisterVSyncListener(mVSyncListener);
265a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org            mVSyncListenerRegistered = false;
26601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        }
267a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
268a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org        mVSyncProvider = vsyncProvider;
269a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org        mVSyncListener = new VSyncManager.Listener() {
270a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org            @Override
271a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org            public void updateVSync(long tickTimeMicros, long intervalMicros) {
272a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org                if (mNativeContentViewCore != 0) {
27301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                    nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
27401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                            intervalMicros);
27501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                }
27601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org            }
27701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
27801d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org            @Override
279a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org            public void onVSync(long frameTimeMicros) {
280a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org                animateIfNecessary(frameTimeMicros);
281a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org
28201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                if (mDidSignalVSyncUsingInputEvent) {
2835041831648212bfd5b68e91e1ff39e2b420f679easapersson@webrtc.org                    TraceEvent.instant("ContentViewCore::onVSync ignored");
2845041831648212bfd5b68e91e1ff39e2b420f679easapersson@webrtc.org                    mDidSignalVSyncUsingInputEvent = false;
28501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                    return;
28601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                }
28701d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                if (mNativeContentViewCore != 0) {
28801d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                    nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
28901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org                }
290a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org            }
291a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org        };
2923dc7ff30185a2d7a597acb69183fca73bd1004d8asapersson@webrtc.org
29301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        if (mVSyncSubscriberCount > 0) {
29401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org            // addVSyncSubscriber() is called before getVSyncListener.
29501d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org            vsyncProvider.registerVSyncListener(mVSyncListener);
2963dc7ff30185a2d7a597acb69183fca73bd1004d8asapersson@webrtc.org            mVSyncListenerRegistered = true;
2973dc7ff30185a2d7a597acb69183fca73bd1004d8asapersson@webrtc.org        }
29872cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org
29901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        return mVSyncListener;
30072cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org    }
30101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
30201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    @CalledByNative
30372cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org    void addVSyncSubscriber() {
30401d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        if (!isVSyncNotificationEnabled()) {
30572cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org            mDidSignalVSyncUsingInputEvent = false;
30601d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        }
30772cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org        if (mVSyncProvider != null && !mVSyncListenerRegistered) {
30872cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org            mVSyncProvider.registerVSyncListener(mVSyncListener);
30901d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org            mVSyncListenerRegistered = true;
31001d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        }
31101d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org        mVSyncSubscriberCount++;
31201d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org    }
31301d06c859328f3fb8c8521eede87063b05583208asapersson@webrtc.org
31472cc32a68bcb9b2aa32f751dc6d120716d220a10asapersson@webrtc.org    @CalledByNative
3154a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    void removeVSyncSubscriber() {
3164a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        if (mVSyncProvider != null && mVSyncSubscriberCount == 1) {
3174a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org            assert mVSyncListenerRegistered;
3184a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org            mVSyncProvider.unregisterVSyncListener(mVSyncListener);
3194a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org            mVSyncListenerRegistered = false;
3204a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        }
3214a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        mVSyncSubscriberCount--;
3224a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        assert mVSyncSubscriberCount >= 0;
3234a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    }
3244a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org
3254a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    @CalledByNative
3264a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    private void resetVSyncNotification() {
3274a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        while (isVSyncNotificationEnabled()) removeVSyncSubscriber();
3284a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        mVSyncSubscriberCount = 0;
3294a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        mVSyncListenerRegistered = false;
3304a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        mNeedAnimate = false;
3314a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    }
3324a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org
3334a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    private boolean isVSyncNotificationEnabled() {
3344a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        return mVSyncProvider != null && mVSyncListenerRegistered;
3354a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    }
3364a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org
3374a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    @CalledByNative
3384a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    private void setNeedsAnimate() {
3394a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        if (!mNeedAnimate) {
3404a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org            mNeedAnimate = true;
3414a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org            addVSyncSubscriber();
3424a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org        }
3434a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    }
3444a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org
3454a1556017653ef7702585897b071872e83bd95b9asapersson@webrtc.org    private final Context mContext;
3467e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ViewGroup mContainerView;
3477e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private InternalAccessDelegate mContainerViewInternals;
3487e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private WebContentsObserverAndroid mWebContentsObserver;
3497e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3507e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ContentViewClient mContentViewClient;
3517e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3527e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ContentSettings mContentSettings;
3537e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3546aae61c2c693fa3425c73c420e7046e95486b592pbos@webrtc.org    // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
3556aae61c2c693fa3425c73c420e7046e95486b592pbos@webrtc.org    private int mNativeContentViewCore = 0;
3566aae61c2c693fa3425c73c420e7046e95486b592pbos@webrtc.org
3577e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mAttachedToWindow = false;
3587e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3597e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Pid of the renderer process backing this ContentViewCore.
3607e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mPid = 0;
3617e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3627e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ContentViewGestureHandler mContentViewGestureHandler;
3637e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private GestureStateListener mGestureStateListener;
3647e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ZoomManager mZoomManager;
3657e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ZoomControlsDelegate mZoomControlsDelegate;
3667e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3677e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private PopupZoomer mPopupZoomer;
3687e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3697e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private Runnable mFakeMouseMoveRunnable = null;
3707e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3717e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Only valid when focused on a text / password field.
3727e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ImeAdapter mImeAdapter;
3737e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
3747e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private AdapterInputConnection mInputConnection;
3757e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3767e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private SelectionHandleController mSelectionHandleController;
3777e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private InsertionHandleController mInsertionHandleController;
3787e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3797e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private Runnable mDeferredHandleFadeInRunnable;
3807e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3817e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Size of the viewport in physical pixels as set from onSizeChanged.
3827e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mViewportWidthPix;
3837e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mViewportHeightPix;
3847e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mPhysicalBackingWidthPix;
3857e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mPhysicalBackingHeightPix;
3867e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mOverdrawBottomHeightPix;
3877e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mViewportSizeOffsetWidthPix;
3887e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private int mViewportSizeOffsetHeightPix;
3897e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3907e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Cached copy of all positions and scales as reported by the renderer.
3917e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private final RenderCoordinates mRenderCoordinates;
3927e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3937e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private final RenderCoordinates.NormalizedPoint mStartHandlePoint;
3947e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private final RenderCoordinates.NormalizedPoint mEndHandlePoint;
3957e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private final RenderCoordinates.NormalizedPoint mInsertionHandlePoint;
3967e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
3977e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Tracks whether a selection is currently active.  When applied to selected text, indicates
3987e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // whether the last selected text is still highlighted.
3997e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mHasSelection;
4007e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private String mLastSelectedText;
4017e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mSelectionEditable;
4027e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ActionMode mActionMode;
4037e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mUnselectAllOnActionModeDismiss;
4047e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4057e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
4067e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ContentViewDownloadDelegate mDownloadDelegate;
4077e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4087e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
4097e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private AccessibilityInjector mAccessibilityInjector;
4107e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4117e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Whether native accessibility, i.e. without any script injection, is allowed.
4127e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mNativeAccessibilityAllowed;
4137e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4147e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Whether native accessibility, i.e. without any script injection, has been enabled.
4157e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mNativeAccessibilityEnabled;
4167e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4177e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Handles native accessibility, i.e. without any script injection.
4187e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private BrowserAccessibilityManager mBrowserAccessibilityManager;
4197e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4207e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // System accessibility service.
4217e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private final AccessibilityManager mAccessibilityManager;
4227e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4237e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Allows us to dynamically respond when the accessibility script injection flag changes.
4247e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ContentObserver mAccessibilityScriptInjectionObserver;
4257e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4267e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Temporary notification to tell onSizeChanged to focus a form element,
4277e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // because the OSK was just brought up.
4287e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mUnfocusOnNextSizeChanged = false;
4297e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private final Rect mFocusPreOSKViewportRect = new Rect();
4307e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4317e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mNeedUpdateOrientationChanged;
4327e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4337e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Used to keep track of whether we should try to undo the last zoom-to-textfield operation.
4347e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mScrolledAndZoomedFocusedEditableNode = false;
4357e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4367e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Whether we use hardware-accelerated drawing.
4377e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mHardwareAccelerated = false;
4387e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4397e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Whether we received a new frame since consumePendingRendererFrame() was last called.
4407e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mPendingRendererFrame = false;
4417e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4427e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    // Whether we should animate at the next vsync tick.
4437e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private boolean mNeedAnimate = false;
4447e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4457e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ViewAndroid mViewAndroid;
4467e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4477e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4487e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    /**
4497e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
4507e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * a ContentViewCore and before using it.
4517e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     *
4527e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * @param context The context used to create this.
4537e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     */
4547e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public ContentViewCore(Context context) {
4557e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mContext = context;
4567e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4577e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        WeakContext.initializeWeakContext(context);
4587e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        HeapStatsLogger.init(mContext.getApplicationContext());
4597e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
4607e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4617e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mRenderCoordinates = new RenderCoordinates();
4627e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mRenderCoordinates.setDeviceScaleFactor(
4637e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                getContext().getResources().getDisplayMetrics().density);
4647e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
4657e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
4667e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
4677e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mAccessibilityManager = (AccessibilityManager)
4687e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
4697e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
4707e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4717e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    /**
4727e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * @return The context used for creating this ContentViewCore.
4737e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     */
4747e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    @CalledByNative
4757e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public Context getContext() {
4767e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        return mContext;
4777e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
4787e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4797e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    /**
4807e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
4817e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     */
4827e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public ViewGroup getContainerView() {
4837e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        return mContainerView;
4847e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
4857e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
4867e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    /**
4877e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * Specifies how much smaller the WebKit layout size should be relative to the size of this
4887e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * view.
4897e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * @param offsetXPix The X amount in pixels to shrink the viewport by.
4907e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * @param offsetYPix The Y amount in pixels to shrink the viewport by.
4917e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     */
4927e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
4937e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        if (offsetXPix != mViewportSizeOffsetWidthPix ||
4947e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                offsetYPix != mViewportSizeOffsetHeightPix) {
4957e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            mViewportSizeOffsetWidthPix = offsetXPix;
4967e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            mViewportSizeOffsetHeightPix = offsetYPix;
4977e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
4987e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        }
4997e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
5007e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5017e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    /**
5027e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * Returns a delegate that can be used to add and remove views from the ContainerView.
5037e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     *
5047e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
5057e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * way. In particular, the Android WebView has limitations on what implementation details can
5067e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * be provided via a child view, as they are visible in the API and could introduce
5077e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * compatibility breaks with existing applications. If in doubt, contact the
5087e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     * android_webview/OWNERS
5097e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     *
5104a1b3e3a69d349b0d3e91f607f24e02d8b975688mflodman@webrtc.org     * @return A ViewAndroidDelegate that can be used to add and remove views.
5117e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org     */
5127e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    @VisibleForTesting
5137e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public ViewAndroidDelegate getViewAndroidDelegate() {
5147e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        return new ViewAndroidDelegate() {
5157e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            @Override
5167e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            public View acquireAnchorView() {
5177e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                View anchorView = new View(getContext());
5187e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                mContainerView.addView(anchorView);
5197e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                return anchorView;
5207e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            }
5217e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5227e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            @Override
5237e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            @SuppressWarnings("deprecation")  // AbsoluteLayout.LayoutParams
5247e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            public void setAnchorViewPosition(
5257e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    View view, float x, float y, float width, float height) {
5267e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                assert(view.getParent() == mContainerView);
5277e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5287e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                float scale = (float) DeviceDisplayInfo.create(getContext()).getDIPScale();
5297e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5307e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                // The anchor view should not go outside the bounds of the ContainerView.
5317e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                int leftMargin = Math.round(x * scale);
5324a1b3e3a69d349b0d3e91f607f24e02d8b975688mflodman@webrtc.org                int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
5337e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                int scaledWidth = Math.round(width * scale);
5347e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                // ContentViewCore currently only supports these two container view types.
535f147639fcf3f45fdf764480834a483eb88b1cf2astefan@webrtc.org                if (mContainerView instanceof FrameLayout) {
536f147639fcf3f45fdf764480834a483eb88b1cf2astefan@webrtc.org                    if (scaledWidth + leftMargin > mContainerView.getWidth()) {
537f147639fcf3f45fdf764480834a483eb88b1cf2astefan@webrtc.org                        scaledWidth = mContainerView.getWidth() - leftMargin;
538f147639fcf3f45fdf764480834a483eb88b1cf2astefan@webrtc.org                    }
5397e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
5407e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                        scaledWidth, Math.round(height * scale));
5417e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    lp.leftMargin = leftMargin;
5427e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    lp.topMargin = topMargin;
5437e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    view.setLayoutParams(lp);
5447e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                } else if (mContainerView instanceof AbsoluteLayout) {
5457e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    // This fixes the offset due to a difference in
5467e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    // scrolling model of WebView vs. Chrome.
5477e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    // TODO(sgurun) fix this to use mContainerView.getScroll[X/Y]()
5487e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    // as it naturally accounts for scroll differences between
5497e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    // these models.
5507e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    leftMargin += mRenderCoordinates.getScrollXPixInt();
5517e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    topMargin += mRenderCoordinates.getScrollYPixInt();
5527e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5537e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    android.widget.AbsoluteLayout.LayoutParams lp =
5547e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                            new android.widget.AbsoluteLayout.LayoutParams(
5557e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                                scaledWidth, (int)(height * scale), leftMargin, topMargin);
5567e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    view.setLayoutParams(lp);
5577e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                } else {
5587e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    Log.e(TAG, "Unknown layout " + mContainerView.getClass().getName());
5597e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                }
5607e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            }
5617e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5624a1b3e3a69d349b0d3e91f607f24e02d8b975688mflodman@webrtc.org            @Override
5637e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            public void releaseAnchorView(View anchorView) {
5647e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                mContainerView.removeView(anchorView);
5657e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org            }
5667e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        };
5677e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
5687e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5697e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    @VisibleForTesting
5707e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public ImeAdapter getImeAdapterForTest() {
5717e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        return mImeAdapter;
5727e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
5737e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5747e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    @VisibleForTesting
5757e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
5767e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        mAdapterInputConnectionFactory = factory;
5777e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
5787e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5797e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    @VisibleForTesting
5807e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    public AdapterInputConnection getInputConnectionForTest() {
5817e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        return mInputConnection;
5827e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    }
5837e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org
5847e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org    private ImeAdapter createImeAdapter(Context context) {
5857e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org        return new ImeAdapter(new InputMethodManagerWrapper(context),
5867e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                new ImeAdapter.ImeAdapterDelegate() {
5877e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    @Override
5887e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                    public void onImeEvent(boolean isFinish) {
5897e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                        getContentViewClient().onImeEvent();
5907e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                        if (!isFinish) {
5917e87822cf6f75bb60c3d47cfb7d76c98b0ad6d6astefan@webrtc.org                            hideHandles();
592a2d942a1e99a150f42f23ef2d6342c91a756dbb0asapersson@webrtc.org                            undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
593                        }
594                    }
595
596                    @Override
597                    public void onSetFieldValue() {
598                        scrollFocusedEditableNodeIntoView();
599                    }
600
601                    @Override
602                    public void onDismissInput() {
603                        getContentViewClient().onImeStateChangeRequested(false);
604                    }
605
606                    @Override
607                    public View getAttachedView() {
608                        return mContainerView;
609                    }
610
611                    @Override
612                    public ResultReceiver getNewShowKeyboardReceiver() {
613                        return new ResultReceiver(new Handler()) {
614                            @Override
615                            public void onReceiveResult(int resultCode, Bundle resultData) {
616                                getContentViewClient().onImeStateChangeRequested(
617                                        resultCode == InputMethodManager.RESULT_SHOWN ||
618                                        resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
619                                if (resultCode == InputMethodManager.RESULT_SHOWN) {
620                                    // If OSK is newly shown, delay the form focus until
621                                    // the onSizeChanged (in order to adjust relative to the
622                                    // new size).
623                                    // TODO(jdduke): We should not assume that onSizeChanged will
624                                    // always be called, crbug.com/294908.
625                                    getContainerView().getWindowVisibleDisplayFrame(
626                                            mFocusPreOSKViewportRect);
627                                } else if (resultCode ==
628                                        InputMethodManager.RESULT_UNCHANGED_SHOWN) {
629                                    // If the OSK was already there, focus the form immediately.
630                                    scrollFocusedEditableNodeIntoView();
631                                } else {
632                                    undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
633                                }
634                            }
635                        };
636                    }
637                }
638        );
639    }
640
641    /**
642     * Returns true if the given Activity has hardware acceleration enabled
643     * in its manifest, or in its foreground window.
644     *
645     * TODO(husky): Remove when initialize() is refactored (see TODO there)
646     * TODO(dtrainor) This is still used by other classes.  Make sure to pull some version of this
647     * out before removing it.
648     */
649    public static boolean hasHardwareAcceleration(Activity activity) {
650        // Has HW acceleration been enabled manually in the current window?
651        Window window = activity.getWindow();
652        if (window != null) {
653            if ((window.getAttributes().flags
654                    & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
655                return true;
656            }
657        }
658
659        // Has HW acceleration been enabled in the manifest?
660        try {
661            ActivityInfo info = activity.getPackageManager().getActivityInfo(
662                    activity.getComponentName(), 0);
663            if ((info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
664                return true;
665            }
666        } catch (PackageManager.NameNotFoundException e) {
667            Log.e("Chrome", "getActivityInfo(self) should not fail");
668        }
669
670        return false;
671    }
672
673    /**
674     * Returns true if the given Context is a HW-accelerated Activity.
675     *
676     * TODO(husky): Remove when initialize() is refactored (see TODO there)
677     */
678    private static boolean hasHardwareAcceleration(Context context) {
679        if (context instanceof Activity) {
680            return hasHardwareAcceleration((Activity) context);
681        }
682        return false;
683    }
684
685    /**
686     *
687     * @param containerView The view that will act as a container for all views created by this.
688     * @param internalDispatcher Handles dispatching all hidden or super methods to the
689     *                           containerView.
690     * @param nativeWebContents A pointer to the native web contents.
691     * @param windowAndroid An instance of the WindowAndroid.
692     */
693    // Perform important post-construction set up of the ContentViewCore.
694    // We do not require the containing view in the constructor to allow embedders to create a
695    // ContentViewCore without having fully created its containing view. The containing view
696    // is a vital component of the ContentViewCore, so embedders must exercise caution in what
697    // they do with the ContentViewCore before calling initialize().
698    // We supply the nativeWebContents pointer here rather than in the constructor to allow us
699    // to set the private browsing mode at a later point for the WebView implementation.
700    // Note that the caller remains the owner of the nativeWebContents and is responsible for
701    // deleting it after destroying the ContentViewCore.
702    public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
703            int nativeWebContents, WindowAndroid windowAndroid,
704            int inputEventDeliveryMode) {
705        // Check whether to use hardware acceleration. This is a bit hacky, and
706        // only works if the Context is actually an Activity (as it is in the
707        // Chrome application).
708        //
709        // What we're doing here is checking whether the app has *requested*
710        // hardware acceleration by setting the appropriate flags. This does not
711        // necessarily mean we're going to *get* hardware acceleration -- that's
712        // up to the Android framework.
713        //
714        // TODO(husky): Once the native code has been updated so that the
715        // HW acceleration flag can be set dynamically (Grace is doing this),
716        // move this check into onAttachedToWindow(), where we can test for
717        // HW support directly.
718        mHardwareAccelerated = hasHardwareAcceleration(mContext);
719
720        mContainerView = containerView;
721
722        int windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
723
724        int viewAndroidNativePointer = 0;
725        if (windowNativePointer != 0) {
726            mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
727            viewAndroidNativePointer = mViewAndroid.getNativePointer();
728        }
729
730        mNativeContentViewCore = nativeInit(mHardwareAccelerated,
731                nativeWebContents, viewAndroidNativePointer, windowNativePointer);
732        mContentSettings = new ContentSettings(this, mNativeContentViewCore);
733        initializeContainerView(internalDispatcher, inputEventDeliveryMode);
734
735        mAccessibilityInjector = AccessibilityInjector.newInstance(this);
736
737        String contentDescription = "Web View";
738        if (R.string.accessibility_content_view == 0) {
739            Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
740        } else {
741            contentDescription = mContext.getResources().getString(
742                    R.string.accessibility_content_view);
743        }
744        mContainerView.setContentDescription(contentDescription);
745        mWebContentsObserver = new WebContentsObserverAndroid(this) {
746            @Override
747            public void didStartLoading(String url) {
748                hidePopupDialog();
749                resetGestureDetectors();
750            }
751        };
752
753        mPid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
754    }
755
756    @CalledByNative
757    void onNativeContentViewCoreDestroyed(int nativeContentViewCore) {
758        assert nativeContentViewCore == mNativeContentViewCore;
759        mNativeContentViewCore = 0;
760    }
761
762    /**
763     * Initializes the View that will contain all Views created by the ContentViewCore.
764     *
765     * @param internalDispatcher Handles dispatching all hidden or super methods to the
766     *                           containerView.
767     */
768    private void initializeContainerView(InternalAccessDelegate internalDispatcher,
769            int inputEventDeliveryMode) {
770        TraceEvent.begin();
771        mContainerViewInternals = internalDispatcher;
772
773        mContainerView.setWillNotDraw(false);
774        mContainerView.setClickable(true);
775
776        mZoomManager = new ZoomManager(mContext, this);
777        mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this, mZoomManager,
778                inputEventDeliveryMode);
779        mZoomControlsDelegate = new ZoomControlsDelegate() {
780            @Override
781            public void invokeZoomPicker() {}
782            @Override
783            public void dismissZoomPicker() {}
784            @Override
785            public void updateZoomControls() {}
786        };
787
788        mRenderCoordinates.reset();
789
790        initPopupZoomer(mContext);
791        mImeAdapter = createImeAdapter(mContext);
792        TraceEvent.end();
793    }
794
795    private void initPopupZoomer(Context context){
796        mPopupZoomer = new PopupZoomer(context);
797        mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
798            @Override
799            public void onPopupZoomerShown(final PopupZoomer zoomer) {
800                mContainerView.post(new Runnable() {
801                    @Override
802                    public void run() {
803                        if (mContainerView.indexOfChild(zoomer) == -1) {
804                            mContainerView.addView(zoomer);
805                        } else {
806                            assert false : "PopupZoomer should never be shown without being hidden";
807                        }
808                    }
809                });
810            }
811
812            @Override
813            public void onPopupZoomerHidden(final PopupZoomer zoomer) {
814                mContainerView.post(new Runnable() {
815                    @Override
816                    public void run() {
817                        if (mContainerView.indexOfChild(zoomer) != -1) {
818                            mContainerView.removeView(zoomer);
819                            mContainerView.invalidate();
820                        } else {
821                            assert false : "PopupZoomer should never be hidden without being shown";
822                        }
823                    }
824                });
825            }
826        });
827        // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
828        // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
829        PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
830            @Override
831            public boolean onSingleTap(View v, MotionEvent e) {
832                mContainerView.requestFocus();
833                if (mNativeContentViewCore != 0) {
834                    nativeSingleTap(mNativeContentViewCore, e.getEventTime(),
835                            e.getX(), e.getY(), true);
836                }
837                return true;
838            }
839
840            @Override
841            public boolean onLongPress(View v, MotionEvent e) {
842                if (mNativeContentViewCore != 0) {
843                    nativeLongPress(mNativeContentViewCore, e.getEventTime(),
844                            e.getX(), e.getY(), true);
845                }
846                return true;
847            }
848        };
849        mPopupZoomer.setOnTapListener(listener);
850    }
851
852    /**
853     * Destroy the internal state of the ContentView. This method may only be
854     * called after the ContentView has been removed from the view system. No
855     * other methods may be called on this ContentView after this method has
856     * been called.
857     */
858    public void destroy() {
859        if (mNativeContentViewCore != 0) {
860            nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
861        }
862        resetVSyncNotification();
863        mVSyncProvider = null;
864        if (mViewAndroid != null) mViewAndroid.destroy();
865        mNativeContentViewCore = 0;
866        mContentSettings = null;
867        mJavaScriptInterfaces.clear();
868        mRetainedJavaScriptObjects.clear();
869        unregisterAccessibilityContentObserver();
870    }
871
872    private void unregisterAccessibilityContentObserver() {
873        if (mAccessibilityScriptInjectionObserver == null) {
874            return;
875        }
876        getContext().getContentResolver().unregisterContentObserver(
877                mAccessibilityScriptInjectionObserver);
878        mAccessibilityScriptInjectionObserver = null;
879    }
880
881    /**
882     * Returns true initially, false after destroy() has been called.
883     * It is illegal to call any other public method after destroy().
884     */
885    public boolean isAlive() {
886        return mNativeContentViewCore != 0;
887    }
888
889    /**
890     * This is only useful for passing over JNI to native code that requires ContentViewCore*.
891     * @return native ContentViewCore pointer.
892     */
893    @CalledByNative
894    public int getNativeContentViewCore() {
895        return mNativeContentViewCore;
896    }
897
898    /**
899     * For internal use. Throws IllegalStateException if mNativeContentView is 0.
900     * Use this to ensure we get a useful Java stack trace, rather than a native
901     * crash dump, from use-after-destroy bugs in Java code.
902     */
903    void checkIsAlive() throws IllegalStateException {
904        if (!isAlive()) {
905            throw new IllegalStateException("ContentView used after destroy() was called");
906        }
907    }
908
909    public void setContentViewClient(ContentViewClient client) {
910        if (client == null) {
911            throw new IllegalArgumentException("The client can't be null.");
912        }
913        mContentViewClient = client;
914    }
915
916    ContentViewClient getContentViewClient() {
917        if (mContentViewClient == null) {
918            // We use the Null Object pattern to avoid having to perform a null check in this class.
919            // We create it lazily because most of the time a client will be set almost immediately
920            // after ContentView is created.
921            mContentViewClient = new ContentViewClient();
922            // We don't set the native ContentViewClient pointer here on purpose. The native
923            // implementation doesn't mind a null delegate and using one is better than passing a
924            // Null Object, since we cut down on the number of JNI calls.
925        }
926        return mContentViewClient;
927    }
928
929    public int getBackgroundColor() {
930        if (mNativeContentViewCore != 0) {
931            return nativeGetBackgroundColor(mNativeContentViewCore);
932        }
933        return Color.WHITE;
934    }
935
936    @CalledByNative
937    private void onBackgroundColorChanged(int color) {
938        getContentViewClient().onBackgroundColorChanged(color);
939    }
940
941    /**
942     * Load url without fixing up the url string. Consumers of ContentView are responsible for
943     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
944     * off during user input).
945     *
946     * @param params Parameters for this load.
947     */
948    public void loadUrl(LoadUrlParams params) {
949        if (mNativeContentViewCore == 0) return;
950
951        nativeLoadUrl(mNativeContentViewCore,
952                params.mUrl,
953                params.mLoadUrlType,
954                params.mTransitionType,
955                params.mUaOverrideOption,
956                params.getExtraHeadersString(),
957                params.mPostData,
958                params.mBaseUrlForDataUrl,
959                params.mVirtualUrlForDataUrl,
960                params.mCanLoadLocalResources);
961    }
962
963    /**
964     * Stops loading the current web contents.
965     */
966    public void stopLoading() {
967        if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
968    }
969
970    /**
971     * Get the URL of the current page.
972     *
973     * @return The URL of the current page.
974     */
975    public String getUrl() {
976        if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
977        return null;
978    }
979
980    /**
981     * Get the title of the current page.
982     *
983     * @return The title of the current page.
984     */
985    public String getTitle() {
986        if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
987        return null;
988    }
989
990    /**
991     * Shows an interstitial page driven by the passed in delegate.
992     *
993     * @param url The URL being blocked by the interstitial.
994     * @param delegate The delegate handling the interstitial.
995     */
996    @VisibleForTesting
997    public void showInterstitialPage(
998            String url, InterstitialPageDelegateAndroid delegate) {
999        if (mNativeContentViewCore == 0) return;
1000        nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
1001    }
1002
1003    /**
1004     * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
1005     */
1006    public boolean isShowingInterstitialPage() {
1007        return mNativeContentViewCore == 0 ?
1008                false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
1009    }
1010
1011    /**
1012     * Mark any new frames that have arrived since this function was last called as non-pending.
1013     *
1014     * @return Whether there was a pending frame from the renderer.
1015     */
1016    public boolean consumePendingRendererFrame() {
1017        boolean hadPendingFrame = mPendingRendererFrame;
1018        mPendingRendererFrame = false;
1019        return hadPendingFrame;
1020    }
1021
1022    /**
1023     * @return Viewport width in physical pixels as set from onSizeChanged.
1024     */
1025    @CalledByNative
1026    public int getViewportWidthPix() { return mViewportWidthPix; }
1027
1028    /**
1029     * @return Viewport height in physical pixels as set from onSizeChanged.
1030     */
1031    @CalledByNative
1032    public int getViewportHeightPix() { return mViewportHeightPix; }
1033
1034    /**
1035     * @return Width of underlying physical surface.
1036     */
1037    @CalledByNative
1038    public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
1039
1040    /**
1041     * @return Height of underlying physical surface.
1042     */
1043    @CalledByNative
1044    public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
1045
1046    /**
1047     * @return Amount the output surface extends past the bottom of the window viewport.
1048     */
1049    @CalledByNative
1050    public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
1051
1052    /**
1053     * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
1054     */
1055    @CalledByNative
1056    public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
1057
1058    /**
1059     * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
1060     */
1061    @CalledByNative
1062    public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
1063
1064    /**
1065     * @see android.webkit.WebView#getContentHeight()
1066     */
1067    public float getContentHeightCss() {
1068        return mRenderCoordinates.getContentHeightCss();
1069    }
1070
1071    /**
1072     * @see android.webkit.WebView#getContentWidth()
1073     */
1074    public float getContentWidthCss() {
1075        return mRenderCoordinates.getContentWidthCss();
1076    }
1077
1078    public Bitmap getBitmap() {
1079        return getBitmap(getViewportWidthPix(), getViewportHeightPix());
1080    }
1081
1082    public Bitmap getBitmap(int width, int height) {
1083        if (width == 0 || height == 0
1084                || getViewportWidthPix() == 0 || getViewportHeightPix() == 0) {
1085            return null;
1086        }
1087
1088        Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1089
1090        if (mNativeContentViewCore != 0 &&
1091                nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) {
1092            // If we successfully grabbed a bitmap, check if we have to draw the Android overlay
1093            // components as well.
1094            if (mContainerView.getChildCount() > 0) {
1095                Canvas c = new Canvas(b);
1096                c.scale(width / (float) getViewportWidthPix(),
1097                        height / (float) getViewportHeightPix());
1098                mContainerView.draw(c);
1099            }
1100            return b;
1101        }
1102
1103        return null;
1104    }
1105
1106    /**
1107     * Generates a bitmap of the content that is performance optimized based on capture time.
1108     *
1109     * <p>
1110     * To have a consistent capture time across devices, we will scale down the captured bitmap
1111     * where necessary to reduce the time to generate the bitmap.
1112     *
1113     * @param width The width of the content to be captured.
1114     * @param height The height of the content to be captured.
1115     * @return A pair of the generated bitmap, and the scale that needs to be applied to return the
1116     *         bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this
1117     *         will be 2).
1118     */
1119    public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
1120        float scale = 1f;
1121        // On tablets, always scale down to MDPI for performance reasons.
1122        if (DeviceUtils.isTablet(getContext())) {
1123            scale = getContext().getResources().getDisplayMetrics().density;
1124        }
1125        return Pair.create(
1126                getBitmap((int) (width / scale), (int) (height / scale)),
1127                scale);
1128    }
1129
1130    /**
1131     * @return Whether the current WebContents has a previous navigation entry.
1132     */
1133    public boolean canGoBack() {
1134        return mNativeContentViewCore != 0 && nativeCanGoBack(mNativeContentViewCore);
1135    }
1136
1137    /**
1138     * @return Whether the current WebContents has a navigation entry after the current one.
1139     */
1140    public boolean canGoForward() {
1141        return mNativeContentViewCore != 0 && nativeCanGoForward(mNativeContentViewCore);
1142    }
1143
1144    /**
1145     * @param offset The offset into the navigation history.
1146     * @return Whether we can move in history by given offset
1147     */
1148    public boolean canGoToOffset(int offset) {
1149        return mNativeContentViewCore != 0 && nativeCanGoToOffset(mNativeContentViewCore, offset);
1150    }
1151
1152    /**
1153     * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1154     * of bounds.
1155     * @param offset The offset into the navigation history.
1156     */
1157    public void goToOffset(int offset) {
1158        if (mNativeContentViewCore != 0) nativeGoToOffset(mNativeContentViewCore, offset);
1159    }
1160
1161    @Override
1162    public void goToNavigationIndex(int index) {
1163        if (mNativeContentViewCore != 0) nativeGoToNavigationIndex(mNativeContentViewCore, index);
1164    }
1165
1166    /**
1167     * Goes to the navigation entry before the current one.
1168     */
1169    public void goBack() {
1170        if (mNativeContentViewCore != 0) nativeGoBack(mNativeContentViewCore);
1171    }
1172
1173    /**
1174     * Goes to the navigation entry following the current one.
1175     */
1176    public void goForward() {
1177        if (mNativeContentViewCore != 0) nativeGoForward(mNativeContentViewCore);
1178    }
1179
1180    /**
1181     * Reload the current page.
1182     */
1183    public void reload() {
1184        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1185        if (mNativeContentViewCore != 0) nativeReload(mNativeContentViewCore);
1186    }
1187
1188    /**
1189     * Cancel the pending reload.
1190     */
1191    public void cancelPendingReload() {
1192        if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1193    }
1194
1195    /**
1196     * Continue the pending reload.
1197     */
1198    public void continuePendingReload() {
1199        if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1200    }
1201
1202    /**
1203     * Clears the ContentViewCore's page history in both the backwards and
1204     * forwards directions.
1205     */
1206    public void clearHistory() {
1207        if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1208    }
1209
1210    /**
1211     * @return The selected text (empty if no text selected).
1212     */
1213    public String getSelectedText() {
1214        return mHasSelection ? mLastSelectedText : "";
1215    }
1216
1217    /**
1218     * @return Whether the current selection is editable (false if no text selected).
1219     */
1220    public boolean isSelectionEditable() {
1221        return mHasSelection ? mSelectionEditable : false;
1222    }
1223
1224    // End FrameLayout overrides.
1225
1226    /**
1227     * @see View#onTouchEvent(MotionEvent)
1228     */
1229    public boolean onTouchEvent(MotionEvent event) {
1230        undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1231        return mContentViewGestureHandler.onTouchEvent(event);
1232    }
1233
1234    /**
1235     * @return ContentViewGestureHandler for all MotionEvent and gesture related calls.
1236     */
1237    ContentViewGestureHandler getContentViewGestureHandler() {
1238        return mContentViewGestureHandler;
1239    }
1240
1241    @Override
1242    public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
1243        if (mNativeContentViewCore != 0) {
1244            return nativeSendTouchEvent(mNativeContentViewCore, timeMs, action, pts);
1245        }
1246        return false;
1247    }
1248
1249    @SuppressWarnings("unused")
1250    @CalledByNative
1251    private void hasTouchEventHandlers(boolean hasTouchHandlers) {
1252        mContentViewGestureHandler.hasTouchEventHandlers(hasTouchHandlers);
1253    }
1254
1255    @SuppressWarnings("unused")
1256    @CalledByNative
1257    private void confirmTouchEvent(int ackResult) {
1258        mContentViewGestureHandler.confirmTouchEvent(ackResult);
1259    }
1260
1261    @SuppressWarnings("unused")
1262    @CalledByNative
1263    private void unhandledFlingStartEvent() {
1264        if (mGestureStateListener != null) {
1265            mGestureStateListener.onUnhandledFlingStartEvent();
1266        }
1267    }
1268
1269    @Override
1270    public boolean sendGesture(int type, long timeMs, int x, int y, Bundle b) {
1271        if (offerGestureToEmbedder(type)) return false;
1272        if (mNativeContentViewCore == 0) return false;
1273        updateTextHandlesForGesture(type);
1274        updateGestureStateListener(type, b);
1275        switch (type) {
1276            case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE:
1277                nativeShowPressState(mNativeContentViewCore, timeMs, x, y);
1278                return true;
1279            case ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL:
1280                nativeShowPressCancel(mNativeContentViewCore, timeMs, x, y);
1281                return true;
1282            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
1283                nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1284                return true;
1285            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP:
1286                nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1287                return true;
1288            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED:
1289                handleTapOrPress(timeMs, x, y, 0,
1290                        b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false));
1291                return true;
1292            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED:
1293                nativeSingleTapUnconfirmed(mNativeContentViewCore, timeMs, x, y);
1294                return true;
1295            case ContentViewGestureHandler.GESTURE_LONG_PRESS:
1296                handleTapOrPress(timeMs, x, y, IS_LONG_PRESS, false);
1297                return true;
1298            case ContentViewGestureHandler.GESTURE_LONG_TAP:
1299                handleTapOrPress(timeMs, x, y, IS_LONG_TAP, false);
1300                return true;
1301            case ContentViewGestureHandler.GESTURE_SCROLL_START:
1302                nativeScrollBegin(mNativeContentViewCore, timeMs, x, y);
1303                return true;
1304            case ContentViewGestureHandler.GESTURE_SCROLL_BY: {
1305                int dx = b.getInt(ContentViewGestureHandler.DISTANCE_X);
1306                int dy = b.getInt(ContentViewGestureHandler.DISTANCE_Y);
1307                nativeScrollBy(mNativeContentViewCore, timeMs, x, y, dx, dy);
1308                return true;
1309            }
1310            case ContentViewGestureHandler.GESTURE_SCROLL_END:
1311                nativeScrollEnd(mNativeContentViewCore, timeMs);
1312                return true;
1313            case ContentViewGestureHandler.GESTURE_FLING_START:
1314                nativeFlingStart(mNativeContentViewCore, timeMs, x, y,
1315                        b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
1316                        b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
1317                return true;
1318            case ContentViewGestureHandler.GESTURE_FLING_CANCEL:
1319                nativeFlingCancel(mNativeContentViewCore, timeMs);
1320                return true;
1321            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
1322                nativePinchBegin(mNativeContentViewCore, timeMs, x, y);
1323                return true;
1324            case ContentViewGestureHandler.GESTURE_PINCH_BY:
1325                nativePinchBy(mNativeContentViewCore, timeMs, x, y,
1326                        b.getFloat(ContentViewGestureHandler.DELTA, 0));
1327                return true;
1328            case ContentViewGestureHandler.GESTURE_PINCH_END:
1329                nativePinchEnd(mNativeContentViewCore, timeMs);
1330                return true;
1331            default:
1332                return false;
1333        }
1334    }
1335
1336    @Override
1337    public void onSentLastGestureForVSync(long eventTimeMs) {
1338        if (isVSyncNotificationEnabled()) {
1339            mDidSignalVSyncUsingInputEvent = true;
1340        }
1341        if (mNativeContentViewCore != 0) {
1342            nativeOnVSync(mNativeContentViewCore, eventTimeMs * 1000);
1343        }
1344    }
1345
1346    public void setGestureStateListener(GestureStateListener pinchGestureStateListener) {
1347        mGestureStateListener = pinchGestureStateListener;
1348    }
1349
1350    void updateGestureStateListener(int gestureType, Bundle b) {
1351        if (mGestureStateListener == null) return;
1352
1353        switch (gestureType) {
1354            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
1355                mGestureStateListener.onPinchGestureStart();
1356                break;
1357            case ContentViewGestureHandler.GESTURE_PINCH_END:
1358                mGestureStateListener.onPinchGestureEnd();
1359                break;
1360            case ContentViewGestureHandler.GESTURE_FLING_START:
1361                mGestureStateListener.onFlingStartGesture(
1362                        b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
1363                        b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
1364                break;
1365            case ContentViewGestureHandler.GESTURE_FLING_CANCEL:
1366                mGestureStateListener.onFlingCancelGesture();
1367                break;
1368            default:
1369                break;
1370        }
1371    }
1372
1373    public interface JavaScriptCallback {
1374        void handleJavaScriptResult(String jsonResult);
1375    }
1376
1377    /**
1378     * Injects the passed Javascript code in the current page and evaluates it.
1379     * If a result is required, pass in a callback.
1380     * Used in automation tests.
1381     *
1382     * @param script The Javascript to execute.
1383     * @param callback The callback to be fired off when a result is ready. The script's
1384     *                 result will be json encoded and passed as the parameter, and the call
1385     *                 will be made on the main thread.
1386     *                 If no result is required, pass null.
1387     */
1388    public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1389        if (mNativeContentViewCore == 0) return;
1390        nativeEvaluateJavaScript(mNativeContentViewCore, script, callback, false);
1391    }
1392
1393    /**
1394     * Injects the passed Javascript code in the current page and evaluates it.
1395     * If there is no page existing, a new one will be created.
1396     *
1397     * @param script The Javascript to execute.
1398     */
1399    public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1400        if (mNativeContentViewCore == 0) return;
1401        nativeEvaluateJavaScript(mNativeContentViewCore, script, null, true);
1402    }
1403
1404    /**
1405     * This method should be called when the containing activity is paused.
1406     */
1407    public void onActivityPause() {
1408        TraceEvent.begin();
1409        hidePopupDialog();
1410        nativeOnHide(mNativeContentViewCore);
1411        TraceEvent.end();
1412    }
1413
1414    /**
1415     * This method should be called when the containing activity is resumed.
1416     */
1417    public void onActivityResume() {
1418        nativeOnShow(mNativeContentViewCore);
1419        setAccessibilityState(mAccessibilityManager.isEnabled());
1420    }
1421
1422    /**
1423     * To be called when the ContentView is shown.
1424     */
1425    public void onShow() {
1426        nativeOnShow(mNativeContentViewCore);
1427        setAccessibilityState(mAccessibilityManager.isEnabled());
1428    }
1429
1430    /**
1431     * To be called when the ContentView is hidden.
1432     */
1433    public void onHide() {
1434        hidePopupDialog();
1435        setInjectedAccessibility(false);
1436        nativeOnHide(mNativeContentViewCore);
1437    }
1438
1439    /**
1440     * Return the ContentSettings object used to retrieve the settings for this
1441     * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1442     * @return A ContentSettings object that can be used to retrieve this
1443     *         ContentViewCore's settings.
1444     */
1445    public ContentSettings getContentSettings() {
1446        return mContentSettings;
1447    }
1448
1449    @Override
1450    public boolean didUIStealScroll(float x, float y) {
1451        return getContentViewClient().shouldOverrideScroll(
1452                x, y, computeHorizontalScrollOffset(), computeVerticalScrollOffset());
1453    }
1454
1455    @Override
1456    public boolean hasFixedPageScale() {
1457        return mRenderCoordinates.hasFixedPageScale();
1458    }
1459
1460    private void hidePopupDialog() {
1461        SelectPopupDialog.hide(this);
1462        hideHandles();
1463        hideSelectActionBar();
1464    }
1465
1466    void hideSelectActionBar() {
1467        if (mActionMode != null) {
1468            mActionMode.finish();
1469            mActionMode = null;
1470        }
1471    }
1472
1473    public boolean isSelectActionBarShowing() {
1474        return mActionMode != null;
1475    }
1476
1477    private void resetGestureDetectors() {
1478        mContentViewGestureHandler.resetGestureHandlers();
1479    }
1480
1481    /**
1482     * @see View#onAttachedToWindow()
1483     */
1484    @SuppressWarnings("javadoc")
1485    public void onAttachedToWindow() {
1486        mAttachedToWindow = true;
1487        if (mNativeContentViewCore != 0) {
1488            assert mPid == nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1489            ChildProcessLauncher.getBindingManager().bindAsHighPriority(mPid);
1490            // Normally the initial binding is removed in onRenderProcessSwap(), but it is possible
1491            // to construct WebContents and spawn the renderer before passing it to ContentViewCore.
1492            // In this case there will be no onRenderProcessSwap() call and the initial binding will
1493            // be removed here.
1494            ChildProcessLauncher.getBindingManager().removeInitialBinding(mPid);
1495        }
1496        setAccessibilityState(mAccessibilityManager.isEnabled());
1497    }
1498
1499    /**
1500     * @see View#onDetachedFromWindow()
1501     */
1502    @SuppressWarnings("javadoc")
1503    public void onDetachedFromWindow() {
1504        mAttachedToWindow = false;
1505        if (mNativeContentViewCore != 0) {
1506            assert mPid == nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1507            ChildProcessLauncher.getBindingManager().unbindAsHighPriority(mPid);
1508        }
1509        setInjectedAccessibility(false);
1510        hidePopupDialog();
1511        mZoomControlsDelegate.dismissZoomPicker();
1512        unregisterAccessibilityContentObserver();
1513    }
1514
1515    /**
1516     * @see View#onVisibilityChanged(android.view.View, int)
1517     */
1518    public void onVisibilityChanged(View changedView, int visibility) {
1519        if (visibility != View.VISIBLE) {
1520            mZoomControlsDelegate.dismissZoomPicker();
1521        }
1522    }
1523
1524    /**
1525     * @see View#onCreateInputConnection(EditorInfo)
1526     */
1527    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1528        if (!mImeAdapter.hasTextInputType()) {
1529            // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1530            // is still used by the InputMethodService. Need to make sure the IME doesn't
1531            // enter fullscreen mode.
1532            outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1533        }
1534        mInputConnection =
1535                mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
1536        return mInputConnection;
1537    }
1538
1539    public Editable getEditableForTest() {
1540        return mInputConnection.getEditable();
1541    }
1542
1543    /**
1544     * @see View#onCheckIsTextEditor()
1545     */
1546    public boolean onCheckIsTextEditor() {
1547        return mImeAdapter.hasTextInputType();
1548    }
1549
1550    /**
1551     * @see View#onConfigurationChanged(Configuration)
1552     */
1553    @SuppressWarnings("javadoc")
1554    public void onConfigurationChanged(Configuration newConfig) {
1555        TraceEvent.begin();
1556
1557        if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1558            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1559                    ImeAdapter.getTextInputTypeNone(),
1560                    AdapterInputConnection.INVALID_SELECTION,
1561                    AdapterInputConnection.INVALID_SELECTION);
1562            InputMethodManager manager = (InputMethodManager)
1563                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1564            manager.restartInput(mContainerView);
1565        }
1566        mContainerViewInternals.super_onConfigurationChanged(newConfig);
1567        mNeedUpdateOrientationChanged = true;
1568        TraceEvent.end();
1569    }
1570
1571    /**
1572     * @see View#onSizeChanged(int, int, int, int)
1573     */
1574    @SuppressWarnings("javadoc")
1575    public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1576        if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1577
1578        mViewportWidthPix = wPix;
1579        mViewportHeightPix = hPix;
1580        if (mNativeContentViewCore != 0) {
1581            nativeWasResized(mNativeContentViewCore);
1582        }
1583
1584        updateAfterSizeChanged();
1585    }
1586
1587    /**
1588     * Called when the underlying surface the compositor draws to changes size.
1589     * This may be larger than the viewport size.
1590     */
1591    public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1592        if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1593
1594        mPhysicalBackingWidthPix = wPix;
1595        mPhysicalBackingHeightPix = hPix;
1596
1597        if (mNativeContentViewCore != 0) {
1598            nativeWasResized(mNativeContentViewCore);
1599        }
1600    }
1601
1602    /**
1603     * Called when the amount the surface is overdrawing off the bottom has changed.
1604     * @param overdrawHeightPix The overdraw height.
1605     */
1606    public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1607        if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1608
1609        mOverdrawBottomHeightPix = overdrawHeightPix;
1610
1611        if (mNativeContentViewCore != 0) {
1612            nativeWasResized(mNativeContentViewCore);
1613        }
1614    }
1615
1616    private void updateAfterSizeChanged() {
1617        mPopupZoomer.hide(false);
1618
1619        // Execute a delayed form focus operation because the OSK was brought
1620        // up earlier.
1621        if (!mFocusPreOSKViewportRect.isEmpty()) {
1622            Rect rect = new Rect();
1623            getContainerView().getWindowVisibleDisplayFrame(rect);
1624            if (!rect.equals(mFocusPreOSKViewportRect)) {
1625                // Only assume the OSK triggered the onSizeChanged if width was preserved.
1626                if (rect.width() == mFocusPreOSKViewportRect.width()) {
1627                    scrollFocusedEditableNodeIntoView();
1628                }
1629                mFocusPreOSKViewportRect.setEmpty();
1630            }
1631        } else if (mUnfocusOnNextSizeChanged) {
1632            undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1633            mUnfocusOnNextSizeChanged = false;
1634        }
1635
1636        if (mNeedUpdateOrientationChanged) {
1637            sendOrientationChangeEvent();
1638            mNeedUpdateOrientationChanged = false;
1639        }
1640    }
1641
1642    private void scrollFocusedEditableNodeIntoView() {
1643        if (mNativeContentViewCore != 0) {
1644            Runnable scrollTask = new Runnable() {
1645                @Override
1646                public void run() {
1647                    if (mNativeContentViewCore != 0) {
1648                        nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1649                    }
1650                }
1651            };
1652
1653            scrollTask.run();
1654
1655            // The native side keeps track of whether the zoom and scroll actually occurred. It is
1656            // more efficient to do it this way and sometimes fire an unnecessary message rather
1657            // than synchronize with the renderer and always have an additional message.
1658            mScrolledAndZoomedFocusedEditableNode = true;
1659        }
1660    }
1661
1662    private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) {
1663        // The only call to this function that matters is the first call after the
1664        // scrollFocusedEditableNodeIntoView function call.
1665        // If the first call to this function is a result of a back button press we want to undo the
1666        // preceding scroll. If the call is a result of some other action we don't want to perform
1667        // an undo.
1668        // All subsequent calls are ignored since only the scroll function sets
1669        // mScrolledAndZoomedFocusedEditableNode to true.
1670        if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed &&
1671                mNativeContentViewCore != 0) {
1672            Runnable scrollTask = new Runnable() {
1673                @Override
1674                public void run() {
1675                    if (mNativeContentViewCore != 0) {
1676                        nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1677                    }
1678                }
1679            };
1680
1681            scrollTask.run();
1682        }
1683        mScrolledAndZoomedFocusedEditableNode = false;
1684    }
1685
1686    /**
1687     * @see View#onWindowFocusChanged(boolean)
1688     */
1689    public void onWindowFocusChanged(boolean hasWindowFocus) {
1690        if (!hasWindowFocus) {
1691            mContentViewGestureHandler.onWindowFocusLost();
1692        }
1693    }
1694
1695    public void onFocusChanged(boolean gainFocus) {
1696        if (!gainFocus) getContentViewClient().onImeStateChangeRequested(false);
1697        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1698    }
1699
1700    /**
1701     * @see View#onKeyUp(int, KeyEvent)
1702     */
1703    public boolean onKeyUp(int keyCode, KeyEvent event) {
1704        if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1705            mPopupZoomer.hide(true);
1706            return true;
1707        }
1708        return mContainerViewInternals.super_onKeyUp(keyCode, event);
1709    }
1710
1711    /**
1712     * @see View#dispatchKeyEventPreIme(KeyEvent)
1713     */
1714    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1715        try {
1716            TraceEvent.begin();
1717            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1718                mUnfocusOnNextSizeChanged = true;
1719            } else {
1720                undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1721            }
1722            return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1723        } finally {
1724            TraceEvent.end();
1725        }
1726    }
1727
1728    /**
1729     * @see View#dispatchKeyEvent(KeyEvent)
1730     */
1731    public boolean dispatchKeyEvent(KeyEvent event) {
1732        if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1733            return mContainerViewInternals.super_dispatchKeyEvent(event);
1734        }
1735
1736        if (mImeAdapter.dispatchKeyEvent(event)) return true;
1737
1738        return mContainerViewInternals.super_dispatchKeyEvent(event);
1739    }
1740
1741    /**
1742     * @see View#onHoverEvent(MotionEvent)
1743     * Mouse move events are sent on hover enter, hover move and hover exit.
1744     * They are sent on hover exit because sometimes it acts as both a hover
1745     * move and hover exit.
1746     */
1747    public boolean onHoverEvent(MotionEvent event) {
1748        TraceEvent.begin("onHoverEvent");
1749        mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1750        if (mBrowserAccessibilityManager != null) {
1751            return mBrowserAccessibilityManager.onHoverEvent(event);
1752        }
1753        if (mNativeContentViewCore != 0) {
1754            nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1755                    event.getX(), event.getY());
1756        }
1757        TraceEvent.end("onHoverEvent");
1758        return true;
1759    }
1760
1761    /**
1762     * @see View#onGenericMotionEvent(MotionEvent)
1763     */
1764    public boolean onGenericMotionEvent(MotionEvent event) {
1765        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1766            switch (event.getAction()) {
1767                case MotionEvent.ACTION_SCROLL:
1768                    nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1769                            event.getX(), event.getY(),
1770                            event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1771
1772                    mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1773                    // Send a delayed onMouseMove event so that we end
1774                    // up hovering over the right position after the scroll.
1775                    final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1776                    mFakeMouseMoveRunnable = new Runnable() {
1777                          @Override
1778                          public void run() {
1779                              onHoverEvent(eventFakeMouseMove);
1780                          }
1781                    };
1782                    mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1783                    return true;
1784            }
1785        }
1786        return mContainerViewInternals.super_onGenericMotionEvent(event);
1787    }
1788
1789    /**
1790     * @see View#scrollBy(int, int)
1791     * Currently the ContentView scrolling happens in the native side. In
1792     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1793     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1794     * (0, 0). This is critical for drawing ContentView correctly.
1795     */
1796    public void scrollBy(int xPix, int yPix) {
1797        if (mNativeContentViewCore != 0) {
1798            nativeScrollBy(mNativeContentViewCore,
1799                    System.currentTimeMillis(), 0, 0, xPix, yPix);
1800        }
1801    }
1802
1803    /**
1804     * @see View#scrollTo(int, int)
1805     */
1806    public void scrollTo(int xPix, int yPix) {
1807        if (mNativeContentViewCore == 0) return;
1808        final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1809        final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1810        final float dxPix = xPix - xCurrentPix;
1811        final float dyPix = yPix - yCurrentPix;
1812        if (dxPix != 0 || dyPix != 0) {
1813            long time = System.currentTimeMillis();
1814            nativeScrollBegin(mNativeContentViewCore, time, xCurrentPix, yCurrentPix);
1815            nativeScrollBy(mNativeContentViewCore,
1816                    time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1817            nativeScrollEnd(mNativeContentViewCore, time);
1818        }
1819    }
1820
1821    // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1822    //       see: b/6029133
1823    public int getNativeScrollXForTest() {
1824        return mRenderCoordinates.getScrollXPixInt();
1825    }
1826
1827    // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1828    //       see: b/6029133
1829    public int getNativeScrollYForTest() {
1830        return mRenderCoordinates.getScrollYPixInt();
1831    }
1832
1833    /**
1834     * @see View#computeHorizontalScrollExtent()
1835     */
1836    @SuppressWarnings("javadoc")
1837    public int computeHorizontalScrollExtent() {
1838        return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1839    }
1840
1841    /**
1842     * @see View#computeHorizontalScrollOffset()
1843     */
1844    @SuppressWarnings("javadoc")
1845    public int computeHorizontalScrollOffset() {
1846        return mRenderCoordinates.getScrollXPixInt();
1847    }
1848
1849    /**
1850     * @see View#computeHorizontalScrollRange()
1851     */
1852    @SuppressWarnings("javadoc")
1853    public int computeHorizontalScrollRange() {
1854        return mRenderCoordinates.getContentWidthPixInt();
1855    }
1856
1857    /**
1858     * @see View#computeVerticalScrollExtent()
1859     */
1860    @SuppressWarnings("javadoc")
1861    public int computeVerticalScrollExtent() {
1862        return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1863    }
1864
1865    /**
1866     * @see View#computeVerticalScrollOffset()
1867     */
1868    @SuppressWarnings("javadoc")
1869    public int computeVerticalScrollOffset() {
1870        return mRenderCoordinates.getScrollYPixInt();
1871    }
1872
1873    /**
1874     * @see View#computeVerticalScrollRange()
1875     */
1876    @SuppressWarnings("javadoc")
1877    public int computeVerticalScrollRange() {
1878        return mRenderCoordinates.getContentHeightPixInt();
1879    }
1880
1881    // End FrameLayout overrides.
1882
1883    /**
1884     * @see View#awakenScrollBars(int, boolean)
1885     */
1886    @SuppressWarnings("javadoc")
1887    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1888        // For the default implementation of ContentView which draws the scrollBars on the native
1889        // side, calling this function may get us into a bad state where we keep drawing the
1890        // scrollBars, so disable it by always returning false.
1891        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1892            return false;
1893        } else {
1894            return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1895        }
1896    }
1897
1898    /**
1899     * Called by native side when the corresponding renderer crashes. Note that if a renderer is
1900     * shared between tabs, this might be called multiple times while the tab is crashed. This is
1901     * because the tabs sharing a renderer also share RenderProcessHost. When one of those tabs
1902     * reloads and a new renderer is created for the shared RenderProcessHost, all tabs are notified
1903     * in onRenderProcessSwap(), not only the one that reloads. If this renderer dies, all the other
1904     * dead tabs are notified again.
1905     * @param alreadyCrashed true iff this tab is already in crashed state but the shared renderer
1906     *                       resurrected and died again since the last time this was called.
1907     */
1908    @SuppressWarnings("unused")
1909    @CalledByNative
1910    private void onTabCrash(boolean alreadyCrashed) {
1911        assert mPid != 0;
1912        if (!alreadyCrashed) {
1913            getContentViewClient().onRendererCrash(
1914                    ChildProcessLauncher.getBindingManager().isOomProtected(mPid));
1915        }
1916        mPid = 0;
1917    }
1918
1919    private void handleTapOrPress(
1920            long timeMs, float xPix, float yPix, int isLongPressOrTap, boolean showPress) {
1921        if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1922                && !mContainerView.isFocused())  {
1923            mContainerView.requestFocus();
1924        }
1925
1926        if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1927
1928        if (isLongPressOrTap == IS_LONG_PRESS) {
1929            getInsertionHandleController().allowAutomaticShowing();
1930            getSelectionHandleController().allowAutomaticShowing();
1931            if (mNativeContentViewCore != 0) {
1932                nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false);
1933            }
1934        } else if (isLongPressOrTap == IS_LONG_TAP) {
1935            getInsertionHandleController().allowAutomaticShowing();
1936            getSelectionHandleController().allowAutomaticShowing();
1937            if (mNativeContentViewCore != 0) {
1938                nativeLongTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1939            }
1940        } else {
1941            if (!showPress && mNativeContentViewCore != 0) {
1942                nativeShowPressState(mNativeContentViewCore, timeMs, xPix, yPix);
1943            }
1944            if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1945            if (mNativeContentViewCore != 0) {
1946                nativeSingleTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1947            }
1948        }
1949    }
1950
1951    public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1952        mZoomControlsDelegate = zoomControlsDelegate;
1953    }
1954
1955    public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1956        mZoomManager.updateMultiTouchSupport(supportsMultiTouchZoom);
1957    }
1958
1959    public void updateDoubleTapDragSupport(boolean supportsDoubleTapDrag) {
1960        mContentViewGestureHandler.updateDoubleTapDragSupport(supportsDoubleTapDrag);
1961    }
1962
1963    public void selectPopupMenuItems(int[] indices) {
1964        if (mNativeContentViewCore != 0) {
1965            nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1966        }
1967    }
1968
1969    /**
1970     * Get the screen orientation from the OS and push it to WebKit.
1971     *
1972     * TODO(husky): Add a hook for mock orientations.
1973     *
1974     * TODO(husky): Currently each new tab starts with an orientation of 0 until you actually
1975     * rotate the device. This is wrong if you actually started in landscape mode. To fix this, we
1976     * need to push the correct orientation, but only after WebKit's Frame object has been fully
1977     * initialized. Need to find a good time to do that. onPageFinished() would probably work but
1978     * it isn't implemented yet.
1979     */
1980    private void sendOrientationChangeEvent() {
1981        if (mNativeContentViewCore == 0) return;
1982
1983        WindowManager windowManager =
1984                (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
1985        switch (windowManager.getDefaultDisplay().getRotation()) {
1986            case Surface.ROTATION_90:
1987                nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
1988                break;
1989            case Surface.ROTATION_180:
1990                nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
1991                break;
1992            case Surface.ROTATION_270:
1993                nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
1994                break;
1995            case Surface.ROTATION_0:
1996                nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
1997                break;
1998            default:
1999                Log.w(TAG, "Unknown rotation!");
2000                break;
2001        }
2002    }
2003
2004    /**
2005     * Register the delegate to be used when content can not be handled by
2006     * the rendering engine, and should be downloaded instead. This will replace
2007     * the current delegate, if any.
2008     * @param delegate An implementation of ContentViewDownloadDelegate.
2009     */
2010    public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
2011        mDownloadDelegate = delegate;
2012    }
2013
2014    // Called by DownloadController.
2015    ContentViewDownloadDelegate getDownloadDelegate() {
2016        return mDownloadDelegate;
2017    }
2018
2019    private SelectionHandleController getSelectionHandleController() {
2020        if (mSelectionHandleController == null) {
2021            mSelectionHandleController = new SelectionHandleController(getContainerView()) {
2022                @Override
2023                public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
2024                    if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
2025                        nativeSelectBetweenCoordinates(mNativeContentViewCore,
2026                                x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
2027                                x2, y2 - mRenderCoordinates.getContentOffsetYPix());
2028                    }
2029                }
2030
2031                @Override
2032                public void showHandles(int startDir, int endDir) {
2033                    super.showHandles(startDir, endDir);
2034                    showSelectActionBar();
2035                }
2036
2037            };
2038
2039            mSelectionHandleController.hideAndDisallowAutomaticShowing();
2040        }
2041
2042        return mSelectionHandleController;
2043    }
2044
2045    private InsertionHandleController getInsertionHandleController() {
2046        if (mInsertionHandleController == null) {
2047            mInsertionHandleController = new InsertionHandleController(getContainerView()) {
2048                private static final int AVERAGE_LINE_HEIGHT = 14;
2049
2050                @Override
2051                public void setCursorPosition(int x, int y) {
2052                    if (mNativeContentViewCore != 0) {
2053                        nativeMoveCaret(mNativeContentViewCore,
2054                                x, y - mRenderCoordinates.getContentOffsetYPix());
2055                    }
2056                }
2057
2058                @Override
2059                public void paste() {
2060                    mImeAdapter.paste();
2061                    hideHandles();
2062                }
2063
2064                @Override
2065                public int getLineHeight() {
2066                    return (int) Math.ceil(
2067                            mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
2068                }
2069
2070                @Override
2071                public void showHandle() {
2072                    super.showHandle();
2073                }
2074            };
2075
2076            mInsertionHandleController.hideAndDisallowAutomaticShowing();
2077        }
2078
2079        return mInsertionHandleController;
2080    }
2081
2082    @VisibleForTesting
2083    public InsertionHandleController getInsertionHandleControllerForTest() {
2084        return mInsertionHandleController;
2085    }
2086
2087    @VisibleForTesting
2088    public SelectionHandleController getSelectionHandleControllerForTest() {
2089        return mSelectionHandleController;
2090    }
2091
2092    private void updateHandleScreenPositions() {
2093        if (isSelectionHandleShowing()) {
2094            mSelectionHandleController.setStartHandlePosition(
2095                    mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
2096            mSelectionHandleController.setEndHandlePosition(
2097                    mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
2098        }
2099
2100        if (isInsertionHandleShowing()) {
2101            mInsertionHandleController.setHandlePosition(
2102                    mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
2103        }
2104    }
2105
2106    private void hideHandles() {
2107        if (mSelectionHandleController != null) {
2108            mSelectionHandleController.hideAndDisallowAutomaticShowing();
2109        }
2110        if (mInsertionHandleController != null) {
2111            mInsertionHandleController.hideAndDisallowAutomaticShowing();
2112        }
2113    }
2114
2115    private void showSelectActionBar() {
2116        if (mActionMode != null) {
2117            mActionMode.invalidate();
2118            return;
2119        }
2120
2121        // Start a new action mode with a SelectActionModeCallback.
2122        SelectActionModeCallback.ActionHandler actionHandler =
2123                new SelectActionModeCallback.ActionHandler() {
2124            @Override
2125            public void selectAll() {
2126                mImeAdapter.selectAll();
2127            }
2128
2129            @Override
2130            public void cut() {
2131                mImeAdapter.cut();
2132            }
2133
2134            @Override
2135            public void copy() {
2136                mImeAdapter.copy();
2137            }
2138
2139            @Override
2140            public void paste() {
2141                mImeAdapter.paste();
2142            }
2143
2144            @Override
2145            public void share() {
2146                final String query = getSelectedText();
2147                if (TextUtils.isEmpty(query)) return;
2148
2149                Intent send = new Intent(Intent.ACTION_SEND);
2150                send.setType("text/plain");
2151                send.putExtra(Intent.EXTRA_TEXT, query);
2152                try {
2153                    Intent i = Intent.createChooser(send, getContext().getString(
2154                            R.string.actionbar_share));
2155                    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2156                    getContext().startActivity(i);
2157                } catch (android.content.ActivityNotFoundException ex) {
2158                    // If no app handles it, do nothing.
2159                }
2160            }
2161
2162            @Override
2163            public void search() {
2164                final String query = getSelectedText();
2165                if (TextUtils.isEmpty(query)) return;
2166
2167                // See if ContentViewClient wants to override
2168                if (getContentViewClient().doesPerformWebSearch()) {
2169                    getContentViewClient().performWebSearch(query);
2170                    return;
2171                }
2172
2173                Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2174                i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2175                i.putExtra(SearchManager.QUERY, query);
2176                i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2177                if (!(getContext() instanceof Activity)) {
2178                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2179                }
2180                try {
2181                    getContext().startActivity(i);
2182                } catch (android.content.ActivityNotFoundException ex) {
2183                    // If no app handles it, do nothing.
2184                }
2185            }
2186
2187            @Override
2188            public boolean isSelectionEditable() {
2189                return mSelectionEditable;
2190            }
2191
2192            @Override
2193            public void onDestroyActionMode() {
2194                mActionMode = null;
2195                if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2196                getContentViewClient().onContextualActionBarHidden();
2197            }
2198
2199            @Override
2200            public boolean isShareAvailable() {
2201                Intent intent = new Intent(Intent.ACTION_SEND);
2202                intent.setType("text/plain");
2203                return getContext().getPackageManager().queryIntentActivities(intent,
2204                        PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2205            }
2206
2207            @Override
2208            public boolean isWebSearchAvailable() {
2209                if (getContentViewClient().doesPerformWebSearch()) return true;
2210                Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2211                intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2212                return getContext().getPackageManager().queryIntentActivities(intent,
2213                        PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2214            }
2215        };
2216        mActionMode = null;
2217        // On ICS, startActionMode throws an NPE when getParent() is null.
2218        if (mContainerView.getParent() != null) {
2219            mActionMode = mContainerView.startActionMode(
2220                    getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2221                            nativeIsIncognito(mNativeContentViewCore)));
2222        }
2223        mUnselectAllOnActionModeDismiss = true;
2224        if (mActionMode == null) {
2225            // There is no ActionMode, so remove the selection.
2226            mImeAdapter.unselect();
2227        } else {
2228            getContentViewClient().onContextualActionBarShown();
2229        }
2230    }
2231
2232    public boolean getUseDesktopUserAgent() {
2233        if (mNativeContentViewCore != 0) {
2234            return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2235        }
2236        return false;
2237    }
2238
2239    /**
2240     * Set whether or not we're using a desktop user agent for the currently loaded page.
2241     * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
2242     * @param reloadOnChange Reload the page if the UA has changed.
2243     */
2244    public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2245        if (mNativeContentViewCore != 0) {
2246            nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2247        }
2248    }
2249
2250    public void clearSslPreferences() {
2251        nativeClearSslPreferences(mNativeContentViewCore);
2252    }
2253
2254    /**
2255     * @return Whether the native ContentView has crashed.
2256     */
2257    public boolean isCrashed() {
2258        if (mNativeContentViewCore == 0) return false;
2259        return nativeCrashed(mNativeContentViewCore);
2260    }
2261
2262    private boolean isSelectionHandleShowing() {
2263        return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2264    }
2265
2266    private boolean isInsertionHandleShowing() {
2267        return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2268    }
2269
2270    private void updateTextHandlesForGesture(int type) {
2271        switch(type) {
2272            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
2273            case ContentViewGestureHandler.GESTURE_SCROLL_START:
2274            case ContentViewGestureHandler.GESTURE_FLING_START:
2275            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
2276                temporarilyHideTextHandles();
2277                break;
2278
2279            default:
2280                break;
2281        }
2282    }
2283
2284    // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2285    // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2286    private void temporarilyHideTextHandles() {
2287        if (isSelectionHandleShowing()) {
2288            mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2289        }
2290        if (isInsertionHandleShowing()) {
2291            mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2292        }
2293        scheduleTextHandleFadeIn();
2294    }
2295
2296    private boolean allowTextHandleFadeIn() {
2297        if (mContentViewGestureHandler.isNativeScrolling() ||
2298                mContentViewGestureHandler.isNativePinching()) {
2299            return false;
2300        }
2301
2302        if (mPopupZoomer.isShowing()) return false;
2303
2304        return true;
2305    }
2306
2307    // Cancels any pending fade in and schedules a new one.
2308    private void scheduleTextHandleFadeIn() {
2309        if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2310
2311        if (mDeferredHandleFadeInRunnable == null) {
2312            mDeferredHandleFadeInRunnable = new Runnable() {
2313                @Override
2314                public void run() {
2315                    if (!allowTextHandleFadeIn()) {
2316                        // Delay fade in until it is allowed.
2317                        scheduleTextHandleFadeIn();
2318                    } else {
2319                        if (isSelectionHandleShowing()) {
2320                            mSelectionHandleController.beginHandleFadeIn();
2321                        }
2322                        if (isInsertionHandleShowing()) {
2323                            mInsertionHandleController.beginHandleFadeIn();
2324                        }
2325                    }
2326                }
2327            };
2328        }
2329
2330        mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2331        mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2332    }
2333
2334    /**
2335     * Shows the IME if the focused widget could accept text input.
2336     */
2337    public void showImeIfNeeded() {
2338        if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2339    }
2340
2341    @SuppressWarnings("unused")
2342    @CalledByNative
2343    private void updateFrameInfo(
2344            float scrollOffsetX, float scrollOffsetY,
2345            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2346            float contentWidth, float contentHeight,
2347            float viewportWidth, float viewportHeight,
2348            float controlsOffsetYCss, float contentOffsetYCss,
2349            float overdrawBottomHeightCss) {
2350        TraceEvent.instant("ContentViewCore:updateFrameInfo");
2351        // Adjust contentWidth/Height to be always at least as big as
2352        // the actual viewport (as set by onSizeChanged).
2353        contentWidth = Math.max(contentWidth,
2354                mRenderCoordinates.fromPixToLocalCss(mViewportWidthPix));
2355        contentHeight = Math.max(contentHeight,
2356                mRenderCoordinates.fromPixToLocalCss(mViewportHeightPix));
2357
2358        final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2359
2360        final boolean contentSizeChanged =
2361                contentWidth != mRenderCoordinates.getContentWidthCss()
2362                || contentHeight != mRenderCoordinates.getContentHeightCss();
2363        final boolean scaleLimitsChanged =
2364                minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2365                || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2366        final boolean pageScaleChanged =
2367                pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2368        final boolean scrollChanged =
2369                pageScaleChanged
2370                || scrollOffsetX != mRenderCoordinates.getScrollX()
2371                || scrollOffsetY != mRenderCoordinates.getScrollY();
2372        final boolean contentOffsetChanged =
2373                contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2374
2375        final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2376        final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2377        final boolean needTemporarilyHideHandles = scrollChanged;
2378
2379        if (needHidePopupZoomer) mPopupZoomer.hide(true);
2380
2381        if (pageScaleChanged) {
2382            // This function should be called back from native as soon
2383            // as the scroll is applied to the backbuffer.  We should only
2384            // update mNativeScrollX/Y here for consistency.
2385            getContentViewClient().onScaleChanged(
2386                    mRenderCoordinates.getPageScaleFactor(), pageScaleFactor);
2387        }
2388
2389        mRenderCoordinates.updateFrameInfo(
2390                scrollOffsetX, scrollOffsetY,
2391                contentWidth, contentHeight,
2392                viewportWidth, viewportHeight,
2393                pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2394                contentOffsetYPix);
2395
2396        if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2397        if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2398        if (contentOffsetChanged) updateHandleScreenPositions();
2399
2400        // Update offsets for fullscreen.
2401        final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2402        final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2403        final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2404        getContentViewClient().onOffsetsForFullscreenChanged(
2405                controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2406
2407        mPendingRendererFrame = true;
2408        if (mBrowserAccessibilityManager != null) {
2409            mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2410        }
2411
2412        // Update geometry for external video surface.
2413        getContentViewClient().onGeometryChanged(-1, null);
2414    }
2415
2416    @SuppressWarnings("unused")
2417    @CalledByNative
2418    private void updateImeAdapter(int nativeImeAdapterAndroid, int textInputType,
2419            String text, int selectionStart, int selectionEnd,
2420            int compositionStart, int compositionEnd, boolean showImeIfNeeded) {
2421        TraceEvent.begin();
2422        mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2423
2424        if (mActionMode != null) mActionMode.invalidate();
2425
2426        mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
2427                selectionStart, selectionEnd, showImeIfNeeded);
2428
2429        if (mInputConnection != null) {
2430            mInputConnection.setEditableText(text, selectionStart, selectionEnd,
2431                    compositionStart, compositionEnd);
2432        }
2433        TraceEvent.end();
2434    }
2435
2436    @SuppressWarnings("unused")
2437    @CalledByNative
2438    private void processImeBatchStateAck(boolean isBegin) {
2439        if (mInputConnection == null) return;
2440        mInputConnection.setIgnoreTextInputStateUpdates(isBegin);
2441    }
2442
2443    @SuppressWarnings("unused")
2444    @CalledByNative
2445    private void setTitle(String title) {
2446        getContentViewClient().onUpdateTitle(title);
2447    }
2448
2449    /**
2450     * Called (from native) when the <select> popup needs to be shown.
2451     * @param items           Items to show.
2452     * @param enabled         POPUP_ITEM_TYPEs for items.
2453     * @param multiple        Whether the popup menu should support multi-select.
2454     * @param selectedIndices Indices of selected items.
2455     */
2456    @SuppressWarnings("unused")
2457    @CalledByNative
2458    private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
2459            int[] selectedIndices) {
2460        SelectPopupDialog.show(this, items, enabled, multiple, selectedIndices);
2461    }
2462
2463    @SuppressWarnings("unused")
2464    @CalledByNative
2465    private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2466        mPopupZoomer.setBitmap(zoomedBitmap);
2467        mPopupZoomer.show(targetRect);
2468        temporarilyHideTextHandles();
2469    }
2470
2471    @SuppressWarnings("unused")
2472    @CalledByNative
2473    private GenericTouchGesture createOnePointTouchGesture(int startX,
2474            int startY, int deltaX, int deltaY) {
2475        return new GenericTouchGesture(this, startX, startY, deltaX, deltaY);
2476    }
2477
2478    @SuppressWarnings("unused")
2479    @CalledByNative
2480    private GenericTouchGesture createTwoPointTouchGesture(
2481            int startX0, int startY0, int deltaX0, int deltaY0,
2482            int startX1, int startY1, int deltaX1, int deltaY1) {
2483        return new GenericTouchGesture(this, startX0, startY0, deltaX0, deltaY0,
2484                startX1, startY1, deltaX1, deltaY1);
2485    }
2486
2487    @SuppressWarnings("unused")
2488    @CalledByNative
2489    private void onSelectionChanged(String text) {
2490        mLastSelectedText = text;
2491    }
2492
2493    @SuppressWarnings("unused")
2494    @CalledByNative
2495    private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2496            int focusDir, boolean isAnchorFirst) {
2497        // All coordinates are in DIP.
2498        int x1 = anchorRectDip.left;
2499        int y1 = anchorRectDip.bottom;
2500        int x2 = focusRectDip.left;
2501        int y2 = focusRectDip.bottom;
2502
2503        if (x1 != x2 || y1 != y2 ||
2504                (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2505            if (mInsertionHandleController != null) {
2506                mInsertionHandleController.hide();
2507            }
2508            if (isAnchorFirst) {
2509                mStartHandlePoint.setLocalDip(x1, y1);
2510                mEndHandlePoint.setLocalDip(x2, y2);
2511            } else {
2512                mStartHandlePoint.setLocalDip(x2, y2);
2513                mEndHandlePoint.setLocalDip(x1, y1);
2514            }
2515
2516            if (!getSelectionHandleController().isShowing()) {
2517                // TODO(cjhopman): Remove this when there is a better signal that long press caused
2518                // a selection. See http://crbug.com/150151.
2519                mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2520            }
2521
2522            getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2523            updateHandleScreenPositions();
2524            mHasSelection = true;
2525        } else {
2526            mUnselectAllOnActionModeDismiss = false;
2527            hideSelectActionBar();
2528            if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2529                // Selection is a caret, and a text field is focused.
2530                if (mSelectionHandleController != null) {
2531                    mSelectionHandleController.hide();
2532                }
2533                mInsertionHandlePoint.setLocalDip(x1, y1);
2534
2535                getInsertionHandleController().onCursorPositionChanged();
2536                updateHandleScreenPositions();
2537                InputMethodManager manager = (InputMethodManager)
2538                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
2539                if (manager.isWatchingCursor(mContainerView)) {
2540                    final int xPix = (int) mInsertionHandlePoint.getXPix();
2541                    final int yPix = (int) mInsertionHandlePoint.getYPix();
2542                    manager.updateCursor(mContainerView, xPix, yPix, xPix, yPix);
2543                }
2544            } else {
2545                // Deselection
2546                if (mSelectionHandleController != null) {
2547                    mSelectionHandleController.hideAndDisallowAutomaticShowing();
2548                }
2549                if (mInsertionHandleController != null) {
2550                    mInsertionHandleController.hideAndDisallowAutomaticShowing();
2551                }
2552            }
2553            mHasSelection = false;
2554        }
2555    }
2556
2557    @SuppressWarnings("unused")
2558    @CalledByNative
2559    private static void onEvaluateJavaScriptResult(
2560            String jsonResult, JavaScriptCallback callback) {
2561        callback.handleJavaScriptResult(jsonResult);
2562    }
2563
2564    @SuppressWarnings("unused")
2565    @CalledByNative
2566    private void showPastePopup(int xDip, int yDip) {
2567        mInsertionHandlePoint.setLocalDip(xDip, yDip);
2568        getInsertionHandleController().showHandle();
2569        updateHandleScreenPositions();
2570        getInsertionHandleController().showHandleWithPastePopup();
2571    }
2572
2573    @SuppressWarnings("unused")
2574    @CalledByNative
2575    private void onRenderProcessSwap(int oldPid, int newPid) {
2576        assert mPid == oldPid || mPid == newPid;
2577        if (mAttachedToWindow && oldPid != newPid) {
2578            ChildProcessLauncher.getBindingManager().unbindAsHighPriority(oldPid);
2579            ChildProcessLauncher.getBindingManager().bindAsHighPriority(newPid);
2580        }
2581
2582        // We want to remove the initial binding even if the ContentView is not attached, so that
2583        // renderers for ContentViews loading in background do not retain the high priority.
2584        ChildProcessLauncher.getBindingManager().removeInitialBinding(newPid);
2585        mPid = newPid;
2586    }
2587
2588    @SuppressWarnings("unused")
2589    @CalledByNative
2590    private void onWebContentsConnected() {
2591        if (mImeAdapter != null &&
2592                !mImeAdapter.isNativeImeAdapterAttached() && mNativeContentViewCore != 0) {
2593            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2594        }
2595    }
2596
2597    @SuppressWarnings("unused")
2598    @CalledByNative
2599    private void onWebContentsSwapped() {
2600        if (mImeAdapter != null && mNativeContentViewCore != 0) {
2601            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2602        }
2603    }
2604
2605    /**
2606     * @return Whether a reload happens when this ContentView is activated.
2607     */
2608    public boolean needsReload() {
2609        return mNativeContentViewCore != 0 && nativeNeedsReload(mNativeContentViewCore);
2610    }
2611
2612    /**
2613     * @see View#hasFocus()
2614     */
2615    @CalledByNative
2616    public boolean hasFocus() {
2617        return mContainerView.hasFocus();
2618    }
2619
2620    /**
2621     * Checks whether the ContentViewCore can be zoomed in.
2622     *
2623     * @return True if the ContentViewCore can be zoomed in.
2624     */
2625    // This method uses the term 'zoom' for legacy reasons, but relates
2626    // to what chrome calls the 'page scale factor'.
2627    public boolean canZoomIn() {
2628        final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2629                - mRenderCoordinates.getPageScaleFactor();
2630        return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2631    }
2632
2633    /**
2634     * Checks whether the ContentViewCore can be zoomed out.
2635     *
2636     * @return True if the ContentViewCore can be zoomed out.
2637     */
2638    // This method uses the term 'zoom' for legacy reasons, but relates
2639    // to what chrome calls the 'page scale factor'.
2640    public boolean canZoomOut() {
2641        final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2642                - mRenderCoordinates.getMinPageScaleFactor();
2643        return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2644    }
2645
2646    /**
2647     * Zooms in the ContentViewCore by 25% (or less if that would result in
2648     * zooming in more than possible).
2649     *
2650     * @return True if there was a zoom change, false otherwise.
2651     */
2652    // This method uses the term 'zoom' for legacy reasons, but relates
2653    // to what chrome calls the 'page scale factor'.
2654    public boolean zoomIn() {
2655        if (!canZoomIn()) {
2656            return false;
2657        }
2658        return zoomByDelta(1.25f);
2659    }
2660
2661    /**
2662     * Zooms out the ContentViewCore by 20% (or less if that would result in
2663     * zooming out more than possible).
2664     *
2665     * @return True if there was a zoom change, false otherwise.
2666     */
2667    // This method uses the term 'zoom' for legacy reasons, but relates
2668    // to what chrome calls the 'page scale factor'.
2669    public boolean zoomOut() {
2670        if (!canZoomOut()) {
2671            return false;
2672        }
2673        return zoomByDelta(0.8f);
2674    }
2675
2676    /**
2677     * Resets the zoom factor of the ContentViewCore.
2678     *
2679     * @return True if there was a zoom change, false otherwise.
2680     */
2681    // This method uses the term 'zoom' for legacy reasons, but relates
2682    // to what chrome calls the 'page scale factor'.
2683    public boolean zoomReset() {
2684        // The page scale factor is initialized to mNativeMinimumScale when
2685        // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2686        if (!canZoomOut()) return false;
2687        return zoomByDelta(
2688                mRenderCoordinates.getMinPageScaleFactor()
2689                        / mRenderCoordinates.getPageScaleFactor());
2690    }
2691
2692    private boolean zoomByDelta(float delta) {
2693        if (mNativeContentViewCore == 0) {
2694            return false;
2695        }
2696
2697        long timeMs = System.currentTimeMillis();
2698        int xPix = getViewportWidthPix() / 2;
2699        int yPix = getViewportHeightPix() / 2;
2700
2701        getContentViewGestureHandler().pinchBegin(timeMs, xPix, yPix);
2702        getContentViewGestureHandler().pinchBy(timeMs, xPix, yPix, delta);
2703        getContentViewGestureHandler().pinchEnd(timeMs);
2704
2705        return true;
2706    }
2707
2708    /**
2709     * Invokes the graphical zoom picker widget for this ContentView.
2710     */
2711    @Override
2712    public void invokeZoomPicker() {
2713        mZoomControlsDelegate.invokeZoomPicker();
2714    }
2715
2716    /**
2717     * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2718     * and automatically pass in {@link JavascriptInterface} as the required annotation.
2719     *
2720     * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2721     *               values are ignored.
2722     * @param name   The name used to expose the instance in JavaScript.
2723     */
2724    public void addJavascriptInterface(Object object, String name) {
2725        addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2726    }
2727
2728    /**
2729     * This method injects the supplied Java object into the ContentViewCore.
2730     * The object is injected into the JavaScript context of the main frame,
2731     * using the supplied name. This allows the Java object to be accessed from
2732     * JavaScript. Note that that injected objects will not appear in
2733     * JavaScript until the page is next (re)loaded. For example:
2734     * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2735     * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2736     * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2737     * <p><strong>IMPORTANT:</strong>
2738     * <ul>
2739     * <li> addJavascriptInterface() can be used to allow JavaScript to control
2740     * the host application. This is a powerful feature, but also presents a
2741     * security risk. Use of this method in a ContentViewCore containing
2742     * untrusted content could allow an attacker to manipulate the host
2743     * application in unintended ways, executing Java code with the permissions
2744     * of the host application. Use extreme care when using this method in a
2745     * ContentViewCore which could contain untrusted content. Particular care
2746     * should be taken to avoid unintentional access to inherited methods, such
2747     * as {@link Object#getClass()}. To prevent access to inherited methods,
2748     * pass an annotation for {@code requiredAnnotation}.  This will ensure
2749     * that only methods with {@code requiredAnnotation} are exposed to the
2750     * Javascript layer.  {@code requiredAnnotation} will be passed to all
2751     * subsequently injected Java objects if any methods return an object.  This
2752     * means the same restrictions (or lack thereof) will apply.  Alternatively,
2753     * {@link #addJavascriptInterface(Object, String)} can be called, which
2754     * automatically uses the {@link JavascriptInterface} annotation.
2755     * <li> JavaScript interacts with Java objects on a private, background
2756     * thread of the ContentViewCore. Care is therefore required to maintain
2757     * thread safety.</li>
2758     * </ul></p>
2759     *
2760     * @param object             The Java object to inject into the
2761     *                           ContentViewCore's JavaScript context. Null
2762     *                           values are ignored.
2763     * @param name               The name used to expose the instance in
2764     *                           JavaScript.
2765     * @param requiredAnnotation Restrict exposed methods to ones with this
2766     *                           annotation.  If {@code null} all methods are
2767     *                           exposed.
2768     *
2769     */
2770    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2771            Class<? extends Annotation> requiredAnnotation) {
2772        if (mNativeContentViewCore != 0 && object != null) {
2773            mJavaScriptInterfaces.put(name, object);
2774            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
2775                    mRetainedJavaScriptObjects);
2776        }
2777    }
2778
2779    /**
2780     * Removes a previously added JavaScript interface with the given name.
2781     *
2782     * @param name The name of the interface to remove.
2783     */
2784    public void removeJavascriptInterface(String name) {
2785        mJavaScriptInterfaces.remove(name);
2786        if (mNativeContentViewCore != 0) {
2787            nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2788        }
2789    }
2790
2791    /**
2792     * Return the current scale of the ContentView.
2793     * @return The current page scale factor.
2794     */
2795    public float getScale() {
2796        return mRenderCoordinates.getPageScaleFactor();
2797    }
2798
2799    /**
2800     * If the view is ready to draw contents to the screen. In hardware mode,
2801     * the initialization of the surface texture may not occur until after the
2802     * view has been added to the layout. This method will return {@code true}
2803     * once the texture is actually ready.
2804     */
2805    public boolean isReady() {
2806        return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2807    }
2808
2809    @CalledByNative
2810    private void startContentIntent(String contentUrl) {
2811        getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2812    }
2813
2814    @Override
2815    public void onAccessibilityStateChanged(boolean enabled) {
2816        setAccessibilityState(enabled);
2817    }
2818
2819    /**
2820     * Determines whether or not this ContentViewCore can handle this accessibility action.
2821     * @param action The action to perform.
2822     * @return Whether or not this action is supported.
2823     */
2824    public boolean supportsAccessibilityAction(int action) {
2825        return mAccessibilityInjector.supportsAccessibilityAction(action);
2826    }
2827
2828    /**
2829     * Attempts to perform an accessibility action on the web content.  If the accessibility action
2830     * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2831     * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2832     * Otherwise the return value from this method should be used.
2833     * @param action The action to perform.
2834     * @param arguments Optional action arguments.
2835     * @return Whether the action was performed or {@code null} if the call should be delegated to
2836     *         the super {@link View} class.
2837     */
2838    public boolean performAccessibilityAction(int action, Bundle arguments) {
2839        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2840            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2841        }
2842
2843        return false;
2844    }
2845
2846    /**
2847     * Set the BrowserAccessibilityManager, used for native accessibility
2848     * (not script injection). This is only set when system accessibility
2849     * has been enabled.
2850     * @param manager The new BrowserAccessibilityManager.
2851     */
2852    public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2853        mBrowserAccessibilityManager = manager;
2854    }
2855
2856    /**
2857     * Get the BrowserAccessibilityManager, used for native accessibility
2858     * (not script injection). This will return null when system accessibility
2859     * is not enabled.
2860     * @return This view's BrowserAccessibilityManager.
2861     */
2862    public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2863        return mBrowserAccessibilityManager;
2864    }
2865
2866    /**
2867     * If native accessibility (not script injection) is enabled, and if this is
2868     * running on JellyBean or later, returns an AccessibilityNodeProvider that
2869     * implements native accessibility for this view. Returns null otherwise.
2870     * Lazily initializes native accessibility here if it's allowed.
2871     * @return The AccessibilityNodeProvider, if available, or null otherwise.
2872     */
2873    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2874        if (mBrowserAccessibilityManager != null) {
2875            return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2876        }
2877
2878        if (mNativeAccessibilityAllowed &&
2879                !mNativeAccessibilityEnabled &&
2880                mNativeContentViewCore != 0 &&
2881                Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2882            mNativeAccessibilityEnabled = true;
2883            nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2884        }
2885
2886        return null;
2887    }
2888
2889    /**
2890     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2891     */
2892    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2893        // Note: this is only used by the script-injecting accessibility code.
2894        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2895    }
2896
2897    /**
2898     * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2899     */
2900    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2901        // Note: this is only used by the script-injecting accessibility code.
2902        event.setClassName(this.getClass().getName());
2903
2904        // Identify where the top-left of the screen currently points to.
2905        event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2906        event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2907
2908        // The maximum scroll values are determined by taking the content dimensions and
2909        // subtracting off the actual dimensions of the ChromeView.
2910        int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2911        int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2912        event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2913
2914        // Setting the maximum scroll values requires API level 15 or higher.
2915        final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2916        if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2917            event.setMaxScrollX(maxScrollXPix);
2918            event.setMaxScrollY(maxScrollYPix);
2919        }
2920    }
2921
2922    /**
2923     * Returns whether accessibility script injection is enabled on the device
2924     */
2925    public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2926        try {
2927            if (!mContentSettings.getJavaScriptEnabled()) {
2928                return false;
2929            }
2930
2931            int result = getContext().checkCallingOrSelfPermission(
2932                    android.Manifest.permission.INTERNET);
2933            if (result != PackageManager.PERMISSION_GRANTED) {
2934                return false;
2935            }
2936
2937            Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2938            field.setAccessible(true);
2939            String accessibilityScriptInjection = (String) field.get(null);
2940            ContentResolver contentResolver = getContext().getContentResolver();
2941
2942            if (mAccessibilityScriptInjectionObserver == null) {
2943                ContentObserver contentObserver = new ContentObserver(new Handler()) {
2944                    public void onChange(boolean selfChange, Uri uri) {
2945                        setAccessibilityState(mAccessibilityManager.isEnabled());
2946                    }
2947                };
2948                contentResolver.registerContentObserver(
2949                    Settings.Secure.getUriFor(accessibilityScriptInjection),
2950                    false,
2951                    contentObserver);
2952                mAccessibilityScriptInjectionObserver = contentObserver;
2953            }
2954
2955            return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2956        } catch (NoSuchFieldException e) {
2957        } catch (IllegalAccessException e) {
2958        }
2959        return false;
2960    }
2961
2962    /**
2963     * Returns whether or not accessibility injection is being used.
2964     */
2965    public boolean isInjectingAccessibilityScript() {
2966        return mAccessibilityInjector.accessibilityIsAvailable();
2967    }
2968
2969    /**
2970     * Turns browser accessibility on or off.
2971     * If |state| is |false|, this turns off both native and injected accessibility.
2972     * Otherwise, if accessibility script injection is enabled, this will enable the injected
2973     * accessibility scripts. Native accessibility is enabled on demand.
2974     */
2975    public void setAccessibilityState(boolean state) {
2976        if (!state) {
2977            setInjectedAccessibility(false);
2978            return;
2979        }
2980
2981        if (isDeviceAccessibilityScriptInjectionEnabled()) {
2982            setInjectedAccessibility(true);
2983            return;
2984        }
2985
2986        mNativeAccessibilityAllowed = true;
2987    }
2988
2989    /**
2990     * Enable or disable injected accessibility features
2991     */
2992    public void setInjectedAccessibility(boolean enabled) {
2993        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2994        mAccessibilityInjector.setScriptEnabled(enabled);
2995    }
2996
2997    /**
2998     * Stop any TTS notifications that are currently going on.
2999     */
3000    public void stopCurrentAccessibilityNotifications() {
3001        mAccessibilityInjector.onPageLostFocus();
3002    }
3003
3004    /**
3005     * Inform WebKit that Fullscreen mode has been exited by the user.
3006     */
3007    public void exitFullscreen() {
3008        nativeExitFullscreen(mNativeContentViewCore);
3009    }
3010
3011    /**
3012     * Changes whether hiding the top controls is enabled.
3013     *
3014     * @param enableHiding Whether hiding the top controls should be enabled or not.
3015     * @param enableShowing Whether showing the top controls should be enabled or not.
3016     * @param animate Whether the transition should be animated or not.
3017     */
3018    public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
3019            boolean animate) {
3020        nativeUpdateTopControlsState(mNativeContentViewCore, enableHiding, enableShowing, animate);
3021    }
3022
3023    /**
3024     * Callback factory method for nativeGetNavigationHistory().
3025     */
3026    @CalledByNative
3027    private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
3028            String originalUrl, String title, Bitmap favicon) {
3029        NavigationEntry entry = new NavigationEntry(
3030                index, url, virtualUrl, originalUrl, title, favicon);
3031        ((NavigationHistory) history).addEntry(entry);
3032    }
3033
3034    /**
3035     * Get a copy of the navigation history of the view.
3036     */
3037    public NavigationHistory getNavigationHistory() {
3038        NavigationHistory history = new NavigationHistory();
3039        int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
3040        history.setCurrentEntryIndex(currentIndex);
3041        return history;
3042    }
3043
3044    @Override
3045    public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
3046        NavigationHistory history = new NavigationHistory();
3047        nativeGetDirectedNavigationHistory(mNativeContentViewCore, history, isForward, itemLimit);
3048        return history;
3049    }
3050
3051    /**
3052     * @return The original request URL for the current navigation entry, or null if there is no
3053     *         current entry.
3054     */
3055    public String getOriginalUrlForActiveNavigationEntry() {
3056        return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
3057    }
3058
3059    /**
3060     * @return The cached copy of render positions and scales.
3061     */
3062    public RenderCoordinates getRenderCoordinates() {
3063        return mRenderCoordinates;
3064    }
3065
3066    @CalledByNative
3067    private static Rect createRect(int x, int y, int right, int bottom) {
3068        return new Rect(x, y, right, bottom);
3069    }
3070
3071    public void attachExternalVideoSurface(int playerId, Surface surface) {
3072        if (mNativeContentViewCore != 0) {
3073            nativeAttachExternalVideoSurface(mNativeContentViewCore, playerId, surface);
3074        }
3075    }
3076
3077    public void detachExternalVideoSurface(int playerId) {
3078        if (mNativeContentViewCore != 0) {
3079            nativeDetachExternalVideoSurface(mNativeContentViewCore, playerId);
3080        }
3081    }
3082
3083    private boolean onAnimate(long frameTimeMicros) {
3084        if (mNativeContentViewCore == 0) return false;
3085        return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
3086    }
3087
3088    private void animateIfNecessary(long frameTimeMicros) {
3089        if (mNeedAnimate) {
3090            mNeedAnimate = onAnimate(frameTimeMicros);
3091            if (!mNeedAnimate) removeVSyncSubscriber();
3092        }
3093    }
3094
3095    @CalledByNative
3096    private void notifyExternalSurface(
3097            int playerId, boolean isRequest, float x, float y, float width, float height) {
3098        if (isRequest) getContentViewClient().onExternalVideoSurfaceRequested(playerId);
3099        getContentViewClient().onGeometryChanged(playerId, new RectF(x, y, x + width, y + height));
3100    }
3101
3102    /**
3103     * Offer a subset of gesture events to the embedding View,
3104     * primarily for WebView compatibility.
3105     *
3106     * @param type The type of the event.
3107     *
3108     * @return true if the embedder handled the event.
3109     */
3110    private boolean offerGestureToEmbedder(int type) {
3111        if (type == ContentViewGestureHandler.GESTURE_LONG_PRESS) {
3112            return mContainerView.performLongClick();
3113        }
3114        return false;
3115    }
3116
3117    private native int nativeInit(boolean hardwareAccelerated, int webContentsPtr,
3118            int viewAndroidPtr, int windowAndroidPtr);
3119
3120    @CalledByNative
3121    private ContentVideoViewClient getContentVideoViewClient() {
3122        return mContentViewClient.getContentVideoViewClient();
3123    }
3124
3125    private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl);
3126
3127    private native void nativeLoadUrl(
3128            int nativeContentViewCoreImpl,
3129            String url,
3130            int loadUrlType,
3131            int transitionType,
3132            int uaOverrideOption,
3133            String extraHeaders,
3134            byte[] postData,
3135            String baseUrlForDataUrl,
3136            String virtualUrlForDataUrl,
3137            boolean canLoadLocalResources);
3138
3139    private native String nativeGetURL(int nativeContentViewCoreImpl);
3140
3141    private native String nativeGetTitle(int nativeContentViewCoreImpl);
3142
3143    private native void nativeShowInterstitialPage(
3144            int nativeContentViewCoreImpl, String url, int nativeInterstitialPageDelegateAndroid);
3145    private native boolean nativeIsShowingInterstitialPage(int nativeContentViewCoreImpl);
3146
3147    private native boolean nativeIsIncognito(int nativeContentViewCoreImpl);
3148
3149    // Returns true if the native side crashed so that java side can draw a sad tab.
3150    private native boolean nativeCrashed(int nativeContentViewCoreImpl);
3151
3152    private native void nativeSetFocus(int nativeContentViewCoreImpl, boolean focused);
3153
3154    private native void nativeSendOrientationChangeEvent(
3155            int nativeContentViewCoreImpl, int orientation);
3156
3157    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3158    private native boolean nativeSendTouchEvent(
3159            int nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts);
3160
3161    private native int nativeSendMouseMoveEvent(
3162            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3163
3164    private native int nativeSendMouseWheelEvent(
3165            int nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3166
3167    private native void nativeScrollBegin(
3168            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3169
3170    private native void nativeScrollEnd(int nativeContentViewCoreImpl, long timeMs);
3171
3172    private native void nativeScrollBy(
3173            int nativeContentViewCoreImpl, long timeMs, float x, float y,
3174            float deltaX, float deltaY);
3175
3176    private native void nativeFlingStart(
3177            int nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3178
3179    private native void nativeFlingCancel(int nativeContentViewCoreImpl, long timeMs);
3180
3181    private native void nativeSingleTap(
3182            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3183
3184    private native void nativeSingleTapUnconfirmed(
3185            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3186
3187    private native void nativeShowPressState(
3188            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3189
3190    private native void nativeShowPressCancel(
3191            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3192
3193    private native void nativeDoubleTap(
3194            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3195
3196    private native void nativeLongPress(
3197            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3198
3199    private native void nativeLongTap(
3200            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3201
3202    private native void nativePinchBegin(
3203            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3204
3205    private native void nativePinchEnd(int nativeContentViewCoreImpl, long timeMs);
3206
3207    private native void nativePinchBy(int nativeContentViewCoreImpl, long timeMs,
3208            float anchorX, float anchorY, float deltaScale);
3209
3210    private native void nativeSelectBetweenCoordinates(
3211            int nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3212
3213    private native void nativeMoveCaret(int nativeContentViewCoreImpl, float x, float y);
3214
3215    private native boolean nativeCanGoBack(int nativeContentViewCoreImpl);
3216    private native boolean nativeCanGoForward(int nativeContentViewCoreImpl);
3217    private native boolean nativeCanGoToOffset(int nativeContentViewCoreImpl, int offset);
3218    private native void nativeGoBack(int nativeContentViewCoreImpl);
3219    private native void nativeGoForward(int nativeContentViewCoreImpl);
3220    private native void nativeGoToOffset(int nativeContentViewCoreImpl, int offset);
3221    private native void nativeGoToNavigationIndex(int nativeContentViewCoreImpl, int index);
3222
3223    private native void nativeStopLoading(int nativeContentViewCoreImpl);
3224
3225    private native void nativeReload(int nativeContentViewCoreImpl);
3226
3227    private native void nativeCancelPendingReload(int nativeContentViewCoreImpl);
3228
3229    private native void nativeContinuePendingReload(int nativeContentViewCoreImpl);
3230
3231    private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices);
3232
3233    private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
3234    private native void nativeUndoScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
3235    private native boolean nativeNeedsReload(int nativeContentViewCoreImpl);
3236
3237    private native void nativeClearHistory(int nativeContentViewCoreImpl);
3238
3239    private native void nativeEvaluateJavaScript(int nativeContentViewCoreImpl,
3240            String script, JavaScriptCallback callback, boolean startRenderer);
3241
3242    private native int nativeGetNativeImeAdapter(int nativeContentViewCoreImpl);
3243
3244    private native int nativeGetCurrentRenderProcessId(int nativeContentViewCoreImpl);
3245
3246    private native int nativeGetBackgroundColor(int nativeContentViewCoreImpl);
3247
3248    private native void nativeOnShow(int nativeContentViewCoreImpl);
3249    private native void nativeOnHide(int nativeContentViewCoreImpl);
3250
3251    private native void nativeSetUseDesktopUserAgent(int nativeContentViewCoreImpl,
3252            boolean enabled, boolean reloadOnChange);
3253    private native boolean nativeGetUseDesktopUserAgent(int nativeContentViewCoreImpl);
3254
3255    private native void nativeClearSslPreferences(int nativeContentViewCoreImpl);
3256
3257    private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object,
3258            String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
3259
3260    private native void nativeRemoveJavascriptInterface(int nativeContentViewCoreImpl, String name);
3261
3262    private native int nativeGetNavigationHistory(int nativeContentViewCoreImpl, Object context);
3263    private native void nativeGetDirectedNavigationHistory(int nativeContentViewCoreImpl,
3264            Object context, boolean isForward, int maxEntries);
3265    private native String nativeGetOriginalUrlForActiveNavigationEntry(
3266            int nativeContentViewCoreImpl);
3267
3268    private native void nativeUpdateVSyncParameters(int nativeContentViewCoreImpl,
3269            long timebaseMicros, long intervalMicros);
3270
3271    private native void nativeOnVSync(int nativeContentViewCoreImpl, long frameTimeMicros);
3272
3273    private native boolean nativeOnAnimate(int nativeContentViewCoreImpl, long frameTimeMicros);
3274
3275    private native boolean nativePopulateBitmapFromCompositor(int nativeContentViewCoreImpl,
3276            Bitmap bitmap);
3277
3278    private native void nativeWasResized(int nativeContentViewCoreImpl);
3279
3280    private native boolean nativeIsRenderWidgetHostViewReady(int nativeContentViewCoreImpl);
3281
3282    private native void nativeExitFullscreen(int nativeContentViewCoreImpl);
3283    private native void nativeUpdateTopControlsState(int nativeContentViewCoreImpl,
3284            boolean enableHiding, boolean enableShowing, boolean animate);
3285
3286    private native void nativeShowImeIfNeeded(int nativeContentViewCoreImpl);
3287
3288    private native void nativeAttachExternalVideoSurface(
3289            int nativeContentViewCoreImpl, int playerId, Surface surface);
3290
3291    private native void nativeDetachExternalVideoSurface(
3292            int nativeContentViewCoreImpl, int playerId);
3293
3294    private native void nativeSetAccessibilityEnabled(
3295            int nativeContentViewCoreImpl, boolean enabled);
3296}
3297