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